fix: fixed upload and download for new networking

This commit is contained in:
alina 🌸 2023-08-23 22:11:42 +03:00
parent 85c43d804d
commit 4a0d6fbc88
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
30 changed files with 1014 additions and 821 deletions

View file

@ -181,6 +181,10 @@ module.exports = {
],
globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' },
parser: '@typescript-eslint/parser',
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
},
plugins: ['@typescript-eslint'],
rules: {
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules

View file

@ -27,15 +27,15 @@
"@types/node": "18.16.0",
"@types/node-forge": "1.3.2",
"@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "5.59.8",
"@typescript-eslint/parser": "5.59.8",
"@typescript-eslint/eslint-plugin": "6.4.0",
"@typescript-eslint/parser": "6.4.0",
"chai": "4.3.7",
"dotenv-flow": "3.2.0",
"eslint": "8.42.0",
"eslint": "8.47.0",
"eslint-config-prettier": "8.8.0",
"eslint-import-resolver-typescript": "3.5.5",
"eslint-import-resolver-typescript": "3.6.0",
"eslint-plugin-ascii": "1.0.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-import": "2.28.0",
"eslint-plugin-simple-import-sort": "10.0.0",
"glob": "10.2.6",
"husky": "^8.0.3",

View file

@ -64,8 +64,11 @@ async function addSingleMethod(state, fileName) {
if (
!stmt.importClause.namedBindings ||
stmt.importClause.namedBindings.kind !== ts.SyntaxKind.NamedImports
) { throwError(stmt, fileName, 'Only named imports are supported!') }
stmt.importClause.namedBindings.kind !==
ts.SyntaxKind.NamedImports
) {
throwError(stmt, fileName, 'Only named imports are supported!')
}
let module = stmt.moduleSpecifier.text
@ -131,11 +134,7 @@ async function addSingleMethod(state, fileName) {
})()
if (!isExported && !isPrivate) {
throwError(
stmt,
fileName,
'Public methods MUST be exported.',
)
throwError(stmt, fileName, 'Public methods MUST be exported.')
}
if (isExported && !checkForFlag(stmt, '@internal')) {
@ -182,16 +181,20 @@ async function addSingleMethod(state, fileName) {
)
}
const returnsExported = (stmt.body ?
ts.getLeadingCommentRanges(fileFullText, stmt.body.pos + 2) ||
(stmt.statements &&
stmt.statements.length &&
ts.getLeadingCommentRanges(
fileFullText,
stmt.statements[0].pos,
)) ||
[] :
[]
const returnsExported = (
stmt.body ?
ts.getLeadingCommentRanges(
fileFullText,
stmt.body.pos + 2,
) ||
(stmt.statements &&
stmt.statements.length &&
ts.getLeadingCommentRanges(
fileFullText,
stmt.statements[0].pos,
)) ||
[] :
[]
)
.map((range) => fileFullText.substring(range.pos, range.end))
.join('\n')
@ -275,7 +278,9 @@ async function addSingleMethod(state, fileName) {
}
async function main() {
const output = fs.createWriteStream(path.join(__dirname, '../src/client.ts'))
const output = fs.createWriteStream(
path.join(__dirname, '../src/client.ts'),
)
const state = {
imports: {},
fields: [],
@ -295,7 +300,8 @@ async function main() {
}
output.write(
'/* THIS FILE WAS AUTO-GENERATED */\n' +
'/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/unified-signatures */\n' +
'/* THIS FILE WAS AUTO-GENERATED */\n' +
"import { BaseTelegramClient, BaseTelegramClientOptions } from '@mtcute/core'\n" +
"import { tl } from '@mtcute/tl'\n",
)
@ -336,7 +342,9 @@ async function main() {
* @param name Event name
* @param handler ${updates.toSentence(type, 'full')}
*/
on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this\n`)
on(name: '${type.typeName}', handler: ((upd: ${
type.updateType
}) => void)): this\n`)
})
const printer = ts.createPrinter()
@ -406,7 +414,9 @@ on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this
it.initializer = undefined
const deleteParents = (obj) => {
if (Array.isArray(obj)) { return obj.forEach((it) => deleteParents(it)) }
if (Array.isArray(obj)) {
return obj.forEach((it) => deleteParents(it))
}
if (obj.parent) delete obj.parent
@ -455,7 +465,7 @@ on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this
for (const name of [origName, ...aliases]) {
if (!hasOverloads) {
if (!comment.match(/\/\*\*?\s*\*\//)) {
// empty comment, no need to write it
// empty comment, no need to write it
output.write(comment + '\n')
}
@ -465,18 +475,14 @@ on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this
}
if (!overload) {
classContents.push(
`${name} = ${origName}`,
)
classContents.push(`${name} = ${origName}`)
}
}
},
)
output.write('}\n')
output.write(
'\nexport class TelegramClient extends BaseTelegramClient {\n',
)
output.write('\nexport class TelegramClient extends BaseTelegramClient {\n')
state.fields.forEach(({ code }) => output.write(`protected ${code}\n`))
@ -501,10 +507,9 @@ on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this
await fs.promises.writeFile(targetFile, fullSource)
// fix using eslint
require('child_process').execSync(
`pnpm exec eslint --fix ${targetFile}`,
{ stdio: 'inherit' },
)
require('child_process').execSync(`pnpm exec eslint --fix ${targetFile}`, {
stdio: 'inherit',
})
}
main().catch(console.error)

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
/* THIS FILE WAS AUTO-GENERATED */
import { Readable } from 'stream'
@ -8,7 +9,6 @@ import {
Deque,
MaybeArray,
MaybeAsync,
SessionConnection,
SortedLinkedList,
} from '@mtcute/core'
import { ConditionVariable } from '@mtcute/core/src/utils/condition-variable'
@ -1908,14 +1908,15 @@ export interface TelegramClient extends BaseTelegramClient {
/**
* Total file size. Automatically inferred for Buffer, File and local files.
*
* When using with streams, if `fileSize` is not passed, the entire file is
* first loaded into memory to determine file size, and used as a Buffer later.
* This might be a major performance bottleneck, so be sure to provide file size
* when using streams and file size is known (which often is the case).
*/
fileSize?: number
/**
* If the file size is unknown, you can provide an estimate,
* which will be used to determine appropriate part size.
*/
estimatedSize?: number
/**
* File MIME type. By default is automatically inferred from magic number
* If MIME can't be inferred, it defaults to `application/octet-stream`
@ -1930,11 +1931,16 @@ export interface TelegramClient extends BaseTelegramClient {
*/
partSize?: number
/**
* Number of parts to be sent in parallel per connection.
*/
requestsPerConnection?: number
/**
* Function that will be called after some part has been uploaded.
*
* @param uploaded Number of bytes already uploaded
* @param total Total file size
* @param total Total file size, if known
*/
progressCallback?: (uploaded: number, total: number) => void
}): Promise<UploadedFile>
@ -4019,7 +4025,6 @@ export class TelegramClient extends BaseTelegramClient {
protected _selfUsername: string | null
protected _pendingConversations: Record<number, Conversation[]>
protected _hasConversations: boolean
protected _downloadConnections: Record<number, SessionConnection>
protected _parseModes: Record<string, IMessageEntityParser>
protected _defaultParseMode: string | null
protected _updatesLoopActive: boolean
@ -4054,7 +4059,6 @@ export class TelegramClient extends BaseTelegramClient {
this.log.prefix = '[USER N/A] '
this._pendingConversations = {}
this._hasConversations = false
this._downloadConnections = {}
this._parseModes = {}
this._defaultParseMode = null
this._updatesLoopActive = false

View file

@ -2,12 +2,7 @@
import { Readable } from 'stream'
// @copy
import {
AsyncLock,
MaybeArray,
MaybeAsync,
SessionConnection,
} from '@mtcute/core'
import { AsyncLock, MaybeArray, MaybeAsync } from '@mtcute/core'
// @copy
import { Logger } from '@mtcute/core/src/utils/logger'
// @copy

View file

@ -155,8 +155,9 @@ export async function start(
me.isBot,
)
// todo where is this._disableUpdates?
if (!false) {
this.network.setIsPremium(me.isPremium)
if (!this.network.params.disableUpdates) {
this._catchUpChannels = Boolean(params.catchUp)
if (!params.catchUp) {
@ -176,14 +177,18 @@ export async function start(
if (!(e instanceof tl.errors.AuthKeyUnregisteredError)) throw e
}
if (!params.phone && !params.botToken) { throw new MtArgumentError('Neither phone nor bot token were provided') }
if (!params.phone && !params.botToken) {
throw new MtArgumentError('Neither phone nor bot token were provided')
}
let phone = params.phone ? await resolveMaybeDynamic(params.phone) : null
if (phone) {
phone = normalizePhoneNumber(phone)
if (!params.code) { throw new MtArgumentError('You must pass `code` to use `phone`') }
if (!params.code) {
throw new MtArgumentError('You must pass `code` to use `phone`')
}
} else {
const botToken = params.botToken ?
await resolveMaybeDynamic(params.botToken) :

View file

@ -1,13 +0,0 @@
import { SessionConnection } from '@mtcute/core'
import { TelegramClient } from '../../client'
// @extension
interface FilesExtension {
_downloadConnections: Record<number, SessionConnection>
}
// @initialize
function _initializeFiles(this: TelegramClient): void {
this._downloadConnections = {}
}

View file

@ -1,3 +1,4 @@
import { ConditionVariable, ConnectionKind } from '@mtcute/core'
import {
fileIdToInputFileLocation,
fileIdToInputWebFileLocation,
@ -10,9 +11,16 @@ import {
FileDownloadParameters,
FileLocation,
MtArgumentError,
MtUnsupportedError,
} from '../../types'
import { determinePartSize } from '../../utils/file-utils'
// small files (less than 128 kb) are downloaded using the "downloadSmall" pool
// furthermore, if the file is small and is located on our main DC, it will be downloaded
// using the current main connection
const SMALL_FILE_MAX_SIZE = 131072
const REQUESTS_PER_CONNECTION = 3 // some arbitrary magic value that seems to work best
/**
* Download a file and return it as an iterable, which yields file contents
* in chunks of a given size. Order of the chunks is guaranteed to be
@ -25,16 +33,6 @@ export async function* downloadAsIterable(
this: TelegramClient,
params: FileDownloadParameters,
): AsyncIterableIterator<Buffer> {
const partSizeKb =
params.partSize ??
(params.fileSize ? determinePartSize(params.fileSize) : 64)
if (partSizeKb % 4 !== 0) {
throw new MtArgumentError(
`Invalid part size: ${partSizeKb}. Must be divisible by 4.`,
)
}
const offset = params.offset ?? 0
if (offset % 4096 !== 0) {
@ -77,91 +75,151 @@ export async function* downloadAsIterable(
// we will receive a FileMigrateError in case this is invalid
if (!dcId) dcId = this._defaultDc.id
const partSizeKb =
params.partSize ?? (fileSize ? determinePartSize(fileSize) : 64)
if (partSizeKb % 4 !== 0) {
throw new MtArgumentError(
`Invalid part size: ${partSizeKb}. Must be divisible by 4.`,
)
}
const chunkSize = partSizeKb * 1024
const limit =
params.limit ??
// derive limit from chunk size, file size and offset
(fileSize ?
~~((fileSize + chunkSize - offset - 1) / chunkSize) :
// we will receive an error when we have reached the end anyway
Infinity)
let limitBytes = params.limit ?? fileSize ?? Infinity
if (limitBytes === 0) return
// fixme
throw new Error('TODO')
let numChunks =
limitBytes === Infinity ?
Infinity :
~~((limitBytes + chunkSize - offset - 1) / chunkSize)
// let connection = this._downloadConnections[dcId]
let nextChunkIdx = 0
let nextWorkerChunkIdx = 0
const nextChunkCv = new ConditionVariable()
const buffer: Record<number, Buffer> = {}
// if (!connection) {
// connection = await this.createAdditionalConnection(dcId)
// this._downloadConnections[dcId] = connection
// }
//
// const requestCurrent = async (): Promise<Buffer> => {
// let result:
// | tl.RpcCallReturn['upload.getFile']
// | tl.RpcCallReturn['upload.getWebFile']
//
// try {
// result = await this.call(
// {
// _: isWeb ? 'upload.getWebFile' : 'upload.getFile',
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
// location: location as any,
// offset,
// limit: chunkSize,
// },
// { connection },
// )
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
// } catch (e: any) {
// if (e.constructor === tl.errors.FileMigrateXError) {
// connection = this._downloadConnections[e.new_dc]
//
// if (!connection) {
// connection = await this.createAdditionalConnection(e.new_dc)
// this._downloadConnections[e.new_dc] = connection
// }
//
// return requestCurrent()
// } else if (e.constructor === tl.errors.FilerefUpgradeNeededError) {
// // todo: implement someday
// // see: https://github.com/LonamiWebs/Telethon/blob/0e8bd8248cc649637b7c392616887c50986427a0/telethon/client/downloads.py#L99
// throw new MtUnsupportedError('File ref expired!')
// } else throw e
// }
//
// if (result._ === 'upload.fileCdnRedirect') {
// // we shouldnt receive them since cdnSupported is not set in the getFile request.
// // also, i couldnt find any media that would be downloaded from cdn, so even if
// // i implemented that, i wouldnt be able to test that, so :shrug:
// throw new MtUnsupportedError(
// 'Received CDN redirect, which is not supported (yet)',
// )
// }
//
// if (
// result._ === 'upload.webFile' &&
// result.size &&
// limit === Infinity
// ) {
// limit = result.size
// }
//
// return result.bytes
// }
//
// for (let i = 0; i < limit; i++) {
// const buf = await requestCurrent()
//
// if (buf.length === 0) {
// // we've reached the end
// return
// }
//
// yield buf
// offset += chunkSize
//
// params.progressCallback?.(offset, limit)
// }
const isSmall = fileSize && fileSize <= SMALL_FILE_MAX_SIZE
let connectionKind: ConnectionKind
if (isSmall) {
connectionKind =
dcId === this.network.getPrimaryDcId() ? 'main' : 'downloadSmall'
} else {
connectionKind = 'download'
}
const poolSize = this.network.getPoolSize(connectionKind, dcId)
this.log.debug(
'Downloading file of size %d from dc %d using %s connection pool (pool size: %d)',
limitBytes,
dcId,
connectionKind,
poolSize,
)
const downloadChunk = async (
chunk = nextWorkerChunkIdx++,
): Promise<void> => {
let result:
| tl.RpcCallReturn['upload.getFile']
| tl.RpcCallReturn['upload.getWebFile']
try {
result = await this.call(
{
_: isWeb ? 'upload.getWebFile' : 'upload.getFile',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
location: location as any,
offset: chunkSize * chunk,
limit: chunkSize,
},
{ dcId, kind: connectionKind },
)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
if (e.constructor === tl.errors.FileMigrateXError) {
dcId = e.new_dc
return downloadChunk(chunk)
} else if (e.constructor === tl.errors.FilerefUpgradeNeededError) {
// todo: implement someday
// see: https://github.com/LonamiWebs/Telethon/blob/0e8bd8248cc649637b7c392616887c50986427a0/telethon/client/downloads.py#L99
throw new MtUnsupportedError('File ref expired!')
} else throw e
}
if (result._ === 'upload.fileCdnRedirect') {
// we shouldnt receive them since cdnSupported is not set in the getFile request.
// also, i couldnt find any media that would be downloaded from cdn, so even if
// i implemented that, i wouldnt be able to test that, so :shrug:
throw new MtUnsupportedError(
'Received CDN redirect, which is not supported (yet)',
)
}
if (
result._ === 'upload.webFile' &&
result.size &&
limitBytes === Infinity
) {
limitBytes = result.size
numChunks = ~~((limitBytes + chunkSize - offset - 1) / chunkSize)
}
buffer[chunk] = result.bytes
if (chunk === nextChunkIdx) {
nextChunkCv.notify()
}
if (
nextWorkerChunkIdx < numChunks &&
result.bytes.length === chunkSize
) {
return downloadChunk()
}
}
let error: unknown = undefined
Promise.all(
Array.from(
{ length: Math.min(poolSize * REQUESTS_PER_CONNECTION, numChunks) },
downloadChunk,
),
)
.catch((e) => {
this.log.debug('download workers errored: %s', e.message)
error = e
nextChunkCv.notify()
})
.then(() => {
this.log.debug('download workers finished')
})
let position = offset
while (position < limitBytes) {
await nextChunkCv.wait()
if (error) throw error
while (nextChunkIdx in buffer) {
const buf = buffer[nextChunkIdx]
delete buffer[nextChunkIdx]
position += buf.length
params.progressCallback?.(position, limitBytes)
yield buf
nextChunkIdx++
if (buf.length < chunkSize) {
// we received the last chunk
return
}
}
}
}

View file

@ -3,7 +3,7 @@ import { fromBuffer as fileTypeFromBuffer } from 'file-type'
import type { ReadStream } from 'fs'
import { Readable } from 'stream'
import { randomLong } from '@mtcute/core'
import { AsyncLock, randomLong } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { TelegramClient } from '../../client'
@ -13,7 +13,6 @@ import {
bufferToStream,
convertWebStreamToNodeReadable,
readBytesFromStream,
readStreamUntilEnd,
} from '../../utils/stream-utils'
let fs: any = null
@ -29,6 +28,14 @@ const OVERRIDE_MIME: Record<string, string> = {
'audio/opus': 'audio/ogg',
}
// small files (less than 128 kb) are uploaded using the current connection and not the "upload" pool
const SMALL_FILE_MAX_SIZE = 131072
const BIG_FILE_MIN_SIZE = 10485760 // files >10 MB are considered "big"
const DEFAULT_FILE_NAME = 'unnamed'
const REQUESTS_PER_CONNECTION = 3
const MAX_PART_COUNT = 4000 // 512 kb * 4000 = 2000 MiB
const MAX_PART_COUNT_PREMIUM = 8000 // 512 kb * 8000 = 4000 MiB
/**
* Upload a file to Telegram servers, without actually
* sending a message anywhere. Useful when an `InputFile` is required.
@ -60,14 +67,15 @@ export async function uploadFile(
/**
* Total file size. Automatically inferred for Buffer, File and local files.
*
* When using with streams, if `fileSize` is not passed, the entire file is
* first loaded into memory to determine file size, and used as a Buffer later.
* This might be a major performance bottleneck, so be sure to provide file size
* when using streams and file size is known (which often is the case).
*/
fileSize?: number
/**
* If the file size is unknown, you can provide an estimate,
* which will be used to determine appropriate part size.
*/
estimatedSize?: number
/**
* File MIME type. By default is automatically inferred from magic number
* If MIME can't be inferred, it defaults to `application/octet-stream`
@ -82,11 +90,16 @@ export async function uploadFile(
*/
partSize?: number
/**
* Number of parts to be sent in parallel per connection.
*/
requestsPerConnection?: number
/**
* Function that will be called after some part has been uploaded.
*
* @param uploaded Number of bytes already uploaded
* @param total Total file size
* @param total Total file size, if known
*/
progressCallback?: (uploaded: number, total: number) => void
},
@ -94,7 +107,7 @@ export async function uploadFile(
// normalize params
let file = params.file
let fileSize = -1 // unknown
let fileName = 'unnamed'
let fileName = DEFAULT_FILE_NAME
let fileMime = params.fileMime
if (Buffer.isBuffer(file)) {
@ -162,12 +175,12 @@ export async function uploadFile(
}
}
if (fileName === 'unnamed') {
if (fileName === DEFAULT_FILE_NAME) {
// try to infer from url
const url = new URL(file.url)
const name = url.pathname.split('/').pop()
if (name && name.indexOf('.') > -1) {
if (name && name.includes('.')) {
fileName = name
}
}
@ -192,42 +205,88 @@ export async function uploadFile(
// set file size if not automatically inferred
if (fileSize === -1 && params.fileSize) fileSize = params.fileSize
if (fileSize === -1) {
// load the entire stream into memory
const buffer = await readStreamUntilEnd(file as Readable)
fileSize = buffer.length
file = bufferToStream(buffer)
let partSizeKb = params.partSize
if (!partSizeKb) {
if (fileSize === -1) {
partSizeKb = params.estimatedSize ?
determinePartSize(params.estimatedSize) :
64
} else {
partSizeKb = determinePartSize(fileSize)
}
}
if (!(file instanceof Readable)) {
throw new MtArgumentError('Could not convert input `file` to stream!')
}
const partSizeKb = params.partSize ?? determinePartSize(fileSize)
if (partSizeKb > 512) {
throw new MtArgumentError(`Invalid part size: ${partSizeKb}KB`)
}
const partSize = partSizeKb * 1024
const isBig = fileSize > 10485760 // 10 MB
const hash = this._crypto.createMd5()
let partCount =
fileSize === -1 ? -1 : ~~((fileSize + partSize - 1) / partSize)
const maxPartCount = this.network.params.isPremium ?
MAX_PART_COUNT_PREMIUM :
MAX_PART_COUNT
if (partCount > maxPartCount) {
throw new MtArgumentError(
`File is too large (max ${maxPartCount} parts, got ${partCount})`,
)
}
const isBig = fileSize === -1 || fileSize > BIG_FILE_MIN_SIZE
const isSmall = fileSize !== -1 && fileSize < SMALL_FILE_MAX_SIZE
const connectionKind = isSmall ? 'main' : 'upload'
const connectionPoolSize = Math.min(
this.network.getPoolSize(connectionKind),
partCount,
)
const requestsPerConnection =
params.requestsPerConnection ?? REQUESTS_PER_CONNECTION
const partCount = ~~((fileSize + partSize - 1) / partSize)
this.log.debug(
'uploading %d bytes file in %d chunks, each %d bytes',
'uploading %d bytes file in %d chunks, each %d bytes in %s connection pool of size %d',
fileSize,
partCount,
partSize,
connectionKind,
connectionPoolSize,
)
// why is the file id generated by the client?
// isn't the server supposed to generate it and handle collisions?
const fileId = randomLong()
let pos = 0
const stream = file
for (let idx = 0; idx < partCount; idx++) {
const part = await readBytesFromStream(file, partSize)
let pos = 0
let idx = 0
const lock = new AsyncLock()
const uploadNextPart = async (): Promise<void> => {
const thisIdx = idx++
let part
try {
await lock.acquire()
part = await readBytesFromStream(stream, partSize)
} finally {
lock.release()
}
if (fileSize === -1 && stream.readableEnded) {
fileSize = pos + (part?.length ?? 0)
partCount = ~~((fileSize + partSize - 1) / partSize)
this.log.debug(
'readable ended, file size = %d, part count = %d',
fileSize,
partCount,
)
}
if (!part) {
throw new MtArgumentError(
@ -236,15 +295,15 @@ export async function uploadFile(
}
if (!Buffer.isBuffer(part)) {
throw new MtArgumentError(`Part ${idx} was not a Buffer!`)
throw new MtArgumentError(`Part ${thisIdx} was not a Buffer!`)
}
if (part.length > partSize) {
throw new MtArgumentError(
`Part ${idx} had invalid size (expected ${partSize}, got ${part.length})`,
`Part ${thisIdx} had invalid size (expected ${partSize}, got ${part.length})`,
)
}
if (idx === 0 && fileMime === undefined) {
if (thisIdx === 0 && fileMime === undefined) {
const fileType = await fileTypeFromBuffer(part)
fileMime = fileType?.mime
@ -260,37 +319,43 @@ export async function uploadFile(
}
}
if (!isBig) {
// why md5 only small files?
// big files have more chance of corruption, but whatever
// also isn't integrity guaranteed by mtproto?
await hash.update(part)
}
pos += part.length
// why
const request = isBig ?
({
_: 'upload.saveBigFilePart',
fileId,
filePart: idx,
filePart: thisIdx,
fileTotalParts: partCount,
bytes: part,
} as tl.upload.RawSaveBigFilePartRequest) :
} satisfies tl.upload.RawSaveBigFilePartRequest) :
({
_: 'upload.saveFilePart',
fileId,
filePart: idx,
filePart: thisIdx,
bytes: part,
} as tl.upload.RawSaveFilePartRequest)
} satisfies tl.upload.RawSaveFilePartRequest)
const result = await this.call(request)
const result = await this.call(request, { kind: connectionKind })
if (!result) throw new Error(`Failed to upload part ${idx}`)
pos += part.length
params.progressCallback?.(pos, fileSize)
if (idx === partCount) return
return uploadNextPart()
}
await Promise.all(
Array.from(
{
length: connectionPoolSize * requestsPerConnection,
},
uploadNextPart,
),
)
let inputFile: tl.TypeInputFile
if (isBig) {
@ -306,7 +371,7 @@ export async function uploadFile(
id: fileId,
parts: partCount,
name: fileName,
md5Checksum: (await hash.digest()).toString('hex'),
md5Checksum: '', // tdlib doesn't do this, why should we?
}
}

View file

@ -558,11 +558,15 @@ async function _fetchPeersForShort(
if (
msg.replyTo._ === 'messageReplyHeader' &&
!(await fetchPeer(msg.replyTo.replyToPeerId))
) { return null }
) {
return null
}
if (
msg.replyTo._ === 'messageReplyStoryHeader' &&
!(await fetchPeer(msg.replyTo.userId))
) { return null }
) {
return null
}
}
if (msg._ !== 'messageService') {
@ -791,7 +795,7 @@ async function _fetchChannelDifference(
if (!_pts) _pts = fallbackPts
if (!_pts) {
this._updsLog.warn(
this._updsLog.debug(
'fetchChannelDifference failed for channel %d: base pts not available',
channelId,
)

View file

@ -133,8 +133,7 @@ export class Conversation {
const pending = this.client['_pendingConversations']
const idx =
pending[this._chatId].indexOf(this)
const idx = pending[this._chatId].indexOf(this)
if (idx > -1) {
// just in case
@ -143,8 +142,7 @@ export class Conversation {
if (!pending[this._chatId].length) {
delete pending[this._chatId]
}
this.client['_hasConversations'] =
Object.keys(pending).length > 0
this.client['_hasConversations'] = Object.keys(pending).length > 0
// reset pending status
this._queuedNewMessage.clear()
@ -279,6 +277,7 @@ export class Conversation {
if (timeout !== null) {
timer = setTimeout(() => {
console.log('timed out')
promise.reject(new tl.errors.TimeoutError())
this._queuedNewMessage.removeBy((it) => it.promise === promise)
}, timeout)
@ -537,7 +536,9 @@ export class Conversation {
it.promise.resolve(msg)
delete this._pendingEditMessage[msg.id]
}
})().catch((e) => this.client['_emitError'](e))
})().catch((e) => {
this.client['_emitError'](e)
})
}
private _onHistoryRead(upd: HistoryReadUpdate) {

View file

@ -4,6 +4,7 @@ import { tl } from '@mtcute/tl'
import { TelegramClient } from '../../client'
import { makeInspectable } from '../utils'
import { FileDownloadParameters } from './utils'
/**
* Information about file location.
@ -50,48 +51,61 @@ export class FileLocation {
* in chunks of a given size. Order of the chunks is guaranteed to be
* consecutive.
*
* Shorthand for `client.downloadAsIterable({ location: this })`
*
* @param params Download parameters
* @link TelegramClient.downloadAsIterable
*/
downloadIterable(): AsyncIterableIterator<Buffer> {
return this.client.downloadAsIterable({ location: this })
downloadIterable(
params?: Partial<FileDownloadParameters>,
): AsyncIterableIterator<Buffer> {
return this.client.downloadAsIterable({
...params,
location: this,
})
}
/**
* Download a file and return it as a Node readable stream,
* streaming file contents.
*
* Shorthand for `client.downloadAsStream({ location: this })`
*
* @link TelegramClient.downloadAsStream
*/
downloadStream(): Readable {
return this.client.downloadAsStream({ location: this })
downloadStream(params?: Partial<FileDownloadParameters>): Readable {
return this.client.downloadAsStream({
...params,
location: this,
})
}
/**
* Download a file and return its contents as a Buffer.
*
* Shorthand for `client.downloadAsBuffer({ location: this })`
*
* @param params File download parameters
* @link TelegramClient.downloadAsBuffer
*/
downloadBuffer(): Promise<Buffer> {
return this.client.downloadAsBuffer({ location: this })
downloadBuffer(params?: Partial<FileDownloadParameters>): Promise<Buffer> {
return this.client.downloadAsBuffer({
...params,
location: this,
})
}
/**
* Download a remote file to a local file (only for NodeJS).
* Promise will resolve once the download is complete.
*
* Shorthand for `client.downloadToFile(filename, { location: this })`
*
* @param filename Local file name
* @param params File download parameters
* @link TelegramClient.downloadToFile
*/
downloadToFile(filename: string): Promise<void> {
return this.client.downloadToFile(filename, { location: this })
downloadToFile(
filename: string,
params?: Partial<FileDownloadParameters>,
): Promise<void> {
return this.client.downloadToFile(filename, {
...params,
location: this,
fileSize: this.fileSize,
})
}
}

View file

@ -97,7 +97,7 @@ export interface FileDownloadParameters {
offset?: number
/**
* Number of chunks (!) of that given size that will be downloaded.
* Number of bytes to be downloaded.
* By default, downloads the entire file
*/
limit?: number

View file

@ -5,10 +5,9 @@ import { MtArgumentError } from '../types'
* for upload/download operations.
*/
export function determinePartSize(fileSize: number): number {
if (fileSize <= 104857600) return 128 // 100 MB
if (fileSize <= 262078465) return 128 // 200 MB
if (fileSize <= 786432000) return 256 // 750 MB
if (fileSize <= 2097152000) return 512 // 2000 MB
if (fileSize <= 4194304000) return 1024 // 4000 MB
throw new MtArgumentError('File is too large')
}

View file

@ -35,7 +35,9 @@ class NodeReadable extends Readable {
return
}
if (this.push(res.value)) {
return doRead()
doRead()
return
}
this._reading = false
this._reader.releaseLock()
@ -49,7 +51,9 @@ class NodeReadable extends Readable {
const promise = new Promise<void>((resolve) => {
this._doneReading = resolve
})
promise.then(() => this._handleDestroy(err, callback))
promise.then(() => {
this._handleDestroy(err, callback)
})
} else {
this._handleDestroy(err, callback)
}
@ -71,26 +75,6 @@ export function convertWebStreamToNodeReadable(
return new NodeReadable(webStream, opts)
}
export async function readStreamUntilEnd(stream: Readable): Promise<Buffer> {
const chunks = []
let length = 0
while (stream.readable) {
const c = await stream.read()
if (c === null) break
length += c.length
if (length > 2097152000) {
throw new Error('File is too big')
}
chunks.push(c)
}
return Buffer.concat(chunks)
}
export function bufferToStream(buf: Buffer): Readable {
return new Readable({
read() {
@ -109,15 +93,17 @@ export async function readBytesFromStream(
let res = stream.read(size)
if (!res) {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
stream.on('readable', function handler() {
res = stream.read(size)
if (res) {
stream.off('readable', handler)
stream.off('error', reject)
resolve(res)
}
})
stream.on('error', reject)
})
}

View file

@ -1,21 +0,0 @@
import { expect } from 'chai'
import { describe, it } from 'mocha'
import { Readable } from 'stream'
import { readStreamUntilEnd } from '../src/utils/stream-utils'
describe('readStreamUntilEnd', () => {
it('should read stream until end', async () => {
const stream = new Readable({
read() {
this.push(Buffer.from('aaeeff', 'hex'))
this.push(Buffer.from('ff33ee', 'hex'))
this.push(null)
},
})
expect((await readStreamUntilEnd(stream)).toString('hex')).eq(
'aaeeffff33ee',
)
})
})

View file

@ -245,7 +245,7 @@ export class BaseTelegramClient extends EventEmitter {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected _handleUpdate(update: tl.TypeUpdates): void {}
readonly log = new LogManager()
readonly log = new LogManager('client')
readonly network: NetworkManager
constructor(opts: BaseTelegramClientOptions) {
@ -451,7 +451,6 @@ export class BaseTelegramClient extends EventEmitter {
* @param factory New transport factory
*/
changeTransport(factory: TransportFactory): void {
// todo
this.network.changeTransport(factory)
}

View file

@ -1,4 +1,8 @@
export { NetworkManagerExtraParams, RpcCallOptions } from './network-manager'
export {
ConnectionKind,
NetworkManagerExtraParams,
RpcCallOptions,
} from './network-manager'
export * from './reconnection'
export * from './session-connection'
export * from './transports'

View file

@ -8,7 +8,8 @@ import {
TlWriterMap,
} from '@mtcute/tl-runtime'
import { ControllablePromise,
import {
ControllablePromise,
Deque,
getRandomInt,
ICryptoProvider,
@ -118,9 +119,18 @@ export class MtprotoSession {
pendingMessages = new LongMap<PendingMessage>()
destroySessionIdToMsgId = new LongMap<Long>()
lastPingRtt = NaN
lastPingTime = 0
lastPingMsgId = Long.ZERO
lastSessionCreatedUid = Long.ZERO
initConnectionCalled = false
authorizationPending = false
next429Timeout = 1000
current429Timeout?: NodeJS.Timeout
next429ResetTimeout?: NodeJS.Timeout
constructor(
readonly _crypto: ICryptoProvider,
readonly log: Logger,
@ -130,6 +140,15 @@ export class MtprotoSession {
this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `
}
get hasPendingMessages(): boolean {
return Boolean(
this.queuedRpc.length ||
this.queuedAcks.length ||
this.queuedStateReq.length ||
this.queuedResendReq.length,
)
}
/**
* Reset session by resetting auth key(s) and session state
*/
@ -140,7 +159,9 @@ export class MtprotoSession {
this._authKeyTempSecondary.reset()
}
clearTimeout(this.current429Timeout)
this.resetState()
this.resetLastPing(true)
}
/**
@ -221,11 +242,11 @@ export class MtprotoSession {
}
getSeqNo(isContentRelated = true): number {
let seqNo = this._seqNo * 2
let seqNo = this._seqNo
if (isContentRelated) {
seqNo += 1
this._seqNo += 1
this._seqNo += 2
}
return seqNo
@ -293,4 +314,43 @@ export class MtprotoSession {
return messageId
}
onTransportFlood(callback: () => void) {
if (this.current429Timeout) return // already waiting
// all active queries must be resent after a timeout
this.resetLastPing(true)
const timeout = this.next429Timeout
this.next429Timeout = Math.min(this.next429Timeout * 2, 32000)
clearTimeout(this.current429Timeout)
clearTimeout(this.next429ResetTimeout)
this.current429Timeout = setTimeout(() => {
this.current429Timeout = undefined
callback()
}, timeout)
this.next429ResetTimeout = setTimeout(() => {
this.next429ResetTimeout = undefined
this.next429Timeout = 1000
}, 60000)
this.log.debug(
'transport flood, waiting for %d ms before proceeding',
timeout,
)
return Date.now() + timeout
}
resetLastPing(withTime = false): void {
if (withTime) this.lastPingTime = 0
if (!this.lastPingMsgId.isZero()) {
this.pendingMessages.delete(this.lastPingMsgId)
}
this.lastPingMsgId = Long.ZERO
}
}

View file

@ -32,43 +32,48 @@ export class MultiSessionConnection extends EventEmitter {
protected _connections: SessionConnection[] = []
setCount(count: number, doUpdate = true): void {
setCount(count: number, connect = this.params.isMainConnection): void {
this._count = count
if (doUpdate) this._updateConnections(true)
this._updateConnections(connect)
}
private _updateSessions(): void {
// there are two cases
// 1. this msc is main, in which case every connection should have its own session
// 2. this msc is not main, in which case all connections should share the same session
// if (!this.params.isMainConnection) {
// // case 2
// this._log.debug(
// 'updating sessions count: %d -> 1',
// this._sessions.length,
// )
//
// if (this._sessions.length === 0) {
// this._sessions.push(
// new MtprotoSession(
// this.params.crypto,
// this._log.create('session'),
// this.params.readerMap,
// this.params.writerMap,
// ),
// )
// }
//
// // shouldn't happen, but just in case
// while (this._sessions.length > 1) {
// this._sessions.pop()!.reset()
// }
//
// return
// }
this._log.debug(
'updating sessions count: %d -> %d',
this._sessions.length,
this._count,
)
// there are two cases
// 1. this msc is main, in which case every connection should have its own session
// 2. this msc is not main, in which case all connections should share the same session
if (!this.params.isMainConnection) {
// case 2
if (this._sessions.length === 0) {
this._sessions.push(
new MtprotoSession(
this.params.crypto,
this._log.create('session'),
this.params.readerMap,
this.params.writerMap,
),
)
}
// shouldn't happen, but just in case
while (this._sessions.length > 1) {
this._sessions.pop()!.reset()
}
return
}
// case 1
if (this._sessions.length === this._count) return
@ -99,7 +104,7 @@ export class MultiSessionConnection extends EventEmitter {
}
}
private _updateConnections(active = false): void {
private _updateConnections(connect = false): void {
this._updateSessions()
if (this._connections.length === this._count) return
@ -141,9 +146,8 @@ export class MultiSessionConnection extends EventEmitter {
// create new connections
for (let i = this._connections.length; i < this._count; i++) {
const session = this.params.isMainConnection ?
this._sessions[i] :
this._sessions[0]
const session = this._sessions[i] // this.params.isMainConnection ? // :
// this._sessions[0]
const conn = new SessionConnection(
{
...this.params,
@ -185,9 +189,14 @@ export class MultiSessionConnection extends EventEmitter {
})
conn.on('usable', () => this.emit('usable', i))
conn.on('request-auth', () => this.emit('request-auth', i))
conn.on('flood-done', () => {
this._log.debug('received flood-done from connection %d', i)
this._connections.forEach((it) => it.flushWhenIdle())
})
this._connections.push(conn)
if (active) conn.connect()
if (connect) conn.connect()
}
}
@ -207,31 +216,32 @@ export class MultiSessionConnection extends EventEmitter {
stack?: string,
timeout?: number,
): Promise<tl.RpcCallReturn[T['_']]> {
if (this.params.isMainConnection) {
// find the least loaded connection
let min = Infinity
let minIdx = 0
// if (this.params.isMainConnection) {
// find the least loaded connection
let min = Infinity
let minIdx = 0
for (let i = 0; i < this._connections.length; i++) {
const conn = this._connections[i]
const total =
conn._session.queuedRpc.length +
conn._session.pendingMessages.size()
for (let i = 0; i < this._connections.length; i++) {
const conn = this._connections[i]
const total =
conn._session.queuedRpc.length +
conn._session.pendingMessages.size()
if (total < min) {
min = total
minIdx = i
}
if (total < min) {
min = total
minIdx = i
}
return this._connections[minIdx].sendRpc(request, stack, timeout)
}
return this._connections[minIdx].sendRpc(request, stack, timeout)
// }
// round-robin connections
// since they all share the same session, it doesn't matter which one we use
return this._connections[
this._nextConnection++ % this._connections.length
].sendRpc(request, stack, timeout)
// the connection chosen here will only affect the first attempt at sending
// return this._connections[
// this._nextConnection++ % this._connections.length
// ].sendRpc(request, stack, timeout)
}
connect(): void {
@ -308,4 +318,8 @@ export class MultiSessionConnection extends EventEmitter {
changeTransport(factory: TransportFactory): void {
this._connections.forEach((conn) => conn.changeTransport(factory))
}
getPoolSize(): number {
return this._connections.length
}
}

View file

@ -2,7 +2,12 @@ import { tl } from '@mtcute/tl'
import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
import { ITelegramStorage } from '../storage'
import { ICryptoProvider, Logger, sleep } from '../utils'
import {
createControllablePromise,
ICryptoProvider,
Logger,
sleep,
} from '../utils'
import { ConfigManager } from './config-manager'
import { MultiSessionConnection } from './multi-session-connection'
import { PersistentConnectionParams } from './persistent-connection'
@ -18,6 +23,16 @@ import { defaultTransportFactory, TransportFactory } from './transports'
export type ConnectionKind = 'main' | 'upload' | 'download' | 'downloadSmall'
const CLIENT_ERRORS = {
'303': 1,
'400': 1,
'401': 1,
'403': 1,
'404': 1,
'406': 1,
'420': 1,
}
/**
* Params passed into {@link NetworkManager} by {@link TelegramClient}.
* This type is intended for internal usage only.
@ -62,8 +77,9 @@ const defaultConnectionCountDelegate: ConnectionCountDelegate = (
case 'upload':
return isPremium || (dcId !== 2 && dcId !== 4) ? 8 : 4
case 'download':
return 8 // fixme isPremium ? 8 : 2
case 'downloadSmall':
return isPremium ? 8 : 2
return 2
}
}
@ -79,13 +95,14 @@ export interface NetworkManagerExtraParams {
usePfs?: boolean
/**
* Connection count for each connection kind
* Connection count for each connection kind.
* The function should be pure to avoid unexpected behavior.
*
* Defaults to TDLib logic:
* - main: handled internally, **cannot be changed here**
* - upload: if premium or dc id is other than 2 or 4, then 8, otherwise 4
* - download: if premium then 8, otherwise 2
* - downloadSmall: if premium then 8, otherwise 2
* - downloadSmall: 2
*/
connectionCount?: ConnectionCountDelegate
@ -174,7 +191,24 @@ export class DcConnectionManager {
)
download = new MultiSessionConnection(
this.__baseConnectionParams(),
// this.__baseConnectionParams(),
// fixme
{
...this.__baseConnectionParams(),
dc: {
_: 'dcOption',
ipv6: false,
mediaOnly: true,
tcpoOnly: false,
cdn: false,
static: false,
thisPortOnly: false,
id: 2,
ipAddress: '149.154.167.222',
port: 443,
secret: undefined,
},
},
this.manager._connectionCount(
'download',
this._dc.id,
@ -313,6 +347,10 @@ export class DcConnectionManager {
connection.on('request-auth', () => {
this.main.requestAuth()
})
connection.on('error', (err, conn) => {
this.manager.params._emitError(err, conn)
})
}
setIsPrimary(isPrimary: boolean): void {
@ -328,6 +366,22 @@ export class DcConnectionManager {
}
}
setIsPremium(isPremium: boolean): void {
this.upload.setCount(
this.manager._connectionCount('upload', this._dc.id, isPremium),
)
this.download.setCount(
this.manager._connectionCount('download', this._dc.id, isPremium),
)
this.downloadSmall.setCount(
this.manager._connectionCount(
'downloadSmall',
this._dc.id,
isPremium,
),
)
}
async loadKeys(): Promise<boolean> {
const permanent = await this.manager._storage.getAuthKeyFor(this.dcId)
@ -471,9 +525,9 @@ export class NetworkManager {
Promise.resolve(this._storage.getSelf()).then((self) => {
if (self?.isBot) {
// bots may receive tmpSessions, which we should respect
this.config
.update(true)
.catch((e) => this.params._emitError(e))
this.config.update(true).catch((e) => {
this.params._emitError(e)
})
}
})
})
@ -485,35 +539,54 @@ export class NetworkManager {
// this._cleanupPrimaryConnection()
// )
dc.main.on('error', (err, conn) => this.params._emitError(err, conn))
dc.loadKeys()
.catch((e) => this.params._emitError(e))
.then(() => dc.main.ensureConnected())
.catch((e) => {
this.params._emitError(e)
})
.then(() => {
dc.main.ensureConnected()
})
}
private _dcCreationPromise: Record<number, Promise<void>> = {}
async _getOtherDc(dcId: number): Promise<DcConnectionManager> {
if (!this._dcConnections[dcId]) {
if (dcId in this._dcCreationPromise) {
this._log.debug('waiting for DC %d to be created', dcId)
await this._dcCreationPromise[dcId]
return this._dcConnections[dcId]
}
const promise = createControllablePromise<void>()
this._dcCreationPromise[dcId] = promise
this._log.debug('creating new DC %d', dcId)
const dcOption = await this.config.findOption({
dcId,
allowIpv6: this.params.useIpv6,
preferIpv6: this.params.useIpv6,
allowMedia: false,
cdn: false,
})
try {
const dcOption = await this.config.findOption({
dcId,
allowIpv6: this.params.useIpv6,
preferIpv6: this.params.useIpv6,
allowMedia: true,
preferMedia: true,
cdn: false,
})
if (!dcOption) {
throw new Error(`Could not find DC ${dcId}`)
if (!dcOption) {
throw new Error(`Could not find DC ${dcId}`)
}
const dc = new DcConnectionManager(this, dcId, dcOption)
if (!(await dc.loadKeys())) {
dc.main.requestAuth()
}
this._dcConnections[dcId] = dc
promise.resolve()
} catch (e) {
promise.reject(e)
}
const dc = new DcConnectionManager(this, dcId, dcOption)
if (!(await dc.loadKeys())) {
dc.main.requestAuth()
}
this._dcConnections[dcId] = dc
}
return this._dcConnections[dcId]
@ -532,7 +605,7 @@ export class NetworkManager {
const dc = new DcConnectionManager(this, defaultDc.id, defaultDc)
this._dcConnections[defaultDc.id] = dc
await this._switchPrimaryDc(dc)
this._switchPrimaryDc(dc)
}
private async _exportAuthTo(manager: DcConnectionManager): Promise<void> {
@ -575,16 +648,28 @@ export class NetworkManager {
}
}
setIsPremium(isPremium: boolean): void {
this._log.debug('setting isPremium to %s', isPremium)
this.params.isPremium = isPremium
Object.values(this._dcConnections).forEach((dc) => {
dc.setIsPremium(isPremium)
})
}
async notifyLoggedIn(auth: tl.auth.TypeAuthorization): Promise<void> {
if (
auth._ === 'auth.authorizationSignUpRequired' ||
auth.user._ === 'userEmpty'
) { return }
) {
return
}
if (auth.tmpSessions) {
this._primaryDc?.main.setCount(auth.tmpSessions)
}
this.setIsPremium(auth.user.premium!)
await this.exportAuth()
}
@ -689,8 +774,12 @@ export class NetworkManager {
} catch (e: any) {
lastError = e
if (e instanceof tl.errors.InternalError) {
this._log.warn('Telegram is having internal issues: %s', e)
if (e.code && !(e.code in CLIENT_ERRORS)) {
this._log.warn(
'Telegram is having internal issues: %d %s, retrying',
e.code,
e.message,
)
if (e.message === 'WORKER_BUSY_TOO_LONG_RETRY') {
// according to tdlib, "it is dangerous to resend query without timeout, so use 1"
@ -769,6 +858,32 @@ export class NetworkManager {
})
}
getPoolSize(kind: ConnectionKind, dcId?: number) {
const dc = dcId ? this._dcConnections[dcId] : this._primaryDc
if (!dc) {
if (!this._primaryDc) {
throw new Error('Not connected to any DC')
}
// guess based on the provided delegate. it is most likely correct,
// but we should give actual values if possible
return this._connectionCount(
kind,
dcId ?? this._primaryDc.dcId,
this.params.isPremium,
)
}
return dc[kind].getPoolSize()
}
getPrimaryDcId() {
if (!this._primaryDc) throw new Error('Not connected to any DC')
return this._primaryDc.dcId
}
destroy(): void {
for (const dc of Object.values(this._dcConnections)) {
dc.main.destroy()

View file

@ -86,11 +86,29 @@ export abstract class PersistentConnection extends EventEmitter {
onTransportReady(): void {
// transport ready does not mean actual mtproto is ready
if (this._sendOnceConnected.length) {
this._transport.send(Buffer.concat(this._sendOnceConnected))
const sendNext = () => {
if (!this._sendOnceConnected.length) {
this.onConnected()
return
}
const data = this._sendOnceConnected.shift()!
this._transport
.send(data)
.then(sendNext)
.catch((err) => {
this.log.error('error sending queued data: %s', err)
this._sendOnceConnected.unshift(data)
})
}
sendNext()
return
}
this._sendOnceConnected = []
this.onConnected()
}
@ -125,7 +143,12 @@ export abstract class PersistentConnection extends EventEmitter {
this._consequentFails,
this._previousWait,
)
if (wait === false) return this.destroy()
if (wait === false) {
this.destroy()
return
}
this.emit('wait', wait)

View file

@ -75,19 +75,13 @@ export class SessionConnection extends PersistentConnection {
NodeJS.Timeout
][] = []
private _next429Timeout = 1000
private _current429Timeout?: NodeJS.Timeout
private _lastPingRtt = NaN
private _lastPingTime = 0
private _lastPingMsgId = Long.ZERO
private _lastSessionCreatedUid = Long.ZERO
private _usePfs = this.params.usePfs ?? false
private _isPfsBindingPending = false
private _isPfsBindingPendingInBackground = false
private _pfsUpdateTimeout?: NodeJS.Timeout
private _inactivityPendingFlush = false
private _readerMap: TlReaderMap
private _writerMap: TlWriterMap
@ -149,9 +143,7 @@ export class SessionConnection extends PersistentConnection {
reset(forever = false): void {
this._session.initConnectionCalled = false
this._resetLastPing(true)
this._flushTimer.reset()
clearTimeout(this._current429Timeout!)
if (forever) {
this.removeAllListeners()
@ -242,26 +234,15 @@ export class SessionConnection extends PersistentConnection {
this._onAllFailed(`transport error ${error.code}`)
if (error.code === 429) {
// all active queries must be resent
const timeout = this._next429Timeout
this._next429Timeout = Math.min(this._next429Timeout * 2, 16000)
clearTimeout(this._current429Timeout!)
this._current429Timeout = setTimeout(() => {
this._current429Timeout = undefined
this._flushTimer.emitNow()
}, timeout)
this.log.debug(
'transport flood, waiting for %d ms before proceeding',
timeout,
this._session.onTransportFlood(
this.emit.bind(this, 'flood-done'),
)
return
}
}
this.emit('api-error', error)
this.emit('error', error)
}
protected onConnectionUsable() {
@ -622,9 +603,7 @@ export class SessionConnection extends PersistentConnection {
// rpc_result
message.uint()
this._sendAck(messageId)
return this._onRpcResult(message)
return this._onRpcResult(messageId, message)
}
// we are safe.. i guess
@ -648,7 +627,7 @@ export class SessionConnection extends PersistentConnection {
}
const message = message_ as mtp.TlObject
this.log.verbose('received %s (msg_id: %l)', message._, messageId)
this.log.debug('received %s (msg_id: %l)', message._, messageId)
this._session.recentIncomingMsgIds.add(messageId)
switch (message._) {
@ -738,7 +717,7 @@ export class SessionConnection extends PersistentConnection {
}
}
private _onRpcResult(message: TlBinaryReader): void {
private _onRpcResult(messageId: Long, message: TlBinaryReader): void {
if (this._usable && this.params.inactivityTimeout) {
this._rescheduleInactivity()
}
@ -790,9 +769,12 @@ export class SessionConnection extends PersistentConnection {
return
}
this._sendAck(messageId)
// special case for auth key binding
if (msg._ !== 'rpc') {
if (msg._ === 'bind') {
this._sendAck(messageId)
msg.promise.resolve(message.object())
return
@ -902,7 +884,9 @@ export class SessionConnection extends PersistentConnection {
const msg = this._session.pendingMessages.get(msgId)
if (!msg) {
this.log.warn('received ack for unknown message %l', msgId)
if (!this._session.recentOutgoingMsgIds.has(msgId)) {
this.log.warn('received ack for unknown message %l', msgId)
}
return
}
@ -969,7 +953,6 @@ export class SessionConnection extends PersistentConnection {
// e.g. when server returns 429
// most service messages can be omitted as stale
this._resetLastPing(true)
for (const msgId of this._session.pendingMessages.keys()) {
const info = this._session.pendingMessages.get(msgId)!
@ -1025,7 +1008,7 @@ export class SessionConnection extends PersistentConnection {
reason,
)
// restart ping
this._resetLastPing(true)
this._session.resetLastPing(true)
break
case 'rpc': {
@ -1093,16 +1076,6 @@ export class SessionConnection extends PersistentConnection {
this._session.pendingMessages.delete(msgId)
}
private _resetLastPing(withTime = false): void {
if (withTime) this._lastPingTime = 0
if (!this._lastPingMsgId.isZero()) {
this._session.pendingMessages.delete(this._lastPingMsgId)
}
this._lastPingMsgId = Long.ZERO
}
private _registerOutgoingMsgId(msgId: Long): Long {
this._session.recentOutgoingMsgIds.add(msgId)
@ -1142,8 +1115,8 @@ export class SessionConnection extends PersistentConnection {
)
}
const rtt = Date.now() - this._lastPingTime
this._lastPingRtt = rtt
const rtt = Date.now() - this._session.lastPingTime
this._session.lastPingRtt = rtt
if (info.containerId.neq(msgId)) {
this._onMessageAcked(info.containerId)
@ -1155,7 +1128,7 @@ export class SessionConnection extends PersistentConnection {
pingId,
rtt,
)
this._resetLastPing()
this._session.resetLastPing()
}
private _onBadServerSalt(msg: mtp.RawMt_bad_server_salt): void {
@ -1211,7 +1184,7 @@ export class SessionConnection extends PersistentConnection {
serverSalt,
uniqueId,
}: mtp.RawMt_new_session_created): void {
if (uniqueId.eq(this._lastSessionCreatedUid)) {
if (uniqueId.eq(this._session.lastSessionCreatedUid)) {
this.log.debug(
'received new_session_created with the same uid = %l, ignoring',
uniqueId,
@ -1221,7 +1194,7 @@ export class SessionConnection extends PersistentConnection {
}
if (
!this._lastSessionCreatedUid.isZero() &&
!this._session.lastSessionCreatedUid.isZero() &&
!this.params.disableUpdates
) {
// force the client to fetch missed updates
@ -1277,10 +1250,12 @@ export class SessionConnection extends PersistentConnection {
const info = this._session.pendingMessages.get(msgId)
if (!info) {
this.log.info(
'received message info about unknown message %l',
msgId,
)
if (!this._session.recentOutgoingMsgIds.has(msgId)) {
this.log.warn(
'received message info about unknown message %l',
msgId,
)
}
return
}
@ -1417,7 +1392,7 @@ export class SessionConnection extends PersistentConnection {
this._session.queuedAcks.push(msgId)
if (this._session.queuedAcks.length >= 100) {
this._flushTimer.emitNow()
this._flushTimer.emitWhenIdle()
}
}
@ -1586,49 +1561,36 @@ export class SessionConnection extends PersistentConnection {
}
}
private get _hasPendingServiceMessages(): boolean {
return Boolean(
this._session.queuedRpc.length ||
this._session.queuedAcks.length ||
this._session.queuedStateReq.length ||
this._session.queuedResendReq.length,
)
}
protected _onInactivityTimeout() {
// we should send all pending acks and other service messages
// before dropping the connection
if (!this._hasPendingServiceMessages) {
if (!this._session.hasPendingMessages) {
this.log.debug('no pending service messages, closing connection')
super._onInactivityTimeout()
return
}
this._flush(() => {
if (this._hasPendingServiceMessages) {
// the callback will be called again once all pending messages are sent
return
}
this.log.debug('pending service messages sent, closing connection')
this._flushTimer.reset()
super._onInactivityTimeout()
})
this._inactivityPendingFlush = true
this._flush()
}
private _flush(callback?: () => void): void {
flushWhenIdle(): void {
this._flushTimer.emitWhenIdle()
}
private _flush(): void {
if (
!this._session._authKey.ready ||
this._isPfsBindingPending ||
this._current429Timeout
this._session.current429Timeout
) {
this.log.debug(
'skipping flush, connection is not usable (auth key ready = %b, pfs binding pending = %b, 429 timeout = %b)',
this._session._authKey.ready,
this._isPfsBindingPending,
Boolean(this._current429Timeout),
Boolean(this._session.current429Timeout),
)
// it will be flushed once connection is usable
@ -1636,7 +1598,7 @@ export class SessionConnection extends PersistentConnection {
}
try {
this._doFlush(callback)
this._doFlush()
} catch (e: any) {
this.log.error('flush error: %s', e.stack)
// should not happen unless there's a bug in the code
@ -1645,19 +1607,22 @@ export class SessionConnection extends PersistentConnection {
// schedule next flush
// if there are more queued requests, flush immediately
// (they likely didn't fit into one message)
if (
this._session.queuedRpc.length ||
this._session.queuedAcks.length ||
this._session.queuedStateReq.length ||
this._session.queuedResendReq.length
) {
this._flush(callback)
if (this._session.hasPendingMessages) {
// we schedule it on the next tick, so we can load-balance
// between multiple connections using the same session
this._flushTimer.emitWhenIdle()
} else if (this._inactivityPendingFlush) {
this.log.debug('pending messages sent, closing connection')
this._flushTimer.reset()
this._inactivityPendingFlush = false
super._onInactivityTimeout()
} else {
this._flushTimer.emitBefore(this._lastPingTime + 60000)
this._flushTimer.emitBefore(this._session.lastPingTime + 60000)
}
}
private _doFlush(callback?: () => void): void {
private _doFlush(): void {
this.log.debug(
'flushing send queue. queued rpc: %d',
this._session.queuedRpc.length,
@ -1716,13 +1681,15 @@ export class SessionConnection extends PersistentConnection {
const getStateTime = now + 1500
if (now - this._lastPingTime > 60000) {
if (!this._lastPingMsgId.isZero()) {
if (now - this._session.lastPingTime > 60000) {
if (!this._session.lastPingMsgId.isZero()) {
this.log.warn(
"didn't receive pong for previous ping (msg_id = %l)",
this._lastPingMsgId,
this._session.lastPingMsgId,
)
this._session.pendingMessages.delete(
this._session.lastPingMsgId,
)
this._session.pendingMessages.delete(this._lastPingMsgId)
}
pingId = randomLong()
@ -1731,7 +1698,7 @@ export class SessionConnection extends PersistentConnection {
pingId,
}
this._lastPingTime = Date.now()
this._session.lastPingTime = Date.now()
pingRequest = TlBinaryWriter.serializeObject(this._writerMap, obj)
containerSize += pingRequest.length + 16
@ -1836,13 +1803,17 @@ export class SessionConnection extends PersistentConnection {
// if message was already assigned a msg_id,
// we must wrap it in a container with a newer msg_id
if (msg.msgId) forceContainer = true
// having >1 upload.getFile within a container seems to cause flood_wait errors
// also a crutch for load-balancing
if (msg.method === 'upload.getFile') break
}
packetSize += containerSize
messageCount += containerMessageCount + rpcToSend.length
if (!messageCount) {
this.log.debug('flush failed: nothing to flush')
this.log.debug('flush did not happen: nothing to flush')
return
}
@ -1875,7 +1846,7 @@ export class SessionConnection extends PersistentConnection {
pingMsgId = this._registerOutgoingMsgId(
this._session.writeMessage(writer, pingRequest),
)
this._lastPingMsgId = pingMsgId
this._session.lastPingMsgId = pingMsgId
const pingPending: PendingMessage = {
_: 'ping',
pingId: pingId!,
@ -2065,7 +2036,6 @@ export class SessionConnection extends PersistentConnection {
this._session
.encryptMessage(result)
.then((enc) => this.send(enc))
.then(callback)
.catch((err) => {
this.log.error(
'error while sending pending messages (root msg_id = %l): %s',

View file

@ -48,7 +48,9 @@ export abstract class BaseTcpTransport
// eslint-disable-next-line @typescript-eslint/no-unused-vars
connect(dc: tl.RawDcOption, testMode: boolean): void {
if (this._state !== TransportState.Idle) { throw new Error('Transport is not IDLE') }
if (this._state !== TransportState.Idle) {
throw new Error('Transport is not IDLE')
}
if (!this.packetCodecInitialized) {
this._packetCodec.setup?.(this._crypto, this.log)
@ -69,7 +71,9 @@ export abstract class BaseTcpTransport
this.handleConnect.bind(this),
)
this._socket.on('data', (data) => this._packetCodec.feed(data))
this._socket.on('data', (data) => {
this._packetCodec.feed(data)
})
this._socket.on('error', this.handleError.bind(this))
this._socket.on('close', this.close.bind(this))
}
@ -87,7 +91,7 @@ export abstract class BaseTcpTransport
this._packetCodec.reset()
}
async handleError(error: Error): Promise<void> {
handleError(error: Error): void {
this.log.error('error: %s', error.stack)
this.emit('error', error)
}
@ -99,7 +103,11 @@ export abstract class BaseTcpTransport
if (initialMessage.length) {
this._socket!.write(initialMessage, (err) => {
if (err) {
this.emit('error', err)
this.log.error(
'failed to write initial message: %s',
err.stack,
)
this.emit('error')
this.close()
} else {
this._state = TransportState.Ready
@ -113,12 +121,20 @@ export abstract class BaseTcpTransport
}
async send(bytes: Buffer): Promise<void> {
if (this._state !== TransportState.Ready) { throw new Error('Transport is not READY') }
if (this._state !== TransportState.Ready) {
throw new Error('Transport is not READY')
}
const framed = await this._packetCodec.encode(bytes)
return new Promise((res, rej) => {
this._socket!.write(framed, (err) => (err ? rej(err) : res()))
return new Promise((resolve, reject) => {
this._socket!.write(framed, (error) => {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}
}

View file

@ -8,12 +8,6 @@ export interface IEncryptionScheme {
decrypt(data: Buffer): MaybeAsync<Buffer>
}
export interface IHashMethod {
update(data: Buffer): MaybeAsync<void>
digest(): MaybeAsync<Buffer>
}
export interface ICryptoProvider {
initialize?(): MaybeAsync<void>
@ -38,8 +32,6 @@ export interface ICryptoProvider {
createAesEcb(key: Buffer): IEncryptionScheme
createMd5(): IHashMethod
factorizePQ(pq: Buffer): MaybeAsync<[Buffer, Buffer]>
}

View file

@ -3,7 +3,6 @@ import {
BaseCryptoProvider,
ICryptoProvider,
IEncryptionScheme,
IHashMethod,
} from './abstract'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -108,15 +107,6 @@ export class ForgeCryptoProvider
)
}
createMd5(): IHashMethod {
const hash = forge.md.md5.create()
return {
update: (data) => hash.update(data.toString('binary')),
digest: () => Buffer.from(hash.digest().data, 'binary'),
}
}
hmacSha256(data: Buffer, key: Buffer): MaybeAsync<Buffer> {
const hmac = forge.hmac.create()
hmac.start('sha256', key.toString('binary'))

View file

@ -11,7 +11,6 @@ import {
BaseCryptoProvider,
ICryptoProvider,
IEncryptionScheme,
IHashMethod,
} from './abstract'
export class NodeCryptoProvider
@ -83,10 +82,6 @@ export class NodeCryptoProvider
return createHash('sha256').update(data).digest()
}
createMd5(): IHashMethod {
return createHash('md5') as unknown as IHashMethod
}
hmacSha256(data: Buffer, key: Buffer): MaybeAsync<Buffer> {
return createHmac('sha256', key).update(data).digest()
}

View file

@ -84,9 +84,16 @@ export class Logger {
if (m === '%j') {
return JSON.stringify(val, (k, v) => {
if (typeof v === 'object' && v.type === 'Buffer' && Array.isArray(v.data)) {
if (
typeof v === 'object' &&
v.type === 'Buffer' &&
Array.isArray(v.data)
) {
let str = Buffer.from(v.data).toString('base64')
if (str.length > 300) str = str.slice(0, 300) + '...'
if (str.length > 300) {
str = str.slice(0, 300) + '...'
}
return str
}
@ -143,10 +150,10 @@ export class LogManager extends Logger {
static DEBUG = 4
static VERBOSE = 5
constructor() {
constructor(tag = 'base') {
// workaround because we cant pass this to super
// eslint-disable-next-line @typescript-eslint/no-explicit-any
super(null as any, 'base')
super(null as any, tag)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(this as any).mgr = this
}

View file

@ -164,31 +164,6 @@ export function testCryptoProvider(c: ICryptoProvider): void {
'99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b',
)
})
it('should calculate md5', async () => {
const test = async (...parts: string[]): Promise<Buffer> => {
const md5 = c.createMd5()
for (const p of parts) await md5.update(Buffer.from(p, 'hex'))
return md5.digest()
}
expect((await test()).toString('hex')).eq(
'd41d8cd98f00b204e9800998ecf8427e',
)
expect((await test('aaeeff')).toString('hex')).eq(
'9c20ec5e212b4fcfa4666a8b165c6d5d',
)
expect((await test('aaeeffffeeaa')).toString('hex')).eq(
'cf216071768a7b610d079e5eb7b68b74',
)
expect((await test('aaeeff', 'ffeeaa')).toString('hex')).eq(
'cf216071768a7b610d079e5eb7b68b74',
)
expect((await test('aa', 'ee', 'ff', 'ffeeaa')).toString('hex')).eq(
'cf216071768a7b610d079e5eb7b68b74',
)
})
}
describe('NodeCryptoProvider', () => {

View file

@ -34,11 +34,11 @@ importers:
specifier: 8.5.4
version: 8.5.4
'@typescript-eslint/eslint-plugin':
specifier: 5.59.8
version: 5.59.8(@typescript-eslint/parser@5.59.8)(eslint@8.42.0)(typescript@5.0.4)
specifier: 6.4.0
version: 6.4.0(@typescript-eslint/parser@6.4.0)(eslint@8.47.0)(typescript@5.0.4)
'@typescript-eslint/parser':
specifier: 5.59.8
version: 5.59.8(eslint@8.42.0)(typescript@5.0.4)
specifier: 6.4.0
version: 6.4.0(eslint@8.47.0)(typescript@5.0.4)
chai:
specifier: 4.3.7
version: 4.3.7
@ -46,23 +46,23 @@ importers:
specifier: 3.2.0
version: 3.2.0
eslint:
specifier: 8.42.0
version: 8.42.0
specifier: 8.47.0
version: 8.47.0
eslint-config-prettier:
specifier: 8.8.0
version: 8.8.0(eslint@8.42.0)
version: 8.8.0(eslint@8.47.0)
eslint-import-resolver-typescript:
specifier: 3.5.5
version: 3.5.5(@typescript-eslint/parser@5.59.8)(eslint-plugin-import@2.27.5)(eslint@8.42.0)
specifier: 3.6.0
version: 3.6.0(@typescript-eslint/parser@6.4.0)(eslint-plugin-import@2.28.0)(eslint@8.47.0)
eslint-plugin-ascii:
specifier: 1.0.0
version: 1.0.0
eslint-plugin-import:
specifier: 2.27.5
version: 2.27.5(@typescript-eslint/parser@5.59.8)(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0)
specifier: 2.28.0
version: 2.28.0(@typescript-eslint/parser@6.4.0)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0)
eslint-plugin-simple-import-sort:
specifier: 10.0.0
version: 10.0.0(eslint@8.42.0)
version: 10.0.0(eslint@8.47.0)
glob:
specifier: 10.2.6
version: 10.2.6
@ -351,6 +351,11 @@ importers:
packages:
/@aashutoshrathi/word-wrap@1.2.6:
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
dev: true
/@ampproject/remapping@2.2.0:
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'}
@ -723,14 +728,14 @@ packages:
'@jridgewell/trace-mapping': 0.3.9
dev: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.42.0):
/@eslint-community/eslint-utils@4.4.0(eslint@8.47.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
eslint: 8.42.0
eslint-visitor-keys: 3.4.1
eslint: 8.47.0
eslint-visitor-keys: 3.4.3
dev: true
/@eslint-community/regexpp@4.5.1:
@ -738,13 +743,18 @@ packages:
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
dev: true
/@eslint/eslintrc@2.0.3:
resolution: {integrity: sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==}
/@eslint-community/regexpp@4.6.2:
resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
dev: true
/@eslint/eslintrc@2.1.2:
resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4(supports-color@8.1.1)
espree: 9.5.2
espree: 9.6.1
globals: 13.20.0
ignore: 5.2.0
import-fresh: 3.3.0
@ -755,8 +765,8 @@ packages:
- supports-color
dev: true
/@eslint/js@8.42.0:
resolution: {integrity: sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==}
/@eslint/js@8.47.0:
resolution: {integrity: sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
@ -886,18 +896,6 @@ packages:
dev: true
optional: true
/@pkgr/utils@2.4.1:
resolution: {integrity: sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
dependencies:
cross-spawn: 7.0.3
fast-glob: 3.2.12
is-glob: 4.0.3
open: 9.1.0
picocolors: 1.0.0
tslib: 2.5.3
dev: true
/@tokenizer/token@0.3.0:
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
dev: false
@ -941,8 +939,8 @@ packages:
resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==}
dev: true
/@types/json-schema@7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
/@types/json-schema@7.0.12:
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
dev: true
/@types/json5@0.0.29:
@ -984,133 +982,134 @@ packages:
'@types/node': 18.16.0
dev: true
/@typescript-eslint/eslint-plugin@5.59.8(@typescript-eslint/parser@5.59.8)(eslint@8.42.0)(typescript@5.0.4):
resolution: {integrity: sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/eslint-plugin@6.4.0(@typescript-eslint/parser@6.4.0)(eslint@8.47.0)(typescript@5.0.4):
resolution: {integrity: sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
'@typescript-eslint/parser': ^5.0.0
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@eslint-community/regexpp': 4.5.1
'@typescript-eslint/parser': 5.59.8(eslint@8.42.0)(typescript@5.0.4)
'@typescript-eslint/scope-manager': 5.59.8
'@typescript-eslint/type-utils': 5.59.8(eslint@8.42.0)(typescript@5.0.4)
'@typescript-eslint/utils': 5.59.8(eslint@8.42.0)(typescript@5.0.4)
'@typescript-eslint/parser': 6.4.0(eslint@8.47.0)(typescript@5.0.4)
'@typescript-eslint/scope-manager': 6.4.0
'@typescript-eslint/type-utils': 6.4.0(eslint@8.47.0)(typescript@5.0.4)
'@typescript-eslint/utils': 6.4.0(eslint@8.47.0)(typescript@5.0.4)
'@typescript-eslint/visitor-keys': 6.4.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.42.0
grapheme-splitter: 1.0.4
ignore: 5.2.0
natural-compare-lite: 1.4.0
semver: 7.5.1
tsutils: 3.21.0(typescript@5.0.4)
eslint: 8.47.0
graphemer: 1.4.0
ignore: 5.2.4
natural-compare: 1.4.0
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.0.4)
typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/parser@5.59.8(eslint@8.42.0)(typescript@5.0.4):
resolution: {integrity: sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/parser@6.4.0(eslint@8.47.0)(typescript@5.0.4):
resolution: {integrity: sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 5.59.8
'@typescript-eslint/types': 5.59.8
'@typescript-eslint/typescript-estree': 5.59.8(typescript@5.0.4)
'@typescript-eslint/scope-manager': 6.4.0
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/typescript-estree': 6.4.0(typescript@5.0.4)
'@typescript-eslint/visitor-keys': 6.4.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.42.0
eslint: 8.47.0
typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/scope-manager@5.59.8:
resolution: {integrity: sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/scope-manager@6.4.0:
resolution: {integrity: sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==}
engines: {node: ^16.0.0 || >=18.0.0}
dependencies:
'@typescript-eslint/types': 5.59.8
'@typescript-eslint/visitor-keys': 5.59.8
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/visitor-keys': 6.4.0
dev: true
/@typescript-eslint/type-utils@5.59.8(eslint@8.42.0)(typescript@5.0.4):
resolution: {integrity: sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/type-utils@6.4.0(eslint@8.47.0)(typescript@5.0.4):
resolution: {integrity: sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: '*'
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.59.8(typescript@5.0.4)
'@typescript-eslint/utils': 5.59.8(eslint@8.42.0)(typescript@5.0.4)
'@typescript-eslint/typescript-estree': 6.4.0(typescript@5.0.4)
'@typescript-eslint/utils': 6.4.0(eslint@8.47.0)(typescript@5.0.4)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.42.0
tsutils: 3.21.0(typescript@5.0.4)
eslint: 8.47.0
ts-api-utils: 1.0.1(typescript@5.0.4)
typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/types@5.59.8:
resolution: {integrity: sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/types@6.4.0:
resolution: {integrity: sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==}
engines: {node: ^16.0.0 || >=18.0.0}
dev: true
/@typescript-eslint/typescript-estree@5.59.8(typescript@5.0.4):
resolution: {integrity: sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/typescript-estree@6.4.0(typescript@5.0.4):
resolution: {integrity: sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 5.59.8
'@typescript-eslint/visitor-keys': 5.59.8
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/visitor-keys': 6.4.0
debug: 4.3.4(supports-color@8.1.1)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.1
tsutils: 3.21.0(typescript@5.0.4)
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.0.4)
typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/utils@5.59.8(eslint@8.42.0)(typescript@5.0.4):
resolution: {integrity: sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/utils@6.4.0(eslint@8.47.0)(typescript@5.0.4):
resolution: {integrity: sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
eslint: ^7.0.0 || ^8.0.0
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.42.0)
'@types/json-schema': 7.0.11
'@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0)
'@types/json-schema': 7.0.12
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 5.59.8
'@typescript-eslint/types': 5.59.8
'@typescript-eslint/typescript-estree': 5.59.8(typescript@5.0.4)
eslint: 8.42.0
eslint-scope: 5.1.1
semver: 7.5.1
'@typescript-eslint/scope-manager': 6.4.0
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/typescript-estree': 6.4.0(typescript@5.0.4)
eslint: 8.47.0
semver: 7.5.4
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/visitor-keys@5.59.8:
resolution: {integrity: sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/visitor-keys@6.4.0:
resolution: {integrity: sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==}
engines: {node: ^16.0.0 || >=18.0.0}
dependencies:
'@typescript-eslint/types': 5.59.8
'@typescript-eslint/types': 6.4.0
eslint-visitor-keys: 3.4.1
dev: true
@ -1126,12 +1125,12 @@ packages:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
dev: false
/acorn-jsx@5.3.2(acorn@8.8.2):
/acorn-jsx@5.3.2(acorn@8.10.0):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
acorn: 8.8.2
acorn: 8.10.0
dev: true
/acorn-walk@8.2.0:
@ -1139,6 +1138,12 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
/acorn@8.10.0:
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
/acorn@8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
engines: {node: '>=0.4.0'}
@ -1322,6 +1327,17 @@ packages:
engines: {node: '>=8'}
dev: true
/array.prototype.findlastindex@1.2.2:
resolution: {integrity: sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
es-abstract: 1.21.2
es-shim-unscopables: 1.0.0
get-intrinsic: 1.2.1
dev: true
/array.prototype.flat@1.3.1:
resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==}
engines: {node: '>= 0.4'}
@ -1379,6 +1395,7 @@ packages:
/big-integer@1.6.51:
resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==}
engines: {node: '>=0.6'}
dev: false
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
@ -1403,13 +1420,6 @@ packages:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
dev: true
/bplist-parser@0.2.0:
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
engines: {node: '>= 5.10.0'}
dependencies:
big-integer: 1.6.51
dev: true
/brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@ -1451,13 +1461,6 @@ packages:
ieee754: 1.2.1
dev: false
/bundle-name@3.0.0:
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
engines: {node: '>=12'}
dependencies:
run-applescript: 5.0.0
dev: true
/cacache@16.0.7:
resolution: {integrity: sha512-a4zfQpp5vm4Ipdvbj+ZrPonikRhm6WBEd4zT1Yc1DXsmAxrPgDwWBLF/u/wTVXSFPIgOJ1U3ghSa2Xm4s3h28w==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@ -1905,24 +1908,6 @@ packages:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
/default-browser-id@3.0.0:
resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
engines: {node: '>=12'}
dependencies:
bplist-parser: 0.2.0
untildify: 4.0.0
dev: true
/default-browser@4.0.0:
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
engines: {node: '>=14.16'}
dependencies:
bundle-name: 3.0.0
default-browser-id: 3.0.0
execa: 7.1.1
titleize: 3.0.0
dev: true
/default-require-extensions@3.0.0:
resolution: {integrity: sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==}
engines: {node: '>=8'}
@ -1930,11 +1915,6 @@ packages:
strip-bom: 4.0.0
dev: true
/define-lazy-prop@3.0.0:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
dev: true
/define-properties@1.2.0:
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
@ -2204,13 +2184,13 @@ packages:
engines: {node: '>=10'}
dev: true
/eslint-config-prettier@8.8.0(eslint@8.42.0):
/eslint-config-prettier@8.8.0(eslint@8.47.0):
resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
eslint: 8.42.0
eslint: 8.47.0
dev: true
/eslint-import-resolver-node@0.3.7:
@ -2218,13 +2198,13 @@ packages:
dependencies:
debug: 3.2.7
is-core-module: 2.12.1
resolve: 1.22.2
resolve: 1.22.4
transitivePeerDependencies:
- supports-color
dev: true
/eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.8)(eslint-plugin-import@2.27.5)(eslint@8.42.0):
resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==}
/eslint-import-resolver-typescript@3.6.0(@typescript-eslint/parser@6.4.0)(eslint-plugin-import@2.28.0)(eslint@8.47.0):
resolution: {integrity: sha512-QTHR9ddNnn35RTxlaEnx2gCxqFlF2SEN0SE2d17SqwyM7YOSI2GHWRYp5BiRkObTUNYPupC/3Fq2a0PpT+EKpg==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
eslint: '*'
@ -2232,14 +2212,13 @@ packages:
dependencies:
debug: 4.3.4(supports-color@8.1.1)
enhanced-resolve: 5.14.1
eslint: 8.42.0
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.8)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0)
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.8)(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0)
eslint: 8.47.0
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.4.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0)
eslint-plugin-import: 2.28.0(@typescript-eslint/parser@6.4.0)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0)
fast-glob: 3.3.1
get-tsconfig: 4.6.0
globby: 13.1.4
is-core-module: 2.12.1
is-glob: 4.0.3
synckit: 0.8.5
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-node
@ -2247,7 +2226,7 @@ packages:
- supports-color
dev: true
/eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.8)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0):
/eslint-module-utils@2.8.0(@typescript-eslint/parser@6.4.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'}
peerDependencies:
@ -2268,11 +2247,11 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.59.8(eslint@8.42.0)(typescript@5.0.4)
'@typescript-eslint/parser': 6.4.0(eslint@8.47.0)(typescript@5.0.4)
debug: 3.2.7
eslint: 8.42.0
eslint: 8.47.0
eslint-import-resolver-node: 0.3.7
eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.8)(eslint-plugin-import@2.27.5)(eslint@8.42.0)
eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@6.4.0)(eslint-plugin-import@2.28.0)(eslint@8.47.0)
transitivePeerDependencies:
- supports-color
dev: true
@ -2281,8 +2260,8 @@ packages:
resolution: {integrity: sha512-NT2asS7tLkXMKBk0GuX6eDUZvb5DWTFDCt7R6a8tvWs5P0my2ybxmCFy3Afxgdcam+wQRAn8JrldLmcK0H5Axg==}
dev: true
/eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.8)(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0):
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
/eslint-plugin-import@2.28.0(@typescript-eslint/parser@6.4.0)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0):
resolution: {integrity: sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
@ -2291,22 +2270,25 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.59.8(eslint@8.42.0)(typescript@5.0.4)
'@typescript-eslint/parser': 6.4.0(eslint@8.47.0)(typescript@5.0.4)
array-includes: 3.1.6
array.prototype.findlastindex: 1.2.2
array.prototype.flat: 1.3.1
array.prototype.flatmap: 1.3.1
debug: 3.2.7
doctrine: 2.1.0
eslint: 8.42.0
eslint: 8.47.0
eslint-import-resolver-node: 0.3.7
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.8)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0)
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.4.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0)
has: 1.0.3
is-core-module: 2.12.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.6
object.groupby: 1.0.0
object.values: 1.1.6
resolve: 1.22.2
semver: 6.3.0
resolve: 1.22.4
semver: 6.3.1
tsconfig-paths: 3.14.2
transitivePeerDependencies:
- eslint-import-resolver-typescript
@ -2314,24 +2296,16 @@ packages:
- supports-color
dev: true
/eslint-plugin-simple-import-sort@10.0.0(eslint@8.42.0):
/eslint-plugin-simple-import-sort@10.0.0(eslint@8.47.0):
resolution: {integrity: sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==}
peerDependencies:
eslint: '>=5.0.0'
dependencies:
eslint: 8.42.0
eslint: 8.47.0
dev: true
/eslint-scope@5.1.1:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
engines: {node: '>=8.0.0'}
dependencies:
esrecurse: 4.3.0
estraverse: 4.3.0
dev: true
/eslint-scope@7.2.0:
resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==}
/eslint-scope@7.2.2:
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
esrecurse: 4.3.0
@ -2343,15 +2317,20 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/eslint@8.42.0:
resolution: {integrity: sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==}
/eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/eslint@8.47.0:
resolution: {integrity: sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.42.0)
'@eslint-community/regexpp': 4.5.1
'@eslint/eslintrc': 2.0.3
'@eslint/js': 8.42.0
'@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0)
'@eslint-community/regexpp': 4.6.2
'@eslint/eslintrc': 2.1.2
'@eslint/js': 8.47.0
'@humanwhocodes/config-array': 0.11.10
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
@ -2361,9 +2340,9 @@ packages:
debug: 4.3.4(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.0
eslint-visitor-keys: 3.4.1
espree: 9.5.2
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3
espree: 9.6.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@ -2373,7 +2352,6 @@ packages:
globals: 13.20.0
graphemer: 1.4.0
ignore: 5.2.0
import-fresh: 3.3.0
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
@ -2383,21 +2361,20 @@ packages:
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.1
optionator: 0.9.3
strip-ansi: 6.0.1
strip-json-comments: 3.1.1
text-table: 0.2.0
transitivePeerDependencies:
- supports-color
dev: true
/espree@9.5.2:
resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
/espree@9.6.1:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
acorn: 8.8.2
acorn-jsx: 5.3.2(acorn@8.8.2)
eslint-visitor-keys: 3.4.1
acorn: 8.10.0
acorn-jsx: 5.3.2(acorn@8.10.0)
eslint-visitor-keys: 3.4.3
dev: true
/esprima@4.0.1:
@ -2420,11 +2397,6 @@ packages:
estraverse: 5.3.0
dev: true
/estraverse@4.3.0:
resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
engines: {node: '>=4.0'}
dev: true
/estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
@ -2484,8 +2456,8 @@ packages:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
/fast-glob@3.2.11:
resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
/fast-glob@3.2.12:
resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
engines: {node: '>=8.6.0'}
dependencies:
'@nodelib/fs.stat': 2.0.5
@ -2495,8 +2467,8 @@ packages:
micromatch: 4.0.5
dev: true
/fast-glob@3.2.12:
resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
/fast-glob@3.3.1:
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
engines: {node: '>=8.6.0'}
dependencies:
'@nodelib/fs.stat': 2.0.5
@ -2834,23 +2806,12 @@ packages:
dependencies:
array-union: 2.1.0
dir-glob: 3.0.1
fast-glob: 3.2.11
fast-glob: 3.2.12
ignore: 5.2.0
merge2: 1.4.1
slash: 3.0.0
dev: true
/globby@13.1.4:
resolution: {integrity: sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
dir-glob: 3.0.1
fast-glob: 3.2.11
ignore: 5.2.0
merge2: 1.4.1
slash: 4.0.0
dev: true
/gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
@ -2860,10 +2821,6 @@ packages:
/graceful-fs@4.2.10:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
/grapheme-splitter@1.0.4:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
dev: true
/graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: true
@ -3031,6 +2988,11 @@ packages:
engines: {node: '>= 4'}
dev: true
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
dev: true
/import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@ -3125,6 +3087,12 @@ packages:
has: 1.0.3
dev: true
/is-core-module@2.13.0:
resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==}
dependencies:
has: 1.0.3
dev: true
/is-date-object@1.0.5:
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
engines: {node: '>= 0.4'}
@ -3132,18 +3100,6 @@ packages:
has-tostringtag: 1.0.0
dev: true
/is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
hasBin: true
dev: true
/is-docker@3.0.0:
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
dev: true
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@ -3172,14 +3128,6 @@ packages:
is-extglob: 2.1.1
dev: true
/is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
engines: {node: '>=14.16'}
hasBin: true
dependencies:
is-docker: 3.0.0
dev: true
/is-lambda@1.0.1:
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
dev: false
@ -3297,13 +3245,6 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
dependencies:
is-docker: 2.2.1
dev: true
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: false
@ -3887,10 +3828,6 @@ packages:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
dev: false
/natural-compare-lite@1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
dev: true
/natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
@ -4079,6 +4016,24 @@ packages:
object-keys: 1.1.1
dev: true
/object.fromentries@2.0.6:
resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
es-abstract: 1.21.2
dev: true
/object.groupby@1.0.0:
resolution: {integrity: sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==}
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
es-abstract: 1.21.2
get-intrinsic: 1.2.1
dev: true
/object.values@1.1.6:
resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==}
engines: {node: '>= 0.4'}
@ -4107,26 +4062,16 @@ packages:
mimic-fn: 4.0.0
dev: true
/open@9.1.0:
resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==}
engines: {node: '>=14.16'}
dependencies:
default-browser: 4.0.0
define-lazy-prop: 3.0.0
is-inside-container: 1.0.0
is-wsl: 2.2.0
dev: true
/optionator@0.9.1:
resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
/optionator@0.9.3:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
dependencies:
'@aashutoshrathi/word-wrap': 1.2.6
deep-is: 0.1.4
fast-levenshtein: 2.0.6
levn: 0.4.1
prelude-ls: 1.2.1
type-check: 0.4.0
word-wrap: 1.2.3
dev: true
/p-limit@2.3.0:
@ -4514,6 +4459,15 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
/resolve@1.22.4:
resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
hasBin: true
dependencies:
is-core-module: 2.13.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
@ -4550,13 +4504,6 @@ packages:
glob: 10.2.6
dev: true
/run-applescript@5.0.0:
resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
engines: {node: '>=12'}
dependencies:
execa: 5.1.1
dev: true
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@ -4599,6 +4546,11 @@ packages:
hasBin: true
dev: true
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
dev: true
/semver@7.5.0:
resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==}
engines: {node: '>=10'}
@ -4614,6 +4566,14 @@ packages:
dependencies:
lru-cache: 6.0.0
/semver@7.5.4:
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: true
/serialize-javascript@6.0.0:
resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
dependencies:
@ -4677,11 +4637,6 @@ packages:
engines: {node: '>=8'}
dev: true
/slash@4.0.0:
resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==}
engines: {node: '>=12'}
dev: true
/slice-ansi@3.0.0:
resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
engines: {node: '>=8'}
@ -4950,14 +4905,6 @@ packages:
engines: {node: '>= 0.4'}
dev: true
/synckit@0.8.5:
resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==}
engines: {node: ^14.18.0 || >=16.0.0}
dependencies:
'@pkgr/utils': 2.4.1
tslib: 2.5.3
dev: true
/tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@ -5023,11 +4970,6 @@ packages:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: true
/titleize@3.0.0:
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
engines: {node: '>=12'}
dev: true
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@ -5053,6 +4995,15 @@ packages:
engines: {node: '>=8'}
dev: true
/ts-api-utils@1.0.1(typescript@5.0.4):
resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
engines: {node: '>=16.13.0'}
peerDependencies:
typescript: '>=4.2.0'
dependencies:
typescript: 5.0.4
dev: true
/ts-node@10.9.1(@types/node@18.16.0)(typescript@5.0.4):
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
@ -5102,24 +5053,10 @@ packages:
strip-bom: 3.0.0
dev: true
/tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
dev: true
/tslib@2.5.3:
resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
dev: true
/tsutils@3.21.0(typescript@5.0.4):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
typescript: 5.0.4
dev: true
/tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
@ -5223,11 +5160,6 @@ packages:
engines: {node: '>= 10.0.0'}
dev: true
/untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
dev: true
/uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
@ -5301,11 +5233,6 @@ packages:
string-width: 4.2.3
dev: false
/word-wrap@1.2.3:
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
engines: {node: '>=0.10.0'}
dev: true
/workerpool@6.2.1:
resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==}
dev: true