chore!: use more utils from fuman

This commit is contained in:
alina 🌸 2024-09-07 18:52:21 +03:00
parent 4668b356db
commit f3c0daa835
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
15 changed files with 48 additions and 252 deletions

View file

@ -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:^"

View file

@ -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,

View file

@ -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
} }

View file

@ -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
}
}

View file

@ -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": {

View file

@ -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.

View file

@ -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

View file

@ -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'

View file

@ -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', () => {})
}

View file

@ -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
}

View file

@ -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'

View file

@ -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
}

View file

@ -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": {

View file

@ -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')

View file

@ -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