chore!: moved away from global platform

This commit is contained in:
alina 🌸 2024-09-18 02:10:36 +03:00
parent 2ac7cbd35b
commit cb04b111a5
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
48 changed files with 166 additions and 211 deletions

View file

@ -11,19 +11,18 @@ import {
BaseTelegramClient as BaseTelegramClientBase, BaseTelegramClient as BaseTelegramClientBase,
TelegramClient as TelegramClientBase, TelegramClient as TelegramClientBase,
} from '@mtcute/core/client.js' } from '@mtcute/core/client.js'
import { setPlatform } from '@mtcute/core/platform.js'
import { downloadToFile } from './methods/download-file.js' import { downloadToFile } from './methods/download-file.js'
import { downloadAsNodeStream } from './methods/download-node-stream.js' import { downloadAsNodeStream } from './methods/download-node-stream.js'
import { BunPlatform } from './platform.js'
import { SqliteStorage } from './sqlite/index.js' import { SqliteStorage } from './sqlite/index.js'
import { BunCryptoProvider } from './utils/crypto.js' import { BunCryptoProvider } from './utils/crypto.js'
import { TcpTransport } from './utils/tcp.js' import { TcpTransport } from './utils/tcp.js'
import { BunPlatform } from './platform.js'
export type { TelegramClientOptions } export type { TelegramClientOptions }
export interface BaseTelegramClientOptions export interface BaseTelegramClientOptions
extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> { extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto' | 'platform'> {
/** /**
* Storage to use for this client. * Storage to use for this client.
* *
@ -33,23 +32,14 @@ export interface BaseTelegramClientOptions
* @default `"client.session"` * @default `"client.session"`
*/ */
storage?: string | ITelegramStorageProvider storage?: string | ITelegramStorageProvider
/**
* **ADVANCED USE ONLY**
*
* Whether to not set up the platform.
* This is useful if you call `setPlatform` yourself.
*/
platformless?: boolean
} }
export class BaseTelegramClient extends BaseTelegramClientBase { export class BaseTelegramClient extends BaseTelegramClientBase {
constructor(opts: BaseTelegramClientOptions) { constructor(opts: BaseTelegramClientOptions) {
if (!opts.platformless) setPlatform(new BunPlatform())
super({ super({
crypto: new BunCryptoProvider(), crypto: new BunCryptoProvider(),
transport: TcpTransport, transport: TcpTransport,
platform: new BunPlatform(),
...opts, ...opts,
storage: storage:
typeof opts.storage === 'string' typeof opts.storage === 'string'

View file

@ -1,6 +1,7 @@
import { afterAll, beforeAll, describe } from 'vitest' import { afterAll, beforeAll, describe } from 'vitest'
import { LogManager } from '@mtcute/core/utils.js' import { LogManager } from '@mtcute/core/utils.js'
import { import {
defaultPlatform,
testAuthKeysRepository, testAuthKeysRepository,
testKeyValueRepository, testKeyValueRepository,
testPeersRepository, testPeersRepository,
@ -14,7 +15,7 @@ if (import.meta.env.TEST_ENV === 'bun') {
const storage = new SqliteStorage(':memory:') const storage = new SqliteStorage(':memory:')
beforeAll(async () => { beforeAll(async () => {
storage.driver.setup(new LogManager()) storage.driver.setup(new LogManager(undefined, defaultPlatform), defaultPlatform)
await storage.driver.load() await storage.driver.load()
}) })

View file

@ -1,13 +1,11 @@
import { Worker, parentPort } from 'node:worker_threads' import { Worker, parentPort } from 'node:worker_threads'
import { setPlatform } from '@mtcute/core/platform.js'
import type { import type {
ClientMessageHandler, ClientMessageHandler,
RespondFn, RespondFn,
SendFn, SendFn,
SomeWorker, SomeWorker,
TelegramWorkerOptions, TelegramWorkerOptions,
TelegramWorkerPortOptions,
WorkerCustomMethods, WorkerCustomMethods,
WorkerMessageHandler, WorkerMessageHandler,
} from '@mtcute/core/worker.js' } from '@mtcute/core/worker.js'
@ -16,9 +14,13 @@ import {
TelegramWorkerPort as TelegramWorkerPortBase, TelegramWorkerPort as TelegramWorkerPortBase,
} from '@mtcute/core/worker.js' } from '@mtcute/core/worker.js'
import { BunPlatform } from './platform.js' import { BunPlatform } from './platform'
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods } export type { TelegramWorkerOptions, WorkerCustomMethods }
export interface TelegramWorkerPortOptions {
worker: SomeWorker
}
let _registered = false let _registered = false
@ -45,9 +47,11 @@ export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorke
} }
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> { export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
constructor(readonly options: TelegramWorkerPortOptions) { constructor(options: TelegramWorkerPortOptions) {
setPlatform(new BunPlatform()) super({
super(options) worker: options.worker,
platform: new BunPlatform(),
})
} }
connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] { connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {

View file

@ -14,7 +14,7 @@
"dependencies": { "dependencies": {
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@fuman/utils": "workspace:^", "@fuman/utils": "workspace:^",
"@fuman/ip": "workspace:^" "@fuman/net": "workspace:^"
}, },
"devDependencies": { "devDependencies": {
"@mtcute/test": "workspace:^" "@mtcute/test": "workspace:^"

View file

@ -1,6 +1,6 @@
import { MtArgumentError } from '@mtcute/core' import { MtArgumentError } from '@mtcute/core'
import { base64, typed } from '@fuman/utils' import { base64, typed } from '@fuman/utils'
import { ip } from '@fuman/ip' import { ip } from '@fuman/net'
import type { TelethonSession } from './types.js' import type { TelethonSession } from './types.js'

View file

@ -1,6 +1,6 @@
import { MtArgumentError } from '@mtcute/core' import { MtArgumentError } from '@mtcute/core'
import { base64, typed } from '@fuman/utils' import { base64, typed } from '@fuman/utils'
import { ip } from '@fuman/ip' import { ip } from '@fuman/net'
import type { TelethonSession } from './types.js' import type { TelethonSession } from './types.js'

View file

@ -12,8 +12,7 @@
"./utils.js": "./src/utils/index.ts", "./utils.js": "./src/utils/index.ts",
"./client.js": "./src/highlevel/client.ts", "./client.js": "./src/highlevel/client.ts",
"./worker.js": "./src/highlevel/worker/index.ts", "./worker.js": "./src/highlevel/worker/index.ts",
"./methods.js": "./src/highlevel/methods.ts", "./methods.js": "./src/highlevel/methods.ts"
"./platform.js": "./src/platform.ts"
}, },
"scripts": { "scripts": {
"build": "pnpm run -w build-package core", "build": "pnpm run -w build-package core",

View file

@ -23,6 +23,7 @@ import {
writeStringSession, writeStringSession,
} from '../utils/index.js' } from '../utils/index.js'
import { LogManager } from '../utils/logger.js' import { LogManager } from '../utils/logger.js'
import type { ICorePlatform } from '../types/platform'
import type { ConnectionState, ITelegramClient, ServerUpdateHandler } from './client.types.js' import type { ConnectionState, ITelegramClient, ServerUpdateHandler } from './client.types.js'
import { AppConfigManager } from './managers/app-config-manager.js' import { AppConfigManager } from './managers/app-config-manager.js'
@ -57,9 +58,11 @@ export class BaseTelegramClient implements ITelegramClient {
readonly mt: MtClient readonly mt: MtClient
readonly crypto: ICryptoProvider readonly crypto: ICryptoProvider
readonly storage: TelegramStorageManager readonly storage: TelegramStorageManager
readonly platform: ICorePlatform
constructor(readonly params: BaseTelegramClientOptions) { constructor(readonly params: BaseTelegramClientOptions) {
this.log = this.params.logger ?? new LogManager('client') this.log = this.params.logger ?? new LogManager('client', params.platform)
this.platform = this.params.platform
this.mt = new MtClient({ this.mt = new MtClient({
...this.params, ...this.params,
logger: this.log.create('mtproto'), logger: this.log.create('mtproto'),
@ -238,11 +241,12 @@ export class BaseTelegramClient implements ITelegramClient {
if (defaultDcAuthKey && !force) return if (defaultDcAuthKey && !force) return
const data = typeof session === 'string' ? readStringSession(session) : session const data = typeof session === 'string' ? readStringSession(session) : session
const testMode = data.primaryDcs.main.testMode
if (data.testMode && !this.params.testMode) { if (testMode && !this.params.testMode) {
throw new Error( throw new Error(
'This session string is not for the current backend. ' 'This session string is not for the current backend. '
+ `Session is ${data.testMode ? 'test' : 'prod'}, ` + `Session is ${testMode ? 'test' : 'prod'}, `
+ `but the client is ${this.params.testMode ? 'test' : 'prod'}`, + `but the client is ${this.params.testMode ? 'test' : 'prod'}`,
) )
} }
@ -285,7 +289,6 @@ export class BaseTelegramClient implements ITelegramClient {
return writeStringSession({ return writeStringSession({
version: 3, version: 3,
self: await this.storage.self.fetch(), self: await this.storage.self.fetch(),
testMode: Boolean(this.params.testMode),
primaryDcs, primaryDcs,
authKey, authKey,
}) })

View file

@ -281,7 +281,7 @@ import { withParams } from './methods/misc/with-params.js'
// from methods/_init.ts // from methods/_init.ts
// @copy // @copy
type TelegramClientOptions = ( type TelegramClientOptions = (
| (PartialOnly<Omit<BaseTelegramClientOptions, 'storage'>, 'transport' | 'crypto'> & { | (PartialOnly<Omit<BaseTelegramClientOptions, 'storage'>, 'transport' | 'crypto' | 'platform'> & {
/** /**
* Storage to use for this client. * Storage to use for this client.
* *

View file

@ -4,6 +4,7 @@ import type Long from 'long'
import type { ConnectionKind, RpcCallOptions } from '../network/index.js' import type { ConnectionKind, RpcCallOptions } from '../network/index.js'
import type { MustEqual, PublicPart } from '../types/utils.js' import type { MustEqual, PublicPart } from '../types/utils.js'
import type { Logger } from '../utils/logger.js' import type { Logger } from '../utils/logger.js'
import type { ICorePlatform } from '../types/platform'
import type { AppConfigManager } from './managers/app-config-manager.js' import type { AppConfigManager } from './managers/app-config-manager.js'
import type { TelegramStorageManager } from './storage/storage.js' import type { TelegramStorageManager } from './storage/storage.js'
@ -35,6 +36,7 @@ export interface ITelegramClient {
readonly storage: PublicPart<TelegramStorageManager> readonly storage: PublicPart<TelegramStorageManager>
readonly appConfig: PublicPart<AppConfigManager> readonly appConfig: PublicPart<AppConfigManager>
readonly stopSignal: AbortSignal readonly stopSignal: AbortSignal
readonly platform: ICorePlatform
prepare(): Promise<void> prepare(): Promise<void>
connect(): Promise<void> connect(): Promise<void>

View file

@ -15,7 +15,7 @@ import { makeParsedUpdateHandler } from '../updates/parsed.js'
// @copy // @copy
type TelegramClientOptions = ( type TelegramClientOptions = (
| (PartialOnly<Omit<BaseTelegramClientOptions, 'storage'>, 'transport' | 'crypto'> & { | (PartialOnly<Omit<BaseTelegramClientOptions, 'storage'>, 'transport' | 'crypto' | 'platform'> & {
/** /**
* Storage to use for this client. * Storage to use for this client.
* *

View file

@ -2,7 +2,6 @@ import Long from 'long'
import { parseFileId, tdFileId } from '@mtcute/file-id' import { parseFileId, tdFileId } from '@mtcute/file-id'
import { tl } from '@mtcute/tl' import { tl } from '@mtcute/tl'
import { getPlatform } from '../../../platform.js'
import { assertTypeIs } from '../../../utils/type-assertions.js' import { assertTypeIs } from '../../../utils/type-assertions.js'
import type { ITelegramClient } from '../../client.types.js' import type { ITelegramClient } from '../../client.types.js'
import { isUploadedFile } from '../../types/files/uploaded-file.js' import { isUploadedFile } from '../../types/files/uploaded-file.js'
@ -321,7 +320,7 @@ export async function _normalizeInputMedia(
} else if (typeof input === 'string' && input.match(/^file:/)) { } else if (typeof input === 'string' && input.match(/^file:/)) {
await upload(input.substring(5)) await upload(input.substring(5))
} else { } else {
const parsed = typeof input === 'string' ? parseFileId(getPlatform(), input) : input const parsed = typeof input === 'string' ? parseFileId(input) : input
if (parsed.location._ === 'photo') { if (parsed.location._ === 'photo') {
return { return {

View file

@ -3,7 +3,6 @@ import type { IReadable } from '@fuman/io'
import { read } from '@fuman/io' import { read } from '@fuman/io'
import { AsyncLock } from '@fuman/utils' import { AsyncLock } from '@fuman/utils'
import { getPlatform } from '../../../platform.js'
import { MtArgumentError } from '../../../types/errors.js' import { MtArgumentError } from '../../../types/errors.js'
import { randomLong } from '../../../utils/long-utils.js' import { randomLong } from '../../../utils/long-utils.js'
import type { ITelegramClient } from '../../client.types.js' import type { ITelegramClient } from '../../client.types.js'
@ -111,10 +110,8 @@ export async function uploadFile(
let fileName = params.fileName let fileName = params.fileName
let fileMime = params.fileMime let fileMime = params.fileMime
const platform = getPlatform() if (client.platform.normalizeFile) {
const res = await client.platform.normalizeFile(file)
if (platform.normalizeFile) {
const res = await platform.normalizeFile(file)
if (res?.file) { if (res?.file) {
file = res.file file = res.file

View file

@ -1,7 +1,6 @@
import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id'
import type { tl } from '@mtcute/tl' import type { tl } from '@mtcute/tl'
import { getPlatform } from '../../../platform.js'
import { makeInspectable } from '../../utils/index.js' import { makeInspectable } from '../../utils/index.js'
import { memoizeGetters } from '../../utils/memoize.js' import { memoizeGetters } from '../../utils/memoize.js'
import { FileLocation } from '../files/index.js' import { FileLocation } from '../files/index.js'
@ -125,7 +124,7 @@ export abstract class RawDocument extends FileLocation {
* representing this document. * representing this document.
*/ */
get fileId(): string { get fileId(): string {
return toFileId(getPlatform(), { return toFileId({
type: this._fileIdType(), type: this._fileIdType(),
dcId: this.raw.dcId, dcId: this.raw.dcId,
fileReference: this.raw.fileReference, fileReference: this.raw.fileReference,
@ -141,7 +140,7 @@ export abstract class RawDocument extends FileLocation {
* Get a unique File ID representing this document. * Get a unique File ID representing this document.
*/ */
get uniqueFileId(): string { get uniqueFileId(): string {
return toUniqueFileId(getPlatform(), td.FileType.Document, { return toUniqueFileId(td.FileType.Document, {
_: 'common', _: 'common',
id: this.raw.id, id: this.raw.id,
}) })

View file

@ -2,7 +2,6 @@ import Long from 'long'
import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id'
import type { tl } from '@mtcute/tl' import type { tl } from '@mtcute/tl'
import { getPlatform } from '../../../platform.js'
import { MtArgumentError, MtTypeAssertionError } from '../../../types/errors.js' import { MtArgumentError, MtTypeAssertionError } from '../../../types/errors.js'
import { assertTypeIs } from '../../../utils/type-assertions.js' import { assertTypeIs } from '../../../utils/type-assertions.js'
import { inflateSvgPath, strippedPhotoToJpg, svgPathToFile } from '../../utils/file-utils.js' import { inflateSvgPath, strippedPhotoToJpg, svgPathToFile } from '../../utils/file-utils.js'
@ -204,7 +203,7 @@ export class Thumbnail extends FileLocation {
} }
if (this._media._ === 'stickerSet') { if (this._media._ === 'stickerSet') {
return toFileId(getPlatform(), { return toFileId({
type: td.FileType.Thumbnail, type: td.FileType.Thumbnail,
dcId: this.dcId!, dcId: this.dcId!,
fileReference: null, fileReference: null,
@ -222,7 +221,7 @@ export class Thumbnail extends FileLocation {
}) })
} }
return toFileId(getPlatform(), { return toFileId({
type: this._media._ === 'photo' ? td.FileType.Photo : td.FileType.Thumbnail, type: this._media._ === 'photo' ? td.FileType.Photo : td.FileType.Thumbnail,
dcId: this.dcId!, dcId: this.dcId!,
fileReference: this._media.fileReference, fileReference: this._media.fileReference,
@ -251,7 +250,7 @@ export class Thumbnail extends FileLocation {
} }
if (this._media._ === 'stickerSet') { if (this._media._ === 'stickerSet') {
return toUniqueFileId(getPlatform(), td.FileType.Thumbnail, { return toUniqueFileId(td.FileType.Thumbnail, {
_: 'photo', _: 'photo',
id: Long.ZERO, id: Long.ZERO,
source: { source: {
@ -263,7 +262,7 @@ export class Thumbnail extends FileLocation {
}) })
} }
return toUniqueFileId(getPlatform(), this._media._ === 'photo' ? td.FileType.Photo : td.FileType.Thumbnail, { return toUniqueFileId(this._media._ === 'photo' ? td.FileType.Photo : td.FileType.Thumbnail, {
_: 'photo', _: 'photo',
id: this._media.id, id: this._media.id,
source: { source: {

View file

@ -2,7 +2,6 @@ import Long from 'long'
import { tdFileId, toFileId, toUniqueFileId } from '@mtcute/file-id' import { tdFileId, toFileId, toUniqueFileId } from '@mtcute/file-id'
import type { tl } from '@mtcute/tl' import type { tl } from '@mtcute/tl'
import { getPlatform } from '../../../platform.js'
import { MtArgumentError } from '../../../types/errors.js' import { MtArgumentError } from '../../../types/errors.js'
import { toggleChannelIdMark } from '../../../utils/peer-utils.js' import { toggleChannelIdMark } from '../../../utils/peer-utils.js'
import { strippedPhotoToJpg } from '../../utils/file-utils.js' import { strippedPhotoToJpg } from '../../utils/file-utils.js'
@ -62,7 +61,7 @@ export class ChatPhotoSize extends FileLocation {
throw new MtArgumentError('Input peer was invalid') throw new MtArgumentError('Input peer was invalid')
} }
return toFileId(getPlatform(), { return toFileId({
dcId: this.obj.dcId, dcId: this.obj.dcId,
type: tdFileId.FileType.ProfilePhoto, type: tdFileId.FileType.ProfilePhoto,
fileReference: null, fileReference: null,
@ -84,7 +83,7 @@ export class ChatPhotoSize extends FileLocation {
* TDLib and Bot API compatible unique File ID representing this size * TDLib and Bot API compatible unique File ID representing this size
*/ */
get uniqueFileId(): string { get uniqueFileId(): string {
return toUniqueFileId(getPlatform(), tdFileId.FileType.ProfilePhoto, { return toUniqueFileId(tdFileId.FileType.ProfilePhoto, {
_: 'photo', _: 'photo',
id: this.obj.photoId, id: this.obj.photoId,
// eslint-disable-next-line ts/no-unsafe-assignment // eslint-disable-next-line ts/no-unsafe-assignment

View file

@ -3,7 +3,6 @@ import { parseFileId, tdFileId as td } from '@mtcute/file-id'
import type { tl } from '@mtcute/tl' import type { tl } from '@mtcute/tl'
import { parseMarkedPeerId } from '../../utils/peer-utils.js' import { parseMarkedPeerId } from '../../utils/peer-utils.js'
import { getPlatform } from '../../platform.js'
import { assertNever } from '../../types/utils.js' import { assertNever } from '../../types/utils.js'
import FileType = td.FileType import FileType = td.FileType
@ -45,7 +44,7 @@ function dialogPhotoToInputPeer(
* @param fileId File ID, either parsed or as a string * @param fileId File ID, either parsed or as a string
*/ */
export function fileIdToInputWebFileLocation(fileId: string | FileId): tl.RawInputWebFileLocation { export function fileIdToInputWebFileLocation(fileId: string | FileId): tl.RawInputWebFileLocation {
if (typeof fileId === 'string') fileId = parseFileId(getPlatform(), fileId) if (typeof fileId === 'string') fileId = parseFileId(fileId)
if (fileId.location._ !== 'web') { if (fileId.location._ !== 'web') {
throw new td.ConversionError('inputWebFileLocation') throw new td.ConversionError('inputWebFileLocation')
@ -65,7 +64,7 @@ export function fileIdToInputWebFileLocation(fileId: string | FileId): tl.RawInp
* @param fileId File ID, either parsed or as a string * @param fileId File ID, either parsed or as a string
*/ */
export function fileIdToInputFileLocation(fileId: string | FileId): tl.TypeInputFileLocation { export function fileIdToInputFileLocation(fileId: string | FileId): tl.TypeInputFileLocation {
if (typeof fileId === 'string') fileId = parseFileId(getPlatform(), fileId) if (typeof fileId === 'string') fileId = parseFileId(fileId)
const loc = fileId.location const loc = fileId.location
@ -219,7 +218,7 @@ export function fileIdToInputFileLocation(fileId: string | FileId): tl.TypeInput
* @param fileId File ID, either parsed or as a string * @param fileId File ID, either parsed or as a string
*/ */
export function fileIdToInputDocument(fileId: string | FileId): tl.RawInputDocument { export function fileIdToInputDocument(fileId: string | FileId): tl.RawInputDocument {
if (typeof fileId === 'string') fileId = parseFileId(getPlatform(), fileId) if (typeof fileId === 'string') fileId = parseFileId(fileId)
if ( if (
fileId.location._ !== 'common' fileId.location._ !== 'common'
@ -256,7 +255,7 @@ export function fileIdToInputDocument(fileId: string | FileId): tl.RawInputDocum
* @param fileId File ID, either parsed or as a string * @param fileId File ID, either parsed or as a string
*/ */
export function fileIdToInputPhoto(fileId: string | FileId): tl.RawInputPhoto { export function fileIdToInputPhoto(fileId: string | FileId): tl.RawInputPhoto {
if (typeof fileId === 'string') fileId = parseFileId(getPlatform(), fileId) if (typeof fileId === 'string') fileId = parseFileId(fileId)
if (fileId.location._ !== 'photo') { if (fileId.location._ !== 'photo') {
throw new td.ConversionError('inputPhoto') throw new td.ConversionError('inputPhoto')
@ -281,7 +280,7 @@ export function fileIdToInputPhoto(fileId: string | FileId): tl.RawInputPhoto {
* @param fileId File ID, either parsed or as a string * @param fileId File ID, either parsed or as a string
*/ */
export function fileIdToEncryptedFile(fileId: string | FileId): tl.RawInputEncryptedFile { export function fileIdToEncryptedFile(fileId: string | FileId): tl.RawInputEncryptedFile {
if (typeof fileId === 'string') fileId = parseFileId(getPlatform(), fileId) if (typeof fileId === 'string') fileId = parseFileId(fileId)
if (fileId.location._ !== 'common' || fileId.type !== FileType.Encrypted) { if (fileId.location._ !== 'common' || fileId.type !== FileType.Encrypted) {
throw new td.ConversionError('inputEncryptedFile') throw new td.ConversionError('inputEncryptedFile')
@ -301,7 +300,7 @@ export function fileIdToEncryptedFile(fileId: string | FileId): tl.RawInputEncry
* @param fileId File ID, either parsed or as a string * @param fileId File ID, either parsed or as a string
*/ */
export function fileIdToSecureFile(fileId: string | FileId): tl.RawInputSecureFile { export function fileIdToSecureFile(fileId: string | FileId): tl.RawInputSecureFile {
if (typeof fileId === 'string') fileId = parseFileId(getPlatform(), fileId) if (typeof fileId === 'string') fileId = parseFileId(fileId)
if (fileId.location._ !== 'common' || (fileId.type !== FileType.Secure && fileId.type !== FileType.SecureRaw)) { if (fileId.location._ !== 'common' || (fileId.type !== FileType.Secure && fileId.type !== FileType.SecureRaw)) {
throw new td.ConversionError('inputSecureFile') throw new td.ConversionError('inputSecureFile')

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { StubTelegramClient } from '@mtcute/test' import { StubTelegramClient } from '@mtcute/test'
import { sleep } from '@fuman/utils'
import { sleep } from '../../utils/misc-utils.js'
import type { ITelegramClient } from '../client.types.js' import type { ITelegramClient } from '../client.types.js'
import { batchedQuery } from './query-batcher.js' import { batchedQuery } from './query-batcher.js'

View file

@ -6,6 +6,7 @@ import { LogManager } from '../../utils/logger.js'
import type { ConnectionState, ITelegramClient, ServerUpdateHandler } from '../client.types.js' import type { ConnectionState, ITelegramClient, ServerUpdateHandler } from '../client.types.js'
import { PeersIndex } from '../types/peers/peers-index.js' import { PeersIndex } from '../types/peers/peers-index.js'
import type { RawUpdateHandler } from '../updates/types.js' import type { RawUpdateHandler } from '../updates/types.js'
import type { ICorePlatform } from '../../types/platform'
import { AppConfigManagerProxy } from './app-config.js' import { AppConfigManagerProxy } from './app-config.js'
import { WorkerInvoker } from './invoker.js' import { WorkerInvoker } from './invoker.js'
@ -15,10 +16,12 @@ import { TelegramStorageProxy } from './storage.js'
export interface TelegramWorkerPortOptions { export interface TelegramWorkerPortOptions {
worker: SomeWorker worker: SomeWorker
platform: ICorePlatform
} }
export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> implements ITelegramClient { export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> implements ITelegramClient {
readonly log: LogManager readonly log: LogManager
readonly platform: ICorePlatform
private _connection private _connection
private _invoker private _invoker
@ -51,7 +54,8 @@ export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> imp
readonly stopSignal: AbortSignal = this._abortController.signal readonly stopSignal: AbortSignal = this._abortController.signal
constructor(readonly options: TelegramWorkerPortOptions) { constructor(readonly options: TelegramWorkerPortOptions) {
this.log = new LogManager('worker') this.log = new LogManager('worker', options.platform)
this.platform = options.platform
this._connection = this.connectToWorker(this.options.worker, this._onMessage) this._connection = this.connectToWorker(this.options.worker, this._onMessage)
this._invoker = new WorkerInvoker(this._connection[0]) this._invoker = new WorkerInvoker(this._connection[0])

View file

@ -1,6 +1,6 @@
import Long from 'long' import Long from 'long'
import { describe, expect, it, vi } from 'vitest' import { describe, expect, it, vi } from 'vitest'
import { defaultTestCryptoProvider } from '@mtcute/test' import { defaultPlatform, defaultTestCryptoProvider } from '@mtcute/test'
import type { TlBinaryReader, TlReaderMap } from '@mtcute/tl-runtime' import type { TlBinaryReader, TlReaderMap } from '@mtcute/tl-runtime'
import { hex, utf8 } from '@fuman/utils' import { hex, utf8 } from '@fuman/utils'
@ -16,7 +16,7 @@ for (let i = 0; i < 256; i += 32) {
describe('AuthKey', () => { describe('AuthKey', () => {
async function create() { async function create() {
const logger = new LogManager() const logger = new LogManager(undefined, defaultPlatform)
const readerMap: TlReaderMap = {} const readerMap: TlReaderMap = {}
const crypto = await defaultTestCryptoProvider() const crypto = await defaultTestCryptoProvider()

View file

@ -26,6 +26,7 @@ import {
defaultTestIpv6Dc, defaultTestIpv6Dc,
isTlRpcError, isTlRpcError,
} from '../utils/index.js' } from '../utils/index.js'
import type { ICorePlatform } from '../types/platform.js'
import { ConfigManager } from './config-manager.js' import { ConfigManager } from './config-manager.js'
import type { NetworkManagerExtraParams, RpcCallOptions } from './network-manager.js' import type { NetworkManagerExtraParams, RpcCallOptions } from './network-manager.js'
@ -57,6 +58,8 @@ export interface MtClientOptions {
*/ */
crypto: ICryptoProvider crypto: ICryptoProvider
platform: ICorePlatform
/** /**
* Whether to use IPv6 datacenters * Whether to use IPv6 datacenters
* (IPv6 will be preferred when choosing a DC by id) * (IPv6 will be preferred when choosing a DC by id)
@ -223,7 +226,7 @@ export class MtClient extends EventEmitter {
constructor(readonly params: MtClientOptions) { constructor(readonly params: MtClientOptions) {
super() super()
this.log = params.logger ?? new LogManager() this.log = params.logger ?? new LogManager(undefined, params.platform)
if (params.logLevel !== undefined) { if (params.logLevel !== undefined) {
this.log.mgr.level = params.logLevel this.log.mgr.level = params.logLevel
@ -256,6 +259,7 @@ export class MtClient extends EventEmitter {
log: this.log, log: this.log,
readerMap: this._readerMap, readerMap: this._readerMap,
writerMap: this._writerMap, writerMap: this._writerMap,
platform: params.platform,
...params.storageOptions, ...params.storageOptions,
}) })
@ -282,6 +286,7 @@ export class MtClient extends EventEmitter {
onNetworkChanged: connected => this.emit('networkChanged', connected), onNetworkChanged: connected => this.emit('networkChanged', connected),
onUpdate: upd => this.emit('update', upd), onUpdate: upd => this.emit('update', upd),
stopSignal: this.stopSignal, stopSignal: this.stopSignal,
platform: params.platform,
...params.network, ...params.network,
}, },
this._config, this._config,

View file

@ -4,13 +4,13 @@ import type Long from 'long'
import { type ReconnectionStrategy, defaultReconnectionStrategy } from '@fuman/net' import { type ReconnectionStrategy, defaultReconnectionStrategy } from '@fuman/net'
import { Deferred } from '@fuman/utils' import { Deferred } from '@fuman/utils'
import { getPlatform } from '../platform.js'
import type { StorageManager } from '../storage/storage.js' import type { StorageManager } from '../storage/storage.js'
import { MtArgumentError, MtUnsupportedError, MtcuteError } from '../types/index.js' import { MtArgumentError, MtUnsupportedError, MtcuteError } from '../types/index.js'
import type { ComposedMiddleware, Middleware } from '../utils/composer.js' import type { ComposedMiddleware, Middleware } from '../utils/composer.js'
import { composeMiddlewares } from '../utils/composer.js' import { composeMiddlewares } from '../utils/composer.js'
import type { DcOptions, ICryptoProvider, Logger } from '../utils/index.js' import type { DcOptions, ICryptoProvider, Logger } from '../utils/index.js'
import { assertTypeIs, isTlRpcError } from '../utils/type-assertions.js' import { assertTypeIs, isTlRpcError } from '../utils/type-assertions.js'
import type { ICorePlatform } from '../types/platform'
import type { ConfigManager } from './config-manager.js' import type { ConfigManager } from './config-manager.js'
import { basic as defaultMiddlewares } from './middlewares/default.js' import { basic as defaultMiddlewares } from './middlewares/default.js'
@ -29,6 +29,7 @@ export interface NetworkManagerParams {
storage: StorageManager storage: StorageManager
crypto: ICryptoProvider crypto: ICryptoProvider
log: Logger log: Logger
platform: ICorePlatform
enableErrorReporting: boolean enableErrorReporting: boolean
apiId: number apiId: number
@ -253,6 +254,7 @@ export class DcConnectionManager {
inactivityTimeout: this.manager.params.inactivityTimeout ?? 60_000, inactivityTimeout: this.manager.params.inactivityTimeout ?? 60_000,
enableErrorReporting: this.manager.params.enableErrorReporting, enableErrorReporting: this.manager.params.enableErrorReporting,
salts: this._salts, salts: this._salts,
platform: this.manager.params.platform,
}) })
const mainParams = baseConnectionParams() const mainParams = baseConnectionParams()
@ -485,7 +487,7 @@ export class NetworkManager {
readonly params: NetworkManagerParams & NetworkManagerExtraParams, readonly params: NetworkManagerParams & NetworkManagerExtraParams,
readonly config: ConfigManager, readonly config: ConfigManager,
) { ) {
const deviceModel = `mtcute on ${getPlatform().getDeviceModel()}` const deviceModel = `mtcute on ${params.platform.getDeviceModel()}`
this._initConnectionParams = { this._initConnectionParams = {
_: 'initConnection', _: 'initConnection',
@ -616,7 +618,7 @@ export class NetworkManager {
throw new MtArgumentError('DC manager already exists') throw new MtArgumentError('DC manager already exists')
} }
this._resetOnNetworkChange = getPlatform().onNetworkChanged?.(this.notifyNetworkChanged.bind(this)) this._resetOnNetworkChange = this.params.platform.onNetworkChanged?.(this.notifyNetworkChanged.bind(this))
const dc = new DcConnectionManager(this, defaultDcs.main.id, defaultDcs, true) const dc = new DcConnectionManager(this, defaultDcs.main.id, defaultDcs, true)
this._dcConnections.set(defaultDcs.main.id, dc) this._dcConnections.set(defaultDcs.main.id, dc)

View file

@ -149,6 +149,8 @@ export abstract class PersistentConnection extends EventEmitter {
private async _onError(err: Error) { private async _onError(err: Error) {
this._updateLogPrefix() this._updateLogPrefix()
this.onError(err) this.onError(err)
return 'reconnect' as const
} }
async changeTransport(transport: TelegramTransport): Promise<void> { async changeTransport(transport: TelegramTransport): Promise<void> {

View file

@ -5,7 +5,6 @@ import type { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime' import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime'
import { Deferred, u8 } from '@fuman/utils' import { Deferred, u8 } from '@fuman/utils'
import { getPlatform } from '../platform.js'
import { MtArgumentError, MtTimeoutError, MtcuteError } from '../types/index.js' import { MtArgumentError, MtTimeoutError, MtcuteError } from '../types/index.js'
import { createAesIgeForMessageOld } from '../utils/crypto/mtproto.js' import { createAesIgeForMessageOld } from '../utils/crypto/mtproto.js'
import type { ICryptoProvider } from '../utils/index.js' import type { ICryptoProvider } from '../utils/index.js'
@ -16,6 +15,7 @@ import {
removeFromLongArray, removeFromLongArray,
timers, timers,
} from '../utils/index.js' } from '../utils/index.js'
import type { ICorePlatform } from '../types/platform'
import { doAuthorization } from './authorization.js' import { doAuthorization } from './authorization.js'
import type { MtprotoSession, PendingMessage, PendingRpc } from './mtproto-session.js' import type { MtprotoSession, PendingMessage, PendingRpc } from './mtproto-session.js'
@ -39,6 +39,7 @@ export interface SessionConnectionParams extends PersistentConnectionParams {
readerMap: TlReaderMap readerMap: TlReaderMap
writerMap: TlWriterMap writerMap: TlWriterMap
platform: ICorePlatform
} }
const TEMP_AUTH_KEY_EXPIRY = 86400 // 24 hours const TEMP_AUTH_KEY_EXPIRY = 86400 // 24 hours
@ -97,7 +98,7 @@ export class SessionConnection extends PersistentConnection {
this._handleRawMessage = this._handleRawMessage.bind(this) this._handleRawMessage = this._handleRawMessage.bind(this)
this._usePfs = this.params.usePfs ?? false this._usePfs = this.params.usePfs ?? false
this._online = getPlatform().isOnline?.() ?? true this._online = params.platform.isOnline?.() ?? true
} }
private _online private _online

View file

@ -1,23 +1,20 @@
import { describe, expect, it, vi } from 'vitest' import { describe, expect, it, vi } from 'vitest'
import { defaultTestCryptoProvider } from '@mtcute/test' import { defaultPlatform, defaultTestCryptoProvider } from '@mtcute/test'
import { Bytes } from '@fuman/io' import { Bytes } from '@fuman/io'
import { hex } from '@fuman/utils' import { hex } from '@fuman/utils'
import { getPlatform } from '../../platform.js'
import { LogManager } from '../../utils/index.js' import { LogManager } from '../../utils/index.js'
import { IntermediatePacketCodec } from './intermediate.js' import { IntermediatePacketCodec } from './intermediate.js'
import type { MtProxyInfo } from './obfuscated.js' import type { MtProxyInfo } from './obfuscated.js'
import { ObfuscatedPacketCodec } from './obfuscated.js' import { ObfuscatedPacketCodec } from './obfuscated.js'
import { TransportError } from './abstract' import { TransportError } from './abstract.js'
const p = getPlatform()
describe('ObfuscatedPacketCodec', () => { describe('ObfuscatedPacketCodec', () => {
const create = async (randomSource?: string, proxy?: MtProxyInfo) => { const create = async (randomSource?: string, proxy?: MtProxyInfo) => {
const codec = new ObfuscatedPacketCodec(new IntermediatePacketCodec(), proxy) const codec = new ObfuscatedPacketCodec(new IntermediatePacketCodec(), proxy)
const crypto = await defaultTestCryptoProvider(randomSource) const crypto = await defaultTestCryptoProvider(randomSource)
codec.setup(crypto, new LogManager()) codec.setup(crypto, new LogManager(undefined, defaultPlatform))
return [codec, crypto] as const return [codec, crypto] as const
} }
@ -191,7 +188,7 @@ describe('ObfuscatedPacketCodec', () => {
const spyInnerReset = vi.spyOn(inner, 'reset') const spyInnerReset = vi.spyOn(inner, 'reset')
const codec = new ObfuscatedPacketCodec(inner) const codec = new ObfuscatedPacketCodec(inner)
codec.setup(await defaultTestCryptoProvider(), new LogManager()) codec.setup(await defaultTestCryptoProvider(), new LogManager(undefined, defaultPlatform))
await codec.tag() await codec.tag()

View file

@ -1,49 +0,0 @@
import type { UploadFileLike } from './highlevel/types/files/utils.js'
import { MtUnsupportedError } from './types/errors.js'
import type { MaybePromise } from './types/index.js'
// todo: can we make this non-global?
export interface ICorePlatform {
beforeExit: (fn: () => void) => () => void
log: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void
getDefaultLogLevel: () => number | null
getDeviceModel: () => string
normalizeFile?: (file: UploadFileLike) => MaybePromise<{
file?: UploadFileLike
fileSize?: number
fileName?: string
} | null>
onNetworkChanged?: (fn: (connected: boolean) => void) => () => void
isOnline?: () => boolean
}
// NB: when using with some bundlers (e.g. vite) re-importing this module will not return the same object
// so we need to store the platform in a global object to be able to survive hot-reloads etc.
// try to use Symbol if available, otherwise fallback to a string
const platformKey = typeof Symbol !== 'undefined' ? Symbol.for('mtcute.platform') : '__MTCUTE_PLATFORM__'
// eslint-disable-next-line
let _platform: ICorePlatform | null = (globalThis as any)?.[platformKey] ?? null
export function setPlatform(platform: ICorePlatform): void {
if (_platform) {
if (_platform.constructor !== platform.constructor) {
throw new MtUnsupportedError('Platform may not be changed at runtime!')
}
return
}
_platform = platform
;(globalThis as any)[platformKey] = platform
}
export function getPlatform(): ICorePlatform {
if (!_platform) {
throw new MtUnsupportedError('Platform is not set! Have you instantiated the client?')
}
return _platform
}

View file

@ -1,3 +1,4 @@
import type { ICorePlatform } from '../types/platform'
import type { MaybePromise } from '../types/utils.js' import type { MaybePromise } from '../types/utils.js'
import type { Logger } from '../utils/logger.js' import type { Logger } from '../utils/logger.js'
@ -37,7 +38,7 @@ export interface IStorageDriver {
* Setup the driver, passing the logger instance, * Setup the driver, passing the logger instance,
* in case your driver needs it * in case your driver needs it
*/ */
setup?: (log: Logger) => void setup?: (log: Logger, platform: ICorePlatform) => void
} }
/** /**
@ -53,9 +54,11 @@ export abstract class BaseStorageDriver implements IStorageDriver {
private _destroyed = false private _destroyed = false
protected _log!: Logger protected _log!: Logger
protected _platform!: ICorePlatform
setup(log: Logger): void { setup(log: Logger, platform: ICorePlatform): void {
this._log = log.create('sqlite') this._log = log.create('sqlite')
this._platform = platform
} }
protected get loaded(): boolean { protected get loaded(): boolean {

View file

@ -1,5 +1,6 @@
import { __tlReaderMap } from '@mtcute/tl/binary/reader.js' import { __tlReaderMap } from '@mtcute/tl/binary/reader.js'
import { __tlWriterMap } from '@mtcute/tl/binary/writer.js' import { __tlWriterMap } from '@mtcute/tl/binary/writer.js'
import { defaultPlatform } from '@mtcute/test'
import { LogManager } from '../../utils/logger.js' import { LogManager } from '../../utils/logger.js'
import { MemoryStorageDriver } from '../memory/driver.js' import { MemoryStorageDriver } from '../memory/driver.js'
@ -7,7 +8,7 @@ import { MemoryStorageDriver } from '../memory/driver.js'
import type { ServiceOptions } from './base.js' import type { ServiceOptions } from './base.js'
export function testServiceOptions(): ServiceOptions { export function testServiceOptions(): ServiceOptions {
const logger = new LogManager() const logger = new LogManager(undefined, defaultPlatform)
logger.level = 0 logger.level = 0
return { return {

View file

@ -1,4 +1,3 @@
import { getPlatform } from '../../platform.js'
import { BaseStorageDriver } from '../driver.js' import { BaseStorageDriver } from '../driver.js'
import type { ISqliteDatabase, ISqliteStatement } from './types.js' import type { ISqliteDatabase, ISqliteStatement } from './types.js'
@ -136,7 +135,7 @@ export abstract class BaseSqliteStorageDriver extends BaseStorageDriver {
this.db.transaction(() => this._initialize())() this.db.transaction(() => this._initialize())()
this._cleanup = getPlatform().beforeExit(() => { this._cleanup = this._platform.beforeExit(() => {
this._save() this._save()
this._destroy() this._destroy()
}) })

View file

@ -1,8 +1,8 @@
import type { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' import type { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
import { getPlatform } from '../platform.js'
import { asyncResettable } from '../utils/index.js' import { asyncResettable } from '../utils/index.js'
import type { Logger } from '../utils/logger.js' import type { Logger } from '../utils/logger.js'
import type { ICorePlatform } from '../types/platform'
import type { IMtStorageProvider } from './provider.js' import type { IMtStorageProvider } from './provider.js'
import { AuthKeysService } from './service/auth-keys.js' import { AuthKeysService } from './service/auth-keys.js'
@ -13,6 +13,7 @@ import type { IStorageDriver } from './driver.js'
interface StorageManagerOptions { interface StorageManagerOptions {
provider: IMtStorageProvider provider: IMtStorageProvider
platform: ICorePlatform
log: Logger log: Logger
readerMap: TlReaderMap readerMap: TlReaderMap
writerMap: TlWriterMap writerMap: TlWriterMap
@ -44,6 +45,7 @@ export interface StorageManagerExtraOptions {
export class StorageManager { export class StorageManager {
readonly provider: IMtStorageProvider readonly provider: IMtStorageProvider
readonly driver: IStorageDriver readonly driver: IStorageDriver
readonly platform: ICorePlatform
readonly log: Logger readonly log: Logger
readonly dcs: DefaultDcsService readonly dcs: DefaultDcsService
readonly salts: FutureSaltsService readonly salts: FutureSaltsService
@ -51,6 +53,7 @@ export class StorageManager {
constructor(readonly options: StorageManagerOptions & StorageManagerExtraOptions) { constructor(readonly options: StorageManagerOptions & StorageManagerExtraOptions) {
this.provider = this.options.provider this.provider = this.options.provider
this.platform = this.options.platform
this.driver = this.provider.driver this.driver = this.provider.driver
this.log = this.options.log.create('storage') this.log = this.options.log.create('storage')
@ -69,10 +72,10 @@ export class StorageManager {
private _cleanupRestore?: () => void private _cleanupRestore?: () => void
private _load = asyncResettable(async () => { private _load = asyncResettable(async () => {
this.driver.setup?.(this.log) this.driver.setup?.(this.log, this.platform)
if (this.options.cleanup ?? true) { if (this.options.cleanup ?? true) {
this._cleanupRestore = getPlatform().beforeExit(() => { this._cleanupRestore = this.platform.beforeExit(() => {
this._destroy().catch(err => this.log.error('cleanup error: %e', err)) this._destroy().catch(err => this.log.error('cleanup error: %e', err))
}) })
} }

View file

@ -1,3 +1,4 @@
export * from './errors.js' export * from './errors.js'
export * from './peers.js' export * from './peers.js'
export * from './utils.js' export * from './utils.js'
export * from './platform.js'

View file

@ -0,0 +1,17 @@
import type { UploadFileLike } from '../highlevel/types/files/utils.js'
import type { MaybePromise } from './index.js'
export interface ICorePlatform {
beforeExit: (fn: () => void) => () => void
log: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void
getDefaultLogLevel: () => number | null
getDeviceModel: () => string
normalizeFile?: (file: UploadFileLike) => MaybePromise<{
file?: UploadFileLike
fileSize?: number
fileName?: string
} | null>
onNetworkChanged?: (fn: (connected: boolean) => void) => () => void
isOnline?: () => boolean
}

View file

@ -1,12 +1,13 @@
import Long from 'long' import Long from 'long'
import { describe, expect, it, vi } from 'vitest' import { describe, expect, it, vi } from 'vitest'
import { tl } from '@mtcute/tl' import { tl } from '@mtcute/tl'
import { defaultPlatform } from '@mtcute/test'
import { LogManager } from './logger.js' import { LogManager } from './logger.js'
describe('logger', () => { describe('logger', () => {
const createManager = () => { const createManager = () => {
const mgr = new LogManager() const mgr = new LogManager(undefined, defaultPlatform)
mgr.level = LogManager.INFO mgr.level = LogManager.INFO
const spy = vi.fn<typeof mgr.handler>() const spy = vi.fn<typeof mgr.handler>()

View file

@ -1,8 +1,7 @@
import { tl } from '@mtcute/tl' import { tl } from '@mtcute/tl'
import { hex } from '@fuman/utils' import { hex } from '@fuman/utils'
import type { ICorePlatform } from '../platform.js' import type { ICorePlatform } from '../types/platform.js'
import { getPlatform } from '../platform.js'
import { isTlRpcError } from './type-assertions.js' import { isTlRpcError } from './type-assertions.js'
@ -160,19 +159,17 @@ export class LogManager extends Logger {
static DEBUG = 4 static DEBUG = 4
static VERBOSE = 5 static VERBOSE = 5
readonly platform: ICorePlatform
level: number level: number
handler: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void handler: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void
constructor(tag = 'base') { constructor(tag = 'base', platform: ICorePlatform) {
// workaround because we cant pass this to super // workaround because we cant pass this to super
// eslint-disable-next-line ts/no-unsafe-argument // eslint-disable-next-line ts/no-unsafe-argument
super(null as any, tag) super(null as any, tag)
;(this as any).mgr = this ;(this as any).mgr = this
this.platform = getPlatform() this.level = platform.getDefaultLogLevel() ?? DEFAULT_LOG_LEVEL
this.level = this.platform.getDefaultLogLevel() ?? DEFAULT_LOG_LEVEL this.handler = platform.log.bind(platform)
this.handler = this.platform.log.bind(this.platform)
} }
private _filter: (tag: string) => boolean = defaultFilter private _filter: (tag: string) => boolean = defaultFilter

View file

@ -11,17 +11,17 @@ import {
BaseTelegramClient as BaseTelegramClientBase, BaseTelegramClient as BaseTelegramClientBase,
TelegramClient as TelegramClientBase, TelegramClient as TelegramClientBase,
} from '@mtcute/core/client.js' } from '@mtcute/core/client.js'
import { setPlatform } from '@mtcute/core/platform.js'
import { downloadToFile } from './methods/download-file.js' import { downloadToFile } from './methods/download-file.js'
import { DenoPlatform } from './platform.js' import { DenoPlatform } from './platform.js'
import { SqliteStorage } from './sqlite/index.js' import { SqliteStorage } from './sqlite/index.js'
import { DenoCryptoProvider } from './utils/crypto.js' import { DenoCryptoProvider } from './utils/crypto.js'
import { TcpTransport } from './utils/tcp.js'
export type { TelegramClientOptions } export type { TelegramClientOptions }
export interface BaseTelegramClientOptions export interface BaseTelegramClientOptions
extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> { extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto' | 'platform'> {
/** /**
* Storage to use for this client. * Storage to use for this client.
* *
@ -31,23 +31,14 @@ export interface BaseTelegramClientOptions
* @default `"client.session"` * @default `"client.session"`
*/ */
storage?: string | ITelegramStorageProvider storage?: string | ITelegramStorageProvider
/**
* **ADVANCED USE ONLY**
*
* Whether to not set up the platform.
* This is useful if you call `setPlatform` yourself.
*/
platformless?: boolean
} }
export class BaseTelegramClient extends BaseTelegramClientBase { export class BaseTelegramClient extends BaseTelegramClientBase {
constructor(opts: BaseTelegramClientOptions) { constructor(opts: BaseTelegramClientOptions) {
if (!opts.platformless) setPlatform(new DenoPlatform())
super({ super({
crypto: new DenoCryptoProvider(), crypto: new DenoCryptoProvider(),
transport: {} as any, // todo transport: TcpTransport,
platform: new DenoPlatform(),
...opts, ...opts,
storage: storage:
typeof opts.storage === 'string' typeof opts.storage === 'string'

View file

@ -1,4 +1,4 @@
import type { ICorePlatform } from '@mtcute/core/platform.js' import type { ICorePlatform } from '@mtcute/core'
import { defaultLoggingHandler } from './common-internals-web/logging.js' import { defaultLoggingHandler } from './common-internals-web/logging.js'
import { beforeExit } from './utils/exit-hook.js' import { beforeExit } from './utils/exit-hook.js'

View file

@ -1,6 +1,7 @@
import { afterAll, beforeAll, describe } from 'vitest' import { afterAll, beforeAll, describe } from 'vitest'
import { LogManager } from '@mtcute/core/utils.js' import { LogManager } from '@mtcute/core/utils.js'
import { import {
defaultPlatform,
testAuthKeysRepository, testAuthKeysRepository,
testKeyValueRepository, testKeyValueRepository,
testPeersRepository, testPeersRepository,
@ -17,7 +18,7 @@ if (import.meta.env.TEST_ENV === 'deno') {
const storage = new SqliteStorage(':memory:') const storage = new SqliteStorage(':memory:')
beforeAll(async () => { beforeAll(async () => {
storage.driver.setup(new LogManager()) storage.driver.setup(new LogManager(undefined, defaultPlatform), defaultPlatform)
await storage.driver.load() await storage.driver.load()
}) })

View file

@ -1,11 +1,9 @@
import { setPlatform } from '@mtcute/core/platform.js'
import type { import type {
ClientMessageHandler, ClientMessageHandler,
RespondFn, RespondFn,
SendFn, SendFn,
SomeWorker, SomeWorker,
TelegramWorkerOptions, TelegramWorkerOptions,
TelegramWorkerPortOptions,
WorkerCustomMethods, WorkerCustomMethods,
WorkerMessageHandler, WorkerMessageHandler,
} from '@mtcute/core/worker.js' } from '@mtcute/core/worker.js'
@ -16,8 +14,10 @@ import {
import { DenoPlatform } from './platform.js' import { DenoPlatform } from './platform.js'
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods } export type { TelegramWorkerOptions, WorkerCustomMethods }
export interface TelegramWorkerPortOptions {
worker: SomeWorker
}
let _registered = false let _registered = false
export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> { export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> {
@ -44,9 +44,11 @@ export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorke
const platform = new DenoPlatform() const platform = new DenoPlatform()
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> { export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
constructor(readonly options: TelegramWorkerPortOptions) { constructor(options: TelegramWorkerPortOptions) {
setPlatform(platform) super({
super(options) worker: options.worker,
platform,
})
} }
connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] { connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {

View file

@ -11,15 +11,13 @@ import {
BaseTelegramClient as BaseTelegramClientBase, BaseTelegramClient as BaseTelegramClientBase,
TelegramClient as TelegramClientBase, TelegramClient as TelegramClientBase,
} from '@mtcute/core/client.js' } from '@mtcute/core/client.js'
import { setPlatform } from '@mtcute/core/platform.js'
import { NodePlatform } from './common-internals-node/platform.js'
import { downloadToFile } from './methods/download-file.js' import { downloadToFile } from './methods/download-file.js'
import { downloadAsNodeStream } from './methods/download-node-stream.js' import { downloadAsNodeStream } from './methods/download-node-stream.js'
import { SqliteStorage } from './sqlite/index.js' import { SqliteStorage } from './sqlite/index.js'
import { NodeCryptoProvider } from './utils/crypto.js' import { NodeCryptoProvider } from './utils/crypto.js'
import { TcpTransport } from './utils/tcp.js' import { TcpTransport } from './utils/tcp.js'
// import { TcpTransport } from './utils/tcp.js' import { NodePlatform } from './common-internals-node/platform.js'
export type { TelegramClientOptions } export type { TelegramClientOptions }
@ -34,7 +32,7 @@ try {
} catch {} } catch {}
export interface BaseTelegramClientOptions export interface BaseTelegramClientOptions
extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> { extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto' | 'platform'> {
/** /**
* Storage to use for this client. * Storage to use for this client.
* *
@ -45,23 +43,15 @@ export interface BaseTelegramClientOptions
*/ */
storage?: string | ITelegramStorageProvider storage?: string | ITelegramStorageProvider
/**
* **ADVANCED USE ONLY**
*
* Whether to not set up the platform.
* This is useful if you call `setPlatform` yourself.
*/
platformless?: boolean
} }
export class BaseTelegramClient extends BaseTelegramClientBase { export class BaseTelegramClient extends BaseTelegramClientBase {
constructor(opts: BaseTelegramClientOptions) { constructor(opts: BaseTelegramClientOptions) {
if (!opts.platformless) setPlatform(new NodePlatform())
super({ super({
// eslint-disable-next-line // eslint-disable-next-line
crypto: nativeCrypto ? new nativeCrypto() : new NodeCryptoProvider(), crypto: nativeCrypto ? new nativeCrypto() : new NodeCryptoProvider(),
transport: TcpTransport, transport: TcpTransport,
platform: new NodePlatform(),
...opts, ...opts,
storage: storage:
typeof opts.storage === 'string' typeof opts.storage === 'string'

View file

@ -1,14 +1,12 @@
import * as os from 'node:os' import * as os from 'node:os'
import type { ICorePlatform } from '@mtcute/core/platform.js' import type { ICorePlatform } from '@mtcute/core'
import { normalizeFile } from '../utils/normalize-file.js' import { normalizeFile } from '../utils/normalize-file.js'
import { beforeExit } from './exit-hook.js' import { beforeExit } from './exit-hook.js'
import { defaultLoggingHandler } from './logging.js' import { defaultLoggingHandler } from './logging.js'
const toBuffer = (buf: Uint8Array): Buffer => Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
export class NodePlatform implements ICorePlatform { export class NodePlatform implements ICorePlatform {
// ICorePlatform // ICorePlatform
declare log: typeof defaultLoggingHandler declare log: typeof defaultLoggingHandler

View file

@ -1,6 +1,7 @@
import { afterAll, beforeAll, describe } from 'vitest' import { afterAll, beforeAll, describe } from 'vitest'
import { LogManager } from '@mtcute/core/utils.js' import { LogManager } from '@mtcute/core/utils.js'
import { import {
defaultPlatform,
testAuthKeysRepository, testAuthKeysRepository,
testKeyValueRepository, testKeyValueRepository,
testPeersRepository, testPeersRepository,
@ -14,7 +15,7 @@ if (import.meta.env.TEST_ENV === 'node') {
const storage = new SqliteStorage(':memory:') const storage = new SqliteStorage(':memory:')
beforeAll(async () => { beforeAll(async () => {
storage.driver.setup(new LogManager()) storage.driver.setup(new LogManager(undefined, defaultPlatform), defaultPlatform)
await storage.driver.load() await storage.driver.load()
}) })

View file

@ -1,13 +1,11 @@
import { Worker, parentPort } from 'node:worker_threads' import { Worker, parentPort } from 'node:worker_threads'
import { setPlatform } from '@mtcute/core/platform.js'
import type { import type {
ClientMessageHandler, ClientMessageHandler,
RespondFn, RespondFn,
SendFn, SendFn,
SomeWorker, SomeWorker,
TelegramWorkerOptions, TelegramWorkerOptions,
TelegramWorkerPortOptions,
WorkerCustomMethods, WorkerCustomMethods,
WorkerMessageHandler, WorkerMessageHandler,
} from '@mtcute/core/worker.js' } from '@mtcute/core/worker.js'
@ -18,7 +16,11 @@ import {
import { NodePlatform } from './common-internals-node/platform.js' import { NodePlatform } from './common-internals-node/platform.js'
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods } export type { TelegramWorkerOptions, WorkerCustomMethods }
export interface TelegramWorkerPortOptions {
worker: SomeWorker
}
let _registered = false let _registered = false
@ -45,9 +47,11 @@ export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorke
} }
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> { export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
constructor(readonly options: TelegramWorkerPortOptions) { constructor(options: TelegramWorkerPortOptions) {
setPlatform(new NodePlatform()) super({
super(options) worker: options.worker,
platform: new NodePlatform(),
})
} }
connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] { connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {

View file

@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { getPlatform } from '@mtcute/core/platform.js' import { hex } from '@fuman/utils'
import { StubTelegramClient } from './client.js' import { StubTelegramClient } from './client.js'
import { createStub } from './stub.js' import { createStub } from './stub.js'
@ -25,7 +25,7 @@ describe('client stub', () => {
const client = new StubTelegramClient() const client = new StubTelegramClient()
client.onRawMessage((msg) => { client.onRawMessage((msg) => {
log.push(`message ctor=${getPlatform().hexEncode(msg.subarray(0, 4))}`) log.push(`message ctor=${hex.encode(msg.subarray(0, 4))}`)
client.close().catch(() => {}) client.close().catch(() => {})
}) })

View file

@ -3,7 +3,7 @@ import { IntermediatePacketCodec, tl } from '@mtcute/core'
import type { BaseTelegramClientOptions } from '@mtcute/core/client.js' import type { BaseTelegramClientOptions } from '@mtcute/core/client.js'
import { BaseTelegramClient } from '@mtcute/core/client.js' import { BaseTelegramClient } from '@mtcute/core/client.js'
import { defaultCryptoProvider } from './platform.js' import { defaultCryptoProvider, defaultPlatform } from './platform.js'
import { StubMemoryTelegramStorage } from './storage.js' import { StubMemoryTelegramStorage } from './storage.js'
// import { StubTelegramTransport } from './transport.js' // import { StubTelegramTransport } from './transport.js'
import type { InputResponder } from './types.js' import type { InputResponder } from './types.js'
@ -57,6 +57,7 @@ export class StubTelegramClient extends BaseTelegramClient {
packetCodec: () => new IntermediatePacketCodec(), packetCodec: () => new IntermediatePacketCodec(),
}, },
crypto: defaultCryptoProvider, crypto: defaultCryptoProvider,
platform: defaultPlatform,
...params, ...params,
}) })
} }

View file

@ -7,17 +7,16 @@ import {
BaseTelegramClient as BaseTelegramClientBase, BaseTelegramClient as BaseTelegramClientBase,
TelegramClient as TelegramClientBase, TelegramClient as TelegramClientBase,
} from '@mtcute/core/client.js' } from '@mtcute/core/client.js'
import { setPlatform } from '@mtcute/core/platform.js'
import { WebCryptoProvider } from './crypto.js' import { WebCryptoProvider } from './crypto.js'
import { IdbStorage } from './idb/index.js' import { IdbStorage } from './idb/index.js'
import { WebPlatform } from './platform.js'
import { WebSocketTransport } from './websocket.js' import { WebSocketTransport } from './websocket.js'
import { WebPlatform } from './platform.js'
export type { TelegramClientOptions } export type { TelegramClientOptions }
export interface BaseTelegramClientOptions export interface BaseTelegramClientOptions
extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> { extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto' | 'platform'> {
/** /**
* Storage to use for this client. * Storage to use for this client.
* *
@ -27,23 +26,14 @@ export interface BaseTelegramClientOptions
* @default `"client.session"` * @default `"client.session"`
*/ */
storage?: string | ITelegramStorageProvider storage?: string | ITelegramStorageProvider
/**
* **ADVANCED USE ONLY**
*
* Whether to not set up the platform.
* This is useful if you call `setPlatform` yourself.
*/
platformless?: boolean
} }
export class BaseTelegramClient extends BaseTelegramClientBase { export class BaseTelegramClient extends BaseTelegramClientBase {
constructor(opts: BaseTelegramClientOptions) { constructor(opts: BaseTelegramClientOptions) {
if (!opts.platformless) setPlatform(new WebPlatform())
super({ super({
crypto: new WebCryptoProvider(), crypto: new WebCryptoProvider(),
transport: new WebSocketTransport(), transport: new WebSocketTransport(),
platform: new WebPlatform(),
...opts, ...opts,
storage: storage:
typeof opts.storage === 'string' typeof opts.storage === 'string'

View file

@ -1,4 +1,4 @@
import type { ICorePlatform } from '@mtcute/core/platform.js' import type { ICorePlatform } from '@mtcute/core'
import { defaultLoggingHandler } from './common-internals-web/logging.js' import { defaultLoggingHandler } from './common-internals-web/logging.js'
import { beforeExit } from './exit-hook.js' import { beforeExit } from './exit-hook.js'

View file

@ -1,12 +1,10 @@
/* eslint-disable no-restricted-globals */ /* eslint-disable no-restricted-globals */
import { setPlatform } from '@mtcute/core/platform.js'
import type { import type {
ClientMessageHandler, ClientMessageHandler,
RespondFn, RespondFn,
SendFn, SendFn,
SomeWorker, SomeWorker,
TelegramWorkerOptions, TelegramWorkerOptions,
TelegramWorkerPortOptions,
WorkerCustomMethods, WorkerCustomMethods,
WorkerMessageHandler, WorkerMessageHandler,
} from '@mtcute/core/worker.js' } from '@mtcute/core/worker.js'
@ -17,8 +15,10 @@ import {
import { WebPlatform } from './platform.js' import { WebPlatform } from './platform.js'
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods } export type { TelegramWorkerOptions, WorkerCustomMethods }
export interface TelegramWorkerPortOptions {
worker: SomeWorker
}
let _registered = false let _registered = false
export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> { export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> {
@ -103,12 +103,14 @@ export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorke
} }
} }
const platform = new WebPlatform() const platform = /* #__PURE__ */ new WebPlatform()
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> { export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
constructor(readonly options: TelegramWorkerPortOptions) { constructor(options: TelegramWorkerPortOptions) {
setPlatform(platform) super({
super(options) worker: options.worker,
platform,
})
} }
connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] { connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {

View file

@ -132,9 +132,9 @@ importers:
packages/convert: packages/convert:
dependencies: dependencies:
'@fuman/ip': '@fuman/net':
specifier: workspace:^ specifier: workspace:^
version: link:../../private/fuman/packages/ip version: link:../../private/fuman/packages/net
'@fuman/utils': '@fuman/utils':
specifier: workspace:^ specifier: workspace:^
version: link:../../private/fuman/packages/utils version: link:../../private/fuman/packages/utils