chore!: use more utils from fuman
This commit is contained in:
parent
4668b356db
commit
f3c0daa835
15 changed files with 48 additions and 252 deletions
|
@ -13,7 +13,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@fuman/utils": "workspace:^"
|
"@fuman/utils": "workspace:^",
|
||||||
|
"@fuman/ip": "workspace:^"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^"
|
"@mtcute/test": "workspace:^"
|
||||||
|
|
|
@ -1,7 +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 { parseIpFromBytes } from '../utils/ip.js'
|
|
||||||
|
|
||||||
import type { TelethonSession } from './types.js'
|
import type { TelethonSession } from './types.js'
|
||||||
|
|
||||||
|
@ -26,11 +25,16 @@ export function parseTelethonSession(session: string): TelethonSession {
|
||||||
pos += 2
|
pos += 2
|
||||||
const authKey = data.subarray(pos, pos + 256)
|
const authKey = data.subarray(pos, pos + 256)
|
||||||
|
|
||||||
const ip = parseIpFromBytes(ipBytes)
|
let parsedIp
|
||||||
|
if (ipSize === 16) {
|
||||||
|
parsedIp = ip.stringifyV6(ip.fromBytesV6(ipBytes))
|
||||||
|
} else {
|
||||||
|
parsedIp = ip.stringifyV4({ type: 'ipv4', parts: ipBytes })
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dcId,
|
dcId,
|
||||||
ipAddress: ip,
|
ipAddress: parsedIp,
|
||||||
ipv6: ipSize === 16,
|
ipv6: ipSize === 16,
|
||||||
port,
|
port,
|
||||||
authKey,
|
authKey,
|
||||||
|
|
|
@ -1,7 +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 { serializeIpv4ToBytes, serializeIpv6ToBytes } from '../utils/ip.js'
|
|
||||||
|
|
||||||
import type { TelethonSession } from './types.js'
|
import type { TelethonSession } from './types.js'
|
||||||
|
|
||||||
|
@ -19,10 +18,10 @@ export function serializeTelethonSession(session: TelethonSession): string {
|
||||||
let pos
|
let pos
|
||||||
|
|
||||||
if (session.ipv6) {
|
if (session.ipv6) {
|
||||||
serializeIpv6ToBytes(session.ipAddress, u8.subarray(1, 17))
|
u8.subarray(1, 17).set(ip.toBytesV6(ip.parseV6(session.ipAddress)))
|
||||||
pos = 17
|
pos = 17
|
||||||
} else {
|
} else {
|
||||||
serializeIpv4ToBytes(session.ipAddress, u8.subarray(1, 5))
|
u8.subarray(1, 5).set(ip.parseV4(session.ipAddress).parts)
|
||||||
pos = 5
|
pos = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
|
||||||
|
|
||||||
// todo: use @fuman/ip
|
|
||||||
export function parseIpFromBytes(data: Uint8Array): string {
|
|
||||||
if (data.length === 4) {
|
|
||||||
return `${data[0]}.${data[1]}.${data[2]}.${data[3]}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length === 16) {
|
|
||||||
let res = ''
|
|
||||||
|
|
||||||
for (let i = 0; i < 16; i += 2) {
|
|
||||||
res += data[i].toString(16).padStart(2, '0')
|
|
||||||
res += data[i + 1].toString(16).padStart(2, '0')
|
|
||||||
if (i < 14) res += ':'
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new MtArgumentError('Invalid IP address length')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializeIpv4ToBytes(ip: string, buf: Uint8Array): void {
|
|
||||||
const parts = ip.split('.')
|
|
||||||
|
|
||||||
if (parts.length !== 4) {
|
|
||||||
throw new MtArgumentError('Invalid IPv4 address')
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[0] = Number(parts[0])
|
|
||||||
buf[1] = Number(parts[1])
|
|
||||||
buf[2] = Number(parts[2])
|
|
||||||
buf[3] = Number(parts[3])
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializeIpv6ToBytes(ip: string, buf: Uint8Array): void {
|
|
||||||
const parts = ip.split(':')
|
|
||||||
|
|
||||||
if (parts.length !== 8) {
|
|
||||||
throw new MtArgumentError('Invalid IPv6 address')
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
const val = Number.parseInt(parts[i], 16)
|
|
||||||
buf[i * 2] = val >> 8
|
|
||||||
buf[i * 2 + 1] = val & 0xFF
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@
|
||||||
"@mtcute/markdown-parser": "workspace:^",
|
"@mtcute/markdown-parser": "workspace:^",
|
||||||
"@mtcute/wasm": "workspace:^",
|
"@mtcute/wasm": "workspace:^",
|
||||||
"@fuman/net": "workspace:^",
|
"@fuman/net": "workspace:^",
|
||||||
"@fuman/node-net": "workspace:^",
|
"@fuman/node": "workspace:^",
|
||||||
"better-sqlite3": "11.3.0"
|
"better-sqlite3": "11.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -2,8 +2,7 @@ import type { Readable } from 'node:stream'
|
||||||
|
|
||||||
import type { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core'
|
import type { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core'
|
||||||
import { downloadAsStream } from '@mtcute/core/methods.js'
|
import { downloadAsStream } from '@mtcute/core/methods.js'
|
||||||
|
import { webStreamToNode } from '@fuman/node'
|
||||||
import { webStreamToNode } from '../utils/stream-utils.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download a remote file as a Node.js Readable stream.
|
* Download a remote file as a Node.js Readable stream.
|
||||||
|
|
|
@ -4,8 +4,7 @@ import { basename } from 'node:path'
|
||||||
import { Readable } from 'node:stream'
|
import { Readable } from 'node:stream'
|
||||||
|
|
||||||
import type { UploadFileLike } from '@mtcute/core'
|
import type { UploadFileLike } from '@mtcute/core'
|
||||||
|
import { nodeStreamToWeb } from '@fuman/node'
|
||||||
import { nodeStreamToWeb } from './stream-utils.js'
|
|
||||||
|
|
||||||
export async function normalizeFile(file: UploadFileLike): Promise<{
|
export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
file: UploadFileLike
|
file: UploadFileLike
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type { SecureContextOptions } from 'node:tls'
|
||||||
|
|
||||||
import type { HttpProxySettings as FumanHttpProxySettings, ITcpConnection, SocksProxySettings, TcpEndpoint } from '@fuman/net'
|
import type { HttpProxySettings as FumanHttpProxySettings, ITcpConnection, SocksProxySettings, TcpEndpoint } from '@fuman/net'
|
||||||
import { performHttpProxyHandshake, performSocksHandshake } from '@fuman/net'
|
import { performHttpProxyHandshake, performSocksHandshake } from '@fuman/net'
|
||||||
import { connectTcp, connectTls } from '@fuman/node-net'
|
import { connectTcp, connectTls } from '@fuman/node'
|
||||||
import { BaseMtProxyTransport, type ITelegramConnection, IntermediatePacketCodec, type TelegramTransport } from '@mtcute/core'
|
import { BaseMtProxyTransport, type ITelegramConnection, IntermediatePacketCodec, type TelegramTransport } from '@mtcute/core'
|
||||||
import type { BasicDcOption } from '@mtcute/core/utils.js'
|
import type { BasicDcOption } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import { Readable } from 'node:stream'
|
|
||||||
|
|
||||||
import { describe, expect, it } from 'vitest'
|
|
||||||
|
|
||||||
if (import.meta.env.TEST_ENV === 'node' || import.meta.env.TEST_ENV === 'bun') {
|
|
||||||
const { nodeStreamToWeb, webStreamToNode } = await import('./stream-utils.js')
|
|
||||||
|
|
||||||
describe('nodeStreamToWeb', () => {
|
|
||||||
it('should correctly convert a readable stream', async () => {
|
|
||||||
const stream = new Readable({
|
|
||||||
read() {
|
|
||||||
this.push(Buffer.from([1, 2, 3]))
|
|
||||||
|
|
||||||
this.push(Buffer.from([4, 5, 6]))
|
|
||||||
this.push(null)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const webStream = nodeStreamToWeb(stream)
|
|
||||||
const reader = webStream.getReader()
|
|
||||||
|
|
||||||
expect(await reader.read()).to.deep.equal({ value: new Uint8Array([1, 2, 3]), done: false })
|
|
||||||
expect(await reader.read()).to.deep.equal({ value: new Uint8Array([4, 5, 6]), done: false })
|
|
||||||
expect(await reader.read()).to.deep.equal({ value: undefined, done: true })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('webStreamToNode', () => {
|
|
||||||
it('should correctly convert a readable stream', async () => {
|
|
||||||
const stream = new ReadableStream<Uint8Array>({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(new Uint8Array([1, 2, 3]))
|
|
||||||
controller.enqueue(new Uint8Array([4, 5, 6]))
|
|
||||||
controller.close()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const nodeStream = webStreamToNode(stream)
|
|
||||||
const chunks: Buffer[] = []
|
|
||||||
|
|
||||||
nodeStream.on('data', (chunk) => {
|
|
||||||
chunks.push(chunk as Buffer)
|
|
||||||
})
|
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
|
||||||
nodeStream.on('end', () => {
|
|
||||||
try {
|
|
||||||
expect(chunks).to.deep.equal([Buffer.from([1, 2, 3]), Buffer.from([4, 5, 6])])
|
|
||||||
resolve()
|
|
||||||
} catch (err) {
|
|
||||||
reject(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
describe.skip('node stream utils', () => {})
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
import { Readable } from 'node:stream'
|
|
||||||
|
|
||||||
import { isNodeVersionAfter } from './version.js'
|
|
||||||
|
|
||||||
export function nodeStreamToWeb(stream: Readable): ReadableStream<Uint8Array> {
|
|
||||||
if (typeof Readable.toWeb === 'function') {
|
|
||||||
return Readable.toWeb(stream) as unknown as ReadableStream<Uint8Array>
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, use a silly little adapter
|
|
||||||
|
|
||||||
stream.pause()
|
|
||||||
|
|
||||||
return new ReadableStream({
|
|
||||||
start(c) {
|
|
||||||
stream.on('data', (chunk) => {
|
|
||||||
c.enqueue(chunk as Uint8Array)
|
|
||||||
})
|
|
||||||
stream.on('end', () => {
|
|
||||||
c.close()
|
|
||||||
})
|
|
||||||
stream.on('error', (err) => {
|
|
||||||
c.error(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
pull() {
|
|
||||||
stream.resume()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function webStreamToNode(stream: ReadableStream<Uint8Array>): Readable {
|
|
||||||
if (
|
|
||||||
typeof Readable.fromWeb === 'function'
|
|
||||||
&& isNodeVersionAfter(18, 13, 0) // https://github.com/nodejs/node/issues/42694
|
|
||||||
) {
|
|
||||||
// @ts-expect-error node typings are wrong lmao
|
|
||||||
return Readable.fromWeb(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = stream.getReader()
|
|
||||||
let ended = false
|
|
||||||
|
|
||||||
const readable = new Readable({
|
|
||||||
async read() {
|
|
||||||
try {
|
|
||||||
const { done, value } = await reader.read()
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
this.push(null)
|
|
||||||
} else {
|
|
||||||
this.push(Buffer.from(value.buffer, value.byteOffset, value.byteLength))
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.destroy(err as Error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
destroy(error, cb) {
|
|
||||||
if (!ended) {
|
|
||||||
void reader
|
|
||||||
.cancel(error)
|
|
||||||
.catch(() => {})
|
|
||||||
.then(() => {
|
|
||||||
cb(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(error)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
reader.closed
|
|
||||||
.then(() => {
|
|
||||||
ended = true
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
readable.destroy(err as Error)
|
|
||||||
})
|
|
||||||
|
|
||||||
return readable
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { connectTcp } from '@fuman/node-net'
|
import { connectTcp } from '@fuman/node'
|
||||||
import type { TelegramTransport } from '@mtcute/core'
|
import type { TelegramTransport } from '@mtcute/core'
|
||||||
import { IntermediatePacketCodec } from '@mtcute/core'
|
import { IntermediatePacketCodec } from '@mtcute/core'
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
export const NODE_VERSION: string | null
|
|
||||||
= typeof process !== 'undefined' && 'node' in process.versions ? process.versions.node : null
|
|
||||||
export const NODE_VERSION_TUPLE: number[] | null
|
|
||||||
= NODE_VERSION ? /* #__PURE__ */ NODE_VERSION.split('.').map(Number) : null
|
|
||||||
|
|
||||||
export function isNodeVersionAfter(major: number, minor: number, patch: number): boolean {
|
|
||||||
if (!NODE_VERSION_TUPLE) return true // assume non-node environment is always "after"
|
|
||||||
|
|
||||||
const [a, b, c] = NODE_VERSION_TUPLE
|
|
||||||
if (a > major) return true
|
|
||||||
if (a < major) return false
|
|
||||||
if (b > minor) return true
|
|
||||||
if (b < minor) return false
|
|
||||||
|
|
||||||
return c >= patch
|
|
||||||
}
|
|
|
@ -22,10 +22,10 @@
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/node": "workspace:^",
|
"@mtcute/node": "workspace:^",
|
||||||
"@mtcute/tl-utils": "workspace:^",
|
"@mtcute/tl-utils": "workspace:^",
|
||||||
|
"@fuman/utils": "workspace:^",
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "^4.0.5",
|
||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "1.0.0-rc.12",
|
||||||
"csv-parse": "^5.5.0",
|
"csv-parse": "^5.5.0",
|
||||||
"eager-async-pool": "^1.0.0",
|
|
||||||
"js-yaml": "4.1.0"
|
"js-yaml": "4.1.0"
|
||||||
},
|
},
|
||||||
"typedoc": {
|
"typedoc": {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url'
|
||||||
import { createInterface } from 'node:readline'
|
import { createInterface } from 'node:readline'
|
||||||
|
|
||||||
import * as cheerio from 'cheerio'
|
import * as cheerio from 'cheerio'
|
||||||
import { asyncPoolCallback } from 'eager-async-pool'
|
import { asyncPool } from '@fuman/utils'
|
||||||
import jsYaml from 'js-yaml'
|
import jsYaml from 'js-yaml'
|
||||||
import type {
|
import type {
|
||||||
TlEntry,
|
TlEntry,
|
||||||
|
@ -408,6 +408,8 @@ export async function fetchDocumentation(
|
||||||
}
|
}
|
||||||
|
|
||||||
ret[entry.kind === 'class' ? 'classes' : 'methods'][entry.name] = retClass
|
ret[entry.kind === 'class' ? 'classes' : 'methods'][entry.name] = retClass
|
||||||
|
|
||||||
|
log(`📥 ${entry.kind} ${entry.name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchDocsForUnion(name: string) {
|
async function fetchDocsForUnion(name: string) {
|
||||||
|
@ -427,36 +429,32 @@ export async function fetchDocumentation(
|
||||||
|
|
||||||
const description = extractDescription($)
|
const description = extractDescription($)
|
||||||
if (description) ret.unions[name] = description
|
if (description) ret.unions[name] = description
|
||||||
|
|
||||||
|
log(`📥 union ${name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await asyncPoolCallback(
|
await asyncPool(
|
||||||
fetchDocsForEntry,
|
|
||||||
schema.entries,
|
schema.entries,
|
||||||
({ item, error }) => {
|
fetchDocsForEntry,
|
||||||
if (error) {
|
{
|
||||||
console.log(`❌ ${item.kind} ${item.name} (${error.message})`)
|
limit: 16,
|
||||||
|
onError: (item, error) => {
|
||||||
return
|
console.log(`❌ ${item.kind} ${item.name} (${error})`)
|
||||||
}
|
return 'throw'
|
||||||
|
},
|
||||||
log(`📥 ${item.kind} ${item.name}`)
|
|
||||||
},
|
},
|
||||||
{ limit: 16 },
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await asyncPoolCallback(
|
await asyncPool(
|
||||||
fetchDocsForUnion,
|
|
||||||
Object.keys(schema.unions),
|
Object.keys(schema.unions),
|
||||||
({ item, error }) => {
|
fetchDocsForUnion,
|
||||||
if (error) {
|
{
|
||||||
console.log(`❌ union ${item} (${error.message})`)
|
limit: 16,
|
||||||
|
onError: (item, error) => {
|
||||||
return
|
console.log(`❌ union ${item} (${error})`)
|
||||||
}
|
return 'throw'
|
||||||
|
},
|
||||||
log(`📥 union ${item}`)
|
|
||||||
},
|
},
|
||||||
{ limit: 16 },
|
|
||||||
)
|
)
|
||||||
|
|
||||||
log('✨ Patching descriptions')
|
log('✨ Patching descriptions')
|
||||||
|
|
|
@ -132,6 +132,9 @@ importers:
|
||||||
|
|
||||||
packages/convert:
|
packages/convert:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fuman/ip':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/ip
|
||||||
'@fuman/utils':
|
'@fuman/utils':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../private/fuman/packages/utils
|
version: link:../../private/fuman/packages/utils
|
||||||
|
@ -324,9 +327,9 @@ importers:
|
||||||
'@fuman/net':
|
'@fuman/net':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../private/fuman/packages/net
|
version: link:../../private/fuman/packages/net
|
||||||
'@fuman/node-net':
|
'@fuman/node':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../private/fuman/packages/node-net
|
version: link:../../private/fuman/packages/node
|
||||||
'@mtcute/core':
|
'@mtcute/core':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../core
|
version: link:../core
|
||||||
|
@ -384,6 +387,9 @@ importers:
|
||||||
specifier: 5.2.3
|
specifier: 5.2.3
|
||||||
version: 5.2.3
|
version: 5.2.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@fuman/utils':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/utils
|
||||||
'@mtcute/core':
|
'@mtcute/core':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../core
|
version: link:../core
|
||||||
|
@ -402,9 +408,6 @@ importers:
|
||||||
csv-parse:
|
csv-parse:
|
||||||
specifier: ^5.5.0
|
specifier: ^5.5.0
|
||||||
version: 5.5.0
|
version: 5.5.0
|
||||||
eager-async-pool:
|
|
||||||
specifier: ^1.0.0
|
|
||||||
version: 1.0.0
|
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
|
|
Loading…
Reference in a new issue