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": {
|
||||
"@types/node": "18.16.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'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import type { ReadStream } from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
|
||||
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
|
||||
import { randomLong } from '@mtcute/core/utils.js'
|
||||
|
||||
import { UploadedFile, UploadFileLike } from '../../types/index.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
|
||||
|
||||
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> = {
|
||||
// tg doesn't interpret `audio/opus` files as voice messages for some reason
|
||||
'audio/opus': 'audio/ogg',
|
||||
|
@ -134,20 +119,11 @@ export async function uploadFile(
|
|||
}
|
||||
|
||||
if (typeof file === 'string') {
|
||||
if (!fs) {
|
||||
throw new MtArgumentError('Local paths are only supported for NodeJS!')
|
||||
}
|
||||
file = fs.createReadStream(file)
|
||||
file = _createFileStream(file)
|
||||
}
|
||||
|
||||
if (fs && file instanceof fs.ReadStream) {
|
||||
fileName = path!.basename(file.path.toString())
|
||||
fileSize = await new Promise((res, rej) => {
|
||||
fs!.stat((file as ReadStream).path.toString(), (err, stat) => {
|
||||
if (err) rej(err)
|
||||
res(stat.size)
|
||||
})
|
||||
})
|
||||
if (_isFileStream(file)) {
|
||||
[fileName, fileSize] = await _extractFileStreamMeta(file)
|
||||
// fs.ReadStream is a subclass of Readable, will be handled below
|
||||
}
|
||||
|
||||
|
@ -186,9 +162,7 @@ export async function uploadFile(
|
|||
file = file.body
|
||||
}
|
||||
|
||||
if (nodeStream && file instanceof nodeStream.Readable) {
|
||||
file = nodeReadableToWeb(file)
|
||||
}
|
||||
file = _handleNodeStream(file)
|
||||
|
||||
if (!(file instanceof ReadableStream)) {
|
||||
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-unsafe-call,@typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { createRequire } from 'module'
|
||||
|
||||
import { base64Encode } from '@mtcute/core/utils.js'
|
||||
|
||||
let util: typeof import('util') | null = null
|
||||
|
||||
try {
|
||||
// @only-if-esm
|
||||
const require = createRequire(import.meta.url)
|
||||
// @/only-if-esm
|
||||
util = require('util') as typeof import('util')
|
||||
} catch (e) {}
|
||||
const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom')
|
||||
|
||||
// get all property names. unlike Object.getOwnPropertyNames,
|
||||
// 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
|
||||
return ret
|
||||
}
|
||||
if (util) {
|
||||
obj.prototype[util.inspect.custom] = obj.prototype.toJSON
|
||||
}
|
||||
obj.prototype[customInspectSymbol] = obj.prototype.toJSON
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ export * from './abstract.js'
|
|||
export * from './intermediate.js'
|
||||
export * from './obfuscated.js'
|
||||
export * from './streamed.js'
|
||||
export * from './tcp.js'
|
||||
export * from './websocket.js'
|
||||
export * from './wrapped.js'
|
||||
|
||||
import { _defaultTransportFactory } from '../../utils/platform/transport.js'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import EventEmitter from 'events'
|
||||
import { createRequire } from 'module'
|
||||
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
|
@ -9,22 +8,8 @@ import { IPacketCodec, ITelegramTransport, TransportState } from './abstract.js'
|
|||
import { IntermediatePacketCodec } from './intermediate.js'
|
||||
import { ObfuscatedPacketCodec } from './obfuscated.js'
|
||||
|
||||
let ws: {
|
||||
new (address: string, options?: 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
|
||||
export type WebSocketConstructor = {
|
||||
new (address: string, protocol?: string): WebSocket
|
||||
}
|
||||
|
||||
const subdomainsMap: Record<string, string> = {
|
||||
|
@ -51,20 +36,36 @@ export abstract class BaseWebSocketTransport extends EventEmitter implements ITe
|
|||
|
||||
private _baseDomain: string
|
||||
private _subdomains: Record<string, string>
|
||||
private _WebSocket: WebSocketConstructor
|
||||
|
||||
/**
|
||||
* @param baseDomain Base WebSocket domain
|
||||
* @param subdomains Map of sub-domains (key is DC ID, value is string)
|
||||
*/
|
||||
constructor(baseDomain = 'web.telegram.org', subdomains = subdomainsMap) {
|
||||
constructor({
|
||||
ws = WebSocket,
|
||||
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()
|
||||
|
||||
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._subdomains = subdomains
|
||||
this._WebSocket = ws
|
||||
|
||||
this.close = this.close.bind(this)
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ export abstract class BaseWebSocketTransport extends EventEmitter implements ITe
|
|||
|
||||
this._state = TransportState.Connecting
|
||||
this._currentDc = dc
|
||||
this._socket = new ws!(
|
||||
this._socket = new this._WebSocket(
|
||||
`wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${testMode ? '_test' : ''}`,
|
||||
'binary',
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue