diff --git a/.eslintrc.js b/.eslintrc.js index 6a3ba8f9..a340ed5d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -176,7 +176,7 @@ module.exports = { files: ['**/*.ts', '**/*.tsx'], env: { browser: true, es6: true, node: true }, extends: [ - 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/strict-type-checked', 'plugin:import/typescript', ], globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' }, @@ -213,6 +213,18 @@ module.exports = { ], '@typescript-eslint/no-non-null-assertion': 'off', // todo MTQ-36 '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/no-unnecessary-condition': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { allowNever: true }, + ], + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/no-invalid-void-type': 'off', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/no-dynamic-delete': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', }, settings: { 'import/resolver': { diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index e127382f..a31046ea 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ +/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/unified-signatures */ /* THIS FILE WAS AUTO-GENERATED */ import { Readable } from 'stream' diff --git a/packages/client/src/methods/auth/log-out.ts b/packages/client/src/methods/auth/log-out.ts index 15dd1aae..2d9ac71c 100644 --- a/packages/client/src/methods/auth/log-out.ts +++ b/packages/client/src/methods/auth/log-out.ts @@ -14,7 +14,8 @@ export async function logOut(this: TelegramClient): Promise { this._userId = null this._isBot = false - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // some implicit magic in favor of performance + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment this._pts = this._seq = this._date = undefined as any this._selfUsername = null this._selfChanged = true diff --git a/packages/client/src/methods/bots/answer-inline-query.ts b/packages/client/src/methods/bots/answer-inline-query.ts index f8611409..2d50ac84 100644 --- a/packages/client/src/methods/bots/answer-inline-query.ts +++ b/packages/client/src/methods/bots/answer-inline-query.ts @@ -97,7 +97,11 @@ export async function answerInlineQuery( ): Promise { if (!params) params = {} - const [gallery, tlResults] = await BotInline._convertToTl(this, results, params!.parseMode) + const [gallery, tlResults] = await BotInline._convertToTl( + this, + results, + params.parseMode, + ) await this.call({ _: 'messages.setInlineBotResults', diff --git a/packages/client/src/methods/bots/get-game-high-scores.ts b/packages/client/src/methods/bots/get-game-high-scores.ts index 803f2feb..a14a4758 100644 --- a/packages/client/src/methods/bots/get-game-high-scores.ts +++ b/packages/client/src/methods/bots/get-game-high-scores.ts @@ -53,7 +53,7 @@ export async function getInlineGameHighScores( messageId: string | tl.TypeInputBotInlineMessageID, userId?: InputPeerLike, ): Promise { - const id = await normalizeInlineId(messageId) + const id = normalizeInlineId(messageId) let user: tl.TypeInputUser diff --git a/packages/client/src/methods/bots/set-game-score.ts b/packages/client/src/methods/bots/set-game-score.ts index 091ab765..86b127ff 100644 --- a/packages/client/src/methods/bots/set-game-score.ts +++ b/packages/client/src/methods/bots/set-game-score.ts @@ -87,7 +87,7 @@ export async function setInlineGameScore( const user = normalizeToInputUser(await this.resolvePeer(userId), userId) - const id = await normalizeInlineId(messageId) + const id = normalizeInlineId(messageId) await this.call( { diff --git a/packages/client/src/methods/chats/add-chat-members.ts b/packages/client/src/methods/chats/add-chat-members.ts index aa5b5175..6933ceff 100644 --- a/packages/client/src/methods/chats/add-chat-members.ts +++ b/packages/client/src/methods/chats/add-chat-members.ts @@ -45,10 +45,7 @@ export async function addChatMembers( const updates = await this.call({ _: 'channels.inviteToChannel', channel: normalizeToInputChannel(chat), - users: await this.resolvePeerMany( - users as InputPeerLike[], - normalizeToInputUser, - ), + users: await this.resolvePeerMany(users, normalizeToInputUser), }) this._handleUpdate(updates) } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') diff --git a/packages/client/src/methods/chats/create-group.ts b/packages/client/src/methods/chats/create-group.ts index 5ed6bb99..4b8e4bff 100644 --- a/packages/client/src/methods/chats/create-group.ts +++ b/packages/client/src/methods/chats/create-group.ts @@ -24,10 +24,7 @@ export async function createGroup( ): Promise { if (!Array.isArray(users)) users = [users] - const peers = await this.resolvePeerMany( - users as InputPeerLike[], - normalizeToInputUser, - ) + const peers = await this.resolvePeerMany(users, normalizeToInputUser) const res = await this.call({ _: 'messages.createChat', diff --git a/packages/client/src/methods/dialogs/_init-conversation.ts b/packages/client/src/methods/dialogs/_init-conversation.ts index 1af24457..88fcb058 100644 --- a/packages/client/src/methods/dialogs/_init-conversation.ts +++ b/packages/client/src/methods/dialogs/_init-conversation.ts @@ -28,7 +28,7 @@ export function _pushConversationMessage( const chatId = getMarkedPeerId(msg.raw.peerId) const msgId = msg.raw.id - this._pendingConversations[chatId]?.forEach((conv) => { + this._pendingConversations[chatId].forEach((conv) => { conv['_lastMessage'] = msgId if (incoming) conv['_lastReceivedMessage'] = msgId }) diff --git a/packages/client/src/methods/files/download-file.ts b/packages/client/src/methods/files/download-file.ts index 08bb2b59..32b0bd47 100644 --- a/packages/client/src/methods/files/download-file.ts +++ b/packages/client/src/methods/files/download-file.ts @@ -5,11 +5,10 @@ import { MtUnsupportedError, } from '../../types' -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let fs: any = null +let fs: typeof import('fs') | null = null try { - fs = require('fs') + fs = require('fs') as typeof import('fs') } catch (e) {} /** @@ -39,7 +38,7 @@ export function downloadToFile( const buf = params.location.location return new Promise((resolve, reject) => { - fs.writeFile(filename, buf, (err?: Error) => { + fs!.writeFile(filename, buf, (err) => { if (err) reject(err) else resolve() }) diff --git a/packages/client/src/methods/files/download-iterable.ts b/packages/client/src/methods/files/download-iterable.ts index 3ff2b5a7..0d310ce0 100644 --- a/packages/client/src/methods/files/download-iterable.ts +++ b/packages/client/src/methods/files/download-iterable.ts @@ -47,19 +47,20 @@ export async function* downloadAsIterable( const input = params.location let location: tl.TypeInputFileLocation | tl.TypeInputWebFileLocation if (input instanceof FileLocation) { - if (typeof input.location === 'function') { - (input as tl.Mutable).location = input.location() + let locationInner = input.location + + if (typeof locationInner === 'function') { + locationInner = locationInner() } - if (Buffer.isBuffer(input.location)) { - yield input.location + if (Buffer.isBuffer(locationInner)) { + yield locationInner return } if (!dcId) dcId = input.dcId if (!fileSize) fileSize = input.fileSize - // eslint-disable-next-line @typescript-eslint/no-explicit-any - location = input.location as any + location = locationInner } else if (typeof input === 'string') { const parsed = parseFileId(input) @@ -129,7 +130,7 @@ export async function* downloadAsIterable( result = await this.call( { _: isWeb ? 'upload.getWebFile' : 'upload.getFile', - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line location: location as any, offset: chunkSize * chunk, limit: chunkSize, @@ -182,7 +183,7 @@ export async function* downloadAsIterable( } let error: unknown = undefined - Promise.all( + void Promise.all( Array.from( { length: Math.min(poolSize * REQUESTS_PER_CONNECTION, numChunks) }, downloadChunk, @@ -202,6 +203,7 @@ export async function* downloadAsIterable( while (position < limitBytes) { await nextChunkCv.wait() + // eslint-disable-next-line @typescript-eslint/no-throw-literal if (error) throw error while (nextChunkIdx in buffer) { diff --git a/packages/client/src/methods/files/download-stream.ts b/packages/client/src/methods/files/download-stream.ts index 47f4559e..8a98936e 100644 --- a/packages/client/src/methods/files/download-stream.ts +++ b/packages/client/src/methods/files/download-stream.ts @@ -26,6 +26,7 @@ export function downloadAsStream( async read() {}, }) + // eslint-disable-next-line @typescript-eslint/no-misused-promises setTimeout(async () => { try { for await (const chunk of this.downloadAsIterable(params)) { diff --git a/packages/client/src/methods/files/upload-file.ts b/packages/client/src/methods/files/upload-file.ts index c969cc3b..da49f97d 100644 --- a/packages/client/src/methods/files/upload-file.ts +++ b/packages/client/src/methods/files/upload-file.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { fromBuffer as fileTypeFromBuffer } from 'file-type' import type { ReadStream } from 'fs' import { Readable } from 'stream' @@ -15,12 +14,12 @@ import { readBytesFromStream, } from '../../utils/stream-utils' -let fs: any = null -let path: any = null +let fs: typeof import('fs') | null = null +let path: typeof import('path') | null = null try { - fs = require('fs') - path = require('path') + fs = require('fs') as typeof import('fs') + path = require('path') as typeof import('path') } catch (e) {} const OVERRIDE_MIME: Record = { @@ -132,15 +131,12 @@ export async function uploadFile( } if (fs && file instanceof fs.ReadStream) { - fileName = path.basename((file as ReadStream).path.toString()) + fileName = path!.basename(file.path.toString()) fileSize = await new Promise((res, rej) => { - fs.stat( - (file as ReadStream).path.toString(), - (err?: any, stat?: any) => { - if (err) rej(err) - res(stat.size) - }, - ) + fs!.stat((file as ReadStream).path.toString(), (err, stat) => { + if (err) rej(err) + res(stat.size) + }) }) // fs.ReadStream is a subclass of Readable, no conversion needed } @@ -171,7 +167,7 @@ export async function uploadFile( if (idx > -1) { const raw = disposition.slice(idx + 9).split(';')[0] - fileName = JSON.parse(raw) + fileName = JSON.parse(raw) as string } } diff --git a/packages/client/src/methods/files/upload-media.ts b/packages/client/src/methods/files/upload-media.ts index 40287770..0104551a 100644 --- a/packages/client/src/methods/files/upload-media.ts +++ b/packages/client/src/methods/files/upload-media.ts @@ -91,7 +91,7 @@ export async function uploadMedia( assertTypeIs('uploadMedia', res, 'messageMediaDocument') assertTypeIs('uploadMedia', res.document!, 'document') - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line return parseDocument(this, res.document) as any case 'inputMediaStory': throw new MtArgumentError("This media (story) can't be uploaded") diff --git a/packages/client/src/methods/messages/edit-inline-message.ts b/packages/client/src/methods/messages/edit-inline-message.ts index 94363b14..315b2444 100644 --- a/packages/client/src/methods/messages/edit-inline-message.ts +++ b/packages/client/src/methods/messages/edit-inline-message.ts @@ -76,7 +76,7 @@ export async function editInlineMessage( let entities: tl.TypeMessageEntity[] | undefined let media: tl.TypeInputMedia | undefined = undefined - const id = await normalizeInlineId(messageId) + const id = normalizeInlineId(messageId) if (params.media) { media = await this._normalizeInputMedia(params.media, params, true) diff --git a/packages/client/src/methods/messages/edit-message.ts b/packages/client/src/methods/messages/edit-message.ts index 73584515..597a9920 100644 --- a/packages/client/src/methods/messages/edit-message.ts +++ b/packages/client/src/methods/messages/edit-message.ts @@ -114,6 +114,6 @@ export async function editMessage( media, }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line return this._findMessageInUpdate(res, true) as any } diff --git a/packages/client/src/methods/messages/forward-messages.ts b/packages/client/src/methods/messages/forward-messages.ts index 52e877a2..3b19596a 100644 --- a/packages/client/src/methods/messages/forward-messages.ts +++ b/packages/client/src/methods/messages/forward-messages.ts @@ -291,13 +291,18 @@ export async function forwardMessages( }, ): Promise> { if (!params) params = {} - const isSingle = !Array.isArray(messages) - if (isSingle) messages = [messages as number] + + let isSingle = false + + if (!Array.isArray(messages)) { + isSingle = true + messages = [messages] + } // sending more than 100 will not result in a server-sent // error, instead only first 100 IDs will be forwarded, // which is definitely not the best outcome. - if ((messages as number[]).length > 100) { + if (messages.length > 100) { throw new MtArgumentError( 'You can forward no more than 100 messages at once', ) @@ -338,12 +343,10 @@ export async function forwardMessages( _: 'messages.forwardMessages', toPeer, fromPeer: await this.resolvePeer(fromChatId), - id: messages as number[], + id: messages, silent: params.silent, scheduleDate: normalizeDate(params.schedule), - randomId: [...Array((messages as number[]).length)].map(() => - randomLong(), - ), + randomId: Array.from({ length: messages.length }, () => randomLong()), dropAuthor: params.noAuthor, dropMediaCaptions: params.noCaption, noforwards: params.forbidForwards, diff --git a/packages/client/src/methods/stickers/add-sticker-to-set.ts b/packages/client/src/methods/stickers/add-sticker-to-set.ts index 3a11e520..87ea81c8 100644 --- a/packages/client/src/methods/stickers/add-sticker-to-set.ts +++ b/packages/client/src/methods/stickers/add-sticker-to-set.ts @@ -56,7 +56,7 @@ export async function addStickerToSet( maskCoords: sticker.maskPosition ? { _: 'maskCoords', - n: MASK_POS[sticker.maskPosition.point as keyof typeof MASK_POS], + n: MASK_POS[sticker.maskPosition.point], x: sticker.maskPosition.x, y: sticker.maskPosition.y, zoom: sticker.maskPosition.scale, diff --git a/packages/client/src/methods/stickers/create-sticker-set.ts b/packages/client/src/methods/stickers/create-sticker-set.ts index b2282010..34e07f6f 100644 --- a/packages/client/src/methods/stickers/create-sticker-set.ts +++ b/packages/client/src/methods/stickers/create-sticker-set.ts @@ -106,7 +106,10 @@ export async function createStickerSet( ) } - const owner = normalizeToInputUser(await this.resolvePeer(params.owner), params.owner) + const owner = normalizeToInputUser( + await this.resolvePeer(params.owner), + params.owner, + ) const inputStickers: tl.TypeInputStickerSetItem[] = [] @@ -124,7 +127,7 @@ export async function createStickerSet( maskCoords: sticker.maskPosition ? { _: 'maskCoords', - n: MASK_POS[sticker.maskPosition.point as keyof typeof MASK_POS], + n: MASK_POS[sticker.maskPosition.point], x: sticker.maskPosition.x, y: sticker.maskPosition.y, zoom: sticker.maskPosition.scale, diff --git a/packages/client/src/methods/stickers/get-custom-emojis.ts b/packages/client/src/methods/stickers/get-custom-emojis.ts index b30bd5e6..bde9ca50 100644 --- a/packages/client/src/methods/stickers/get-custom-emojis.ts +++ b/packages/client/src/methods/stickers/get-custom-emojis.ts @@ -33,6 +33,6 @@ export async function getCustomEmojis( ) } - return doc as Sticker + return doc }) } diff --git a/packages/client/src/methods/updates.ts b/packages/client/src/methods/updates.ts index f53ac505..f72a823e 100644 --- a/packages/client/src/methods/updates.ts +++ b/packages/client/src/methods/updates.ts @@ -940,7 +940,7 @@ function _fetchChannelDifferenceLater( fallbackPts?: number, force = false, ): void { - if (!requestedDiff[channelId]) { + if (!(channelId in requestedDiff)) { requestedDiff[channelId] = _fetchChannelDifference .call(this, channelId, fallbackPts, force) .catch((err) => { @@ -1074,7 +1074,7 @@ function _fetchDifferenceLater( this: TelegramClient, requestedDiff: Record>, ): void { - if (!requestedDiff[0]) { + if (!(0 in requestedDiff)) { requestedDiff[0] = _fetchDifference .call(this, requestedDiff) .catch((err) => { diff --git a/packages/client/src/methods/users/resolve-peer.ts b/packages/client/src/methods/users/resolve-peer.ts index 255d3b34..1345f8dd 100644 --- a/packages/client/src/methods/users/resolve-peer.ts +++ b/packages/client/src/methods/users/resolve-peer.ts @@ -8,7 +8,11 @@ import { import { tl } from '@mtcute/tl' import { TelegramClient } from '../../client' -import { InputPeerLike, MtNotFoundError, MtTypeAssertionError } from '../../types' +import { + InputPeerLike, + MtNotFoundError, + MtTypeAssertionError, +} from '../../types' import { normalizeToInputPeer } from '../../utils/peer-utils' import { assertTypeIs } from '../../utils/type-assertion' @@ -109,7 +113,12 @@ export async function resolvePeer( const found = res.chats.find((it) => it.id === id) if (found) { - if (!(found._ === 'channel' || found._ === 'channelForbidden')) { + if ( + !( + found._ === 'channel' || + found._ === 'channelForbidden' + ) + ) { // chats can't have usernames // furthermore, our id is a channel id, so it must be a channel // this should never happen, unless Telegram goes crazy @@ -205,7 +214,7 @@ export async function resolvePeer( // break } case 'channel': { - const id = toggleChannelIdMark(peerId as number) + const id = toggleChannelIdMark(peerId) const res = await this.call({ _: 'channels.getChannels', diff --git a/packages/client/src/types/conversation.ts b/packages/client/src/types/conversation.ts index a3f5d9bd..5a714431 100644 --- a/packages/client/src/types/conversation.ts +++ b/packages/client/src/types/conversation.ts @@ -251,6 +251,7 @@ export class Conversation { this.stop() + // eslint-disable-next-line @typescript-eslint/no-throw-literal if (err) throw err return res! @@ -501,7 +502,7 @@ export class Conversation { const it = this._queuedNewMessage.peekFront()! // order does matter for new messages - this._lock.acquire().then(async () => { + void this._lock.acquire().then(async () => { try { if (!it.check || (await it.check(msg))) { if (it.timeout) clearTimeout(it.timeout) diff --git a/packages/client/src/types/media/sticker.ts b/packages/client/src/types/media/sticker.ts index c14c115f..a433248e 100644 --- a/packages/client/src/types/media/sticker.ts +++ b/packages/client/src/types/media/sticker.ts @@ -140,7 +140,7 @@ export class Sticker extends RawDocument { */ get customEmojiFree(): boolean { return this.attr._ === 'documentAttributeCustomEmoji' ? - this.attr?.free ?? false : + this.attr.free ?? false : false } diff --git a/packages/client/src/types/messages/message.ts b/packages/client/src/types/messages/message.ts index 46f793e5..2955c156 100644 --- a/packages/client/src/types/messages/message.ts +++ b/packages/client/src/types/messages/message.ts @@ -329,7 +329,7 @@ export class Message { get replyToMessageId(): number | null { if (this.raw.replyTo?._ !== 'messageReplyHeader') return null - return this.raw.replyTo?.replyToMsgId ?? null + return this.raw.replyTo.replyToMsgId ?? null } /** @@ -339,7 +339,7 @@ export class Message { get replyToThreadId(): number | null { if (this.raw.replyTo?._ !== 'messageReplyHeader') return null - return this.raw.replyTo?.replyToTopId ?? null + return this.raw.replyTo.replyToTopId ?? null } /** diff --git a/packages/client/src/types/misc/sticker-set.ts b/packages/client/src/types/misc/sticker-set.ts index e71225ca..0ac7f28a 100644 --- a/packages/client/src/types/misc/sticker-set.ts +++ b/packages/client/src/types/misc/sticker-set.ts @@ -239,7 +239,7 @@ export class StickerSet { */ getStickersByEmoji(emoji: string): StickerInfo[] { return this.stickers.filter( - (it) => it.alt === emoji || it.emoji.indexOf(emoji) !== -1, + (it) => it.alt === emoji || it.emoji.includes(emoji), ) } @@ -271,8 +271,8 @@ export class StickerSet { private _getInputDocument(idx: number): tl.TypeInputDocument { if (!this.full) throw new MtEmptyError() - if (idx < 0) idx = this.full!.documents.length + idx - const doc = this.full!.documents[idx] as tl.RawDocument + if (idx < 0) idx = this.full.documents.length + idx + const doc = this.full.documents[idx] as tl.RawDocument if (!doc) { throw new RangeError(`Sticker set does not have sticker ${idx}`) diff --git a/packages/client/src/types/misc/takeout-session.ts b/packages/client/src/types/misc/takeout-session.ts index af8f8af6..275218bc 100644 --- a/packages/client/src/types/misc/takeout-session.ts +++ b/packages/client/src/types/misc/takeout-session.ts @@ -33,6 +33,7 @@ export class TakeoutSession { message: MustEqual, params?: RpcCallOptions, ): Promise { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this.client.call( { _: 'invokeWithTakeout', @@ -76,6 +77,7 @@ export class TakeoutSession { get(target, prop, receiver) { if (prop === 'call') return boundCall + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Reflect.get(target, prop, receiver) }, }) diff --git a/packages/client/src/types/peers/chat-photo.ts b/packages/client/src/types/peers/chat-photo.ts index 4f5b046c..1925190f 100644 --- a/packages/client/src/types/peers/chat-photo.ts +++ b/packages/client/src/types/peers/chat-photo.ts @@ -98,6 +98,7 @@ export class ChatPhotoSize extends FileLocation { { _: 'photo', id: this.obj.photoId, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment source: { _: 'dialogPhoto', big: this.big, diff --git a/packages/client/src/types/peers/chat.ts b/packages/client/src/types/peers/chat.ts index 07fa039d..b5d8086f 100644 --- a/packages/client/src/types/peers/chat.ts +++ b/packages/client/src/types/peers/chat.ts @@ -120,7 +120,7 @@ export class Chat { } } - return this._inputPeer! + return this._inputPeer } private _chatType?: ChatType @@ -148,7 +148,7 @@ export class Chat { } } - return this._chatType! + return this._chatType } /** @@ -594,7 +594,7 @@ export class Chat { return new FormattedString( this.client.getParseMode(parseMode).unparse(text, [ { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line raw: undefined as any, type: 'text_link', offset: 0, @@ -602,7 +602,7 @@ export class Chat { url: `https://t.me/${this.username}`, }, ]), - parseMode!, + parseMode, ) } diff --git a/packages/client/src/types/peers/peers-index.ts b/packages/client/src/types/peers/peers-index.ts index e1b7ead8..8fc3c059 100644 --- a/packages/client/src/types/peers/peers-index.ts +++ b/packages/client/src/types/peers/peers-index.ts @@ -6,8 +6,8 @@ const ERROR_MSG = 'Given peer is not available in this index. This is most likely an internal library error.' export class PeersIndex { - readonly users: Record = Object.create(null) - readonly chats: Record = Object.create(null) + readonly users: Record = {} + readonly chats: Record = {} hasMin = false diff --git a/packages/client/src/types/peers/user.ts b/packages/client/src/types/peers/user.ts index 63661407..8641cb58 100644 --- a/packages/client/src/types/peers/user.ts +++ b/packages/client/src/types/peers/user.ts @@ -324,7 +324,7 @@ export class User { return new FormattedString( this.client.getParseMode(parseMode).unparse(text, [ { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line raw: undefined as any, type: 'text_mention', offset: 0, @@ -332,7 +332,7 @@ export class User { userId: this.raw.id, }, ]), - parseMode!, + parseMode, ) } @@ -383,7 +383,7 @@ export class User { return new FormattedString( this.client.getParseMode(parseMode).unparse(text, [ { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line raw: undefined as any, type: 'text_link', offset: 0, @@ -393,7 +393,7 @@ export class User { }&hash=${this.raw.accessHash.toString(16)}`, }, ]), - parseMode!, + parseMode, ) } diff --git a/packages/client/src/types/updates/chat-member-update.ts b/packages/client/src/types/updates/chat-member-update.ts index 6954e0b6..fd434b54 100644 --- a/packages/client/src/types/updates/chat-member-update.ts +++ b/packages/client/src/types/updates/chat-member-update.ts @@ -1,6 +1,6 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ // ^^ will be looked into in MTQ-35 +import { getMarkedPeerId } from '@mtcute/core' import { tl } from '@mtcute/tl' import { TelegramClient } from '../../client' @@ -37,6 +37,26 @@ export type ChatMemberUpdateType = | 'new_owner' | 'other' +function extractPeerId( + raw?: tl.TypeChatParticipant | tl.TypeChannelParticipant, +) { + if (!raw) return 0 + + if (tl.isAnyChatParticipant(raw)) { + return raw.userId + } + + switch (raw._) { + case 'channelParticipant': + case 'channelParticipantSelf': + case 'channelParticipantCreator': + case 'channelParticipantAdmin': + return raw.userId + default: + return getMarkedPeerId(raw.peer) + } +} + /** * Update representing a change in the status * of a chat/channel member. @@ -82,12 +102,8 @@ export class ChatMemberUpdate { const old = this.raw.prevParticipant const cur = this.raw.newParticipant - const oldId = - (old && ((old as any).userId || (old as any).peer.userId)) || - null - const curId = - (cur && ((cur as any).userId || (cur as any).peer.userId)) || - null + const oldId = extractPeerId(old) + const curId = extractPeerId(cur) const actorId = this.raw.actorId diff --git a/packages/client/src/types/updates/parse-update.ts b/packages/client/src/types/updates/parse-update.ts index 1f66e5f1..010f8576 100644 --- a/packages/client/src/types/updates/parse-update.ts +++ b/packages/client/src/types/updates/parse-update.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument */ import { tl } from '@mtcute/tl' import { TelegramClient } from '../../client' @@ -19,13 +19,14 @@ import { PollVoteUpdate, PreCheckoutQuery, UserStatusUpdate, - UserTypingUpdate } from '../index' + UserTypingUpdate, +} from '../index' type ParserFunction = ( client: TelegramClient, upd: tl.TypeUpdate | tl.TypeMessage, peers: PeersIndex -) => any +) => ParsedUpdate['data'] type UpdateParser = [ParsedUpdate['name'], ParserFunction] const baseMessageParser: ParserFunction = ( @@ -35,7 +36,9 @@ const baseMessageParser: ParserFunction = ( ) => new Message( client, - tl.isAnyMessage(upd) ? upd : (upd as any).message, + tl.isAnyMessage(upd) ? + upd : + (upd as { message: tl.TypeMessage }).message, peers, upd._ === 'updateNewScheduledMessage', ) @@ -142,7 +145,7 @@ export function _parseUpdate( return { name: pair[0], data: pair[1](client, update, peers), - } + } as ParsedUpdate } return null diff --git a/packages/client/src/types/utils.ts b/packages/client/src/types/utils.ts index 9dbe1d5f..ae6b9846 100644 --- a/packages/client/src/types/utils.ts +++ b/packages/client/src/types/utils.ts @@ -1,14 +1,15 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-argument */ import { MaybeAsync } from '@mtcute/core' export type MaybeDynamic = MaybeAsync | (() => MaybeAsync) export type ArrayWithTotal = T[] & { total: number } -let util: any | null = null +let util: typeof import('util') | null = null try { - util = require('util') + util = require('util') as typeof import('util') } catch (e) {} // get all property names. unlike Object.getOwnPropertyNames, @@ -21,7 +22,7 @@ function getAllGettersNames(obj: object): string[] { if ( prop !== '__proto__' && Object.getOwnPropertyDescriptor(obj, prop)?.get && - getters.indexOf(prop) === -1 + !getters.includes(prop) ) { getters.push(prop) } @@ -53,10 +54,11 @@ export function makeInspectable( const getters: string[] = props ? props : [] for (const key of getAllGettersNames(obj.prototype)) { - if (!hide || hide.indexOf(key) === -1) getters.push(key) + if (!hide || !hide.includes(key)) getters.push(key) } // dirty hack to set name for inspect result + // eslint-disable-next-line @typescript-eslint/no-implied-eval const proto = new Function(`return function ${obj.name}(){}`)().prototype obj.prototype.toJSON = function (nested = false) { @@ -86,6 +88,7 @@ export function makeInspectable( Buffer.prototype.toJSON = bufferToJsonOriginal } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return ret } if (util) { diff --git a/packages/client/src/utils/stream-utils.ts b/packages/client/src/utils/stream-utils.ts index db33033c..b507a14a 100644 --- a/packages/client/src/utils/stream-utils.ts +++ b/packages/client/src/utils/stream-utils.ts @@ -21,27 +21,30 @@ class NodeReadable extends Readable { this._reading = true const doRead = () => { - this._reader.read().then((res) => { - if (this._doneReading) { + this._reader + .read() + .then((res) => { + if (this._doneReading) { + this._reading = false + this._reader.releaseLock() + this._doneReading() + } + if (res.done) { + this.push(null) + this._reading = false + this._reader.releaseLock() + + return + } + if (this.push(res.value)) { + doRead() + + return + } this._reading = false this._reader.releaseLock() - this._doneReading() - } - if (res.done) { - this.push(null) - this._reading = false - this._reader.releaseLock() - - return - } - if (this.push(res.value)) { - doRead() - - return - } - this._reading = false - this._reader.releaseLock() - }) + }) + .catch((err) => this.emit('error', err)) } doRead() } @@ -51,9 +54,11 @@ class NodeReadable extends Readable { const promise = new Promise((resolve) => { this._doneReading = resolve }) - promise.then(() => { - this._handleDestroy(err, callback) - }) + promise + .then(() => { + this._handleDestroy(err, callback) + }) + .catch((err) => this.emit('error', err)) } else { this._handleDestroy(err, callback) } @@ -63,8 +68,10 @@ class NodeReadable extends Readable { err: Error | null, callback: (error?: Error | null) => void, ) { - this._webStream.cancel() - super._destroy(err, callback) + this._webStream + .cancel() + .then(() => super._destroy(err, callback)) + .catch((err: Error) => callback(err)) } } @@ -90,12 +97,12 @@ export async function readBytesFromStream( ): Promise { if (stream.readableEnded) return null - let res = stream.read(size) + let res = stream.read(size) as Buffer if (!res) { return new Promise((resolve, reject) => { stream.on('readable', function handler() { - res = stream.read(size) + res = stream.read(size) as Buffer if (res) { stream.off('readable', handler) diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index b9d7e472..45a43f64 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist", }, "include": [ - "./src" + "./src", + "./tests" ] } diff --git a/packages/core/package.json b/packages/core/package.json index 7efb4cb5..4332e848 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -18,17 +18,18 @@ "./storage/json-file.js": false }, "dependencies": { - "@types/node": "18.16.0", - "@types/events": "3.0.0", "@mtcute/tl": "workspace:^160.0.0", "@mtcute/tl-runtime": "workspace:^1.0.0", + "@types/events": "3.0.0", + "@types/node": "18.16.0", "big-integer": "1.6.51", - "long": "5.2.3", - "events": "3.2.0" + "events": "3.2.0", + "long": "5.2.3" }, "devDependencies": { "@mtcute/dispatcher": "workspace:^1.0.0", "@types/ws": "8.5.4", + "exit-hook": "^4.0.0", "ws": "8.13.0" } } \ No newline at end of file diff --git a/packages/core/src/base-client.ts b/packages/core/src/base-client.ts index 37c5ab41..80be67d0 100644 --- a/packages/core/src/base-client.ts +++ b/packages/core/src/base-client.ts @@ -388,7 +388,7 @@ export class BaseTelegramClient extends EventEmitter { promise.resolve() this._connected = true }) - .catch((err) => this._emitError(err)) + .catch((err: Error) => this._emitError(err)) } /** @@ -401,7 +401,7 @@ export class BaseTelegramClient extends EventEmitter { * Close all connections and finalize the client. */ async close(): Promise { - await this._onClose() + this._onClose() this._config.destroy() this.network.destroy() @@ -436,6 +436,7 @@ export class BaseTelegramClient extends EventEmitter { const res = await this.network.call(message, params, stack) await this._cachePeersFrom(res) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return res } @@ -487,7 +488,7 @@ export class BaseTelegramClient extends EventEmitter { let hadMin = false let count = 0 - for (const peer of getAllPeersFrom(obj as any)) { + for (const peer of getAllPeersFrom(obj as tl.TlObject)) { if ((peer as any).min) { // absolutely incredible min peer handling, courtesy of levlam. // see this thread: https://t.me/tdlibchat/15084 diff --git a/packages/core/src/network/authorization.ts b/packages/core/src/network/authorization.ts index e3f98613..19e4030f 100644 --- a/packages/core/src/network/authorization.ts +++ b/packages/core/src/network/authorization.ts @@ -178,6 +178,7 @@ async function rsaPad( const decryptedDataBigint = bufferToBigInt(decryptedData) if (decryptedDataBigint.geq(keyModulus)) { + console.log('retrying because decrypted data is too big') continue } @@ -224,6 +225,7 @@ export async function doAuthorization( const session = connection['_session'] const readerMap = session._readerMap const writerMap = session._writerMap + const log = connection.log.create('auth') function sendPlainMessage(message: mtp.TlObject): Promise { const length = TlSerializationCounter.countNeededBytes( @@ -234,6 +236,7 @@ export async function doAuthorization( const messageId = session.getMessageId() + log.verbose('[PLAIN] >>> %j', message) writer.long(Long.ZERO) writer.long(messageId) writer.uint(length) @@ -243,14 +246,17 @@ export async function doAuthorization( } async function readNext(): Promise { - return TlBinaryReader.deserializeObject( + const res = TlBinaryReader.deserializeObject( readerMap, await connection.waitForUnencryptedMessage(), 20, // skip mtproto header ) + + log.verbose('[PLAIN] <<< %j', res) + + return res } - const log = connection.log.create('auth') if (expiresIn) log.prefix = '[PFS] ' const nonce = randomBytes(16) @@ -508,8 +514,7 @@ export async function doAuthorization( if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash2)) { throw Error('Step 4: invalid retry nonce hash from server') } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - retryId = Long.fromBytesLE(authKeyAuxHash as any) + retryId = Long.fromBytesLE(authKeyAuxHash as unknown as number[]) continue } diff --git a/packages/core/src/network/config-manager.ts b/packages/core/src/network/config-manager.ts index f027857d..b0338048 100644 --- a/packages/core/src/network/config-manager.ts +++ b/packages/core/src/network/config-manager.ts @@ -32,7 +32,7 @@ export class ConfigManager { if (this._updateTimeout) clearTimeout(this._updateTimeout) this._updateTimeout = setTimeout( - () => this.update(), + () => void this.update().catch(() => {}), (config.expires - Date.now() / 1000) * 1000, ) diff --git a/packages/core/src/network/network-manager.ts b/packages/core/src/network/network-manager.ts index 5a6ebb04..d1e1bd5a 100644 --- a/packages/core/src/network/network-manager.ts +++ b/packages/core/src/network/network-manager.ts @@ -250,7 +250,7 @@ export class DcConnectionManager { private _setupMulti(kind: ConnectionKind): void { const connection = this[kind] - connection.on('key-change', (idx, key) => { + connection.on('key-change', (idx, key: Buffer | null) => { if (kind !== 'main') { // main connection is responsible for authorization, // and keys are then sent to other connections @@ -266,51 +266,58 @@ export class DcConnectionManager { this.dcId, idx, ) - this.manager._storage.setAuthKeyFor(this.dcId, key) // send key to other connections Promise.all([ + this.manager._storage.setAuthKeyFor(this.dcId, key), this.upload.setAuthKey(key), this.download.setAuthKey(key), this.downloadSmall.setAuthKey(key), - ]).then(() => { - this.upload.notifyKeyChange() - this.download.notifyKeyChange() - this.downloadSmall.notifyKeyChange() - }) + ]) + .then(() => { + this.upload.notifyKeyChange() + this.download.notifyKeyChange() + this.downloadSmall.notifyKeyChange() + }) + .catch((e: Error) => this.manager.params._emitError(e)) }) - connection.on('tmp-key-change', (idx, key, expires) => { - if (kind !== 'main') { - this.manager._log.warn( - 'got tmp-key-change from non-main connection, ignoring', + connection.on( + 'tmp-key-change', + (idx: number, key: Buffer | null, expires: number) => { + if (kind !== 'main') { + this.manager._log.warn( + 'got tmp-key-change from non-main connection, ignoring', + ) + + return + } + + this.manager._log.debug( + 'temp key change for dc %d from connection %d', + this.dcId, + idx, ) - return - } - - this.manager._log.debug( - 'temp key change for dc %d from connection %d', - this.dcId, - idx, - ) - this.manager._storage.setTempAuthKeyFor( - this.dcId, - idx, - key, - expires * 1000, - ) - - // send key to other connections - Promise.all([ - this.upload.setAuthKey(key, true), - this.download.setAuthKey(key, true), - this.downloadSmall.setAuthKey(key, true), - ]).then(() => { - this.upload.notifyKeyChange() - this.download.notifyKeyChange() - this.downloadSmall.notifyKeyChange() - }) - }) + // send key to other connections + Promise.all([ + this.manager._storage.setTempAuthKeyFor( + this.dcId, + idx, + key, + expires * 1000, + ), + this.upload.setAuthKey(key, true), + this.download.setAuthKey(key, true), + this.downloadSmall.setAuthKey(key, true), + ]) + .then(() => { + this.upload.notifyKeyChange() + this.download.notifyKeyChange() + this.downloadSmall.notifyKeyChange() + }) + .catch((e: Error) => this.manager.params._emitError(e)) + }, + ) connection.on('auth-begin', () => { // we need to propagate auth-begin to all connections @@ -334,7 +341,7 @@ export class DcConnectionManager { this.main.requestAuth() }) - connection.on('error', (err, conn) => { + connection.on('error', (err: Error, conn: SessionConnection) => { this.manager.params._emitError(err, conn) }) } @@ -429,19 +436,21 @@ export class NetworkManager { let deviceModel = 'mtcute on ' let appVersion = 'unknown' if (typeof process !== 'undefined' && typeof require !== 'undefined') { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const os = require('os') + const os = require('os') as typeof import('os') deviceModel += `${os.type()} ${os.arch()} ${os.release()}` try { // for production builds - // eslint-disable-next-line @typescript-eslint/no-var-requires - appVersion = require('../package.json').version + + appVersion = (require('../package.json') as { version: string }) + .version } catch (e) { try { // for development builds (additional /src/ in path) - // eslint-disable-next-line @typescript-eslint/no-var-requires - appVersion = require('../../package.json').version + + appVersion = ( + require('../../package.json') as { version: string } + ).version } catch (e) {} } } else if (typeof navigator !== 'undefined') { @@ -458,7 +467,7 @@ export class NetworkManager { langCode: 'en', ...(params.initConnectionOptions ?? {}), apiId: params.apiId, - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line query: null as any, } @@ -484,7 +493,7 @@ export class NetworkManager { this._lastUpdateTime = Date.now() if (this._keepAliveInterval) clearInterval(this._keepAliveInterval) - this._keepAliveInterval = setInterval(async () => { + this._keepAliveInterval = setInterval(() => { if (Date.now() - this._lastUpdateTime > 900_000) { // telegram asks to fetch pending updates if there are no updates for 15 minutes. // it is up to the user to decide whether to do it or not @@ -494,27 +503,21 @@ export class NetworkManager { } }, 60_000) - 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) - }) - } - }) + Promise.resolve(this._storage.getSelf()) + .then((self) => { + if (self?.isBot) { + // bots may receive tmpSessions, which we should respect + return this.config.update(true) + } + }) + .catch((e: Error) => this.params._emitError(e)) }) - dc.main.on('update', (update) => { + dc.main.on('update', (update: tl.TypeUpdates) => { this._lastUpdateTime = Date.now() this._updateHandler(update) }) - dc.loadKeys() - .catch((e) => { - this.params._emitError(e) - }) - .then(() => { - dc.main.ensureConnected() - }) + return dc.loadKeys().then(() => dc.main.ensureConnected()) } private _dcCreationPromise: Record> = {} @@ -574,7 +577,7 @@ export class NetworkManager { const dc = new DcConnectionManager(this, defaultDc.id, defaultDc) this._dcConnections[defaultDc.id] = dc - this._switchPrimaryDc(dc) + await this._switchPrimaryDc(dc) } private async _exportAuthTo(manager: DcConnectionManager): Promise { @@ -681,9 +684,9 @@ export class NetworkManager { ) } - this._storage.setDefaultDc(option) + await this._storage.setDefaultDc(option) - this._switchPrimaryDc(this._dcConnections[newDc]) + await this._switchPrimaryDc(this._dcConnections[newDc]) } private _floodWaitedRequests: Record = {} @@ -738,10 +741,11 @@ export class NetworkManager { this._lastUpdateTime = Date.now() } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return res // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { - lastError = e + lastError = e as Error if (e.code && !(e.code in CLIENT_ERRORS)) { this._log.warn( @@ -811,7 +815,7 @@ export class NetworkManager { } } - throw lastError + throw lastError! } setUpdateHandler(handler: NetworkManager['_updateHandler']): void { diff --git a/packages/core/src/network/session-connection.ts b/packages/core/src/network/session-connection.ts index eac9a262..10aa1ff6 100644 --- a/packages/core/src/network/session-connection.ts +++ b/packages/core/src/network/session-connection.ts @@ -115,7 +115,7 @@ export class SessionConnection extends PersistentConnection { this._isPfsBindingPending = false this._isPfsBindingPendingInBackground = false this._session._authKeyTemp.reset() - clearTimeout(this._pfsUpdateTimeout!) + clearTimeout(this._pfsUpdateTimeout) } this._resetSession() @@ -296,7 +296,7 @@ export class SessionConnection extends PersistentConnection { } this.onConnectionUsable() }) - .catch((err) => { + .catch((err: Error) => { this._session.authorizationPending = false this.log.error('Authorization error: %s', err.message) this.onError(err) @@ -338,7 +338,7 @@ export class SessionConnection extends PersistentConnection { return } - const tempKey = await this._session._authKeyTempSecondary + const tempKey = this._session._authKeyTempSecondary await tempKey.setup(tempAuthKey) const msgId = this._session.getMessageId() @@ -490,7 +490,7 @@ export class SessionConnection extends PersistentConnection { this._authorizePfs(true) }, (TEMP_AUTH_KEY_EXPIRY - 60) * 1000) }) - .catch((err) => { + .catch((err: Error) => { this.log.error('PFS Authorization error: %s', err.message) if (this._isPfsBindingPendingInBackground) { @@ -699,6 +699,8 @@ export class SessionConnection extends PersistentConnection { this._rescheduleInactivity() } + this.log.verbose('<<< %j', message) + if (this.params.disableUpdates) { this.log.warn( 'received updates, but updates are disabled', @@ -727,6 +729,7 @@ export class SessionConnection extends PersistentConnection { let resultType try { + // eslint-disable-next-line resultType = (message.object() as any)._ } catch (err) { resultType = message.peekUint() @@ -745,6 +748,7 @@ export class SessionConnection extends PersistentConnection { let result try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment result = message.object() as any } catch (err) { result = '[failed to parse]' @@ -1490,6 +1494,7 @@ export class SessionConnection extends PersistentConnection { const pending: PendingRpc = { method, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment promise: undefined as any, // because we need the object to make a promise data: content, stack, @@ -1599,7 +1604,7 @@ export class SessionConnection extends PersistentConnection { try { this._doFlush() } catch (e: any) { - this.log.error('flush error: %s', e.stack) + this.log.error('flush error: %s', (e as Error).stack) // should not happen unless there's a bug in the code } @@ -1718,7 +1723,7 @@ export class SessionConnection extends PersistentConnection { } const idx = this._session.getStateSchedule.index( - { getState: now } as any, + { getState: now } as PendingRpc, true, ) @@ -2035,7 +2040,7 @@ export class SessionConnection extends PersistentConnection { this._session .encryptMessage(result) .then((enc) => this.send(enc)) - .catch((err) => { + .catch((err: Error) => { this.log.error( 'error while sending pending messages (root msg_id = %l): %s', rootMsgId, diff --git a/packages/core/src/network/transports/obfuscated.ts b/packages/core/src/network/transports/obfuscated.ts index 6a05603d..c7934f18 100644 --- a/packages/core/src/network/transports/obfuscated.ts +++ b/packages/core/src/network/transports/obfuscated.ts @@ -98,8 +98,13 @@ export class ObfuscatedPacketCodec feed(data: Buffer): void { const dec = this._decryptor!.decrypt(data) + if (Buffer.isBuffer(dec)) this._inner.feed(dec) - else dec.then((dec) => this._inner.feed(dec)) + else { + dec.then((dec) => this._inner.feed(dec)).catch((err) => + this.emit('error', err), + ) + } } reset(): void { diff --git a/packages/core/src/network/transports/tcp.ts b/packages/core/src/network/transports/tcp.ts index d0b2b6d2..fa647a5e 100644 --- a/packages/core/src/network/transports/tcp.ts +++ b/packages/core/src/network/transports/tcp.ts @@ -96,28 +96,31 @@ export abstract class BaseTcpTransport this.emit('error', error) } - async handleConnect(): Promise { + handleConnect(): void { this.log.info('connected') - const initialMessage = await this._packetCodec.tag() - if (initialMessage.length) { - this._socket!.write(initialMessage, (err) => { - if (err) { - this.log.error( - 'failed to write initial message: %s', - err.stack, - ) - this.emit('error') - this.close() + Promise.resolve(this._packetCodec.tag()) + .then((initialMessage) => { + if (initialMessage.length) { + this._socket!.write(initialMessage, (err) => { + if (err) { + this.log.error( + 'failed to write initial message: %s', + err.stack, + ) + this.emit('error') + this.close() + } else { + this._state = TransportState.Ready + this.emit('ready') + } + }) } else { this._state = TransportState.Ready this.emit('ready') } }) - } else { - this._state = TransportState.Ready - this.emit('ready') - } + .catch((err) => this.emit('error', err)) } async send(bytes: Buffer): Promise { diff --git a/packages/core/src/network/transports/websocket.ts b/packages/core/src/network/transports/websocket.ts index 75a4f773..5da1e0e8 100644 --- a/packages/core/src/network/transports/websocket.ts +++ b/packages/core/src/network/transports/websocket.ts @@ -1,5 +1,4 @@ import EventEmitter from 'events' -import type WebSocket from 'ws' import { tl } from '@mtcute/tl' @@ -14,13 +13,13 @@ let ws: { if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') { try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment ws = require('ws') } catch (e) { ws = null } } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ws = window.WebSocket as any + ws = window.WebSocket } const subdomainsMap: Record = { @@ -93,7 +92,9 @@ export abstract class BaseWebSocketTransport } 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) @@ -117,8 +118,9 @@ export abstract class BaseWebSocketTransport this._socket.binaryType = 'arraybuffer' this._socket.addEventListener('message', (evt) => - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this._packetCodec.feed(typedArrayToBuffer(evt.data as any)), + this._packetCodec.feed( + typedArrayToBuffer(evt.data as NodeJS.TypedArray), + ), ) this._socket.addEventListener('open', this.handleConnect.bind(this)) this._socket.addEventListener('error', this.handleError.bind(this)) @@ -138,22 +140,32 @@ export abstract class BaseWebSocketTransport this._packetCodec.reset() } - async handleError({ error }: { error: Error }): Promise { + handleError(event: Event | { error: Error }): void { + const error = + 'error' in event ? + event.error : + new Error('unknown WebSocket error') + this.log.error('error: %s', error.stack) this.emit('error', error) } - async handleConnect(): Promise { + handleConnect(): void { this.log.info('connected') - const initialMessage = await this._packetCodec.tag() - this._socket!.send(initialMessage) - this._state = TransportState.Ready - this.emit('ready') + Promise.resolve(this._packetCodec.tag()) + .then((initialMessage) => { + this._socket!.send(initialMessage) + this._state = TransportState.Ready + this.emit('ready') + }) + .catch((err) => this.emit('error', err)) } async send(bytes: Buffer): Promise { - 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) diff --git a/packages/core/src/storage/json-file.ts b/packages/core/src/storage/json-file.ts index 20532684..eaae2ebd 100644 --- a/packages/core/src/storage/json-file.ts +++ b/packages/core/src/storage/json-file.ts @@ -1,16 +1,20 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +import type * as exitHookNs from 'exit-hook' +import type * as fsNs from 'fs' + import { JsonMemoryStorage } from './json' -let fs: any = null +type fs = typeof fsNs +let fs: fs | null = null try { - fs = require('fs') + fs = require('fs') as fs } catch (e) {} -let exitHook: any = null +type exitHook = typeof exitHookNs +let exitHook: exitHook | null = null try { - exitHook = require('exit-hook') + exitHook = require('exit-hook') as exitHook } catch (e) {} export class JsonFileStorage extends JsonMemoryStorage { @@ -47,7 +51,9 @@ export class JsonFileStorage extends JsonMemoryStorage { ) { super() - if (!fs || !fs.readFile) { throw new Error('Node fs module is not available!') } + if (!fs || !fs.readFile) { + throw new Error('Node fs module is not available!') + } this._filename = filename this._safe = params?.safe ?? true @@ -60,7 +66,9 @@ export class JsonFileStorage extends JsonMemoryStorage { } if (this._cleanup) { - this._unsubscribe = exitHook(this._onProcessExit.bind(this)) + this._unsubscribe = exitHook!.default( + this._onProcessExit.bind(this), + ) } } @@ -68,12 +76,8 @@ export class JsonFileStorage extends JsonMemoryStorage { try { this._loadJson( await new Promise((res, rej) => - fs.readFile( - this._filename, - 'utf-8', - (err?: Error, data?: string) => - - err ? rej(err) : res(data!), + fs!.readFile(this._filename, 'utf-8', (err, data) => + err ? rej(err) : res(data), ), ), ) @@ -82,16 +86,16 @@ export class JsonFileStorage extends JsonMemoryStorage { save(): Promise { return new Promise((resolve, reject) => { - fs.writeFile( + fs!.writeFile( this._safe ? this._filename + '.tmp' : this._filename, this._saveJson(), - (err?: Error) => { + (err) => { if (err) reject(err) else if (this._safe) { - fs.rename( + fs!.rename( this._filename + '.tmp', this._filename, - (err?: any) => { + (err) => { if (err && err.code !== 'ENOENT') reject(err) else resolve() }, @@ -106,12 +110,12 @@ export class JsonFileStorage extends JsonMemoryStorage { // on exit handler must be synchronous, thus we use sync methods here try { - fs.writeFileSync(this._filename, this._saveJson()) + fs!.writeFileSync(this._filename, this._saveJson()) } catch (e) {} if (this._safe) { try { - fs.unlinkSync(this._filename + '.tmp') + fs!.unlinkSync(this._filename + '.tmp') } catch (e) {} } } diff --git a/packages/core/src/storage/json.ts b/packages/core/src/storage/json.ts index 3d4376ff..8a0a94a4 100644 --- a/packages/core/src/storage/json.ts +++ b/packages/core/src/storage/json.ts @@ -1,5 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +import { tl } from '@mtcute/tl' + import { longFromFastString, longToFastString } from '../utils' -import { MemoryStorage } from './memory' +import { MemorySessionState, MemoryStorage } from './memory' /** * Helper class that provides json serialization functions @@ -12,7 +15,7 @@ export class JsonMemoryStorage extends MemoryStorage { if (key === 'authKeys') { const ret: Record = {} - value.split('|').forEach((pair: string) => { + ;(value as string).split('|').forEach((pair: string) => { const [dcId, b64] = pair.split(',') ret[dcId] = Buffer.from(b64, 'base64') }) @@ -21,27 +24,26 @@ export class JsonMemoryStorage extends MemoryStorage { } if (key === 'accessHash') { - return longFromFastString(value) + return longFromFastString(value as string) } return value - }), + }) as MemorySessionState, ) } protected _saveJson(): string { return JSON.stringify(this._state, (key, value) => { if (key === 'authKeys') { - return Object.entries(value) - .filter((it) => it[1] !== null) - .map( - ([dcId, key]) => - dcId + ',' + (key as Buffer).toString('base64'), - ) + const value_ = value as Record + + return Object.entries(value_) + .filter((it): it is [string, Buffer] => it[1] !== null) + .map(([dcId, key]) => dcId + ',' + key.toString('base64')) .join('|') } if (key === 'accessHash') { - return longToFastString(value) + return longToFastString(value as tl.Long) } return value diff --git a/packages/core/src/storage/localstorage.ts b/packages/core/src/storage/localstorage.ts index e89e054b..a7835b57 100644 --- a/packages/core/src/storage/localstorage.ts +++ b/packages/core/src/storage/localstorage.ts @@ -15,7 +15,7 @@ export class LocalstorageStorage extends JsonMemoryStorage { load(): void { try { - this._loadJson(localStorage[this._key]) + this._loadJson(localStorage[this._key] as string) } catch (e) {} } diff --git a/packages/core/src/storage/memory.ts b/packages/core/src/storage/memory.ts index 27dbff58..f06b1241 100644 --- a/packages/core/src/storage/memory.ts +++ b/packages/core/src/storage/memory.ts @@ -210,7 +210,9 @@ export class MemoryStorage implements ITelegramStorage, IStateStorage { if (tempIndex !== undefined) { const k = `${dcId}:${tempIndex}` - if (Date.now() > (this._state.authKeysTempExpiry[k] ?? 0)) { return null } + if (Date.now() > (this._state.authKeysTempExpiry[k] ?? 0)) { + return null + } return this._state.authKeysTemp[k] } @@ -248,7 +250,9 @@ export class MemoryStorage implements ITelegramStorage, IStateStorage { } } - if (peer.username) { this._state.usernameIndex[peer.username.toLowerCase()] = peer.id } + if (peer.username) { + this._state.usernameIndex[peer.username.toLowerCase()] = peer.id + } if (peer.phone) this._state.phoneIndex[peer.phone] = peer.id this._state.entities[peer.id] = peer } @@ -357,7 +361,7 @@ export class MemoryStorage implements ITelegramStorage, IStateStorage { // IStateStorage implementation - getState(key: string): unknown | null { + getState(key: string): unknown { const val = this._state.fsm[key] if (!val) return null diff --git a/packages/core/src/utils/async-lock.ts b/packages/core/src/utils/async-lock.ts index e118905f..356506a4 100644 --- a/packages/core/src/utils/async-lock.ts +++ b/packages/core/src/utils/async-lock.ts @@ -35,9 +35,10 @@ export class AsyncLock { return this.acquire() .then(() => func()) - .catch((e) => (err = e)) + .catch((e) => void (err = e)) .then(() => { this.release() + // eslint-disable-next-line @typescript-eslint/no-throw-literal if (err) throw err }) } diff --git a/packages/core/src/utils/crypto/forge-crypto.ts b/packages/core/src/utils/crypto/forge-crypto.ts index 1407c817..c4c6cb4e 100644 --- a/packages/core/src/utils/crypto/forge-crypto.ts +++ b/packages/core/src/utils/crypto/forge-crypto.ts @@ -1,3 +1,5 @@ +import type * as forgeNs from 'node-forge' + import { MaybeAsync } from '../../types' import { BaseCryptoProvider, @@ -5,11 +7,11 @@ import { IEncryptionScheme, } from './abstract' -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let forge: any = null +type forge = typeof forgeNs +let forge: forge | null = null try { - forge = require('node-forge') + forge = require('node-forge') as forge } catch (e) {} export class ForgeCryptoProvider @@ -26,14 +28,14 @@ export class ForgeCryptoProvider } createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme { - const cipher = forge.cipher[ + const cipher = forge!.cipher[ encrypt ? 'createCipher' : 'createDecipher' ]('AES-CTR', key.toString('binary')) cipher.start({ iv: iv.toString('binary') }) const update = (data: Buffer): Buffer => { cipher.output.data = '' - cipher.update(forge.util.createBuffer(data.toString('binary'))) + cipher.update(forge!.util.createBuffer(data.toString('binary'))) return Buffer.from(cipher.output.data, 'binary') } @@ -49,19 +51,24 @@ export class ForgeCryptoProvider return { encrypt(data: Buffer) { - const cipher = forge.cipher.createCipher('AES-ECB', keyBuffer) + const cipher = forge!.cipher.createCipher('AES-ECB', keyBuffer) cipher.start({}) + // @ts-expect-error wrong types cipher.mode.pad = cipher.mode.unpad = false - cipher.update(forge.util.createBuffer(data.toString('binary'))) + cipher.update(forge!.util.createBuffer(data.toString('binary'))) cipher.finish() return Buffer.from(cipher.output.data, 'binary') }, decrypt(data: Buffer) { - const cipher = forge.cipher.createDecipher('AES-ECB', keyBuffer) + const cipher = forge!.cipher.createDecipher( + 'AES-ECB', + keyBuffer, + ) cipher.start({}) + // @ts-expect-error wrong types cipher.mode.pad = cipher.mode.unpad = false - cipher.update(forge.util.createBuffer(data.toString('binary'))) + cipher.update(forge!.util.createBuffer(data.toString('binary'))) cipher.finish() return Buffer.from(cipher.output.data, 'binary') @@ -77,12 +84,13 @@ export class ForgeCryptoProvider algo = 'sha512', ): MaybeAsync { return new Promise((resolve, reject) => - forge.pkcs5.pbkdf2( + forge!.pkcs5.pbkdf2( password.toString('binary'), salt.toString('binary'), iterations, keylen, - forge.md[algo].create(), + // eslint-disable-next-line + (forge!.md as any)[algo].create(), (err: Error | null, buf: string) => err !== null ? reject(err) : @@ -93,7 +101,7 @@ export class ForgeCryptoProvider sha1(data: Buffer): MaybeAsync { return Buffer.from( - forge.md.sha1.create().update(data.toString('binary')).digest() + forge!.md.sha1.create().update(data.toString('binary')).digest() .data, 'binary', ) @@ -101,14 +109,14 @@ export class ForgeCryptoProvider sha256(data: Buffer): MaybeAsync { return Buffer.from( - forge.md.sha256.create().update(data.toString('binary')).digest() + forge!.md.sha256.create().update(data.toString('binary')).digest() .data, 'binary', ) } hmacSha256(data: Buffer, key: Buffer): MaybeAsync { - const hmac = forge.hmac.create() + const hmac = forge!.hmac.create() hmac.start('sha256', key.toString('binary')) hmac.update(data.toString('binary')) diff --git a/packages/core/src/utils/crypto/node-crypto.ts b/packages/core/src/utils/crypto/node-crypto.ts index 0d802649..e913c67e 100644 --- a/packages/core/src/utils/crypto/node-crypto.ts +++ b/packages/core/src/utils/crypto/node-crypto.ts @@ -16,10 +16,6 @@ import { export class NodeCryptoProvider extends BaseCryptoProvider implements ICryptoProvider { - constructor() { - super() - } - createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme { const cipher = (encrypt ? createCipheriv : createDecipheriv)( `aes-${key.length * 8}-ctr`, diff --git a/packages/core/src/utils/crypto/password.ts b/packages/core/src/utils/crypto/password.ts index c1d8e3c8..76ab664a 100644 --- a/packages/core/src/utils/crypto/password.ts +++ b/packages/core/src/utils/crypto/password.ts @@ -81,7 +81,7 @@ export async function computeSrpParams( // nice naming thx durov if ( !request.currentAlgo || - request.currentAlgo?._ !== + request.currentAlgo._ !== 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow' ) { throw new Error(`Unknown algo ${request.currentAlgo?._}`) diff --git a/packages/core/src/utils/deque.ts b/packages/core/src/utils/deque.ts index 54d246f9..00928d71 100644 --- a/packages/core/src/utils/deque.ts +++ b/packages/core/src/utils/deque.ts @@ -51,7 +51,7 @@ export class Deque { } } - this._elements = new Array(capacity) + this._elements = new Array(capacity) this._capacity = capacity } @@ -63,7 +63,7 @@ export class Deque { const newCapacity = n << 1 if (newCapacity < 0) throw new Error('Deque is too big') - const arr = new Array(newCapacity) + const arr = new Array(newCapacity) // copy items to the new array // copy head till the end of arr @@ -251,7 +251,7 @@ export class Deque { } clear(): void { - this._elements = new Array(this._capacity) + this._elements = new Array(this._capacity) this._head = this._tail = 0 } } diff --git a/packages/core/src/utils/logger.ts b/packages/core/src/utils/logger.ts index f8a2d836..b9e0f95c 100644 --- a/packages/core/src/utils/logger.ts +++ b/packages/core/src/utils/logger.ts @@ -9,7 +9,7 @@ if (typeof process !== 'undefined') { defaultLogLevel = envLogLevel } } else if (typeof localStorage !== 'undefined') { - const localLogLevel = parseInt(localStorage.MTCUTE_LOG_LEVEL) + const localLogLevel = parseInt(localStorage.MTCUTE_LOG_LEVEL as string) if (!isNaN(localLogLevel)) { defaultLogLevel = localLogLevel @@ -62,10 +62,10 @@ export class Logger { // custom formatters if ( - fmt.indexOf('%h') > -1 || - fmt.indexOf('%b') > -1 || - fmt.indexOf('%j') > -1 || - fmt.indexOf('%l') > -1 + fmt.includes('%h') || + fmt.includes('%b') || + fmt.includes('%j') || + fmt.includes('%l') ) { let idx = 0 fmt = fmt.replace(FORMATTER_RE, (m) => { @@ -89,7 +89,9 @@ export class Logger { v.type === 'Buffer' && Array.isArray(v.data) ) { - let str = Buffer.from(v.data).toString('base64') + let str = Buffer.from( + v.data as number[], + ).toString('base64') if (str.length > 300) { str = str.slice(0, 300) + '...' @@ -98,6 +100,7 @@ export class Logger { return str } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return v }) } @@ -152,7 +155,7 @@ export class LogManager extends Logger { constructor(tag = 'base') { // workaround because we cant pass this to super - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument super(null as any, tag) // eslint-disable-next-line @typescript-eslint/no-explicit-any ;(this as any).mgr = this diff --git a/packages/core/src/utils/long-utils.ts b/packages/core/src/utils/long-utils.ts index 5e603bbd..f6322876 100644 --- a/packages/core/src/utils/long-utils.ts +++ b/packages/core/src/utils/long-utils.ts @@ -1,4 +1,5 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-return */ // ^^ because of performance reasons import Long from 'long' @@ -84,7 +85,9 @@ export function longFromFastString(val: string, unsigned = false): Long { const low = parseInt(parts[0]) const high = parseInt(parts[1]) - if (isNaN(low) || isNaN(high)) { throw new Error(`Invalid long fast string: ${val}`) } + if (isNaN(low) || isNaN(high)) { + throw new Error(`Invalid long fast string: ${val}`) + } return new Long(low, high, unsigned) } @@ -166,19 +169,19 @@ export class LongMap { } private _setForObj(key: Long, value: V): void { - this._obj![longToFastString(key)] = value + this._obj[longToFastString(key)] = value } private _hasForObj(key: Long): boolean { - return longToFastString(key) in this._obj! + return longToFastString(key) in this._obj } private _getForObj(key: Long): V | undefined { - return this._obj![longToFastString(key)] + return this._obj[longToFastString(key)] } private _deleteForObj(key: Long): void { - delete this._obj![longToFastString(key)] + delete this._obj[longToFastString(key)] } private *_keysForObj(unsigned?: boolean): IterableIterator { @@ -188,7 +191,7 @@ export class LongMap { } private *_valuesForObj(): IterableIterator { - yield* Object.values(this._obj!) as any + yield* Object.values(this._obj) as any } private _clearForObj(): void { @@ -254,22 +257,22 @@ export class LongSet { private _addForObj(val: Long) { const k = longToFastString(val) - if (k in this._obj!) return + if (k in this._obj) return - this._obj![k] = true + this._obj[k] = true this._objSize! += 1 } private _deleteForObj(val: Long) { const k = longToFastString(val) - if (!(k in this._obj!)) return + if (!(k in this._obj)) return - delete this._obj![k] + delete this._obj[k] this._objSize! -= 1 } private _hasForObj(val: Long) { - return longToFastString(val) in this._obj! + return longToFastString(val) in this._obj } private _clearForObj() { diff --git a/packages/core/src/utils/lru-map.ts b/packages/core/src/utils/lru-map.ts index 3bcbce7e..f89b0c59 100644 --- a/packages/core/src/utils/lru-map.ts +++ b/packages/core/src/utils/lru-map.ts @@ -1,4 +1,5 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-return */ // ^^ because of performance reasons import { LongMap } from './long-utils' @@ -130,7 +131,7 @@ export class LruMap { if (oldest) { if (oldest.p) { this._last = oldest.p - this._last!.n = undefined + this._last.n = undefined } else { // exhausted this._last = undefined diff --git a/packages/core/src/utils/lru-string-set.ts b/packages/core/src/utils/lru-string-set.ts index c080a6a5..d6feff3e 100644 --- a/packages/core/src/utils/lru-string-set.ts +++ b/packages/core/src/utils/lru-string-set.ts @@ -1,4 +1,5 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ // ^^ because of performance reasons import Long from 'long' diff --git a/packages/core/tests/transport-codecs/intermediate-codec.spec.ts b/packages/core/tests/transport-codecs/intermediate-codec.spec.ts index b843387d..9b785ccc 100644 --- a/packages/core/tests/transport-codecs/intermediate-codec.spec.ts +++ b/packages/core/tests/transport-codecs/intermediate-codec.spec.ts @@ -12,7 +12,7 @@ describe('IntermediatePacketCodec', () => { it('should correctly parse immediate framing', (done) => { const codec = new IntermediatePacketCodec() - codec.on('packet', (data) => { + codec.on('packet', (data: Buffer) => { expect([...data]).eql([5, 1, 2, 3, 4]) done() }) @@ -21,7 +21,7 @@ describe('IntermediatePacketCodec', () => { it('should correctly parse incomplete framing', (done) => { const codec = new IntermediatePacketCodec() - codec.on('packet', (data) => { + codec.on('packet', (data: Buffer) => { expect([...data]).eql([5, 1, 2, 3, 4]) done() }) @@ -34,7 +34,7 @@ describe('IntermediatePacketCodec', () => { let number = 0 - codec.on('packet', (data) => { + codec.on('packet', (data: Buffer) => { if (number === 0) { expect([...data]).eql([5, 1, 2, 3, 4]) number = 1 @@ -51,7 +51,7 @@ describe('IntermediatePacketCodec', () => { it('should correctly parse transport errors', (done) => { const codec = new IntermediatePacketCodec() - codec.on('error', (err) => { + codec.on('error', (err: TransportError) => { expect(err).to.have.instanceOf(TransportError) expect(err.code).eq(404) done() @@ -63,7 +63,7 @@ describe('IntermediatePacketCodec', () => { it('should reset when called reset()', (done) => { const codec = new IntermediatePacketCodec() - codec.on('packet', (data) => { + codec.on('packet', (data: Buffer) => { expect([...data]).eql([1, 2, 3, 4, 5]) done() }) diff --git a/packages/crypto-node/tsconfig.json b/packages/crypto-node/tsconfig.json index 04e9fb30..24505be8 100644 --- a/packages/crypto-node/tsconfig.json +++ b/packages/crypto-node/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src" + "./src", + "./tests" ] } diff --git a/packages/dispatcher/src/callback-data-builder.ts b/packages/dispatcher/src/callback-data-builder.ts index 694838f3..aeffbbfd 100644 --- a/packages/dispatcher/src/callback-data-builder.ts +++ b/packages/dispatcher/src/callback-data-builder.ts @@ -36,7 +36,7 @@ export class CallbackDataBuilder { .map((f) => { const val = obj[f] - if (val.indexOf(this.sep) > -1) { + if (val.includes(this.sep)) { throw new MtArgumentError( `Value for ${f} ${val} contains separator ${this.sep} and cannot be used.`, ) diff --git a/packages/dispatcher/src/dispatcher.ts b/packages/dispatcher/src/dispatcher.ts index 21d7249e..7f6fcc54 100644 --- a/packages/dispatcher/src/dispatcher.ts +++ b/packages/dispatcher/src/dispatcher.ts @@ -1,4 +1,4 @@ -/* eslint-disable max-depth */ +/* eslint-disable */ /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */ // ^^ will be looked into in MTQ-29 @@ -122,7 +122,7 @@ export class Dispatcher { constructor( client?: TelegramClient | IStateStorage | StateKeyDelegate, storage?: IStateStorage | StateKeyDelegate, - key?: StateKeyDelegate, + key?: StateKeyDelegate ) { this.dispatchRawUpdate = this.dispatchRawUpdate.bind(this) this.dispatchUpdate = this.dispatchUpdate.bind(this) @@ -196,15 +196,14 @@ export class Dispatcher { */ dispatchRawUpdate( update: tl.TypeUpdate | tl.TypeMessage, - peers: PeersIndex, + peers: PeersIndex ): void { if (!this._client) return // order does not matter in the dispatcher, // so we can handle each update in its own task this.dispatchRawUpdateNow(update, peers).catch((err) => - // eslint-disable-next-line dot-notation - this._client!['_emitError'](err), + this._client!['_emitError'](err) ) } @@ -222,7 +221,7 @@ export class Dispatcher { */ async dispatchRawUpdateNow( update: tl.TypeUpdate | tl.TypeMessage, - peers: PeersIndex, + peers: PeersIndex ): Promise { if (!this._client) return false @@ -282,8 +281,7 @@ export class Dispatcher { // order does not matter in the dispatcher, // so we can handle each update in its own task this.dispatchUpdateNow(update).catch((err) => - // eslint-disable-next-line dot-notation - this._client!['_emitError'](err), + this._client!['_emitError'](err) ) } @@ -307,7 +305,7 @@ export class Dispatcher { // this is getting a bit crazy lol parsedState?: UpdateState | null, parsedScene?: string | null, - forceScene?: true, + forceScene?: true ): Promise { if (!this._client) return false @@ -349,7 +347,7 @@ export class Dispatcher { update, parsedState, parsedScene, - true, + true ) } } @@ -369,17 +367,16 @@ export class Dispatcher { if ( !this._customStateKeyDelegate || (customKey = await this._customStateKeyDelegate( - update.data, + update.data )) ) { parsedState = new UpdateState( - - this._storage!, + this._storage, key, this._scene ?? null, this._sceneScoped, this._customStorage, - customKey, + customKey ) } } else { @@ -421,12 +418,12 @@ export class Dispatcher { !h.check || (await h.check( update.data as any, - parsedState as never, + parsedState as never )) ) { result = await h.callback( update.data as any, - parsedState as never, + parsedState as never ) handled = true } else continue @@ -443,16 +440,15 @@ export class Dispatcher { case 'scene': { if (!parsedState) { throw new MtArgumentError( - 'Cannot use ToScene without state', + 'Cannot use ToScene without state' ) } - // eslint-disable-next-line dot-notation const scene = parsedState['_scene'] if (!scene) { throw new MtArgumentError( - 'Cannot use ToScene without entering a scene', + 'Cannot use ToScene without entering a scene' ) } @@ -462,7 +458,7 @@ export class Dispatcher { update, undefined, scene, - true, + true ) } } @@ -474,7 +470,7 @@ export class Dispatcher { const handled = await this._errorHandler( e, update, - parsedState as never, + parsedState as never ) if (!handled) throw e } else { @@ -527,7 +523,7 @@ export class Dispatcher { */ removeUpdateHandler( handler: UpdateHandler | UpdateHandler['name'] | 'all', - group = 0, + group = 0 ): void { if (group !== -1 && !(group in this._groups)) { return @@ -581,7 +577,7 @@ export class Dispatcher { update: ParsedUpdate & T, state?: UpdateState ) => MaybeAsync) - | null, + | null ): void { if (handler) this._errorHandler = handler else this._errorHandler = undefined @@ -605,7 +601,7 @@ export class Dispatcher { update: ParsedUpdate & T, state?: UpdateState ) => MaybeAsync) - | null, + | null ): void { if (handler) this._preUpdateHandler = handler else this._preUpdateHandler = undefined @@ -630,7 +626,7 @@ export class Dispatcher { update: ParsedUpdate & T, state?: UpdateState ) => MaybeAsync) - | null, + | null ): void { if (handler) this._postUpdateHandler = handler else this._postUpdateHandler = undefined @@ -643,9 +639,11 @@ export class Dispatcher { propagateErrorToParent( err: Error, update: ParsedUpdate, - state?: UpdateState, + state?: UpdateState ): MaybeAsync { - if (!this.parent) { throw new MtArgumentError('This dispatcher is not a child') } + if (!this.parent) { + throw new MtArgumentError('This dispatcher is not a child') + } if (this.parent._errorHandler) { return this.parent._errorHandler(err, update, state) @@ -667,9 +665,9 @@ export class Dispatcher { if (child._client) { throw new MtArgumentError( 'Provided dispatcher is ' + - (child._parent ? - 'already a child. Use parent.removeChild() before calling addChild()' : - 'already bound to a client. Use unbind() before calling addChild()'), + (child._parent + ? 'already a child. Use parent.removeChild() before calling addChild()' + : 'already bound to a client. Use unbind() before calling addChild()') ) } @@ -695,7 +693,7 @@ export class Dispatcher { * @param child Other dispatcher */ addChild(child: Dispatcher): void { - if (this._children.indexOf(child) > -1) return + if (this._children.includes(child)) return this._prepareChild(child) this._children.push(child) @@ -740,13 +738,13 @@ export class Dispatcher { addScene( uid: SceneName, scene: Dispatcher, - scoped = true, + scoped = true ): void { if (!this._scenes) this._scenes = {} if (uid in this._scenes) { throw new MtArgumentError( - `Scene with UID ${uid} is already registered!`, + `Scene with UID ${uid} is already registered!` ) } @@ -756,7 +754,7 @@ export class Dispatcher { if (scene._scene) { throw new MtArgumentError( - `This dispatcher is already registered as scene ${scene._scene}`, + `This dispatcher is already registered as scene ${scene._scene}` ) } @@ -807,7 +805,7 @@ export class Dispatcher { extend(other: Dispatcher): void { if (other._customStorage || other._customStateKeyDelegate) { throw new MtArgumentError( - 'Provided dispatcher has custom storage and cannot be extended from.', + 'Provided dispatcher has custom storage and cannot be extended from.' ) } @@ -849,7 +847,7 @@ export class Dispatcher { this.addScene( key as any, myScenes[key] as any, - myScenes[key]._sceneScoped as any, + myScenes[key]._sceneScoped as any ) }) } @@ -903,7 +901,7 @@ export class Dispatcher { key as any, scene as any, - this._scenes![key]._sceneScoped as any, + this._scenes![key]._sceneScoped as any ) }) } @@ -937,22 +935,21 @@ export class Dispatcher { object: Parameters[0] ): Promise> getState( - object: string | Parameters[0], + object: string | Parameters[0] ): MaybeAsync> { if (!this._storage) { throw new MtArgumentError( - 'Cannot use getUpdateState() filter without state storage', + 'Cannot use getUpdateState() filter without state storage' ) } if (typeof object === 'string') { return new UpdateState( - - this._storage!, + this._storage, object, this._scene ?? null, this._sceneScoped, - this._customStorage, + this._customStorage ) } @@ -963,12 +960,11 @@ export class Dispatcher { if (!this._customStateKeyDelegate) { return new UpdateState( - this._storage!, key, this._scene ?? null, this._sceneScoped, - this._customStorage, + this._customStorage ) } @@ -976,20 +972,19 @@ export class Dispatcher { (customKey) => { if (!customKey) { throw new MtArgumentError( - 'Cannot derive custom key from given object', + 'Cannot derive custom key from given object' ) } return new UpdateState( - this._storage!, key, this._scene ?? null, this._sceneScoped, this._customStorage, - customKey, + customKey ) - }, + } ) }) } @@ -1001,7 +996,7 @@ export class Dispatcher { * ignoring local custom storage, key delegate and scene scope. */ getGlobalState( - object: Parameters[0], + object: Parameters[0] ): Promise> { if (!this._parent) { throw new MtArgumentError('This dispatcher does not have a parent') @@ -1013,11 +1008,10 @@ export class Dispatcher { } return new UpdateState( - this._storage!, key, this._scene ?? null, - false, + false ) }) } @@ -1028,7 +1022,7 @@ export class Dispatcher { name: UpdateHandler['name'], filter: any, handler?: any, - group?: number, + group?: number ): void { if (typeof handler === 'number' || typeof handler === 'undefined') { this.addUpdateHandler( @@ -1036,7 +1030,7 @@ export class Dispatcher { name, callback: filter, }, - handler, + handler ) } else { this.addUpdateHandler( @@ -1045,7 +1039,7 @@ export class Dispatcher { callback: handler, check: filter, }, - group, + group ) } } diff --git a/packages/dispatcher/src/filters.ts b/packages/dispatcher/src/filters.ts index 96b8a3b5..9e374f94 100644 --- a/packages/dispatcher/src/filters.ts +++ b/packages/dispatcher/src/filters.ts @@ -209,18 +209,18 @@ export namespace filters { fn2: UpdateFilter, ): UpdateFilter { return (upd, state) => { - const res1 = fn1(upd, state as any) + const res1 = fn1(upd, state as UpdateState) if (typeof res1 === 'boolean') { if (!res1) return false - return fn2(upd, state as any) + return fn2(upd, state as UpdateState) } return res1.then((r1) => { if (!r1) return false - return fn2(upd, state as any) + return fn2(upd, state as UpdateState) }) } } @@ -247,18 +247,18 @@ export namespace filters { fn2: UpdateFilter, ): UpdateFilter { return (upd, state) => { - const res1 = fn1(upd, state as any) + const res1 = fn1(upd, state as UpdateState) if (typeof res1 === 'boolean') { if (res1) return true - return fn2(upd, state as any) + return fn2(upd, state as UpdateState) } return res1.then((r1) => { if (r1) return true - return fn2(upd, state as any) + return fn2(upd, state as UpdateState) }) } } @@ -947,7 +947,7 @@ export namespace filters { const m = txt.match(regex) if (m) { - (obj as any).match = m + (obj as typeof obj & { match: RegExpMatchArray }).match = m return true } @@ -1002,14 +1002,14 @@ export namespace filters { return (obj) => { const txt = extractText(obj) - return txt != null && txt.toLowerCase().indexOf(str) > -1 + return txt != null && txt.toLowerCase().includes(str) } } return (obj) => { const txt = extractText(obj) - return txt != null && txt.indexOf(str) > -1 + return txt != null && txt.includes(str) } } @@ -1157,13 +1157,16 @@ export namespace filters { // we use .replace to iterate over global regex, not to replace the text withoutPrefix .slice(m[0].length) - .replace(argumentsRe, ($0, $1, $2, $3) => { - match.push( - ($2 || $3 || '').replace(unescapeRe, '$1'), - ) + .replace( + argumentsRe, + ($0, $1, $2: string, $3: string) => { + match.push( + ($2 || $3 || '').replace(unescapeRe, '$1'), + ) - return '' - }) + return '' + }, + ) ;(msg as Message & { command: string[] }).command = match return true diff --git a/packages/dispatcher/src/state/storage.ts b/packages/dispatcher/src/state/storage.ts index d6f3eb46..de1a50c5 100644 --- a/packages/dispatcher/src/state/storage.ts +++ b/packages/dispatcher/src/state/storage.ts @@ -18,7 +18,7 @@ export interface IStateStorage { * * @param key Key of the state, as defined by {@link StateKeyDelegate} */ - getState(key: string): MaybeAsync + getState(key: string): MaybeAsync /** * Save state to the storage diff --git a/packages/dispatcher/tsconfig.json b/packages/dispatcher/tsconfig.json index 04e9fb30..24505be8 100644 --- a/packages/dispatcher/tsconfig.json +++ b/packages/dispatcher/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src" + "./src", + "./tests" ] } diff --git a/packages/file-id/tsconfig.json b/packages/file-id/tsconfig.json index 04e9fb30..7cd839c6 100644 --- a/packages/file-id/tsconfig.json +++ b/packages/file-id/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src" + "./src", + "./tests", ] } diff --git a/packages/html-parser/src/index.ts b/packages/html-parser/src/index.ts index e947336c..e7e89cb2 100644 --- a/packages/html-parser/src/index.ts +++ b/packages/html-parser/src/index.ts @@ -28,9 +28,14 @@ export function html( if (typeof it === 'boolean' || !it) return if (typeof it === 'string') { - it = HtmlMessageEntityParser.escape(it, Boolean(str.match(/=['"]$/))) + it = HtmlMessageEntityParser.escape( + it, + Boolean(str.match(/=['"]$/)), + ) } else { - if (it.mode && it.mode !== 'html') { throw new Error(`Incompatible parse mode: ${it.mode}`) } + if (it.mode && it.mode !== 'html') { + throw new Error(`Incompatible parse mode: ${it.mode}`) + } it = it.value } @@ -92,7 +97,7 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { function processPendingText(tagEnd = false) { if (!pendingText.length) return - if (!stacks.pre?.length) { + if (!stacks.pre.length) { pendingText = pendingText.replace(/[^\S\u00A0]+/gs, ' ') if (tagEnd) pendingText = pendingText.trimEnd() @@ -119,7 +124,7 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { processPendingText() // ignore tags inside pre (except pre) - if (name !== 'pre' && stacks.pre?.length) return + if (name !== 'pre' && stacks.pre.length) return let entity: tl.TypeMessageEntity @@ -262,9 +267,9 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { name = name.toLowerCase() // ignore tags inside pre (except pre) - if (name !== 'pre' && stacks.pre?.length) return + if (name !== 'pre' && stacks.pre.length) return - const entity = stacks[name]?.pop() + const entity = stacks[name].pop() if (!entity) return // unmatched close tag @@ -338,7 +343,9 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { relativeOffset = lastOffset } - if (length <= 0 || relativeOffset >= end || relativeOffset < 0) { continue } + if (length <= 0 || relativeOffset >= end || relativeOffset < 0) { + continue + } let skip = false diff --git a/packages/html-parser/tests/html-parser.spec.ts b/packages/html-parser/tests/html-parser.spec.ts index 0dc026ed..05635fb0 100644 --- a/packages/html-parser/tests/html-parser.spec.ts +++ b/packages/html-parser/tests/html-parser.spec.ts @@ -620,7 +620,7 @@ describe('HtmlMessageEntityParser', () => { expect(() => html`${unsafeString}`.value).not.throw(Error) // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error expect(() => html`${unsafeString2}`.value).throw(Error) }) }) diff --git a/packages/html-parser/tsconfig.json b/packages/html-parser/tsconfig.json index 04e9fb30..7cd839c6 100644 --- a/packages/html-parser/tsconfig.json +++ b/packages/html-parser/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src" + "./src", + "./tests", ] } diff --git a/packages/http-proxy/index.ts b/packages/http-proxy/index.ts index 619fbaaa..472bb306 100644 --- a/packages/http-proxy/index.ts +++ b/packages/http-proxy/index.ts @@ -79,7 +79,9 @@ export abstract class BaseHttpProxyTcpTransport extends BaseTcpTransport { } connect(dc: tl.RawDcOption): 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.on('error', (err) => this.emit('error', err)) @@ -133,9 +135,10 @@ export abstract class BaseHttpProxyTcpTransport extends BaseTcpTransport { } headers['Proxy-Connection'] = 'Keep-Alive' - const packet = `CONNECT ${ip} HTTP/1.1${Object.keys(headers).map( - (k) => `\r\n${k}: ${headers[k]}`, - )}\r\n\r\n` + const headersStr = Object.keys(headers) + .map((k) => `\r\n${k}: ${headers[k]}`) + .join('') + const packet = `CONNECT ${ip} HTTP/1.1${headersStr}\r\n\r\n` this._socket!.write(packet) this._socket!.once('data', (msg) => { diff --git a/packages/i18n/src/utils.ts b/packages/i18n/src/utils.ts index fddd7a29..faaf2a25 100644 --- a/packages/i18n/src/utils.ts +++ b/packages/i18n/src/utils.ts @@ -1,12 +1,11 @@ -import type { BotChatJoinRequestUpdate, BotStoppedUpdate, CallbackQuery, ChatMemberUpdate, ChosenInlineResult, InlineQuery, Message, ParsedUpdate, PollVoteUpdate, User } from '@mtcute/client' +import type * as clientNs from '@mtcute/client' import { I18nStrings, I18nValue } from './types' -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let client: any +let client: typeof clientNs try { - client = require('@mtcute/client') + client = require('@mtcute/client') as typeof clientNs } catch (e) {} /** @@ -43,7 +42,7 @@ export function createI18nStringsIndex( * @param update Update to extract language from */ export function extractLanguageFromUpdate( - update: ParsedUpdate['data'], + update: clientNs.ParsedUpdate['data'], ): string | null | undefined { if (!client) { throw new Error( @@ -54,23 +53,26 @@ export function extractLanguageFromUpdate( switch (update.constructor) { case client.Message: // if sender is Chat it will just be undefined - return ((update as Message).sender as User).language + return ((update as clientNs.Message).sender as clientNs.User) + .language + case client.PollVoteUpdate: + // if peer is Chat it will just be undefined + return ((update as clientNs.PollVoteUpdate).peer as clientNs.User) + .language case client.ChatMemberUpdate: case client.InlineQuery: case client.ChosenInlineResult: case client.CallbackQuery: - case client.PollVoteUpdate: case client.BotStoppedUpdate: case client.BotChatJoinRequestUpdate: return ( update as - | ChatMemberUpdate - | InlineQuery - | ChosenInlineResult - | CallbackQuery - | PollVoteUpdate - | BotStoppedUpdate - | BotChatJoinRequestUpdate + | clientNs.ChatMemberUpdate + | clientNs.InlineQuery + | clientNs.ChosenInlineResult + | clientNs.CallbackQuery + | clientNs.BotStoppedUpdate + | clientNs.BotChatJoinRequestUpdate ).user.language } diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json index 04e9fb30..7cd839c6 100644 --- a/packages/i18n/tsconfig.json +++ b/packages/i18n/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src" + "./src", + "./tests", ] } diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index 4c39438b..62cf7fe0 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -1,11 +1,15 @@ import Long from 'long' -import type { FormattedString, IMessageEntityParser, MessageEntity, tl } from '@mtcute/client' +import type { + FormattedString, + IMessageEntityParser, + MessageEntity, + tl, +} from '@mtcute/client' const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ -const EMOJI_REGEX = - /^tg:\/\/emoji\?id=(-?\d+)/ +const EMOJI_REGEX = /^tg:\/\/emoji\?id=(-?\d+)/ const TAG_BOLD = '**' const TAG_ITALIC = '__' @@ -41,7 +45,9 @@ export function md( if (typeof it === 'string') it = MarkdownMessageEntityParser.escape(it) else { - if (it.mode && it.mode !== 'markdown') { throw new Error(`Incompatible parse mode: ${it.mode}`) } + if (it.mode && it.mode !== 'markdown') { + throw new Error(`Incompatible parse mode: ${it.mode}`) + } it = it.value } @@ -124,12 +130,12 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { pos += 3 continue - // closed with single or double backtick - // i.e. not closed actually! this is totally valid md: - // ```javascript - // const a = ``; - // ``` - // compensate that `pos` change we made earliers + // closed with single or double backtick + // i.e. not closed actually! this is totally valid md: + // ```javascript + // const a = ``; + // ``` + // compensate that `pos` change we made earliers } else if (c === '\n') { pos -= 1 } @@ -165,7 +171,9 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { pos += 1 // ) - if (pos > text.length) { throw new Error('Malformed LINK entity, expected )') } + if (pos > text.length) { + throw new Error('Malformed LINK entity, expected )') + } if (url.length) { ent.length = result.length - ent.offset @@ -200,9 +208,8 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { ).userId = userId } } else if ((m = EMOJI_REGEX.exec(url))) { - ( - ent as tl.Mutable - )._ = 'messageEntityCustomEmoji' + (ent as tl.Mutable)._ = + 'messageEntityCustomEmoji' ;( ent as tl.Mutable ).documentId = Long.fromString(m[1]) @@ -224,10 +231,11 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { pos += 1 insideLink = true if (!('link' in stacks)) stacks.link = [] + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument stacks.link.push({ offset: result.length, length: 0, - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any) // other fields are added after the second part continue } @@ -308,9 +316,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { if (isBegin) { stacks[type].push({ - // this is valid, but idk how to make typescript happy - // eslint-disable-next-line @typescript-eslint/no-explicit-any - _: ('messageEntity' + type) as any, + _: `messageEntity${type}`, offset: result.length, length: 0, }) @@ -379,7 +385,8 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { end += escapedPos } - let startTag; let endTag: string + let startTag + let endTag: string switch (type) { case 'bold': @@ -420,7 +427,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { break case 'emoji': startTag = '[' - endTag = `](tg://emoji?id=${entity.emojiId})` + endTag = `](tg://emoji?id=${entity.emojiId!.toString()})` break default: continue diff --git a/packages/markdown-parser/tests/markdown-parser.spec.ts b/packages/markdown-parser/tests/markdown-parser.spec.ts index 4d5a0302..69fe572a 100644 --- a/packages/markdown-parser/tests/markdown-parser.spec.ts +++ b/packages/markdown-parser/tests/markdown-parser.spec.ts @@ -689,7 +689,7 @@ describe('MarkdownMessageEntityParser', () => { expect(() => md`${unsafeString}`.value).not.throw(Error) // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error expect(() => md`${unsafeString2}`.value).throw(Error) }) }) diff --git a/packages/markdown-parser/tsconfig.json b/packages/markdown-parser/tsconfig.json index 04e9fb30..7cd839c6 100644 --- a/packages/markdown-parser/tsconfig.json +++ b/packages/markdown-parser/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src" + "./src", + "./tests", ] } diff --git a/packages/mtproxy/index.ts b/packages/mtproxy/index.ts index c09901e0..2e337f8b 100644 --- a/packages/mtproxy/index.ts +++ b/packages/mtproxy/index.ts @@ -112,8 +112,7 @@ export class MtProxyTcpTransport extends BaseTcpTransport { this.packetCodecInitialized = false this._packetCodec.reset() this._packetCodec.removeAllListeners() - // eslint-disable-next-line @typescript-eslint/no-explicit-any - delete (this as any)._packetCodec + delete (this as Partial)._packetCodec } if (!this._packetCodec) { @@ -155,12 +154,16 @@ export class MtProxyTcpTransport extends BaseTcpTransport { this._socket = connect( this._proxy.port, this._proxy.host, + // MTQ-55 + // eslint-disable-next-line @typescript-eslint/no-misused-promises this._handleConnectFakeTls.bind(this), ) } else { this._socket = connect( this._proxy.port, this._proxy.host, + // MTQ-55 + this.handleConnect.bind(this), ) this._socket.on('data', (data) => this._packetCodec.feed(data)) @@ -219,18 +222,17 @@ export class MtProxyTcpTransport extends BaseTcpTransport { } } - const packetHandler = async (buf: Buffer): Promise => { - try { - await checkHelloResponse(buf) + const packetHandler = (buf: Buffer): void => { + checkHelloResponse(buf) + .then(() => { + this._socket!.off('data', packetHandler) + this._socket!.on('data', (data) => + this._packetCodec.feed(data), + ) - this._socket!.on('data', (data) => - this._packetCodec.feed(data), - ) - this._socket!.off('data', packetHandler) - this.handleConnect() - } catch (e) { - this._socket!.emit('error', e) - } + return this.handleConnect() + }) + .catch((err) => this._socket!.emit('error', err)) } this._socket!.write(hello) diff --git a/packages/node/index.ts b/packages/node/index.ts index 8c8bc522..9e61ca38 100644 --- a/packages/node/index.ts +++ b/packages/node/index.ts @@ -16,7 +16,7 @@ export { SqliteStorage } let nativeCrypto: any try { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line nativeCrypto = require('@mtcute/crypto-node').NodeNativeCryptoProvider } catch (e) {} @@ -57,6 +57,7 @@ export interface NodeTelegramClientOptions export class NodeTelegramClient extends TelegramClient { constructor(opts: NodeTelegramClientOptions) { super({ + // eslint-disable-next-line crypto: nativeCrypto ? () => new nativeCrypto() : undefined, ...opts, storage: @@ -68,7 +69,9 @@ export class NodeTelegramClient extends TelegramClient { this.registerParseMode(new HtmlMessageEntityParser()) this.registerParseMode(new MarkdownMessageEntityParser()) - if (opts.defaultParseMode) { this.setDefaultParseMode(opts.defaultParseMode) } + if (opts.defaultParseMode) { + this.setDefaultParseMode(opts.defaultParseMode) + } } private _rl?: RlInterface @@ -98,7 +101,9 @@ export class NodeTelegramClient extends TelegramClient { if (!params.phone) params.phone = () => this.input('Phone > ') if (!params.code) params.code = () => this.input('Code > ') - if (!params.password) { params.password = () => this.input('2FA password > ') } + if (!params.password) { + params.password = () => this.input('2FA password > ') + } } return super.start(params).then((user) => { diff --git a/packages/socks-proxy/index.ts b/packages/socks-proxy/index.ts index c1f190ef..1d3f4d07 100644 --- a/packages/socks-proxy/index.ts +++ b/packages/socks-proxy/index.ts @@ -1,7 +1,7 @@ // ^^ because of this._socket. we know it's not null, almost everywhere, but TS doesn't // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore +// @ts-expect-error import { normalize } from 'ip6' import { connect } from 'net' @@ -115,8 +115,12 @@ function buildSocks5Auth(username: string, password: string) { const usernameBuf = Buffer.from(username) const passwordBuf = Buffer.from(password) - if (usernameBuf.length > 255) { throw new Error(`Too long username (${usernameBuf.length} > 255)`) } - if (passwordBuf.length > 255) { throw new Error(`Too long password (${passwordBuf.length} > 255)`) } + if (usernameBuf.length > 255) { + throw new Error(`Too long username (${usernameBuf.length} > 255)`) + } + if (passwordBuf.length > 255) { + throw new Error(`Too long password (${passwordBuf.length} > 255)`) + } const buf = Buffer.alloc(3 + usernameBuf.length + passwordBuf.length) buf[0] = 0x01 // VER of auth @@ -129,7 +133,8 @@ function buildSocks5Auth(username: string, password: string) { } function writeIpv6(ip: string, buf: Buffer, offset: number): void { - ip = normalize(ip) + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + ip = normalize(ip) as string const parts = ip.split(':') if (parts.length !== 8) { @@ -193,9 +198,14 @@ export abstract class BaseSocksTcpTransport extends BaseTcpTransport { constructor(proxy: SocksProxySettings) { super() - if (proxy.version != null && proxy.version !== 4 && proxy.version !== 5) { + if ( + proxy.version != null && + proxy.version !== 4 && + proxy.version !== 5 + ) { throw new SocksProxyConnectionError( proxy, + `Invalid SOCKS version: ${proxy.version}`, ) } @@ -204,7 +214,9 @@ export abstract class BaseSocksTcpTransport extends BaseTcpTransport { } connect(dc: tl.RawDcOption): 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.on('error', (err) => this.emit('error', err)) diff --git a/packages/sqlite/index.ts b/packages/sqlite/index.ts index e4e73b10..4a256a29 100644 --- a/packages/sqlite/index.ts +++ b/packages/sqlite/index.ts @@ -359,7 +359,7 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage { private _getFromKv(key: string): T | null { const row = this._statements.getKv.get(key) as { value: string } | null - return row ? JSON.parse(row.value) : null + return row ? (JSON.parse(row.value) as T) : null } private _setToKv(key: string, value: unknown, now = false): void { @@ -375,7 +375,7 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage { } private _runMany!: (stmts: [sqlite3.Statement, unknown[]][]) => void - private _updateManyPeers!: (updates: unknown[]) => void + private _updateManyPeers!: (updates: unknown[][]) => void private _upgradeDatabase(from: number): void { if (from < 2 || from > CURRENT_VERSION) { @@ -461,14 +461,16 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage { } // helper methods - this._runMany = this._db.transaction((stmts) => { - stmts.forEach((stmt: [sqlite3.Statement, unknown[]]) => { - stmt[0].run(stmt[1]) - }) - }) + this._runMany = this._db.transaction( + (stmts: [sqlite3.Statement, unknown[]][]) => { + stmts.forEach((stmt) => { + stmt[0].run(stmt[1]) + }) + }, + ) - this._updateManyPeers = this._db.transaction((data) => { - data.forEach((it: unknown[]) => { + this._updateManyPeers = this._db.transaction((data: unknown[][]) => { + data.forEach((it: unknown) => { this._statements.updateCachedEnt.run(it) }) }) @@ -727,7 +729,7 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage { // IStateStorage implementation - getState(key: string, parse = true): unknown | null { + getState(key: string, parse = true): unknown { let val: FsmItem | undefined = this._fsmCache?.get(key) const cached = val diff --git a/packages/tl-runtime/src/platform/gzip.web.ts b/packages/tl-runtime/src/platform/gzip.web.ts index 736f6d39..128ca0ee 100644 --- a/packages/tl-runtime/src/platform/gzip.web.ts +++ b/packages/tl-runtime/src/platform/gzip.web.ts @@ -10,6 +10,8 @@ export function gzipInflate(buf: Buffer): Buffer { return typedArrayToBuffer(inflate(buf)) } +const ERROR_SIZE_LIMIT_REACHED = 'ERR_SIZE_LIMIT_REACHED' + class DeflateLimited extends Deflate { constructor(readonly limit: number) { super() @@ -21,7 +23,9 @@ class DeflateLimited extends Deflate { this._size += (chunk as Uint8Array).length if (this._size > this.limit) { - throw 'ERR_SIZE' + // caught locally + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw ERROR_SIZE_LIMIT_REACHED } super.onData(chunk) @@ -36,9 +40,9 @@ export function gzipDeflate(buf: Buffer, maxRatio?: number): Buffer | null { try { deflator.push(buf, true) } catch (e) { - if (e === 'ERR_SIZE') return null + if (e === ERROR_SIZE_LIMIT_REACHED) return null throw e } - return typedArrayToBuffer(deflator.result as Uint8Array) + return typedArrayToBuffer(deflator.result) } diff --git a/packages/tl-runtime/tests/binary-reader.spec.ts b/packages/tl-runtime/tests/binary-reader.spec.ts index e711aa18..d68c7d87 100644 --- a/packages/tl-runtime/tests/binary-reader.spec.ts +++ b/packages/tl-runtime/tests/binary-reader.spec.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line max-len +/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-return */ import { expect } from 'chai' import { randomBytes } from 'crypto' import Long from 'long' diff --git a/packages/tl-runtime/tests/binary-writer.spec.ts b/packages/tl-runtime/tests/binary-writer.spec.ts index 508be63b..91e2b4e2 100644 --- a/packages/tl-runtime/tests/binary-writer.spec.ts +++ b/packages/tl-runtime/tests/binary-writer.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ import { expect } from 'chai' import { randomBytes } from 'crypto' import Long from 'long' diff --git a/packages/tl-runtime/tsconfig.json b/packages/tl-runtime/tsconfig.json index cd6debd5..7cd839c6 100644 --- a/packages/tl-runtime/tsconfig.json +++ b/packages/tl-runtime/tsconfig.json @@ -5,5 +5,6 @@ }, "include": [ "./src", + "./tests", ] } diff --git a/packages/tl-utils/src/codegen/reader.ts b/packages/tl-utils/src/codegen/reader.ts index 1d1f9306..5b8a16e0 100644 --- a/packages/tl-utils/src/codegen/reader.ts +++ b/packages/tl-utils/src/codegen/reader.ts @@ -188,6 +188,23 @@ export function generateReaderCodeForTlEntries( ret += generateReaderCodeForTlEntry(entry, params) + '\n' }) + const usedInBareVector: Record = {} + ret.replace( + new RegExp(`(?<=r\\.vector\\(${variableName}\\[)(\\d+)(?=])`, 'g'), + (_, id: string) => { + usedInBareVector[id] = 1 + + return _ + }, + ) + + for (const id of Object.keys(usedInBareVector)) { + ret = ret.replace( + new RegExp(`(?<=^${id}:function\\()r(?=\\))`, 'gm'), + 'r=this', + ) + } + if (params.includeMethodResults) { ret += '_results:{\n' diff --git a/packages/tl-utils/src/codegen/writer.ts b/packages/tl-utils/src/codegen/writer.ts index 3f472415..ff1d87ef 100644 --- a/packages/tl-utils/src/codegen/writer.ts +++ b/packages/tl-utils/src/codegen/writer.ts @@ -60,7 +60,10 @@ export function generateWriterCodeForTlEntry( if (entry.id === 0) entry.id = computeConstructorIdFromEntry(entry) const name = bare ? entry.id : `'${entry.name}'` - let ret = `${name}:function(w${entry.arguments.length ? ',v' : ''}){` + const defaultWriter = bare ? '=this' : '' + let ret = `${name}:function(w${defaultWriter}${ + entry.arguments.length ? ',v' : '' + }){` if (!bare) ret += `w.uint(${entry.id});` diff --git a/packages/tl-utils/src/patch.ts b/packages/tl-utils/src/patch.ts index 2d4b3716..99093650 100644 --- a/packages/tl-utils/src/patch.ts +++ b/packages/tl-utils/src/patch.ts @@ -5,7 +5,8 @@ import { generateWriterCodeForTlEntries } from './codegen/writer' import { parseTlToEntries } from './parse' function evalForResult(js: string): T { - return new Function(js)() + // eslint-disable-next-line @typescript-eslint/no-implied-eval + return new Function(js)() as T } /** @@ -57,7 +58,7 @@ export function patchRuntimeTlSchema( }, // ts is not smart enough // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error writerMap: { ...writers, ...newWriters, diff --git a/packages/tl-utils/src/utils.ts b/packages/tl-utils/src/utils.ts index aada1684..d02e7ce7 100644 --- a/packages/tl-utils/src/utils.ts +++ b/packages/tl-utils/src/utils.ts @@ -68,9 +68,9 @@ export function stringifyArgumentType( if (!modifiers) return type let ret = type - if (modifiers?.isBareUnion) ret = `%${ret}` - if (modifiers?.isVector) ret = `Vector<${ret}>` - else if (modifiers?.isBareVector) ret = `vector<${ret}>` + if (modifiers.isBareUnion) ret = `%${ret}` + if (modifiers.isVector) ret = `Vector<${ret}>` + else if (modifiers.isBareVector) ret = `vector<${ret}>` if (modifiers.predicate) ret = `${modifiers.predicate}?${ret}` return ret diff --git a/packages/tl-utils/tests/merge.spec.ts b/packages/tl-utils/tests/merge.spec.ts index 5eb28f83..dea79a0d 100644 --- a/packages/tl-utils/tests/merge.spec.ts +++ b/packages/tl-utils/tests/merge.spec.ts @@ -91,8 +91,8 @@ describe('mergeTlSchemas', () => { ).eq(expected.join('\n')) } - it('merges different constructors', () => { - test( + it('merges different constructors', async () => { + await test( [ ['testClass = Test;'], ['testClass2 = Test;'], @@ -106,8 +106,8 @@ describe('mergeTlSchemas', () => { ) }) - it('merges true flags in constructors', () => { - test( + it('merges true flags in constructors', async () => { + await test( [ ['test foo:flags.0?true = Test;'], ['test bar:flags.0?true = Test;'], @@ -118,8 +118,8 @@ describe('mergeTlSchemas', () => { ) }) - it('resolves conflict using user-provided option', () => { - test( + it('resolves conflict using user-provided option', async () => { + await test( [ ['test foo:int = Test;'], ['test bar:int = Test;'], @@ -128,7 +128,7 @@ describe('mergeTlSchemas', () => { 0, 'test foo:int = Test;', ) - test( + await test( [ ['test foo:int = Test;'], ['test bar:int = Test;'], @@ -137,11 +137,15 @@ describe('mergeTlSchemas', () => { 1, 'test foo:int = Test;', ) - test([['test foo:int = Test;'], [], ['test bar:int = Test;']], 1, '') + await test( + [['test foo:int = Test;'], [], ['test bar:int = Test;']], + 1, + '', + ) }) - it('merges comments', () => { - test( + it('merges comments', async () => { + await test( [ ['test foo:flags.0?true = Test;'], ['// test ctor', 'test bar:flags.0?true = Test;'], @@ -156,8 +160,8 @@ describe('mergeTlSchemas', () => { ) }) - it('merges arguments comments', () => { - test( + it('merges arguments comments', async () => { + await test( [ ['test foo:flags.0?true = Test;'], ['// @bar bar comment', 'test bar:flags.0?true = Test;'], diff --git a/packages/tl-utils/tsconfig.json b/packages/tl-utils/tsconfig.json index 57512d68..f9556918 100644 --- a/packages/tl-utils/tsconfig.json +++ b/packages/tl-utils/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./dist" }, "include": [ - "./src/**/*.ts" + "./src/**/*.ts", + "./tests/**/*.ts" ] } diff --git a/packages/tl/scripts/documentation.ts b/packages/tl/scripts/documentation.ts index e9fa2c4c..525364e0 100644 --- a/packages/tl/scripts/documentation.ts +++ b/packages/tl/scripts/documentation.ts @@ -21,7 +21,7 @@ import { DOC_CACHE_FILE, } from './constants' import { applyDescriptionsYamlFile } from './process-descriptions-yaml' -import { packTlSchema, unpackTlSchema } from './schema' +import { packTlSchema, TlPackedSchema, unpackTlSchema } from './schema' import { fetchRetry } from './utils' export interface CachedDocumentationEntry { @@ -38,7 +38,10 @@ export interface CachedDocumentation { unions: Record } -function normalizeLinks(url: string, el: cheerio.Cheerio): void { +function normalizeLinks( + url: string, + el: cheerio.Cheerio, +): void { el.find('a').each((i, _it) => { const it = cheerio.default(_it) let href = it.attr('href') @@ -314,7 +317,7 @@ export async function getCachedDocumentation(): Promise parseInt(i)) if (major === currentLayer) { @@ -168,7 +170,7 @@ async function overrideInt53(schema: TlFullSchema): Promise { const config = JSON.parse( await readFile(join(__dirname, '../data/int53-overrides.json'), 'utf8'), - ) + ) as Record> schema.entries.forEach((entry) => { const overrides: string[] | undefined = config[entry.kind][entry.name] @@ -274,8 +276,8 @@ async function main() { console.log( 'Conflict detected at %s %s:', - nonEmptyOptions[0].entry?.kind, - nonEmptyOptions[0].entry?.name, + nonEmptyOptions[0].entry.kind, + nonEmptyOptions[0].entry.name, ) console.log('0. Remove') nonEmptyOptions.forEach((opt, idx) => { @@ -329,7 +331,9 @@ async function main() { console.log('Writing diff to file...') const oldSchema = unpackTlSchema( - JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')), + JSON.parse( + await readFile(API_SCHEMA_JSON_FILE, 'utf8'), + ) as TlPackedSchema, ) await writeFile( API_SCHEMA_DIFF_JSON_FILE, diff --git a/packages/tl/scripts/fetch-errors.ts b/packages/tl/scripts/fetch-errors.ts index 3ecc039e..808807e2 100644 --- a/packages/tl/scripts/fetch-errors.ts +++ b/packages/tl/scripts/fetch-errors.ts @@ -83,6 +83,13 @@ const virtualErrors: TlError[] = [ ] virtualErrors.forEach((it) => (it.virtual = true)) +interface TelegramErrorsSpec { + errors: Record> + descriptions: Record + user_only: string[] + bot_only: string[] +} + async function fetchFromTelegram(errors: TlErrors) { const page = await fetch(ERRORS_PAGE_TG).then((it) => it.text()) const jsonUrl = page.match( @@ -90,9 +97,9 @@ async function fetchFromTelegram(errors: TlErrors) { )?.[1] if (!jsonUrl) throw new Error('Cannot find JSON URL') - const json = await fetch(new URL(jsonUrl, ERRORS_PAGE_TG)).then((it) => + const json = (await fetch(new URL(jsonUrl, ERRORS_PAGE_TG)).then((it) => it.json(), - ) + )) as TelegramErrorsSpec // since nobody fucking guarantees that .descriptions // will have description for each described here (or vice versa), @@ -120,7 +127,7 @@ async function fetchFromTelegram(errors: TlErrors) { errors.throws[method] = [] } - if (errors.throws[method].indexOf(name) === -1) { + if (!errors.throws[method].includes(name)) { errors.throws[method].push(name) } } @@ -185,14 +192,17 @@ async function fetchFromTelethon(errors: TlErrors) { // names for better code insights // we also prefer description from telegram, if it's available and doesn't use placeholders if (description) { - const desc = description.replace(/{([a-z0-9_]+)}/gi, (_, name) => { - if (!obj._paramNames) { - obj._paramNames = [] - } - obj._paramNames.push(name) + const desc = description.replace( + /{([a-z0-9_]+)}/gi, + (_, name: string) => { + if (!obj._paramNames) { + obj._paramNames = [] + } + obj._paramNames.push(name) - return '%d' - }) + return '%d' + }, + ) if (!obj.description || obj._paramNames?.length) { obj.description = desc @@ -202,13 +212,24 @@ async function fetchFromTelethon(errors: TlErrors) { return new Promise((resolve, reject) => { parser - .on('data', ({ name, codes, description }) => - addError(name, codes, description), + .on( + 'data', + ({ + name, + codes, + description, + }: { + name: string + codes: string + description: string + }) => addError(name, codes, description), ) .on('end', resolve) .on('error', reject) - csv.text().then((it) => parser.write(it)).catch(reject) + csv.text() + .then((it) => parser.write(it)) + .catch(reject) }) } @@ -229,7 +250,7 @@ async function main() { await fetchFromTelethon(errors) virtualErrors.forEach((err) => { - if (errors.errors[err.name]) { + if (err.name in errors.errors) { console.log(`Error ${err.name} already exists and is not virtual`) return diff --git a/packages/tl/scripts/fetch-mtp.ts b/packages/tl/scripts/fetch-mtp.ts index 9b14e5e7..5633eb27 100644 --- a/packages/tl/scripts/fetch-mtp.ts +++ b/packages/tl/scripts/fetch-mtp.ts @@ -29,13 +29,13 @@ async function main() { // remove manually parsed types entries = entries.filter( (it) => - [ + ![ 'mt_msg_container', 'mt_message', 'mt_msg_copy', 'mt_gzip_packed', 'mt_rpc_result', - ].indexOf(it.name) === -1, + ].includes(it.name), ) // mtproto is handled internally, for simplicity we make them all classes diff --git a/packages/tl/scripts/gen-code.ts b/packages/tl/scripts/gen-code.ts index f104e744..db18ece2 100644 --- a/packages/tl/scripts/gen-code.ts +++ b/packages/tl/scripts/gen-code.ts @@ -6,6 +6,7 @@ import { generateTypescriptDefinitionsForTlSchema, generateWriterCodeForTlEntries, parseFullTlSchema, + TlEntry, TlErrors, TlFullSchema, } from '@mtcute/tl-utils' @@ -16,7 +17,7 @@ import { ESM_PRELUDE, MTP_SCHEMA_JSON_FILE, } from './constants' -import { unpackTlSchema } from './schema' +import { TlPackedSchema, unpackTlSchema } from './schema' const OUT_TYPINGS_FILE = join(__dirname, '../index.d.ts') const OUT_TYPINGS_JS_FILE = join(__dirname, '../index.js') @@ -92,15 +93,17 @@ async function generateWriters( } async function main() { - const errors: TlErrors = JSON.parse( + const errors = JSON.parse( await readFile(ERRORS_JSON_FILE, 'utf8'), - ) + ) as TlErrors const [apiSchema, apiLayer] = unpackTlSchema( - JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')), + JSON.parse( + await readFile(API_SCHEMA_JSON_FILE, 'utf8'), + ) as TlPackedSchema, ) const mtpSchema = parseFullTlSchema( - JSON.parse(await readFile(MTP_SCHEMA_JSON_FILE, 'utf8')), + JSON.parse(await readFile(MTP_SCHEMA_JSON_FILE, 'utf8')) as TlEntry[], ) await generateTypings(apiSchema, apiLayer, mtpSchema, errors) diff --git a/packages/tl/scripts/process-descriptions-yaml.ts b/packages/tl/scripts/process-descriptions-yaml.ts index 1e71cbd5..0e4cfbf1 100644 --- a/packages/tl/scripts/process-descriptions-yaml.ts +++ b/packages/tl/scripts/process-descriptions-yaml.ts @@ -66,7 +66,7 @@ export function applyDescriptionsYamlFile( prefix: string, ) { for (const name in obj) { - objIndex[prefix + name] = obj[name] + objIndex[prefix + name] = obj[name]! } } @@ -75,7 +75,7 @@ export function applyDescriptionsYamlFile( // process byObjects for (const name in byObjects) { - const rules = byObjects[name] + const rules = byObjects[name]! const obj = objIndex[name] if (!obj) continue @@ -88,7 +88,7 @@ export function applyDescriptionsYamlFile( if (rules.arguments) { for (const arg in rules.arguments) { const repl = unwrapMaybe( - rules.arguments[arg], + rules.arguments[arg]!, obj.arguments !== undefined && arg in obj.arguments, ) @@ -102,15 +102,14 @@ export function applyDescriptionsYamlFile( // process byArguments for (const i in objIndex) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const obj = objIndex[i] as any + const obj = objIndex[i]! for (const arg in byArguments) { if (obj.arguments && !(arg in obj.arguments)) continue const repl = unwrapMaybe( - byArguments[arg], - Boolean(obj.arguments) && arg in obj.arguments, + byArguments[arg]!, + Boolean(obj.arguments && arg in obj.arguments), ) if (repl) { @@ -126,7 +125,7 @@ export function applyDescriptionsYamlFile( if (!rule._cached) { let flags = rule.flags || '' - if (flags.indexOf('g') === -1) flags += 'g' + if (!flags.includes('g')) flags += 'g' rule._cached = new RegExp(rule.regex, flags) } @@ -135,7 +134,7 @@ export function applyDescriptionsYamlFile( } for (const i in objIndex) { - const obj = objIndex[i] + const obj = objIndex[i]! byRegex.forEach((rule) => { obj.comment = applyRegex(obj.comment, rule) diff --git a/packages/tl/tsconfig.json b/packages/tl/tsconfig.json index 6a311eb8..203c5363 100644 --- a/packages/tl/tsconfig.json +++ b/packages/tl/tsconfig.json @@ -7,6 +7,7 @@ "./binary/reader.d.ts", "./binary/writer.d.ts", "./binary/rsa-keys.d.ts", - "./scripts" + "./scripts", + "./tests/types.ts" ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cfa8a90e..304e1570 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,6 +157,9 @@ importers: '@types/ws': specifier: 8.5.4 version: 8.5.4 + exit-hook: + specifier: ^4.0.0 + version: 4.0.0 ws: specifier: 8.13.0 version: 8.13.0 @@ -2442,6 +2445,11 @@ packages: strip-final-newline: 3.0.0 dev: true + /exit-hook@4.0.0: + resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==} + engines: {node: '>=18'} + dev: true + /exit-on-epipe@1.0.1: resolution: {integrity: sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==} engines: {node: '>=0.8'}