fix: more fixes for web
This commit is contained in:
parent
c1f679c74c
commit
15c855df84
7 changed files with 92 additions and 71 deletions
|
@ -30,6 +30,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"browser": {
|
||||||
|
"./cjs/methods/files/_platform.js": "./cjs/methods/files/_platform.web.js",
|
||||||
|
"./esm/methods/files/_platform.js": "./esm/methods/files/_platform.web.js"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "18.16.0",
|
"@types/node": "18.16.0",
|
||||||
"@mtcute/core": "workspace:^1.0.0",
|
"@mtcute/core": "workspace:^1.0.0",
|
||||||
|
|
32
packages/client/src/methods/files/_platform.ts
Normal file
32
packages/client/src/methods/files/_platform.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { createReadStream, promises, ReadStream } from 'node:fs'
|
||||||
|
import { basename } from 'node:path'
|
||||||
|
import { Readable } from 'node:stream'
|
||||||
|
|
||||||
|
import { nodeReadableToWeb } from '../../utils/stream-utils.js'
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _createFileStream(path: string): ReadStream {
|
||||||
|
return createReadStream(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _isFileStream(stream: unknown): stream is ReadStream {
|
||||||
|
return stream instanceof ReadStream
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export async function _extractFileStreamMeta(stream: ReadStream): Promise<[string, number]> {
|
||||||
|
const fileName = basename(stream.path.toString())
|
||||||
|
const fileSize = await promises.stat(stream.path.toString()).then((stat) => stat.size)
|
||||||
|
|
||||||
|
return [fileName, fileSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _handleNodeStream<T>(val: T | Readable): T | ReadableStream<Uint8Array> {
|
||||||
|
if (val instanceof Readable) {
|
||||||
|
return nodeReadableToWeb(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
23
packages/client/src/methods/files/_platform.web.ts
Normal file
23
packages/client/src/methods/files/_platform.web.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _createFileStream(): never {
|
||||||
|
throw new MtArgumentError('Cannot create file stream on web platform')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _isFileStream() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _extractFileStreamMeta(): never {
|
||||||
|
throw new Error('UNREACHABLE')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _handleNodeStream(val: unknown) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// all the above functions shall be inlined by terser
|
|
@ -1,30 +1,15 @@
|
||||||
import fileType from 'file-type'
|
import fileType from 'file-type'
|
||||||
// eslint-disable-next-line no-restricted-imports
|
|
||||||
import type { ReadStream } from 'fs'
|
|
||||||
import { createRequire } from 'module'
|
|
||||||
|
|
||||||
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
|
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
|
||||||
import { randomLong } from '@mtcute/core/utils.js'
|
import { randomLong } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
import { UploadedFile, UploadFileLike } from '../../types/index.js'
|
import { UploadedFile, UploadFileLike } from '../../types/index.js'
|
||||||
import { determinePartSize, isProbablyPlainText } from '../../utils/file-utils.js'
|
import { determinePartSize, isProbablyPlainText } from '../../utils/file-utils.js'
|
||||||
import { bufferToStream, createChunkedReader, nodeReadableToWeb, streamToBuffer } from '../../utils/stream-utils.js'
|
import { bufferToStream, createChunkedReader, streamToBuffer } from '../../utils/stream-utils.js'
|
||||||
|
import { _createFileStream, _extractFileStreamMeta, _handleNodeStream, _isFileStream } from './_platform.js'
|
||||||
|
|
||||||
const { fromBuffer: fileTypeFromBuffer } = fileType
|
const { fromBuffer: fileTypeFromBuffer } = fileType
|
||||||
|
|
||||||
let fs: typeof import('fs') | null = null
|
|
||||||
let path: typeof import('path') | null = null
|
|
||||||
let nodeStream: typeof import('stream') | null = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
// @only-if-esm
|
|
||||||
const require = createRequire(import.meta.url)
|
|
||||||
// @/only-if-esm
|
|
||||||
fs = require('fs') as typeof import('fs')
|
|
||||||
path = require('path') as typeof import('path')
|
|
||||||
nodeStream = require('stream') as typeof import('stream')
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
const OVERRIDE_MIME: Record<string, string> = {
|
const OVERRIDE_MIME: Record<string, string> = {
|
||||||
// tg doesn't interpret `audio/opus` files as voice messages for some reason
|
// tg doesn't interpret `audio/opus` files as voice messages for some reason
|
||||||
'audio/opus': 'audio/ogg',
|
'audio/opus': 'audio/ogg',
|
||||||
|
@ -134,20 +119,11 @@ export async function uploadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof file === 'string') {
|
if (typeof file === 'string') {
|
||||||
if (!fs) {
|
file = _createFileStream(file)
|
||||||
throw new MtArgumentError('Local paths are only supported for NodeJS!')
|
|
||||||
}
|
|
||||||
file = fs.createReadStream(file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs && file instanceof fs.ReadStream) {
|
if (_isFileStream(file)) {
|
||||||
fileName = path!.basename(file.path.toString())
|
[fileName, fileSize] = await _extractFileStreamMeta(file)
|
||||||
fileSize = await new Promise((res, rej) => {
|
|
||||||
fs!.stat((file as ReadStream).path.toString(), (err, stat) => {
|
|
||||||
if (err) rej(err)
|
|
||||||
res(stat.size)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// fs.ReadStream is a subclass of Readable, will be handled below
|
// fs.ReadStream is a subclass of Readable, will be handled below
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,9 +162,7 @@ export async function uploadFile(
|
||||||
file = file.body
|
file = file.body
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeStream && file instanceof nodeStream.Readable) {
|
file = _handleNodeStream(file)
|
||||||
file = nodeReadableToWeb(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(file instanceof ReadableStream)) {
|
if (!(file instanceof ReadableStream)) {
|
||||||
throw new MtArgumentError('Could not convert input `file` to stream!')
|
throw new MtArgumentError('Could not convert input `file` to stream!')
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-argument */
|
||||||
|
|
||||||
import { createRequire } from 'module'
|
|
||||||
|
|
||||||
import { base64Encode } from '@mtcute/core/utils.js'
|
import { base64Encode } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
let util: typeof import('util') | null = null
|
const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom')
|
||||||
|
|
||||||
try {
|
|
||||||
// @only-if-esm
|
|
||||||
const require = createRequire(import.meta.url)
|
|
||||||
// @/only-if-esm
|
|
||||||
util = require('util') as typeof import('util')
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
// get all property names. unlike Object.getOwnPropertyNames,
|
// get all property names. unlike Object.getOwnPropertyNames,
|
||||||
// also gets inherited property names
|
// also gets inherited property names
|
||||||
|
@ -83,7 +74,5 @@ export function makeInspectable<T>(obj: new (...args: any[]) => T, props?: (keyo
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
if (util) {
|
obj.prototype[customInspectSymbol] = obj.prototype.toJSON
|
||||||
obj.prototype[util.inspect.custom] = obj.prototype.toJSON
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ export * from './abstract.js'
|
||||||
export * from './intermediate.js'
|
export * from './intermediate.js'
|
||||||
export * from './obfuscated.js'
|
export * from './obfuscated.js'
|
||||||
export * from './streamed.js'
|
export * from './streamed.js'
|
||||||
export * from './tcp.js'
|
|
||||||
export * from './websocket.js'
|
|
||||||
export * from './wrapped.js'
|
export * from './wrapped.js'
|
||||||
|
|
||||||
import { _defaultTransportFactory } from '../../utils/platform/transport.js'
|
import { _defaultTransportFactory } from '../../utils/platform/transport.js'
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import EventEmitter from 'events'
|
import EventEmitter from 'events'
|
||||||
import { createRequire } from 'module'
|
|
||||||
|
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
@ -9,22 +8,8 @@ import { IPacketCodec, ITelegramTransport, TransportState } from './abstract.js'
|
||||||
import { IntermediatePacketCodec } from './intermediate.js'
|
import { IntermediatePacketCodec } from './intermediate.js'
|
||||||
import { ObfuscatedPacketCodec } from './obfuscated.js'
|
import { ObfuscatedPacketCodec } from './obfuscated.js'
|
||||||
|
|
||||||
let ws: {
|
export type WebSocketConstructor = {
|
||||||
new (address: string, options?: string): WebSocket
|
new (address: string, protocol?: string): WebSocket
|
||||||
} | null
|
|
||||||
|
|
||||||
if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {
|
|
||||||
try {
|
|
||||||
// @only-if-esm
|
|
||||||
const require = createRequire(import.meta.url)
|
|
||||||
// @/only-if-esm
|
|
||||||
// eslint-disable-next-line
|
|
||||||
ws = require('ws')
|
|
||||||
} catch (e) {
|
|
||||||
ws = null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ws = window.WebSocket
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const subdomainsMap: Record<string, string> = {
|
const subdomainsMap: Record<string, string> = {
|
||||||
|
@ -51,20 +36,36 @@ export abstract class BaseWebSocketTransport extends EventEmitter implements ITe
|
||||||
|
|
||||||
private _baseDomain: string
|
private _baseDomain: string
|
||||||
private _subdomains: Record<string, string>
|
private _subdomains: Record<string, string>
|
||||||
|
private _WebSocket: WebSocketConstructor
|
||||||
|
|
||||||
/**
|
constructor({
|
||||||
* @param baseDomain Base WebSocket domain
|
ws = WebSocket,
|
||||||
* @param subdomains Map of sub-domains (key is DC ID, value is string)
|
baseDomain = 'web.telegram.org',
|
||||||
*/
|
subdomains = subdomainsMap,
|
||||||
constructor(baseDomain = 'web.telegram.org', subdomains = subdomainsMap) {
|
}: {
|
||||||
|
/** Custom implementation of WebSocket (e.g. https://npm.im/ws) */
|
||||||
|
ws?: WebSocketConstructor
|
||||||
|
/** Base WebSocket domain */
|
||||||
|
baseDomain?: string
|
||||||
|
/** Map of sub-domains (key is DC ID, value is string) */
|
||||||
|
subdomains?: Record<string, string>
|
||||||
|
} = {}) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
if (!ws) {
|
if (!ws) {
|
||||||
throw new MtUnsupportedError('To use WebSocket transport with NodeJS, install `ws` package.')
|
throw new MtUnsupportedError(
|
||||||
|
'To use WebSocket transport with NodeJS, install `ws` package and pass it to constructor',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gotta love cjs/esm compat
|
||||||
|
if ('default' in ws) {
|
||||||
|
ws = ws.default as WebSocketConstructor
|
||||||
}
|
}
|
||||||
|
|
||||||
this._baseDomain = baseDomain
|
this._baseDomain = baseDomain
|
||||||
this._subdomains = subdomains
|
this._subdomains = subdomains
|
||||||
|
this._WebSocket = ws
|
||||||
|
|
||||||
this.close = this.close.bind(this)
|
this.close = this.close.bind(this)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +105,7 @@ export abstract class BaseWebSocketTransport extends EventEmitter implements ITe
|
||||||
|
|
||||||
this._state = TransportState.Connecting
|
this._state = TransportState.Connecting
|
||||||
this._currentDc = dc
|
this._currentDc = dc
|
||||||
this._socket = new ws!(
|
this._socket = new this._WebSocket(
|
||||||
`wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${testMode ? '_test' : ''}`,
|
`wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${testMode ? '_test' : ''}`,
|
||||||
'binary',
|
'binary',
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue