chore: moved build to @fuman/build
This commit is contained in:
parent
0692de2179
commit
9e3e379c25
103 changed files with 1926 additions and 3034 deletions
|
@ -6,7 +6,6 @@ import { globSync } from 'glob'
|
||||||
export function collectTestEntrypoints(params: { skipPackages: string[], skipTests: string[] }) {
|
export function collectTestEntrypoints(params: { skipPackages: string[], skipTests: string[] }) {
|
||||||
const files: string[] = []
|
const files: string[] = []
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
const packages = resolve(__dirname, '../../packages')
|
const packages = resolve(__dirname, '../../packages')
|
||||||
|
|
||||||
const skipTests = params.skipTests.map(path => resolve(packages, path))
|
const skipTests = params.skipTests.map(path => resolve(packages, path))
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
/// <reference types="vitest" />
|
/// <reference types="vitest" />
|
||||||
import { cpSync, existsSync, writeFileSync } from 'node:fs'
|
import { readFileSync } from 'node:fs'
|
||||||
import { relative, resolve } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
// todo
|
||||||
|
import { fumanBuild } from '@fuman/build/vite'
|
||||||
import type { ConfigEnv, UserConfig } from 'vite'
|
import type { ConfigEnv, UserConfig } from 'vite'
|
||||||
import { nodeExternals } from 'rollup-plugin-node-externals'
|
import { nodeExternals } from 'rollup-plugin-node-externals'
|
||||||
import dts from 'vite-plugin-dts'
|
import dts from 'vite-plugin-dts'
|
||||||
|
|
||||||
import { processPackageJson } from './vite-utils/package-json'
|
|
||||||
|
|
||||||
const rootDir = fileURLToPath(new URL('..', import.meta.url))
|
const rootDir = fileURLToPath(new URL('..', import.meta.url))
|
||||||
|
|
||||||
export default async (env: ConfigEnv): Promise<UserConfig> => {
|
export default async (env: ConfigEnv): Promise<UserConfig> => {
|
||||||
|
@ -16,15 +16,7 @@ export default async (env: ConfigEnv): Promise<UserConfig> => {
|
||||||
throw new Error('This config is only for building')
|
throw new Error('This config is only for building')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { packageJson, entrypoints } = processPackageJson(process.cwd())
|
const packageJson = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf8'))
|
||||||
|
|
||||||
let customConfig: any
|
|
||||||
try {
|
|
||||||
const mod = await import(resolve(process.cwd(), 'build.config.js'))
|
|
||||||
customConfig = await mod.default()
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== 'ERR_MODULE_NOT_FOUND') throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
const CJS_DEPRECATION_WARNING = `
|
const CJS_DEPRECATION_WARNING = `
|
||||||
if (typeof globalThis !== 'undefined' && !globalThis._MTCUTE_CJS_DEPRECATION_WARNED) {
|
if (typeof globalThis !== 'undefined' && !globalThis._MTCUTE_CJS_DEPRECATION_WARNED) {
|
||||||
|
@ -34,69 +26,19 @@ if (typeof globalThis !== 'undefined' && !globalThis._MTCUTE_CJS_DEPRECATION_WAR
|
||||||
}
|
}
|
||||||
`.trim()
|
`.trim()
|
||||||
|
|
||||||
if (customConfig?.preBuild) {
|
|
||||||
await customConfig.preBuild()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
plugins: [
|
plugins: [
|
||||||
...(customConfig?.rollupPluginsPre ?? []),
|
|
||||||
nodeExternals({
|
|
||||||
builtinsPrefix: 'ignore',
|
|
||||||
exclude: /^@fuman\//,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
name: 'mtcute-finalize',
|
name: 'mtcute-cjs-deprecated',
|
||||||
renderChunk(code, chunk, options) {
|
renderChunk(code, chunk, options) {
|
||||||
if (options.format !== 'cjs') return null
|
if (options.format !== 'cjs') return null
|
||||||
|
|
||||||
return `${CJS_DEPRECATION_WARNING}\n${code}`
|
return `${CJS_DEPRECATION_WARNING}\n${code}`
|
||||||
},
|
},
|
||||||
async closeBundle() {
|
|
||||||
const packageDir = process.cwd()
|
|
||||||
const outDir = resolve(packageDir, 'dist')
|
|
||||||
|
|
||||||
customConfig?.finalPackageJson?.(packageJson)
|
|
||||||
|
|
||||||
writeFileSync(resolve(outDir, 'package.json'), JSON.stringify(packageJson, null, 4))
|
|
||||||
cpSync(resolve(rootDir, 'LICENSE'), resolve(outDir, 'LICENSE'))
|
|
||||||
cpSync(resolve(process.cwd(), 'README.md'), resolve(outDir, 'README.md'))
|
|
||||||
|
|
||||||
if (existsSync(resolve(outDir, 'chunks/cjs'))) {
|
|
||||||
// write {"type":"commonjs"} into chunks/cjs so that node doesn't complain
|
|
||||||
const cjsFile = resolve(outDir, 'chunks/cjs/package.json')
|
|
||||||
writeFileSync(cjsFile, JSON.stringify({ type: 'commonjs' }, null, 4))
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [name, entry] of Object.entries(entrypoints)) {
|
|
||||||
const dTsFile = resolve(outDir, `${name}.d.ts`)
|
|
||||||
if (!existsSync(dTsFile)) {
|
|
||||||
const entryTypings = resolve(outDir, entry.replace('/src/', '/').replace(/\.ts$/, '.d.ts'))
|
|
||||||
if (!existsSync(entryTypings)) continue
|
|
||||||
|
|
||||||
const relativePath = relative(outDir, entryTypings)
|
|
||||||
writeFileSync(dTsFile, `export * from './${relativePath.replace(/\.d\.ts$/, '.js')}'`)
|
|
||||||
}
|
|
||||||
|
|
||||||
cpSync(dTsFile, dTsFile.replace(/\.d\.ts$/, '.d.cts'))
|
|
||||||
}
|
|
||||||
|
|
||||||
await customConfig?.final?.({ outDir, packageDir })
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
...(customConfig?.rollupPluginsPost ?? []),
|
|
||||||
],
|
],
|
||||||
output: {
|
|
||||||
minifyInternalExports: false,
|
|
||||||
chunkFileNames: 'chunks/[format]/[hash].js',
|
|
||||||
},
|
|
||||||
external: customConfig?.external,
|
|
||||||
},
|
|
||||||
lib: {
|
|
||||||
entry: entrypoints as any,
|
|
||||||
formats: customConfig?.buildCjs === false ? ['es'] : ['es', 'cjs'],
|
|
||||||
},
|
},
|
||||||
minify: false,
|
minify: false,
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
|
@ -104,10 +46,17 @@ if (typeof globalThis !== 'undefined' && !globalThis._MTCUTE_CJS_DEPRECATION_WAR
|
||||||
target: 'es2022',
|
target: 'es2022',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
...(customConfig?.vitePlugins ?? []),
|
nodeExternals({
|
||||||
|
builtinsPrefix: 'ignore',
|
||||||
|
}),
|
||||||
|
fumanBuild({
|
||||||
|
root: rootDir,
|
||||||
|
autoSideEffectsFalse: true,
|
||||||
|
}),
|
||||||
dts({
|
dts({
|
||||||
// broken; see https://github.com/qmhc/vite-plugin-dts/issues/321, https://github.com/microsoft/rushstack/issues/3557
|
// broken; see https://github.com/qmhc/vite-plugin-dts/issues/321, https://github.com/microsoft/rushstack/issues/3557
|
||||||
// rollupTypes: true,
|
// rollupTypes: true,
|
||||||
|
insertTypesEntry: true,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable no-restricted-globals */
|
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
@ -78,15 +77,6 @@ export default defineConfig({
|
||||||
return code
|
return code
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'fix-events',
|
|
||||||
transform(code) {
|
|
||||||
if (!code.includes('events')) return code
|
|
||||||
return code.replace(/^import (.+?) from ['"]events['"]/gms, (_, name) => {
|
|
||||||
return `import ${name} from 'node:events'`
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'fix-wasm-load',
|
name: 'fix-wasm-load',
|
||||||
async transform(code) {
|
async transform(code) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { fixupCjs } from './vite-utils/fixup-cjs'
|
||||||
import { testSetup } from './vite-utils/test-setup-plugin'
|
import { testSetup } from './vite-utils/test-setup-plugin'
|
||||||
import { collectTestEntrypoints } from './vite-utils/collect-test-entrypoints'
|
import { collectTestEntrypoints } from './vite-utils/collect-test-entrypoints'
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
const POLYFILLS = resolve(__dirname, 'vite-utils/polyfills-deno.ts')
|
const POLYFILLS = resolve(__dirname, 'vite-utils/polyfills-deno.ts')
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
|
1
.npmrc
1
.npmrc
|
@ -1 +1,2 @@
|
||||||
@jsr:registry=https://npm.jsr.io
|
@jsr:registry=https://npm.jsr.io
|
||||||
|
@fuman:registry=https://npm.tei.su
|
||||||
|
|
68
build.config.js
Normal file
68
build.config.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/** @type {import('@fuman/build').RootConfig} */
|
||||||
|
export default {
|
||||||
|
jsr: {
|
||||||
|
exclude: ['**/*.{test,bench,test-utils}.ts', '**/__fixtures__/**'],
|
||||||
|
sourceDir: 'src',
|
||||||
|
transformCode: (path, code) => {
|
||||||
|
// add shims for node-specific APIs and replace NodeJS.* types
|
||||||
|
// pretty fragile, but it works for now
|
||||||
|
// todo: remove this god awfulness and use `declare const` in-place instead
|
||||||
|
|
||||||
|
const typesToReplace = {
|
||||||
|
'NodeJS\\.Timeout': 'number',
|
||||||
|
'NodeJS\\.Immediate': 'number',
|
||||||
|
}
|
||||||
|
const nodeSpecificApis = {
|
||||||
|
setImmediate: '(cb: (...args: any[]) => void, ...args: any[]) => number',
|
||||||
|
clearImmediate: '(id: number) => void',
|
||||||
|
Buffer:
|
||||||
|
'{ '
|
||||||
|
+ 'concat: (...args: any[]) => Uint8Array, '
|
||||||
|
+ 'from: (data: any, encoding?: string) => { toString(encoding?: string): string }, '
|
||||||
|
+ ' }',
|
||||||
|
SharedWorker: ['type', 'never'],
|
||||||
|
WorkerGlobalScope:
|
||||||
|
'{ '
|
||||||
|
+ ' new (): typeof WorkerGlobalScope, '
|
||||||
|
+ ' postMessage: (message: any, transfer?: Transferable[]) => void, '
|
||||||
|
+ ' addEventListener: (type: "message", listener: (ev: MessageEvent) => void) => void, '
|
||||||
|
+ ' }',
|
||||||
|
process: '{ ' + 'hrtime: { bigint: () => bigint }, ' + '}',
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, decl_] of Object.entries(nodeSpecificApis)) {
|
||||||
|
if (code.includes(name)) {
|
||||||
|
if (name === 'Buffer' && code.includes('node:buffer')) continue
|
||||||
|
|
||||||
|
const isType = Array.isArray(decl_) && decl_[0] === 'type'
|
||||||
|
const decl = isType ? decl_[1] : decl_
|
||||||
|
|
||||||
|
if (isType) {
|
||||||
|
code = `declare type ${name} = ${decl};\n${code}`
|
||||||
|
} else {
|
||||||
|
code = `declare const ${name}: ${decl};\n${code}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [oldType, newType] of Object.entries(typesToReplace)) {
|
||||||
|
if (code.match(oldType)) {
|
||||||
|
code = code.replace(new RegExp(oldType, 'g'), newType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
},
|
||||||
|
},
|
||||||
|
versioning: {
|
||||||
|
exclude: [
|
||||||
|
'**/*.test.ts',
|
||||||
|
'**/*.test-utils.ts',
|
||||||
|
'**/__fixtures__/**',
|
||||||
|
'**/*.md',
|
||||||
|
'typedoc.cjs',
|
||||||
|
'{scripts,dist,tests,private}/**',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
viteConfig: '.config/vite.build.ts',
|
||||||
|
}
|
|
@ -87,6 +87,7 @@ export default antfu({
|
||||||
'ts/switch-exhaustiveness-check': 'off',
|
'ts/switch-exhaustiveness-check': 'off',
|
||||||
'ts/restrict-template-expressions': 'off',
|
'ts/restrict-template-expressions': 'off',
|
||||||
'ts/method-signature-style': 'off',
|
'ts/method-signature-style': 'off',
|
||||||
|
'style/indent-binary-ops': 'off',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
ignores: [
|
ignores: [
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"homepage": "https://mtcute.dev",
|
"homepage": "https://mtcute.dev",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mtcute/mtcute"
|
"url": "git+https://github.com/mtcute/mtcute.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"telegram",
|
"telegram",
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "node scripts/validate-deps-versions.js && node scripts/remove-jsr-sourcefiles.js",
|
"postinstall": "fuman-build validate-workspace-deps && node scripts/remove-jsr-sourcefiles.js",
|
||||||
"test": "vitest --config .config/vite.ts run",
|
"test": "vitest --config .config/vite.ts run",
|
||||||
"test:dev": "vitest --config .config/vite.ts watch",
|
"test:dev": "vitest --config .config/vite.ts watch",
|
||||||
"test:ui": "vitest --config .config/vite.ts --ui",
|
"test:ui": "vitest --config .config/vite.ts --ui",
|
||||||
|
@ -42,12 +42,13 @@
|
||||||
"lint:fix": "eslint --fix .",
|
"lint:fix": "eslint --fix .",
|
||||||
"publish-all": "node scripts/publish.js all",
|
"publish-all": "node scripts/publish.js all",
|
||||||
"docs": "typedoc --options .config/typedoc/config.cjs",
|
"docs": "typedoc --options .config/typedoc/config.cjs",
|
||||||
"build-package": "node scripts/build-package.js",
|
"build-package": "tsx scripts/build-package.js",
|
||||||
"build-package-vite": "node scripts/build-package-vite.js"
|
"build-package-vite": "node scripts/build-package-vite.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "2.26.0",
|
"@antfu/eslint-config": "2.26.0",
|
||||||
"@fuman/jsr": "workspace:^",
|
"@fuman/build": "0.0.1",
|
||||||
|
"@fuman/utils": "0.0.1",
|
||||||
"@types/deno": "npm:@teidesu/deno-types@1.46.3",
|
"@types/deno": "npm:@teidesu/deno-types@1.46.3",
|
||||||
"@types/node": "20.10.0",
|
"@types/node": "20.10.0",
|
||||||
"@types/ws": "8.5.4",
|
"@types/ws": "8.5.4",
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => ({
|
export default () => ({
|
||||||
buildCjs: false,
|
viteConfig: {
|
||||||
external: ['bun', 'bun:sqlite'],
|
build: {
|
||||||
|
lib: {
|
||||||
|
formats: ['es'],
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['bun', 'bun:sqlite'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,20 +12,20 @@
|
||||||
"./utils.js": "./src/utils.ts",
|
"./utils.js": "./src/utils.ts",
|
||||||
"./methods.js": "./src/methods.ts"
|
"./methods.js": "./src/methods.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package bun"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/html-parser": "workspace:^",
|
"@mtcute/html-parser": "workspace:^",
|
||||||
"@mtcute/markdown-parser": "workspace:^",
|
"@mtcute/markdown-parser": "workspace:^",
|
||||||
"@mtcute/wasm": "workspace:^",
|
"@mtcute/wasm": "workspace:^",
|
||||||
"@fuman/bun": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@fuman/net": "workspace:^",
|
"@fuman/bun": "0.0.1",
|
||||||
"@fuman/io": "workspace:^"
|
"@fuman/net": "0.0.1",
|
||||||
|
"@fuman/io": "0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^"
|
"@mtcute/test": "workspace:^"
|
||||||
|
},
|
||||||
|
"fuman": {
|
||||||
|
"jsr": "skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
BaseTelegramClient as BaseTelegramClientBase,
|
BaseTelegramClient as BaseTelegramClientBase,
|
||||||
TelegramClient as TelegramClientBase,
|
TelegramClient as TelegramClientBase,
|
||||||
} from '@mtcute/core/client.js'
|
} from '@mtcute/core/client.js'
|
||||||
|
import { unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
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'
|
||||||
|
@ -127,7 +128,7 @@ export class TelegramClient extends TelegramClientBase {
|
||||||
|
|
||||||
this.start(params)
|
this.start(params)
|
||||||
.then(then)
|
.then(then)
|
||||||
.catch(err => this.emitError(err))
|
.catch(err => this.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadToFile(
|
downloadToFile(
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
ige256Encrypt,
|
ige256Encrypt,
|
||||||
initSync,
|
initSync,
|
||||||
} from '@mtcute/wasm'
|
} from '@mtcute/wasm'
|
||||||
|
import { u8 } from '@fuman/utils'
|
||||||
|
|
||||||
// we currently prefer wasm for ctr because bun mostly uses browserify polyfills for node:crypto
|
// we currently prefer wasm for ctr because bun mostly uses browserify polyfills for node:crypto
|
||||||
// which are slow AND semi-broken
|
// which are slow AND semi-broken
|
||||||
|
@ -52,20 +53,20 @@ export class BunCryptoProvider extends BaseCryptoProvider implements ICryptoProv
|
||||||
algo = 'sha512',
|
algo = 'sha512',
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
pbkdf2(password, salt, iterations, keylen, algo, (err: Error | null, buf: Uint8Array) =>
|
pbkdf2(password, salt, iterations, keylen, algo, (err: Error | null, buf: Buffer) =>
|
||||||
err !== null ? reject(err) : resolve(buf)),
|
err !== null ? reject(err) : resolve(buf as unknown as Uint8Array)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1(data: Uint8Array): Uint8Array {
|
sha1(data: Uint8Array): Uint8Array {
|
||||||
const res = new Uint8Array(Bun.SHA1.byteLength)
|
const res = u8.alloc(Bun.SHA1.byteLength)
|
||||||
Bun.SHA1.hash(data, res)
|
Bun.SHA1.hash(data, res)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
sha256(data: Uint8Array): Uint8Array {
|
sha256(data: Uint8Array): Uint8Array {
|
||||||
const res = new Uint8Array(Bun.SHA256.byteLength)
|
const res = u8.alloc(Bun.SHA256.byteLength)
|
||||||
Bun.SHA256.hash(data, res)
|
Bun.SHA256.hash(data, res)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
@ -90,7 +91,7 @@ export class BunCryptoProvider extends BaseCryptoProvider implements ICryptoProv
|
||||||
// telegram accepts both zlib and gzip, but zlib is faster and has less overhead, so we use it here
|
// telegram accepts both zlib and gzip, but zlib is faster and has less overhead, so we use it here
|
||||||
return deflateSync(data, {
|
return deflateSync(data, {
|
||||||
maxOutputLength: maxSize,
|
maxOutputLength: maxSize,
|
||||||
})
|
}) as unknown as Uint8Array
|
||||||
// hot path, avoid additional runtime checks
|
// hot path, avoid additional runtime checks
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.code === 'ERR_BUFFER_TOO_LARGE') {
|
if (e.code === 'ERR_BUFFER_TOO_LARGE') {
|
||||||
|
@ -102,7 +103,7 @@ export class BunCryptoProvider extends BaseCryptoProvider implements ICryptoProv
|
||||||
}
|
}
|
||||||
|
|
||||||
gunzip(data: Uint8Array): Uint8Array {
|
gunzip(data: Uint8Array): Uint8Array {
|
||||||
return gunzipSync(data)
|
return gunzipSync(data) as unknown as Uint8Array
|
||||||
}
|
}
|
||||||
|
|
||||||
randomFill(buf: Uint8Array): void {
|
randomFill(buf: Uint8Array): void {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
TelegramWorkerPort as TelegramWorkerPortBase,
|
TelegramWorkerPort as TelegramWorkerPortBase,
|
||||||
} from '@mtcute/core/worker.js'
|
} from '@mtcute/core/worker.js'
|
||||||
|
|
||||||
import { BunPlatform } from './platform'
|
import { BunPlatform } from './platform.js'
|
||||||
|
|
||||||
export type { TelegramWorkerOptions, WorkerCustomMethods }
|
export type { TelegramWorkerOptions, WorkerCustomMethods }
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,10 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm run -w build-package convert"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@fuman/utils": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@fuman/net": "workspace:^"
|
"@fuman/net": "0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^"
|
"@mtcute/test": "workspace:^"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Long, MtArgumentError } from '@mtcute/core'
|
import { Long, MtArgumentError } from '@mtcute/core'
|
||||||
import { base64, typed } from '@fuman/utils'
|
import { base64, typed, u8 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { PyrogramSession } from './types.js'
|
import type { PyrogramSession } from './types.js'
|
||||||
|
|
||||||
|
@ -13,32 +13,32 @@ export function serializePyrogramSession(session: PyrogramSession): string {
|
||||||
|
|
||||||
const userIdLong = Long.fromNumber(session.userId, true)
|
const userIdLong = Long.fromNumber(session.userId, true)
|
||||||
|
|
||||||
let u8: Uint8Array
|
let buf: Uint8Array
|
||||||
|
|
||||||
if (session.apiId === undefined) {
|
if (session.apiId === undefined) {
|
||||||
// old format
|
// old format
|
||||||
u8 = new Uint8Array(SESSION_STRING_SIZE_OLD)
|
buf = u8.alloc(SESSION_STRING_SIZE_OLD)
|
||||||
const dv = typed.toDataView(u8)
|
const dv = typed.toDataView(buf)
|
||||||
|
|
||||||
dv.setUint8(0, session.dcId)
|
dv.setUint8(0, session.dcId)
|
||||||
dv.setUint8(1, session.isTest ? 1 : 0)
|
dv.setUint8(1, session.isTest ? 1 : 0)
|
||||||
u8.set(session.authKey, 2)
|
buf.set(session.authKey, 2)
|
||||||
dv.setUint32(258, userIdLong.high)
|
dv.setUint32(258, userIdLong.high)
|
||||||
dv.setUint32(262, userIdLong.low)
|
dv.setUint32(262, userIdLong.low)
|
||||||
dv.setUint8(266, session.isBot ? 1 : 0)
|
dv.setUint8(266, session.isBot ? 1 : 0)
|
||||||
} else {
|
} else {
|
||||||
u8 = new Uint8Array(SESSION_STRING_SIZE)
|
buf = u8.alloc(SESSION_STRING_SIZE)
|
||||||
const dv = typed.toDataView(u8)
|
const dv = typed.toDataView(buf)
|
||||||
|
|
||||||
dv.setUint8(0, session.dcId)
|
dv.setUint8(0, session.dcId)
|
||||||
dv.setUint32(1, session.apiId)
|
dv.setUint32(1, session.apiId)
|
||||||
dv.setUint8(5, session.isTest ? 1 : 0)
|
dv.setUint8(5, session.isTest ? 1 : 0)
|
||||||
u8.set(session.authKey, 6)
|
buf.set(session.authKey, 6)
|
||||||
|
|
||||||
dv.setUint32(262, userIdLong.high)
|
dv.setUint32(262, userIdLong.high)
|
||||||
dv.setUint32(266, userIdLong.low)
|
dv.setUint32(266, userIdLong.low)
|
||||||
dv.setUint8(270, session.isBot ? 1 : 0)
|
dv.setUint8(270, session.isBot ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return base64.encode(u8, true)
|
return base64.encode(buf, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
import { base64, typed } from '@fuman/utils'
|
import { base64, typed, u8 } from '@fuman/utils'
|
||||||
import { ip } from '@fuman/net'
|
import { ip } from '@fuman/net'
|
||||||
|
|
||||||
import type { TelethonSession } from './types.js'
|
import type { TelethonSession } from './types.js'
|
||||||
|
@ -10,26 +10,26 @@ export function serializeTelethonSession(session: TelethonSession): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipSize = session.ipv6 ? 16 : 4
|
const ipSize = session.ipv6 ? 16 : 4
|
||||||
const u8 = new Uint8Array(259 + ipSize)
|
const buf = u8.alloc(259 + ipSize)
|
||||||
const dv = typed.toDataView(u8)
|
const dv = typed.toDataView(buf)
|
||||||
|
|
||||||
dv.setUint8(0, session.dcId)
|
dv.setUint8(0, session.dcId)
|
||||||
|
|
||||||
let pos
|
let pos
|
||||||
|
|
||||||
if (session.ipv6) {
|
if (session.ipv6) {
|
||||||
u8.subarray(1, 17).set(ip.toBytesV6(ip.parseV6(session.ipAddress)))
|
buf.subarray(1, 17).set(ip.toBytesV6(ip.parseV6(session.ipAddress)))
|
||||||
pos = 17
|
pos = 17
|
||||||
} else {
|
} else {
|
||||||
u8.subarray(1, 5).set(ip.parseV4(session.ipAddress).parts)
|
buf.subarray(1, 5).set(ip.parseV4(session.ipAddress).parts)
|
||||||
pos = 5
|
pos = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
dv.setUint16(pos, session.port)
|
dv.setUint16(pos, session.port)
|
||||||
pos += 2
|
pos += 2
|
||||||
u8.set(session.authKey, pos)
|
buf.set(session.authKey, pos)
|
||||||
|
|
||||||
let b64 = base64.encode(u8, true)
|
let b64 = base64.encode(buf, true)
|
||||||
while (b64.length % 4 !== 0) b64 += '=' // for some reason telethon uses padding
|
while (b64.length % 4 !== 0) b64 += '=' // for some reason telethon uses padding
|
||||||
|
|
||||||
return `1${b64}`
|
return `1${b64}`
|
||||||
|
|
|
@ -5,6 +5,7 @@ import * as fs from 'node:fs'
|
||||||
|
|
||||||
const KNOWN_DECORATORS = ['memoizeGetters', 'makeInspectable']
|
const KNOWN_DECORATORS = ['memoizeGetters', 'makeInspectable']
|
||||||
|
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => {
|
export default () => {
|
||||||
const networkManagerId = fileURLToPath(new URL('./src/network/network-manager.ts', import.meta.url))
|
const networkManagerId = fileURLToPath(new URL('./src/network/network-manager.ts', import.meta.url))
|
||||||
const highlevelTypesDir = fileURLToPath(new URL('./src/highlevel/types', import.meta.url))
|
const highlevelTypesDir = fileURLToPath(new URL('./src/highlevel/types', import.meta.url))
|
||||||
|
@ -18,7 +19,7 @@ export default () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rollupPluginsPre: [
|
pluginsPre: [
|
||||||
{
|
{
|
||||||
name: 'mtcute-core-build-plugin',
|
name: 'mtcute-core-build-plugin',
|
||||||
transform(code, id) {
|
transform(code, id) {
|
||||||
|
@ -70,7 +71,7 @@ export default () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
finalJsr({ outDir }) {
|
finalizeJsr({ outDir }) {
|
||||||
const networkMgrFile = resolve(outDir, 'network/network-manager.ts')
|
const networkMgrFile = resolve(outDir, 'network/network-manager.ts')
|
||||||
const code = fs.readFileSync(networkMgrFile, 'utf8')
|
const code = fs.readFileSync(networkMgrFile, 'utf8')
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,13 @@
|
||||||
"./methods.js": "./src/highlevel/methods.ts"
|
"./methods.js": "./src/highlevel/methods.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run -w build-package core",
|
|
||||||
"gen-client": "node ./scripts/generate-client.cjs",
|
"gen-client": "node ./scripts/generate-client.cjs",
|
||||||
"gen-updates": "node ./scripts/generate-updates.cjs"
|
"gen-updates": "node ./scripts/generate-updates.cjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fuman/io": "workspace:^",
|
"@fuman/io": "0.0.1",
|
||||||
"@fuman/net": "workspace:^",
|
"@fuman/net": "0.0.1",
|
||||||
"@fuman/utils": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@mtcute/file-id": "workspace:^",
|
"@mtcute/file-id": "workspace:^",
|
||||||
"@mtcute/tl": "workspace:^",
|
"@mtcute/tl": "workspace:^",
|
||||||
"@mtcute/tl-runtime": "workspace:^",
|
"@mtcute/tl-runtime": "workspace:^",
|
||||||
|
|
|
@ -579,8 +579,10 @@ withParams(params: RpcCallOptions): this\n`)
|
||||||
it.type = { kind: ts.SyntaxKind.StringKeyword }
|
it.type = { kind: ts.SyntaxKind.StringKeyword }
|
||||||
} else if (
|
} else if (
|
||||||
it.initializer.kind === ts.SyntaxKind.NumericLiteral
|
it.initializer.kind === ts.SyntaxKind.NumericLiteral
|
||||||
|| (it.initializer.kind === ts.SyntaxKind.Identifier
|
|| (
|
||||||
&& (it.initializer.escapedText === 'NaN' || it.initializer.escapedText === 'Infinity'))
|
it.initializer.kind === ts.SyntaxKind.Identifier
|
||||||
|
&& (it.initializer.escapedText === 'NaN' || it.initializer.escapedText === 'Infinity')
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
it.type = { kind: ts.SyntaxKind.NumberKeyword }
|
it.type = { kind: ts.SyntaxKind.NumberKeyword }
|
||||||
} else {
|
} else {
|
||||||
|
@ -723,8 +725,6 @@ withParams(params: RpcCallOptions): this\n`)
|
||||||
'call',
|
'call',
|
||||||
'importSession',
|
'importSession',
|
||||||
'exportSession',
|
'exportSession',
|
||||||
'onError',
|
|
||||||
'emitError',
|
|
||||||
'handleClientUpdate',
|
'handleClientUpdate',
|
||||||
'getApiCrenetials',
|
'getApiCrenetials',
|
||||||
'getPoolSize',
|
'getPoolSize',
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('BaseTelegramClient', () => {
|
||||||
const session = await client.exportSession()
|
const session = await client.exportSession()
|
||||||
|
|
||||||
expect(session).toMatchInlineSnapshot(
|
expect(session).toMatchInlineSnapshot(
|
||||||
`"AwQAAAAXAgIADjE0OS4xNTQuMTY3LjUwALsBAAAXAgICDzE0OS4xNTQuMTY3LjIyMrsBAAD-AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"`,
|
'"AwQAAAAXAgIADjE0OS4xNTQuMTY3LjUwALsBAAAXAgICDzE0OS4xNTQuMTY3LjIyMrsBAAD-AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -290,18 +290,7 @@ export class BaseTelegramClient implements ITelegramClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
onError: Emitter<Error> = new Emitter()
|
||||||
* Register an error handler for the client
|
|
||||||
*
|
|
||||||
* @param handler Error handler.
|
|
||||||
*/
|
|
||||||
onError(handler: (err: unknown) => void): void {
|
|
||||||
this.mt.onError(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
emitError(err: unknown): void {
|
|
||||||
this.mt.emitError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void {
|
handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void {
|
||||||
this.updates?.handleClientUpdate(updates, noDispatch)
|
this.updates?.handleClientUpdate(updates, noDispatch)
|
||||||
|
|
|
@ -6584,12 +6584,6 @@ TelegramClient.prototype.importSession = function (...args) {
|
||||||
TelegramClient.prototype.exportSession = function (...args) {
|
TelegramClient.prototype.exportSession = function (...args) {
|
||||||
return this._client.exportSession(...args)
|
return this._client.exportSession(...args)
|
||||||
}
|
}
|
||||||
TelegramClient.prototype.onError = function (...args) {
|
|
||||||
return this._client.onError(...args)
|
|
||||||
}
|
|
||||||
TelegramClient.prototype.emitError = function (...args) {
|
|
||||||
return this._client.emitError(...args)
|
|
||||||
}
|
|
||||||
TelegramClient.prototype.handleClientUpdate = function (...args) {
|
TelegramClient.prototype.handleClientUpdate = function (...args) {
|
||||||
return this._client.handleClientUpdate(...args)
|
return this._client.handleClientUpdate(...args)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,13 +52,12 @@ export interface ITelegramClient {
|
||||||
): Promise<tl.RpcCallReturn[T['_']]>
|
): Promise<tl.RpcCallReturn[T['_']]>
|
||||||
importSession(session: string | StringSessionData, force?: boolean): Promise<void>
|
importSession(session: string | StringSessionData, force?: boolean): Promise<void>
|
||||||
exportSession(): Promise<string>
|
exportSession(): Promise<string>
|
||||||
onError(handler: (err: unknown) => void): void
|
|
||||||
emitError(err: unknown): void
|
|
||||||
handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void
|
handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void
|
||||||
|
|
||||||
onServerUpdate: Emitter<tl.TypeUpdates>
|
onServerUpdate: Emitter<tl.TypeUpdates>
|
||||||
onRawUpdate: Emitter<RawUpdateInfo>
|
onRawUpdate: Emitter<RawUpdateInfo>
|
||||||
onConnectionState: Emitter<ConnectionState>
|
onConnectionState: Emitter<ConnectionState>
|
||||||
|
onError: Emitter<Error>
|
||||||
|
|
||||||
getApiCrenetials(): Promise<{ id: number, hash: string }>
|
getApiCrenetials(): Promise<{ id: number, hash: string }>
|
||||||
// todo - this is only used for file dl/ul, which should probably be moved
|
// todo - this is only used for file dl/ul, which should probably be moved
|
||||||
|
|
|
@ -1,36 +1,41 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { AsyncResource, asNonNull } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtTypeAssertionError } from '../../types/errors.js'
|
import { MtTypeAssertionError } from '../../types/errors.js'
|
||||||
import { Reloadable } from '../../utils/reloadable.js'
|
|
||||||
import { tlJsonToJson } from '../../utils/tl-json.js'
|
import { tlJsonToJson } from '../../utils/tl-json.js'
|
||||||
import type { BaseTelegramClient } from '../base.js'
|
import type { BaseTelegramClient } from '../base.js'
|
||||||
import type { AppConfigSchema } from '../types/misc/app-config.js'
|
import type { AppConfigSchema } from '../types/misc/app-config.js'
|
||||||
|
|
||||||
export class AppConfigManager {
|
export class AppConfigManager {
|
||||||
constructor(private client: BaseTelegramClient) {}
|
private _resource
|
||||||
|
constructor(private client: BaseTelegramClient) {
|
||||||
|
this._resource = new AsyncResource<tl.help.RawAppConfig>({
|
||||||
|
fetcher: async ({ current }) => {
|
||||||
|
const res = await this.client.call({
|
||||||
|
_: 'help.getAppConfig',
|
||||||
|
hash: current?.hash ?? 0,
|
||||||
|
})
|
||||||
|
|
||||||
private _reloadable = new Reloadable<tl.help.RawAppConfig>({
|
if (res._ === 'help.appConfigNotModified') {
|
||||||
reload: this._reload.bind(this),
|
return {
|
||||||
getExpiresAt: () => 3_600_000,
|
data: asNonNull(current),
|
||||||
disableAutoReload: true,
|
expiresIn: 3_600_000,
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _reload(old?: tl.help.RawAppConfig) {
|
return {
|
||||||
const res = await this.client.call({
|
data: res,
|
||||||
_: 'help.getAppConfig',
|
expiresIn: 3_600_000,
|
||||||
hash: old?.hash ?? 0,
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res._ === 'help.appConfigNotModified') return old!
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _object?: AppConfigSchema
|
private _object?: AppConfigSchema
|
||||||
async get(): Promise<AppConfigSchema> {
|
async get(): Promise<AppConfigSchema> {
|
||||||
if (!this._reloadable.isStale && this._object) return this._object
|
if (!this._resource.isStale && this._object) return this._object
|
||||||
|
|
||||||
const obj = tlJsonToJson((await this._reloadable.get()).config)
|
const obj = tlJsonToJson((await this._resource.get()).config)
|
||||||
|
|
||||||
if (!obj || typeof obj !== 'object') {
|
if (!obj || typeof obj !== 'object') {
|
||||||
throw new MtTypeAssertionError('appConfig', 'object', typeof obj)
|
throw new MtTypeAssertionError('appConfig', 'object', typeof obj)
|
||||||
|
|
|
@ -67,6 +67,7 @@ function _initializeClient(this: TelegramClient, opts: TelegramClientOptions) {
|
||||||
Object.defineProperty(this, 'onServerUpdate', { value: this._client.onServerUpdate })
|
Object.defineProperty(this, 'onServerUpdate', { value: this._client.onServerUpdate })
|
||||||
Object.defineProperty(this, 'onRawUpdate', { value: this._client.onServerUpdate })
|
Object.defineProperty(this, 'onRawUpdate', { value: this._client.onServerUpdate })
|
||||||
Object.defineProperty(this, 'onConnectionState', { value: this._client.onConnectionState })
|
Object.defineProperty(this, 'onConnectionState', { value: this._client.onConnectionState })
|
||||||
|
Object.defineProperty(this, 'onError', { value: this._client.onError })
|
||||||
|
|
||||||
if (!opts.disableUpdates) {
|
if (!opts.disableUpdates) {
|
||||||
const skipConversationUpdates = opts.skipConversationUpdates ?? true
|
const skipConversationUpdates = opts.skipConversationUpdates ?? true
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
import type { ITelegramClient } from '../../client.types.js'
|
import type { ITelegramClient } from '../../client.types.js'
|
||||||
import type { User } from '../../types/index.js'
|
import type { User } from '../../types/index.js'
|
||||||
|
|
||||||
|
@ -22,5 +24,5 @@ export function run(
|
||||||
): void {
|
): void {
|
||||||
start(client, params)
|
start(client, params)
|
||||||
.then(then)
|
.then(then)
|
||||||
.catch(err => client.emitError(err))
|
.catch(err => client.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ import { Deferred, base64 } from '@fuman/utils'
|
||||||
import type { MaybePromise } from '../../../types/utils.js'
|
import type { MaybePromise } from '../../../types/utils.js'
|
||||||
import { sleepWithAbort } from '../../../utils/misc-utils.js'
|
import { sleepWithAbort } from '../../../utils/misc-utils.js'
|
||||||
import { assertTypeIs } from '../../../utils/type-assertions.js'
|
import { assertTypeIs } from '../../../utils/type-assertions.js'
|
||||||
import type { ITelegramClient, ServerUpdateHandler } from '../../client.types.js'
|
import type { ITelegramClient } from '../../client.types.js'
|
||||||
import type { MaybeDynamic } from '../../types/index.js'
|
import type { MaybeDynamic } from '../../types/index.js'
|
||||||
import { User } from '../../types/index.js'
|
import { User } from '../../types/index.js'
|
||||||
import { resolveMaybeDynamic } from '../../utils/misc-utils.js'
|
import { resolveMaybeDynamic } from '../../utils/misc-utils.js'
|
||||||
|
import type { RawUpdateInfo } from '../../updates/types.js'
|
||||||
|
|
||||||
import { checkPassword } from './check-password.js'
|
import { checkPassword } from './check-password.js'
|
||||||
|
|
||||||
|
@ -53,26 +54,19 @@ export async function signInQr(
|
||||||
|
|
||||||
let waiter: Deferred<void> | undefined
|
let waiter: Deferred<void> | undefined
|
||||||
|
|
||||||
// crutch – we need to wait for the updateLoginToken update.
|
// todo: we should probably make this into an await-able function
|
||||||
// we replace the server update handler temporarily because:
|
const onUpdate = ({ update }: RawUpdateInfo) => {
|
||||||
// - updates manager may be disabled, in which case `onUpdate` will never be called
|
if (update._ === 'updateLoginToken') {
|
||||||
// - even if the updates manager is enabled, it won't start until we're logged in
|
|
||||||
//
|
|
||||||
// todo: how can we make this more clean?
|
|
||||||
const originalHandler = client.getServerUpdateHandler()
|
|
||||||
|
|
||||||
const onUpdate: ServerUpdateHandler = (upd) => {
|
|
||||||
if (upd._ === 'updateShort' && upd.update._ === 'updateLoginToken') {
|
|
||||||
onQrScanned?.()
|
onQrScanned?.()
|
||||||
waiter?.resolve()
|
waiter?.resolve()
|
||||||
client.onServerUpdate(originalHandler)
|
client.onRawUpdate.remove(onUpdate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.onServerUpdate(onUpdate)
|
client.onRawUpdate.add(onUpdate)
|
||||||
|
|
||||||
abortSignal?.addEventListener('abort', () => {
|
abortSignal?.addEventListener('abort', () => {
|
||||||
client.onServerUpdate(originalHandler)
|
client.onRawUpdate.remove(onUpdate)
|
||||||
waiter?.reject(abortSignal.reason)
|
waiter?.reject(abortSignal.reason)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -180,6 +174,6 @@ export async function signInQr(
|
||||||
|
|
||||||
return new User(self)
|
return new User(self)
|
||||||
} finally {
|
} finally {
|
||||||
client.onServerUpdate(originalHandler)
|
client.onRawUpdate.remove(onUpdate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
import type { IReadable } from '@fuman/io'
|
import type { IReadable } from '@fuman/io'
|
||||||
import { read } from '@fuman/io'
|
import { read, webReadableToFuman } from '@fuman/io'
|
||||||
import { AsyncLock } from '@fuman/utils'
|
import { AsyncLock } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtArgumentError } from '../../../types/errors.js'
|
import { MtArgumentError } from '../../../types/errors.js'
|
||||||
|
@ -175,7 +175,7 @@ export async function uploadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file instanceof ReadableStream) {
|
if (file instanceof ReadableStream) {
|
||||||
file = read.async.fromWeb(file)
|
file = webReadableToFuman(file)
|
||||||
} else if (!(typeof file === 'object' && 'read' in file)) { // IReadable
|
} else if (!(typeof file === 'object' && 'read' in file)) { // IReadable
|
||||||
throw new MtArgumentError('Could not convert input `file` to stream!')
|
throw new MtArgumentError('Could not convert input `file` to stream!')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime'
|
import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime'
|
||||||
|
import { u8 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { IKeyValueRepository } from '../../../storage/repository/key-value.js'
|
import type { IKeyValueRepository } from '../../../storage/repository/key-value.js'
|
||||||
import type { ServiceOptions } from '../../../storage/service/base.js'
|
import type { ServiceOptions } from '../../../storage/service/base.js'
|
||||||
|
@ -18,7 +19,7 @@ export interface CurrentUserInfo {
|
||||||
const KV_CURRENT_USER = 'current_user'
|
const KV_CURRENT_USER = 'current_user'
|
||||||
|
|
||||||
function serialize(info: CurrentUserInfo | null): Uint8Array {
|
function serialize(info: CurrentUserInfo | null): Uint8Array {
|
||||||
if (!info) return new Uint8Array(0)
|
if (!info) return u8.alloc(0)
|
||||||
|
|
||||||
const hasUsernames = info.usernames.length > 0
|
const hasUsernames = info.usernames.length > 0
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { typed } from '@fuman/utils'
|
import { typed, u8 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { IKeyValueRepository } from '../../../storage/repository/key-value.js'
|
import type { IKeyValueRepository } from '../../../storage/repository/key-value.js'
|
||||||
import type { ServiceOptions } from '../../../storage/service/base.js'
|
import type { ServiceOptions } from '../../../storage/service/base.js'
|
||||||
|
@ -28,7 +28,7 @@ export class UpdatesStateService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setInt(key: string, val: number): Promise<void> {
|
private async _setInt(key: string, val: number): Promise<void> {
|
||||||
const buf = new Uint8Array(4)
|
const buf = u8.alloc(4)
|
||||||
typed.toDataView(buf).setInt32(0, val, true)
|
typed.toDataView(buf).setInt32(0, val, true)
|
||||||
|
|
||||||
await this._kv.set(key, buf)
|
await this._kv.set(key, buf)
|
||||||
|
@ -73,7 +73,7 @@ export class UpdatesStateService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setChannelPts(channelId: number, pts: number): Promise<void> {
|
async setChannelPts(channelId: number, pts: number): Promise<void> {
|
||||||
const buf = new Uint8Array(4)
|
const buf = u8.alloc(4)
|
||||||
typed.toDataView(buf).setUint32(0, pts, true)
|
typed.toDataView(buf).setUint32(0, pts, true)
|
||||||
|
|
||||||
await this._kv.set(KV_CHANNEL_PREFIX + channelId, buf)
|
await this._kv.set(KV_CHANNEL_PREFIX + channelId, buf)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
import { AsyncLock, Deferred, Deque, timers } from '@fuman/utils'
|
import { AsyncLock, Deferred, Deque, timers, unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtArgumentError, MtTimeoutError } from '../../types/errors.js'
|
import { MtArgumentError, MtTimeoutError } from '../../types/errors.js'
|
||||||
import type { MaybePromise } from '../../types/utils.js'
|
import type { MaybePromise } from '../../types/utils.js'
|
||||||
|
@ -564,7 +564,7 @@ export class Conversation {
|
||||||
this._queuedNewMessage.popFront()
|
this._queuedNewMessage.popFront()
|
||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
this.client.emitError(e)
|
this.client.onError.emit(unknownToError(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
this._lastMessage = this._lastReceivedMessage = msg.id
|
this._lastMessage = this._lastReceivedMessage = msg.id
|
||||||
|
@ -594,7 +594,7 @@ export class Conversation {
|
||||||
this._pendingEditMessage.delete(msg.id)
|
this._pendingEditMessage.delete(msg.id)
|
||||||
}
|
}
|
||||||
})().catch((e) => {
|
})().catch((e) => {
|
||||||
this.client.emitError(e)
|
this.client.onError.emit(unknownToError(e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import { AsyncLock, ConditionVariable, Deque, timers } from '@fuman/utils'
|
import { AsyncLock, ConditionVariable, Deque, timers, unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtArgumentError } from '../../types/errors.js'
|
import { MtArgumentError } from '../../types/errors.js'
|
||||||
import type { MaybePromise } from '../../types/utils.js'
|
import type { MaybePromise } from '../../types/utils.js'
|
||||||
|
@ -195,7 +195,7 @@ export class UpdatesManager {
|
||||||
|
|
||||||
notifyLoggedIn(self: CurrentUserInfo): void {
|
notifyLoggedIn(self: CurrentUserInfo): void {
|
||||||
this.auth = self
|
this.auth = self
|
||||||
this.startLoop().catch(err => this.client.emitError(err))
|
this.startLoop().catch(err => this.client.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyLoggedOut(): void {
|
notifyLoggedOut(): void {
|
||||||
|
@ -238,7 +238,7 @@ export class UpdatesManager {
|
||||||
this.updatesLoopActive = true
|
this.updatesLoopActive = true
|
||||||
timers.clearInterval(this.keepAliveInterval)
|
timers.clearInterval(this.keepAliveInterval)
|
||||||
this.keepAliveInterval = timers.setInterval(this._onKeepAlive, KEEP_ALIVE_INTERVAL)
|
this.keepAliveInterval = timers.setInterval(this._onKeepAlive, KEEP_ALIVE_INTERVAL)
|
||||||
this._loop().catch(err => this.client.emitError(err))
|
this._loop().catch(err => this.client.onError.emit(unknownToError(err)))
|
||||||
|
|
||||||
if (this.catchUpOnStart) {
|
if (this.catchUpOnStart) {
|
||||||
this.catchUp()
|
this.catchUp()
|
||||||
|
@ -1245,20 +1245,20 @@ export class UpdatesManager {
|
||||||
// we just needed to apply new pts values
|
// we just needed to apply new pts values
|
||||||
return
|
return
|
||||||
case 'updateDcOptions': {
|
case 'updateDcOptions': {
|
||||||
const config = client.mt.network.config.getNow()
|
const config = client.mt.network.config.getCached()
|
||||||
|
|
||||||
if (config) {
|
if (config) {
|
||||||
client.mt.network.config.setData({
|
client.mt.network.config.setData({
|
||||||
...config,
|
...config,
|
||||||
dcOptions: upd.dcOptions,
|
dcOptions: upd.dcOptions,
|
||||||
})
|
}, config.expires * 1000)
|
||||||
} else {
|
} else {
|
||||||
client.mt.network.config.update(true).catch(err => client.emitError(err))
|
client.mt.network.config.update(true).catch(err => client.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'updateConfig':
|
case 'updateConfig':
|
||||||
client.mt.network.config.update(true).catch(err => client.emitError(err))
|
client.mt.network.config.update(true).catch(err => client.onError.emit(unknownToError(err)))
|
||||||
break
|
break
|
||||||
case 'updateUserName':
|
case 'updateUserName':
|
||||||
// todo
|
// todo
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { typed } from '@fuman/utils'
|
import { typed, u8 } from '@fuman/utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode 5-bit encoded voice message waveform into
|
* Decode 5-bit encoded voice message waveform into
|
||||||
|
@ -49,7 +49,7 @@ export function decodeWaveform(wf: Uint8Array): number[] {
|
||||||
export function encodeWaveform(wf: number[]): Uint8Array {
|
export function encodeWaveform(wf: number[]): Uint8Array {
|
||||||
const bitsCount = wf.length * 5
|
const bitsCount = wf.length * 5
|
||||||
const bytesCount = ~~((bitsCount + 7) / 8)
|
const bytesCount = ~~((bitsCount + 7) / 8)
|
||||||
const result = new Uint8Array(bytesCount + 1)
|
const result = u8.alloc(bytesCount + 1)
|
||||||
const dv = typed.toDataView(result)
|
const dv = typed.toDataView(result)
|
||||||
|
|
||||||
// Write each 0-31 unsigned char as 5 bit to result.
|
// Write each 0-31 unsigned char as 5 bit to result.
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { Emitter } from '@fuman/utils'
|
||||||
|
|
||||||
import type { RpcCallOptions } from '../../network/network-manager.js'
|
import type { RpcCallOptions } from '../../network/network-manager.js'
|
||||||
import type { MustEqual } from '../../types/utils.js'
|
import type { MustEqual } from '../../types/utils.js'
|
||||||
import { LogManager } from '../../utils/logger.js'
|
import { LogManager } from '../../utils/logger.js'
|
||||||
import type { ConnectionState, ITelegramClient, ServerUpdateHandler } from '../client.types.js'
|
import type { ConnectionState, ITelegramClient } 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 { ICorePlatform } from '../../types/platform'
|
import type { ICorePlatform } from '../../types/platform'
|
||||||
|
import type { RawUpdateInfo } from '../updates/types.js'
|
||||||
|
|
||||||
import { AppConfigManagerProxy } from './app-config.js'
|
import { AppConfigManagerProxy } from './app-config.js'
|
||||||
import { WorkerInvoker } from './invoker.js'
|
import { WorkerInvoker } from './invoker.js'
|
||||||
import type { ClientMessageHandler, SendFn, SomeWorker, WorkerCustomMethods } from './protocol.js'
|
import type { ClientMessageHandler, SendFn, SomeWorker, WorkerCustomMethods } from './protocol.js'
|
||||||
import { deserializeResult } from './protocol.js'
|
import { deserializeResult } from './protocol.js'
|
||||||
import { TelegramStorageProxy } from './storage.js'
|
import { TelegramStorageProxy } from './storage.js'
|
||||||
|
import { deserializeError } from './errors.js'
|
||||||
|
|
||||||
export interface TelegramWorkerPortOptions {
|
export interface TelegramWorkerPortOptions {
|
||||||
worker: SomeWorker
|
worker: SomeWorker
|
||||||
|
@ -104,33 +106,10 @@ export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> imp
|
||||||
|
|
||||||
abstract connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void]
|
abstract connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void]
|
||||||
|
|
||||||
private _serverUpdatesHandler: ServerUpdateHandler = () => {}
|
onServerUpdate: Emitter<tl.TypeUpdates> = new Emitter()
|
||||||
onServerUpdate(handler: ServerUpdateHandler): void {
|
onRawUpdate: Emitter<RawUpdateInfo> = new Emitter()
|
||||||
this._serverUpdatesHandler = handler
|
onConnectionState: Emitter<ConnectionState> = new Emitter()
|
||||||
}
|
onError: Emitter<Error> = new Emitter()
|
||||||
|
|
||||||
getServerUpdateHandler(): ServerUpdateHandler {
|
|
||||||
return this._serverUpdatesHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
private _errorHandler: (err: unknown) => void = () => {}
|
|
||||||
onError(handler: (err: unknown) => void): void {
|
|
||||||
this._errorHandler = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
emitError(err: unknown): void {
|
|
||||||
this._errorHandler(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateHandler: RawUpdateHandler = () => {}
|
|
||||||
onUpdate(handler: RawUpdateHandler): void {
|
|
||||||
this._updateHandler = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
private _connectionStateHandler: (state: ConnectionState) => void = () => {}
|
|
||||||
onConnectionState(handler: (state: ConnectionState) => void): void {
|
|
||||||
this._connectionStateHandler = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onMessage: ClientMessageHandler = (message) => {
|
private _onMessage: ClientMessageHandler = (message) => {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
|
@ -138,22 +117,22 @@ export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> imp
|
||||||
this.log.handler(message.color, message.level, message.tag, message.fmt, message.args)
|
this.log.handler(message.color, message.level, message.tag, message.fmt, message.args)
|
||||||
break
|
break
|
||||||
case 'server_update':
|
case 'server_update':
|
||||||
this._serverUpdatesHandler(deserializeResult(message.update))
|
this.onServerUpdate.emit(deserializeResult(message.update))
|
||||||
break
|
break
|
||||||
case 'conn_state':
|
case 'conn_state':
|
||||||
this._connectionStateHandler(message.state)
|
this.onConnectionState.emit(message.state)
|
||||||
break
|
break
|
||||||
case 'update': {
|
case 'update': {
|
||||||
const peers = new PeersIndex(deserializeResult(message.users), deserializeResult(message.chats))
|
const peers = new PeersIndex(deserializeResult(message.users), deserializeResult(message.chats))
|
||||||
peers.hasMin = message.hasMin
|
peers.hasMin = message.hasMin
|
||||||
this._updateHandler(deserializeResult(message.update), peers)
|
this.onRawUpdate.emit({ update: deserializeResult(message.update), peers })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'result':
|
case 'result':
|
||||||
this._invoker.handleResult(message)
|
this._invoker.handleResult(message)
|
||||||
break
|
break
|
||||||
case 'error':
|
case 'error':
|
||||||
this.emitError(message.error)
|
this.onError.emit(deserializeError(message.error))
|
||||||
break
|
break
|
||||||
case 'stop':
|
case 'stop':
|
||||||
this._abortController.abort()
|
this._abortController.abort()
|
||||||
|
|
|
@ -31,7 +31,7 @@ export type WorkerOutboundMessage =
|
||||||
chats: SerializedResult<Map<number, tl.TypeChat>>
|
chats: SerializedResult<Map<number, tl.TypeChat>>
|
||||||
hasMin: boolean
|
hasMin: boolean
|
||||||
}
|
}
|
||||||
| { type: 'error', error: unknown }
|
| { type: 'error', error: SerializedError }
|
||||||
| { type: 'stop' }
|
| { type: 'stop' }
|
||||||
| { type: 'conn_state', state: ConnectionState }
|
| { type: 'conn_state', state: ConnectionState }
|
||||||
| {
|
| {
|
||||||
|
|
|
@ -55,13 +55,13 @@ export abstract class TelegramWorker<T extends WorkerCustomMethods> {
|
||||||
fmt,
|
fmt,
|
||||||
args,
|
args,
|
||||||
})
|
})
|
||||||
client.onError(err =>
|
client.onError.add(err =>
|
||||||
this.broadcast({
|
this.broadcast({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
error: err,
|
error: serializeError(err),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
client.onConnectionState(state =>
|
client.onConnectionState.add(state =>
|
||||||
this.broadcast({
|
this.broadcast({
|
||||||
type: 'conn_state',
|
type: 'conn_state',
|
||||||
state,
|
state,
|
||||||
|
@ -70,7 +70,7 @@ export abstract class TelegramWorker<T extends WorkerCustomMethods> {
|
||||||
client.stopSignal.addEventListener('abort', () => this.broadcast({ type: 'stop' }))
|
client.stopSignal.addEventListener('abort', () => this.broadcast({ type: 'stop' }))
|
||||||
|
|
||||||
if (client.updates) {
|
if (client.updates) {
|
||||||
client.onUpdate((update, peers) =>
|
client.onRawUpdate.add(({ update, peers }) =>
|
||||||
this.broadcast({
|
this.broadcast({
|
||||||
type: 'update',
|
type: 'update',
|
||||||
update: serializeResult(update),
|
update: serializeResult(update),
|
||||||
|
@ -80,7 +80,7 @@ export abstract class TelegramWorker<T extends WorkerCustomMethods> {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
client.onServerUpdate(update =>
|
client.onServerUpdate.add(update =>
|
||||||
this.broadcast({
|
this.broadcast({
|
||||||
type: 'server_update',
|
type: 'server_update',
|
||||||
update: serializeResult(update),
|
update: serializeResult(update),
|
||||||
|
|
|
@ -44,7 +44,7 @@ export class AuthKey {
|
||||||
let padding = (16 /* header size */ + message.length + 12) /* min padding */ % 16
|
let padding = (16 /* header size */ + message.length + 12) /* min padding */ % 16
|
||||||
padding = 12 + (padding ? 16 - padding : 0)
|
padding = 12 + (padding ? 16 - padding : 0)
|
||||||
|
|
||||||
const buf = new Uint8Array(16 + message.length + padding)
|
const buf = u8.alloc(16 + message.length + padding)
|
||||||
const dv = typed.toDataView(buf)
|
const dv = typed.toDataView(buf)
|
||||||
|
|
||||||
dv.setInt32(0, serverSalt.low, true)
|
dv.setInt32(0, serverSalt.low, true)
|
||||||
|
|
|
@ -128,13 +128,13 @@ function rsaPad(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKey): Ui
|
||||||
throw new MtArgumentError('Failed to pad: too big data')
|
throw new MtArgumentError('Failed to pad: too big data')
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataPadded = new Uint8Array(192)
|
const dataPadded = u8.alloc(192)
|
||||||
dataPadded.set(data, 0)
|
dataPadded.set(data, 0)
|
||||||
crypto.randomFill(dataPadded.subarray(data.length))
|
crypto.randomFill(dataPadded.subarray(data.length))
|
||||||
data = dataPadded
|
data = dataPadded
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const aesIv = new Uint8Array(32)
|
const aesIv = u8.alloc(32)
|
||||||
|
|
||||||
const aesKey = crypto.randomBytes(32)
|
const aesKey = crypto.randomBytes(32)
|
||||||
|
|
||||||
|
@ -417,7 +417,7 @@ export async function doAuthorization(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dhGen._ === 'mt_dh_gen_retry') {
|
if (dhGen._ === 'mt_dh_gen_retry') {
|
||||||
const expectedHash = crypto.sha1(u8.concat3(newNonce, new Uint8Array([2]), authKeyAuxHash))
|
const expectedHash = crypto.sha1(u8.concat3(newNonce, [2], authKeyAuxHash))
|
||||||
|
|
||||||
if (!typed.equal(expectedHash.subarray(4, 20), dhGen.newNonceHash2)) {
|
if (!typed.equal(expectedHash.subarray(4, 20), dhGen.newNonceHash2)) {
|
||||||
throw new MtSecurityError('Step 4: invalid retry nonce hash from server')
|
throw new MtSecurityError('Step 4: invalid retry nonce hash from server')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { createStub } from '@mtcute/test'
|
import { createStub } from '@mtcute/test'
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import type { AsyncResourceContext } from '@fuman/utils'
|
||||||
|
|
||||||
import { ConfigManager } from './config-manager.js'
|
import { ConfigManager } from './config-manager.js'
|
||||||
|
|
||||||
|
@ -9,13 +10,20 @@ describe('ConfigManager', () => {
|
||||||
expires: 300,
|
expires: 300,
|
||||||
})
|
})
|
||||||
const getConfig = vi.fn()
|
const getConfig = vi.fn()
|
||||||
|
const fakePerfNow = vi.fn(() => Date.now())
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.useFakeTimers()
|
vi.useFakeTimers()
|
||||||
vi.setSystemTime(0)
|
vi.setSystemTime(0)
|
||||||
|
vi.stubGlobal('performance', {
|
||||||
|
now: fakePerfNow,
|
||||||
|
})
|
||||||
getConfig.mockClear().mockImplementation(() => Promise.resolve(config))
|
getConfig.mockClear().mockImplementation(() => Promise.resolve(config))
|
||||||
})
|
})
|
||||||
afterEach(() => void vi.useRealTimers())
|
afterEach(() => {
|
||||||
|
vi.useRealTimers()
|
||||||
|
vi.unstubAllGlobals()
|
||||||
|
})
|
||||||
|
|
||||||
it('should fetch initial config', async () => {
|
it('should fetch initial config', async () => {
|
||||||
const cm = new ConfigManager(getConfig)
|
const cm = new ConfigManager(getConfig)
|
||||||
|
@ -24,7 +32,7 @@ describe('ConfigManager', () => {
|
||||||
|
|
||||||
expect(getConfig).toHaveBeenCalledTimes(1)
|
expect(getConfig).toHaveBeenCalledTimes(1)
|
||||||
expect(fetchedConfig).toEqual(config)
|
expect(fetchedConfig).toEqual(config)
|
||||||
expect(cm.getNow()).toEqual(config)
|
expect(cm.getCached()).toEqual(config)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should automatically update config', async () => {
|
it('should automatically update config', async () => {
|
||||||
|
@ -47,7 +55,7 @@ describe('ConfigManager', () => {
|
||||||
const cm = new ConfigManager(getConfig)
|
const cm = new ConfigManager(getConfig)
|
||||||
expect(cm.isStale).toBe(true)
|
expect(cm.isStale).toBe(true)
|
||||||
|
|
||||||
cm.setData(config)
|
cm.setData(config, config.expires * 1000)
|
||||||
expect(cm.isStale).toBe(false)
|
expect(cm.isStale).toBe(false)
|
||||||
|
|
||||||
vi.setSystemTime(300_000)
|
vi.setSystemTime(300_000)
|
||||||
|
@ -84,15 +92,23 @@ describe('ConfigManager', () => {
|
||||||
it('should call listeners on config update', async () => {
|
it('should call listeners on config update', async () => {
|
||||||
const cm = new ConfigManager(getConfig)
|
const cm = new ConfigManager(getConfig)
|
||||||
const listener = vi.fn()
|
const listener = vi.fn()
|
||||||
cm.onReload(listener)
|
cm.onUpdated.add(listener)
|
||||||
|
|
||||||
await cm.update()
|
await cm.update()
|
||||||
|
const call = structuredClone(listener.mock.calls[0][0]) as AsyncResourceContext<tl.RawConfig>
|
||||||
|
|
||||||
vi.setSystemTime(300_000)
|
vi.setSystemTime(300_000)
|
||||||
cm.onReload(listener)
|
cm.onUpdated.remove(listener)
|
||||||
await cm.update()
|
await cm.update()
|
||||||
|
|
||||||
expect(listener).toHaveBeenCalledOnce()
|
expect(listener).toHaveBeenCalledOnce()
|
||||||
expect(listener).toHaveBeenCalledWith(config)
|
expect(call).toEqual({
|
||||||
|
abort: {},
|
||||||
|
current: config,
|
||||||
|
currentExpiresAt: 300_000,
|
||||||
|
currentFetchedAt: 0,
|
||||||
|
isBackground: false,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly destroy', async () => {
|
it('should correctly destroy', async () => {
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
|
import { AsyncResource } from '@fuman/utils'
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { Reloadable } from '../utils/reloadable.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config manager is responsible for keeping
|
* Config manager is responsible for keeping
|
||||||
* the current server configuration up-to-date
|
* the current server configuration up-to-date
|
||||||
* and providing methods to find the best DC
|
* and providing methods to find the best DC
|
||||||
* option for the current session.
|
* option for the current session.
|
||||||
*/
|
*/
|
||||||
export class ConfigManager extends Reloadable<tl.RawConfig> {
|
export class ConfigManager extends AsyncResource<tl.RawConfig> {
|
||||||
constructor(update: () => Promise<tl.RawConfig>) {
|
constructor(update: () => Promise<tl.RawConfig>) {
|
||||||
super({
|
super({
|
||||||
reload: update,
|
fetcher: async () => {
|
||||||
getExpiresAt: data => data.expires * 1000,
|
const res = await update()
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: res,
|
||||||
|
expiresIn: res.expires * 1000 - Date.now(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoReload: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +32,9 @@ export class ConfigManager extends Reloadable<tl.RawConfig> {
|
||||||
}): Promise<tl.RawDcOption | undefined> {
|
}): Promise<tl.RawDcOption | undefined> {
|
||||||
if (this.isStale) await this.update()
|
if (this.isStale) await this.update()
|
||||||
|
|
||||||
const options = this._data!.dcOptions.filter((opt) => {
|
const data = this.getCached()!
|
||||||
|
|
||||||
|
const options = data.dcOptions.filter((opt) => {
|
||||||
if (opt.tcpoOnly) return false // unsupported
|
if (opt.tcpoOnly) return false // unsupported
|
||||||
if (opt.ipv6 && !params.allowIpv6) return false
|
if (opt.ipv6 && !params.allowIpv6) return false
|
||||||
if (opt.mediaOnly && !params.allowMedia) return false
|
if (opt.mediaOnly && !params.allowMedia) return false
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import type { Middleware } from '@fuman/utils'
|
||||||
|
|
||||||
import type { Middleware } from '../../utils/composer.js'
|
|
||||||
import type { RpcCallMiddleware, RpcCallMiddlewareContext } from '../network-manager.js'
|
import type { RpcCallMiddleware, RpcCallMiddlewareContext } from '../network-manager.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Long from 'long'
|
||||||
import type { mtp, tl } from '@mtcute/tl'
|
import type { mtp, tl } from '@mtcute/tl'
|
||||||
import type { TlBinaryWriter, TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
import type { TlBinaryWriter, TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
||||||
import { TlSerializationCounter } from '@mtcute/tl-runtime'
|
import { TlSerializationCounter } from '@mtcute/tl-runtime'
|
||||||
import { type Deferred, Deque, LruSet } from '@fuman/utils'
|
import { type Deferred, Deque, LruSet, timers } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtcuteError } from '../types/index.js'
|
import { MtcuteError } from '../types/index.js'
|
||||||
import type {
|
import type {
|
||||||
|
@ -16,7 +16,6 @@ import {
|
||||||
compareLongs,
|
compareLongs,
|
||||||
getRandomInt,
|
getRandomInt,
|
||||||
randomLong,
|
randomLong,
|
||||||
timers,
|
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
|
|
||||||
import { AuthKey } from './auth-key.js'
|
import { AuthKey } from './auth-key.js'
|
||||||
|
|
|
@ -47,32 +47,32 @@ function _isQuadraticResidue(a: bigint): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeTlsOperations(h: TlsHelloWriter): void {
|
function executeTlsOperations(h: TlsHelloWriter): void {
|
||||||
h.string(new Uint8Array([0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xFC, 0x03, 0x03]))
|
h.string([0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xFC, 0x03, 0x03])
|
||||||
h.zero(32)
|
h.zero(32)
|
||||||
h.string(new Uint8Array([0x20]))
|
h.string([0x20])
|
||||||
h.random(32)
|
h.random(32)
|
||||||
h.string(new Uint8Array([0x00, 0x20]))
|
h.string([0x00, 0x20])
|
||||||
h.grease(0)
|
h.grease(0)
|
||||||
/* eslint-disable antfu/consistent-list-newline */
|
/* eslint-disable antfu/consistent-list-newline */
|
||||||
h.string(new Uint8Array([
|
h.string([
|
||||||
0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xC0, 0x2B, 0xC0, 0x2F, 0xC0, 0x2C,
|
0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xC0, 0x2B, 0xC0, 0x2F, 0xC0, 0x2C,
|
||||||
0xC0, 0x30, 0xCC, 0xA9, 0xCC, 0xA8, 0xC0, 0x13, 0xC0, 0x14, 0x00, 0x9C,
|
0xC0, 0x30, 0xCC, 0xA9, 0xCC, 0xA8, 0xC0, 0x13, 0xC0, 0x14, 0x00, 0x9C,
|
||||||
0x00, 0x9D, 0x00, 0x2F, 0x00, 0x35, 0x01, 0x00, 0x01, 0x93,
|
0x00, 0x9D, 0x00, 0x2F, 0x00, 0x35, 0x01, 0x00, 0x01, 0x93,
|
||||||
]))
|
])
|
||||||
h.grease(2)
|
h.grease(2)
|
||||||
h.string(new Uint8Array([0x00, 0x00, 0x00, 0x00]))
|
h.string([0x00, 0x00, 0x00, 0x00])
|
||||||
h.beginScope()
|
h.beginScope()
|
||||||
h.beginScope()
|
h.beginScope()
|
||||||
h.string(new Uint8Array([0x00]))
|
h.string([0x00])
|
||||||
h.beginScope()
|
h.beginScope()
|
||||||
h.domain()
|
h.domain()
|
||||||
h.endScope()
|
h.endScope()
|
||||||
h.endScope()
|
h.endScope()
|
||||||
h.endScope()
|
h.endScope()
|
||||||
h.string(new Uint8Array([0x00, 0x17, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x08]))
|
h.string([0x00, 0x17, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x08])
|
||||||
h.grease(4)
|
h.grease(4)
|
||||||
h.string(
|
h.string(
|
||||||
new Uint8Array([
|
[
|
||||||
0x00, 0x1D, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0B, 0x00, 0x02, 0x01, 0x00,
|
0x00, 0x1D, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0B, 0x00, 0x02, 0x01, 0x00,
|
||||||
0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0E, 0x00, 0x0C, 0x02, 0x68,
|
0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0E, 0x00, 0x0C, 0x02, 0x68,
|
||||||
0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2F, 0x31, 0x2E, 0x31, 0x00, 0x05,
|
0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2F, 0x31, 0x2E, 0x31, 0x00, 0x05,
|
||||||
|
@ -80,16 +80,16 @@ function executeTlsOperations(h: TlsHelloWriter): void {
|
||||||
0x10, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,
|
0x10, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,
|
||||||
0x01, 0x08, 0x06, 0x06, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00,
|
0x01, 0x08, 0x06, 0x06, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00,
|
||||||
0x2B, 0x00, 0x29,
|
0x2B, 0x00, 0x29,
|
||||||
]),
|
],
|
||||||
)
|
)
|
||||||
h.grease(4)
|
h.grease(4)
|
||||||
h.string(new Uint8Array([0x00, 0x01, 0x00, 0x00, 0x1D, 0x00, 0x20]))
|
h.string([0x00, 0x01, 0x00, 0x00, 0x1D, 0x00, 0x20])
|
||||||
h.key()
|
h.key()
|
||||||
h.string(new Uint8Array([0x00, 0x2D, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2B, 0x00, 0x0B, 0x0A]))
|
h.string([0x00, 0x2D, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2B, 0x00, 0x0B, 0x0A])
|
||||||
h.grease(6)
|
h.grease(6)
|
||||||
h.string(new Uint8Array([0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x1B, 0x00, 0x03, 0x02, 0x00, 0x02]))
|
h.string([0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x1B, 0x00, 0x03, 0x02, 0x00, 0x02])
|
||||||
h.grease(3)
|
h.grease(3)
|
||||||
h.string(new Uint8Array([0x00, 0x01, 0x00, 0x00, 0x15]))
|
h.string([0x00, 0x01, 0x00, 0x00, 0x15])
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,12 +124,12 @@ class TlsHelloWriter {
|
||||||
domain: Uint8Array,
|
domain: Uint8Array,
|
||||||
) {
|
) {
|
||||||
this._domain = domain
|
this._domain = domain
|
||||||
this.buf = new Uint8Array(size)
|
this.buf = u8.alloc(size)
|
||||||
this.dv = typed.toDataView(this.buf)
|
this.dv = typed.toDataView(this.buf)
|
||||||
this._grease = initGrease(this.crypto, 7)
|
this._grease = initGrease(this.crypto, 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
string(buf: Uint8Array) {
|
string(buf: ArrayLike<number>) {
|
||||||
this.buf.set(buf, this.pos)
|
this.buf.set(buf, this.pos)
|
||||||
this.pos += buf.length
|
this.pos += buf.length
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ export abstract class BaseMtProxyTransport implements TelegramTransport {
|
||||||
u8.concat([
|
u8.concat([
|
||||||
helloRand,
|
helloRand,
|
||||||
respBuf.slice(0, 11),
|
respBuf.slice(0, 11),
|
||||||
new Uint8Array(32),
|
u8.alloc(32),
|
||||||
respBuf.slice(11 + 32),
|
respBuf.slice(11 + 32),
|
||||||
]),
|
]),
|
||||||
this._rawSecret,
|
this._rawSecret,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { mtp, tl } from '@mtcute/tl'
|
import type { mtp, tl } from '@mtcute/tl'
|
||||||
import { Deferred, Emitter } from '@fuman/utils'
|
import { Deferred, Emitter, unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
import type { Logger } from '../utils/index.js'
|
import type { Logger } from '../utils/index.js'
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ export class MultiSessionConnection {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.onError.emit(err)
|
this.onError.emit(unknownToError(err))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,8 +342,8 @@ export class MultiSessionConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTransport(factory: TelegramTransport): void {
|
async changeTransport(factory: TelegramTransport): Promise<void> {
|
||||||
this._connections.forEach(conn => conn.changeTransport(factory))
|
await Promise.all(this._connections.map(conn => conn.changeTransport(factory)))
|
||||||
}
|
}
|
||||||
|
|
||||||
getPoolSize(): number {
|
getPoolSize(): number {
|
||||||
|
|
|
@ -2,12 +2,11 @@ import type { mtp, tl } from '@mtcute/tl'
|
||||||
import type { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
import type { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
||||||
import type Long from 'long'
|
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 type { AsyncResourceContext, ComposedMiddleware, Middleware } from '@fuman/utils'
|
||||||
|
import { Deferred, composeMiddlewares } from '@fuman/utils'
|
||||||
|
|
||||||
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 { 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 { ICorePlatform } from '../types/platform'
|
||||||
|
@ -279,7 +278,7 @@ export class DcConnectionManager {
|
||||||
mainCount = this._mainCountOverride
|
mainCount = this._mainCountOverride
|
||||||
|
|
||||||
if (mainCount === 0) {
|
if (mainCount === 0) {
|
||||||
mainCount = this.manager.config.getNow()?.tmpSessions ?? 1
|
mainCount = this.manager.config.getCached()?.tmpSessions ?? 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mainCount = 1
|
mainCount = 1
|
||||||
|
@ -519,7 +518,7 @@ export class NetworkManager {
|
||||||
this.call = this._composeCall(params.middlewares)
|
this.call = this._composeCall(params.middlewares)
|
||||||
|
|
||||||
this._onConfigChanged = this._onConfigChanged.bind(this)
|
this._onConfigChanged = this._onConfigChanged.bind(this)
|
||||||
config.onReload(this._onConfigChanged)
|
config.onUpdated.add(this._onConfigChanged)
|
||||||
|
|
||||||
this._log = params.log.create('network')
|
this._log = params.log.create('network')
|
||||||
this._storage = params.storage
|
this._storage = params.storage
|
||||||
|
@ -749,9 +748,9 @@ export class NetworkManager {
|
||||||
dc.downloadSmall.resetSessions()
|
dc.downloadSmall.resetSessions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onConfigChanged(config: tl.RawConfig): void {
|
private _onConfigChanged({ current }: AsyncResourceContext<tl.RawConfig>): void {
|
||||||
if (config.tmpSessions) {
|
if (current?.tmpSessions) {
|
||||||
this._primaryDc?.setMainConnectionCount(config.tmpSessions)
|
this._primaryDc?.setMainConnectionCount(current.tmpSessions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,12 +865,12 @@ export class NetworkManager {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTransport(transport: TelegramTransport): void {
|
async changeTransport(transport: TelegramTransport): Promise<void> {
|
||||||
for (const dc of this._dcConnections.values()) {
|
for (const dc of this._dcConnections.values()) {
|
||||||
dc.main.changeTransport(transport)
|
await dc.main.changeTransport(transport)
|
||||||
dc.upload.changeTransport(transport)
|
await dc.upload.changeTransport(transport)
|
||||||
dc.download.changeTransport(transport)
|
await dc.download.changeTransport(transport)
|
||||||
dc.downloadSmall.changeTransport(transport)
|
await dc.downloadSmall.changeTransport(transport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,7 +901,7 @@ export class NetworkManager {
|
||||||
await dc.destroy()
|
await dc.destroy()
|
||||||
}
|
}
|
||||||
this._dcConnections.clear()
|
this._dcConnections.clear()
|
||||||
this.config.offReload(this._onConfigChanged)
|
this.config.onUpdated.remove(this._onConfigChanged)
|
||||||
this._resetOnNetworkChange?.()
|
this._resetOnNetworkChange?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { mtp } from '@mtcute/tl'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
import type { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
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, Emitter, u8 } from '@fuman/utils'
|
import { Deferred, Emitter, timers, u8 } from '@fuman/utils'
|
||||||
|
|
||||||
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'
|
||||||
|
@ -13,7 +13,6 @@ import {
|
||||||
longFromBuffer,
|
longFromBuffer,
|
||||||
randomLong,
|
randomLong,
|
||||||
removeFromLongArray,
|
removeFromLongArray,
|
||||||
timers,
|
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
import type { ICorePlatform } from '../types/platform'
|
import type { ICorePlatform } from '../types/platform'
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { defaultTestCryptoProvider, useFakeMathRandom } from '@mtcute/test'
|
||||||
import { hex } from '@fuman/utils'
|
import { hex } from '@fuman/utils'
|
||||||
import { Bytes, write } from '@fuman/io'
|
import { Bytes, write } from '@fuman/io'
|
||||||
|
|
||||||
import { IntermediatePacketCodec, PaddedIntermediatePacketCodec } from './intermediate'
|
import { IntermediatePacketCodec, PaddedIntermediatePacketCodec } from './intermediate.js'
|
||||||
import { TransportError } from './abstract'
|
import { TransportError } from './abstract.js'
|
||||||
|
|
||||||
describe('IntermediatePacketCodec', () => {
|
describe('IntermediatePacketCodec', () => {
|
||||||
it('should return correct tag', () => {
|
it('should return correct tag', () => {
|
||||||
|
|
|
@ -179,8 +179,8 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
await codec.tag()
|
await codec.tag()
|
||||||
|
|
||||||
expect(codec.decode(Bytes.from(hex.decode(msg1)), false)).rejects.toThrow(TransportError)
|
await expect(codec.decode(Bytes.from(hex.decode(msg1)), false)).rejects.toThrow(TransportError)
|
||||||
expect(codec.decode(Bytes.from(hex.decode(msg2)), false)).rejects.toThrow(TransportError)
|
await expect(codec.decode(Bytes.from(hex.decode(msg2)), false)).rejects.toThrow(TransportError)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly reset', async () => {
|
it('should correctly reset', async () => {
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
export type MaybePromise<T> = T | Promise<T>
|
export type { MaybeArray, MaybePromise } from '@fuman/utils'
|
||||||
|
|
||||||
export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>
|
export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>
|
||||||
export type PartialOnly<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>
|
export type PartialOnly<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>
|
||||||
export type AnyToNever<T> = any extends T ? never : T
|
export type AnyToNever<T> = any extends T ? never : T
|
||||||
|
|
||||||
export type MaybeArray<T> = T | T[]
|
|
||||||
|
|
||||||
export type MustEqual<T, V> = (() => T) extends () => V ? ((() => V) extends () => T ? T : V) : V
|
export type MustEqual<T, V> = (() => T) extends () => V ? ((() => V) extends () => T ? T : V) : V
|
||||||
|
|
||||||
export type PublicPart<T> = { [K in keyof T]: T[K] }
|
export type PublicPart<T> = { [K in keyof T]: T[K] }
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { u8 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { MaybePromise } from '../../types/index.js'
|
import type { MaybePromise } from '../../types/index.js'
|
||||||
|
|
||||||
import { factorizePQSync } from './factorization.js'
|
import { factorizePQSync } from './factorization.js'
|
||||||
|
@ -50,7 +52,7 @@ export abstract class BaseCryptoProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
randomBytes(size: number): Uint8Array {
|
randomBytes(size: number): Uint8Array {
|
||||||
const buf = new Uint8Array(size)
|
const buf = u8.alloc(size)
|
||||||
this.randomFill(buf)
|
this.randomFill(buf)
|
||||||
|
|
||||||
return buf
|
return buf
|
||||||
|
|
|
@ -42,7 +42,7 @@ export async function computeNewPasswordHash(
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
assertTypeIs('account.getPassword', algo, 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow')
|
assertTypeIs('account.getPassword', algo, 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow')
|
||||||
|
|
||||||
const salt1 = new Uint8Array(algo.salt1.length + 32)
|
const salt1 = u8.alloc(algo.salt1.length + 32)
|
||||||
salt1.set(algo.salt1)
|
salt1.set(algo.salt1)
|
||||||
crypto.randomFill(salt1.subarray(algo.salt1.length))
|
crypto.randomFill(salt1.subarray(algo.salt1.length))
|
||||||
;(algo as tl.Mutable<typeof algo>).salt1 = salt1
|
;(algo as tl.Mutable<typeof algo>).salt1 = salt1
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { timers } from '@fuman/utils'
|
||||||
*/
|
*/
|
||||||
export class EarlyTimer {
|
export class EarlyTimer {
|
||||||
private _timeout?: timers.Timer
|
private _timeout?: timers.Timer
|
||||||
private _immediate?: timers.Immediate
|
|
||||||
private _timeoutTs?: number
|
private _timeoutTs?: number
|
||||||
|
|
||||||
private _handler: () => void = () => {}
|
private _handler: () => void = () => {}
|
||||||
|
@ -20,13 +19,11 @@ export class EarlyTimer {
|
||||||
* (basically `setImmediate()`)
|
* (basically `setImmediate()`)
|
||||||
*/
|
*/
|
||||||
emitWhenIdle(): void {
|
emitWhenIdle(): void {
|
||||||
if (this._immediate) return
|
|
||||||
|
|
||||||
timers.clearTimeout(this._timeout)
|
timers.clearTimeout(this._timeout)
|
||||||
this._timeoutTs = Date.now()
|
this._timeoutTs = Date.now()
|
||||||
|
|
||||||
if (typeof timers.setImmediate !== 'undefined') {
|
if (typeof queueMicrotask !== 'undefined') {
|
||||||
this._immediate = timers.setImmediate(this.emitNow)
|
queueMicrotask(this.emitNow)
|
||||||
} else {
|
} else {
|
||||||
this._timeout = timers.setTimeout(this.emitNow, 0)
|
this._timeout = timers.setTimeout(this.emitNow, 0)
|
||||||
}
|
}
|
||||||
|
@ -68,12 +65,7 @@ export class EarlyTimer {
|
||||||
* Cancel the timer
|
* Cancel the timer
|
||||||
*/
|
*/
|
||||||
reset(): void {
|
reset(): void {
|
||||||
if (this._immediate) {
|
timers.clearTimeout(this._timeout)
|
||||||
timers.clearImmediate(this._immediate)
|
|
||||||
this._immediate = undefined
|
|
||||||
} else {
|
|
||||||
timers.clearTimeout(this._timeout)
|
|
||||||
}
|
|
||||||
this._timeoutTs = undefined
|
this._timeoutTs = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import * as timers from './timers.js'
|
|
||||||
|
|
||||||
export * from '../highlevel/utils/index.js'
|
export * from '../highlevel/utils/index.js'
|
||||||
// todo: remove after 1.0.0
|
// todo: remove after 1.0.0
|
||||||
export * from '../highlevel/storage/service/current-user.js'
|
export * from '../highlevel/storage/service/current-user.js'
|
||||||
|
@ -8,7 +6,6 @@ export * from '../storage/service/base.js'
|
||||||
export * from '../storage/service/default-dcs.js'
|
export * from '../storage/service/default-dcs.js'
|
||||||
// end todo
|
// end todo
|
||||||
export * from './bigint-utils.js'
|
export * from './bigint-utils.js'
|
||||||
export * from './composer.js'
|
|
||||||
export * from './crypto/index.js'
|
export * from './crypto/index.js'
|
||||||
export * from './dcs.js'
|
export * from './dcs.js'
|
||||||
export * from './early-timer.js'
|
export * from './early-timer.js'
|
||||||
|
@ -23,4 +20,3 @@ export * from './sorted-array.js'
|
||||||
export * from './tl-json.js'
|
export * from './tl-json.js'
|
||||||
export * from './type-assertions.js'
|
export * from './type-assertions.js'
|
||||||
export * from '@mtcute/tl-runtime'
|
export * from '@mtcute/tl-runtime'
|
||||||
export { timers }
|
|
||||||
|
|
|
@ -1,61 +1 @@
|
||||||
/* eslint-disable no-restricted-globals, ts/no-implied-eval */
|
export { timers } from '@fuman/utils'
|
||||||
|
|
||||||
// timers typings are mixed up across different runtimes, which leads
|
|
||||||
// to the globals being typed incorrectly.
|
|
||||||
// instead, we can treat the timers as opaque objects, and expose
|
|
||||||
// them through the `timers` esm namespace.
|
|
||||||
// this has near-zero runtime cost, but makes everything type-safe
|
|
||||||
//
|
|
||||||
// NB: we are using wrapper functions instead of...
|
|
||||||
// - directly exposing the globals because the standard doesn't allow that
|
|
||||||
// - .bind()-ing because it makes it harder to mock the timer globals
|
|
||||||
|
|
||||||
export interface Timer { readonly __type: 'Timer' }
|
|
||||||
export interface Interval { readonly __type: 'Interval' }
|
|
||||||
export interface Immediate { readonly __type: 'Immediate' }
|
|
||||||
|
|
||||||
const setTimeoutWrap = (
|
|
||||||
(...args: Parameters<typeof setTimeout>) => setTimeout(...args)
|
|
||||||
) as unknown as <T extends (...args: any[]) => any>(
|
|
||||||
fn: T, ms: number, ...args: Parameters<T>
|
|
||||||
) => Timer
|
|
||||||
const setIntervalWrap = (
|
|
||||||
(...args: Parameters<typeof setInterval>) => setInterval(...args)
|
|
||||||
) as unknown as <T extends (...args: any[]) => any>(
|
|
||||||
fn: T, ms: number, ...args: Parameters<T>
|
|
||||||
) => Interval
|
|
||||||
|
|
||||||
let setImmediateWrap: any
|
|
||||||
if (typeof setImmediate !== 'undefined') {
|
|
||||||
setImmediateWrap = (...args: Parameters<typeof setImmediate>) => setImmediate(...args)
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
setImmediateWrap = (fn: (...args: any[]) => void, ...args: any[]) => setTimeout(fn, 0, ...args)
|
|
||||||
}
|
|
||||||
const setImmediateWrapExported = setImmediateWrap as <T extends (...args: any[]) => any>(
|
|
||||||
fn: T, ...args: Parameters<T>
|
|
||||||
) => Immediate
|
|
||||||
|
|
||||||
const clearTimeoutWrap = (
|
|
||||||
(...args: Parameters<typeof clearTimeout>) => clearTimeout(...args)
|
|
||||||
) as unknown as (timer?: Timer) => void
|
|
||||||
const clearIntervalWrap = (
|
|
||||||
(...args: Parameters<typeof clearInterval>) => clearInterval(...args)
|
|
||||||
) as unknown as (timer?: Interval) => void
|
|
||||||
|
|
||||||
let clearImmediateWrap: any
|
|
||||||
if (typeof clearImmediate !== 'undefined') {
|
|
||||||
clearImmediateWrap = (...args: Parameters<typeof clearImmediate>) => clearImmediate(...args)
|
|
||||||
} else {
|
|
||||||
clearImmediateWrap = (timer: number) => clearTimeout(timer)
|
|
||||||
}
|
|
||||||
const clearImmediateWrapExported = clearImmediateWrap as (timer?: Immediate) => void
|
|
||||||
|
|
||||||
export {
|
|
||||||
setTimeoutWrap as setTimeout,
|
|
||||||
setIntervalWrap as setInterval,
|
|
||||||
setImmediateWrapExported as setImmediate,
|
|
||||||
clearTimeoutWrap as clearTimeout,
|
|
||||||
clearIntervalWrap as clearInterval,
|
|
||||||
clearImmediateWrapExported as clearImmediate,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
import { cpSync } from 'node:fs'
|
import { cpSync } from 'node:fs'
|
||||||
|
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => ({
|
export default () => ({
|
||||||
buildCjs: false,
|
viteConfig: {
|
||||||
final({ outDir, packageDir }) {
|
build: {
|
||||||
|
lib: {
|
||||||
|
formats: ['es'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
finalize({ outDir, packageDir }) {
|
||||||
cpSync(resolve(packageDir, 'template'), resolve(outDir, 'template'), { recursive: true })
|
cpSync(resolve(packageDir, 'template'), resolve(outDir, 'template'), { recursive: true })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
"create-bot": "./src/main.ts"
|
"create-bot": "./src/main.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run -w build-package create-bot",
|
|
||||||
"run": "tsx src/main.ts",
|
"run": "tsx src/main.ts",
|
||||||
"run:deno": "node scripts/generate-import-map.js && deno run --import-map=./scripts/import-map.json -A --unstable-sloppy-imports src/main.ts"
|
"run:deno": "node scripts/generate-import-map.js && deno run --import-map=./scripts/import-map.json -A --unstable-sloppy-imports src/main.ts"
|
||||||
},
|
},
|
||||||
|
@ -26,5 +25,8 @@
|
||||||
"@types/cross-spawn": "^6.0.6",
|
"@types/cross-spawn": "^6.0.6",
|
||||||
"@types/inquirer": "^9.0.6",
|
"@types/inquirer": "^9.0.6",
|
||||||
"@types/openurl": "^1.0.3"
|
"@types/openurl": "^1.0.3"
|
||||||
|
},
|
||||||
|
"fuman": {
|
||||||
|
"jsr": "skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable import/no-relative-packages, no-console, no-restricted-globals */
|
/* eslint-disable no-console, no-restricted-globals */
|
||||||
import { createHash } from 'node:crypto'
|
import { createHash } from 'node:crypto'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
|
@ -155,8 +155,9 @@ async function extractArtifacts(artifacts) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => ({
|
export default () => ({
|
||||||
async final({ packageDir, outDir }) {
|
async finalize({ packageDir, outDir }) {
|
||||||
const libDir = path.resolve(packageDir, 'lib')
|
const libDir = path.resolve(packageDir, 'lib')
|
||||||
|
|
||||||
if (!SKIP_PREBUILT) {
|
if (!SKIP_PREBUILT) {
|
||||||
|
|
|
@ -12,19 +12,19 @@
|
||||||
"./native.js": "./src/native.cjs"
|
"./native.js": "./src/native.cjs"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run -w build-package crypto-node",
|
|
||||||
"install": "node-gyp-build",
|
"install": "node-gyp-build",
|
||||||
"rebuild": "node-gyp configure && node-gyp -j 16 rebuild",
|
"rebuild": "node-gyp configure && node-gyp -j 16 rebuild",
|
||||||
"clean": "node-gyp clean"
|
"clean": "node-gyp clean"
|
||||||
},
|
},
|
||||||
"keepScripts": [
|
|
||||||
"install"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/node": "workspace:^",
|
"@mtcute/node": "workspace:^",
|
||||||
"node-gyp-build": "4.8.1"
|
"node-gyp-build": "4.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^"
|
"@mtcute/test": "workspace:^"
|
||||||
|
},
|
||||||
|
"fuman": {
|
||||||
|
"jsr": "skip",
|
||||||
|
"keepScripts": ["install"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
|
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => ({
|
export default () => ({
|
||||||
finalJsr({ outDir }) {
|
finalizeJsr({ outDir }) {
|
||||||
// jsr doesn't support symlinks, so we need to copy the files manually
|
// jsr doesn't support symlinks, so we need to copy the files manually
|
||||||
const real = fs.realpathSync(`${outDir}/common-internals-web`)
|
const real = fs.realpathSync(`${outDir}/common-internals-web`)
|
||||||
fs.unlinkSync(`${outDir}/common-internals-web`)
|
fs.unlinkSync(`${outDir}/common-internals-web`)
|
||||||
|
|
|
@ -12,16 +12,11 @@
|
||||||
"./utils.js": "./src/utils.ts",
|
"./utils.js": "./src/utils.ts",
|
||||||
"./methods.js": "./src/methods.ts"
|
"./methods.js": "./src/methods.ts"
|
||||||
},
|
},
|
||||||
"main": "src/index.ts",
|
|
||||||
"scripts": {
|
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package deno"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@db/sqlite": "npm:@jsr/db__sqlite@0.12.0",
|
"@db/sqlite": "npm:@jsr/db__sqlite@0.12.0",
|
||||||
"@fuman/deno": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@fuman/net": "workspace:^",
|
"@fuman/net": "0.0.1",
|
||||||
"@fuman/io": "workspace:^",
|
"@fuman/io": "0.0.1",
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/html-parser": "workspace:^",
|
"@mtcute/html-parser": "workspace:^",
|
||||||
"@mtcute/markdown-parser": "workspace:^",
|
"@mtcute/markdown-parser": "workspace:^",
|
||||||
|
@ -30,5 +25,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^"
|
"@mtcute/test": "workspace:^"
|
||||||
|
},
|
||||||
|
"fuman": {
|
||||||
|
"jsr": "only"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
BaseTelegramClient as BaseTelegramClientBase,
|
BaseTelegramClient as BaseTelegramClientBase,
|
||||||
TelegramClient as TelegramClientBase,
|
TelegramClient as TelegramClientBase,
|
||||||
} from '@mtcute/core/client.js'
|
} from '@mtcute/core/client.js'
|
||||||
|
import { unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
import { downloadToFile } from './methods/download-file.js'
|
import { downloadToFile } from './methods/download-file.js'
|
||||||
import { DenoPlatform } from './platform.js'
|
import { DenoPlatform } from './platform.js'
|
||||||
|
@ -128,7 +129,7 @@ export class TelegramClient extends TelegramClientBase {
|
||||||
|
|
||||||
this.start(params)
|
this.start(params)
|
||||||
.then(then)
|
.then(then)
|
||||||
.catch(err => this.emitError(err))
|
.catch(err => this.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadToFile(
|
downloadToFile(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
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'
|
||||||
|
// @ts-expect-error wip
|
||||||
import { connectTcp, connectTls } from '@fuman/deno'
|
import { connectTcp, connectTls } from '@fuman/deno'
|
||||||
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,3 +1,4 @@
|
||||||
|
// @ts-expect-error wip
|
||||||
import { connectTcp } from '@fuman/deno'
|
import { connectTcp } from '@fuman/deno'
|
||||||
import type { ITcpConnection } from '@fuman/net'
|
import type { ITcpConnection } from '@fuman/net'
|
||||||
import { IntermediatePacketCodec, type TelegramTransport } from '@mtcute/core'
|
import { IntermediatePacketCodec, type TelegramTransport } from '@mtcute/core'
|
||||||
|
|
|
@ -9,12 +9,11 @@
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run -w build-package dispatcher",
|
|
||||||
"gen-updates": "node ./scripts/generate.cjs"
|
"gen-updates": "node ./scripts/generate.cjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@fuman/utils": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"events": "3.2.0"
|
"events": "3.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
MtArgumentError,
|
MtArgumentError,
|
||||||
} from '@mtcute/core'
|
} from '@mtcute/core'
|
||||||
import type { TelegramClient } from '@mtcute/core/client.js'
|
import type { TelegramClient } from '@mtcute/core/client.js'
|
||||||
|
import { unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
import type { UpdateContext } from './context/base.js'
|
import type { UpdateContext } from './context/base.js'
|
||||||
import type { BusinessMessageContext } from './context/business-message.js'
|
import type { BusinessMessageContext } from './context/business-message.js'
|
||||||
|
@ -328,7 +329,8 @@ export class Dispatcher<State extends object = never> {
|
||||||
|
|
||||||
// order does not matter in the dispatcher,
|
// order does not matter in the dispatcher,
|
||||||
// so we can handle each update in its own task
|
// so we can handle each update in its own task
|
||||||
this.dispatchRawUpdateNow(update, peers).catch(err => this._client!.emitError(err))
|
this.dispatchRawUpdateNow(update, peers)
|
||||||
|
.catch(err => this._client!.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -400,7 +402,8 @@ export class Dispatcher<State extends object = never> {
|
||||||
|
|
||||||
// order does not matter in the dispatcher,
|
// order does not matter in the dispatcher,
|
||||||
// so we can handle each update in its own task
|
// so we can handle each update in its own task
|
||||||
this.dispatchUpdateNow(update).catch(err => this._client!.emitError(err))
|
this.dispatchUpdateNow(update)
|
||||||
|
.catch(err => this._client!.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { asyncResettable, timers } from '@mtcute/core/utils.js'
|
import { asyncResettable } from '@mtcute/core/utils.js'
|
||||||
import type { MaybePromise } from '@mtcute/core'
|
import type { MaybePromise } from '@mtcute/core'
|
||||||
import { LruMap } from '@fuman/utils'
|
import { LruMap, timers } from '@fuman/utils'
|
||||||
|
|
||||||
import type { IStateStorageProvider } from './provider.js'
|
import type { IStateStorageProvider } from './provider.js'
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,9 @@
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm run -w build-package file-id"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/tl-runtime": "workspace:^",
|
"@mtcute/tl-runtime": "workspace:^",
|
||||||
"@fuman/utils": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"long": "5.2.3"
|
"long": "5.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
||||||
import { base64, utf8 } from '@fuman/utils'
|
import { base64, u8, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { tdFileId as td } from './types.js'
|
import { tdFileId as td } from './types.js'
|
||||||
import { assertNever, telegramRleEncode } from './utils.js'
|
import { assertNever, telegramRleEncode } from './utils.js'
|
||||||
|
@ -105,7 +105,7 @@ export function toFileId(location: Omit<td.RawFullRemoteFileLocation, '_'>): str
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = telegramRleEncode(writer.result())
|
const result = telegramRleEncode(writer.result())
|
||||||
const withSuffix = new Uint8Array(result.length + SUFFIX.length)
|
const withSuffix = u8.alloc(result.length + SUFFIX.length)
|
||||||
withSuffix.set(result)
|
withSuffix.set(result)
|
||||||
withSuffix.set(SUFFIX, result.length)
|
withSuffix.set(SUFFIX, result.length)
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm run -w build-package html-parser"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"htmlparser2": "^6.0.1",
|
"htmlparser2": "^6.0.1",
|
||||||
|
|
|
@ -8,17 +8,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm run -w build-package i18n"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/dispatcher": "workspace:^"
|
"@mtcute/dispatcher": "workspace:^"
|
||||||
},
|
|
||||||
"jsrOnlyFields": {
|
|
||||||
"dependencies": {
|
|
||||||
"@mtcute/core": "workspace:^",
|
|
||||||
"@mtcute/dispatcher": "workspace:^"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm run -w build-package markdown-parser"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"long": "5.2.3"
|
"long": "5.2.3"
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => {
|
export default () => {
|
||||||
const clientId = fileURLToPath(new URL('./src/client.ts', import.meta.url))
|
const clientId = fileURLToPath(new URL('./src/client.ts', import.meta.url))
|
||||||
// const buildingCjs = false
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
external: ['@mtcute/crypto-node'],
|
viteConfig: {
|
||||||
rollupPluginsPre: [
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['@mtcute/crypto-node'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginsPre: [
|
||||||
{
|
{
|
||||||
// very much a crutch, but it works
|
// very much a crutch, but it works
|
||||||
// i couldn't figure out a way to hook into the esm->cjs transform,
|
// i couldn't figure out a way to hook into the esm->cjs transform,
|
||||||
|
|
|
@ -12,21 +12,21 @@
|
||||||
"./utils.js": "./src/utils.ts",
|
"./utils.js": "./src/utils.ts",
|
||||||
"./methods.js": "./src/methods.ts"
|
"./methods.js": "./src/methods.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package node"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/html-parser": "workspace:^",
|
"@mtcute/html-parser": "workspace:^",
|
||||||
"@mtcute/markdown-parser": "workspace:^",
|
"@mtcute/markdown-parser": "workspace:^",
|
||||||
"@mtcute/wasm": "workspace:^",
|
"@mtcute/wasm": "workspace:^",
|
||||||
"@fuman/net": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@fuman/node": "workspace:^",
|
"@fuman/net": "0.0.1",
|
||||||
|
"@fuman/node": "0.0.1",
|
||||||
"better-sqlite3": "11.3.0"
|
"better-sqlite3": "11.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^",
|
"@mtcute/test": "workspace:^",
|
||||||
"@types/better-sqlite3": "7.6.4"
|
"@types/better-sqlite3": "7.6.4"
|
||||||
|
},
|
||||||
|
"fuman": {
|
||||||
|
"jsr": "skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
BaseTelegramClient as BaseTelegramClientBase,
|
BaseTelegramClient as BaseTelegramClientBase,
|
||||||
TelegramClient as TelegramClientBase,
|
TelegramClient as TelegramClientBase,
|
||||||
} from '@mtcute/core/client.js'
|
} from '@mtcute/core/client.js'
|
||||||
|
import { unknownToError } from '@fuman/utils'
|
||||||
|
|
||||||
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'
|
||||||
|
@ -139,7 +140,7 @@ export class TelegramClient extends TelegramClientBase {
|
||||||
|
|
||||||
this.start(params)
|
this.start(params)
|
||||||
.then(then)
|
.then(then)
|
||||||
.catch(err => this.emitError(err))
|
.catch(err => this.onError.emit(unknownToError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadToFile(
|
downloadToFile(
|
||||||
|
|
|
@ -2,7 +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 { webReadableToNode } from '@fuman/node'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download a remote file as a Node.js Readable stream.
|
* Download a remote file as a Node.js Readable stream.
|
||||||
|
@ -14,5 +14,5 @@ export function downloadAsNodeStream(
|
||||||
location: FileDownloadLocation,
|
location: FileDownloadLocation,
|
||||||
params?: FileDownloadParameters,
|
params?: FileDownloadParameters,
|
||||||
): Readable {
|
): Readable {
|
||||||
return webStreamToNode(downloadAsStream(client, location, params))
|
return webReadableToNode(downloadAsStream(client, location, params))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +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 { nodeReadableToFuman } from '@fuman/node'
|
||||||
|
|
||||||
export async function normalizeFile(file: UploadFileLike): Promise<{
|
export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
file: UploadFileLike
|
file: UploadFileLike
|
||||||
|
@ -20,7 +20,7 @@ export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
const fileSize = await stat(file.path.toString()).then(stat => stat.size)
|
const fileSize = await stat(file.path.toString()).then(stat => stat.size)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
file: nodeStreamToWeb(file),
|
file: nodeReadableToFuman(file),
|
||||||
fileName,
|
fileName,
|
||||||
fileSize,
|
fileSize,
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
|
|
||||||
if (file instanceof Readable) {
|
if (file instanceof Readable) {
|
||||||
return {
|
return {
|
||||||
file: nodeStreamToWeb(file),
|
file: nodeReadableToFuman(file),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
export default () => ({ buildCjs: false })
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
|
export default () => ({
|
||||||
|
viteConfig: {
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
formats: ['es'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm run -w build-package test"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/node": "workspace:^",
|
"@mtcute/node": "workspace:^",
|
||||||
|
@ -28,13 +25,16 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"long": "5.2.3",
|
"long": "5.2.3",
|
||||||
"@fuman/utils": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@fuman/net": "workspace:^"
|
"@fuman/net": "0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/tl-utils": "workspace:^"
|
"@mtcute/tl-utils": "workspace:^"
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"./src/platform.js": "./src/platform.web.js"
|
"./src/platform.js": "./src/platform.web.js"
|
||||||
|
},
|
||||||
|
"fuman": {
|
||||||
|
"jsr": "skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ export class StubTelegramClient extends BaseTelegramClient {
|
||||||
onMessage: (data, dcId) => {
|
onMessage: (data, dcId) => {
|
||||||
if (!this._onRawMessage) {
|
if (!this._onRawMessage) {
|
||||||
if (this._responders.size) {
|
if (this._responders.size) {
|
||||||
this.emitError(new Error('Unexpected outgoing message'))
|
this.onError.emit(new Error('Unexpected outgoing message'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -288,9 +288,11 @@ export class StubTelegramClient extends BaseTelegramClient {
|
||||||
|
|
||||||
let error: unknown
|
let error: unknown
|
||||||
|
|
||||||
this.onError((err) => {
|
const handler = (err: Error) => {
|
||||||
error = err
|
error = err
|
||||||
})
|
}
|
||||||
|
|
||||||
|
this.onError.add(handler)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fn()
|
await fn()
|
||||||
|
@ -300,6 +302,8 @@ export class StubTelegramClient extends BaseTelegramClient {
|
||||||
|
|
||||||
await this.close()
|
await this.close()
|
||||||
|
|
||||||
|
this.onError.remove(handler)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
import type { TlArgument } from '@mtcute/tl-utils'
|
import type { TlArgument } from '@mtcute/tl-utils'
|
||||||
|
import { u8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { getEntriesMap } from './schema.js'
|
import { getEntriesMap } from './schema.js'
|
||||||
|
|
||||||
|
@ -21,13 +22,13 @@ function getDefaultFor(arg: TlArgument): unknown {
|
||||||
case 'long':
|
case 'long':
|
||||||
return Long.ZERO
|
return Long.ZERO
|
||||||
case 'int128':
|
case 'int128':
|
||||||
return new Uint8Array(16)
|
return u8.alloc(16)
|
||||||
case 'int256':
|
case 'int256':
|
||||||
return new Uint8Array(32)
|
return u8.alloc(32)
|
||||||
case 'string':
|
case 'string':
|
||||||
return ''
|
return ''
|
||||||
case 'bytes':
|
case 'bytes':
|
||||||
return new Uint8Array(0)
|
return u8.alloc(0)
|
||||||
case 'Bool':
|
case 'Bool':
|
||||||
case 'bool':
|
case 'bool':
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -10,12 +10,8 @@
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package tl-runtime"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"long": "5.2.3",
|
"long": "5.2.3",
|
||||||
"@fuman/utils": "workspace:^"
|
"@fuman/utils": "0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,6 @@
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./json.js": "./src/json/index.ts"
|
"./json.js": "./src/json/index.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package tl-utils"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/tl-runtime": "workspace:^",
|
"@mtcute/tl-runtime": "workspace:^",
|
||||||
"crc-32": "1.2.0"
|
"crc-32": "1.2.0"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { generateWriterCodeForTlEntries } from './codegen/writer.js'
|
||||||
import { parseTlToEntries } from './parse.js'
|
import { parseTlToEntries } from './parse.js'
|
||||||
|
|
||||||
function evalForResult<T>(js: string): T {
|
function evalForResult<T>(js: string): T {
|
||||||
// eslint-disable-next-line ts/no-implied-eval, no-new-func
|
// eslint-disable-next-line ts/no-implied-eval, no-new-func, ts/no-unsafe-call
|
||||||
return new Function(js)() as T
|
return new Function(js)() as T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
"gen-code": "tsx scripts/gen-code.ts",
|
"gen-code": "tsx scripts/gen-code.ts",
|
||||||
"gen-rsa": "tsx scripts/gen-rsa-keys.ts",
|
"gen-rsa": "tsx scripts/gen-rsa-keys.ts",
|
||||||
"fetch-and-gen": "pnpm run fetch-api && pnpm run gen-code",
|
"fetch-and-gen": "pnpm run fetch-api && pnpm run gen-code",
|
||||||
"build": "pnpm run -w build-package tl"
|
"build": "tsx scripts/build-package.ts",
|
||||||
|
"build:jsr": "JSR=1 tsx scripts/build-package.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"long": "5.2.3"
|
"long": "5.2.3"
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/node": "workspace:^",
|
"@mtcute/node": "workspace:^",
|
||||||
"@mtcute/tl-utils": "workspace:^",
|
"@mtcute/tl-utils": "workspace:^",
|
||||||
"@fuman/utils": "workspace:^",
|
"@fuman/utils": "0.0.1",
|
||||||
"@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",
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
"typedoc": {
|
"typedoc": {
|
||||||
"entryPoint": "index.d.ts"
|
"entryPoint": "index.d.ts"
|
||||||
},
|
},
|
||||||
"jsrOnlyFields": {
|
"fuman": {
|
||||||
"exports": {}
|
"ownVersioning": true
|
||||||
}
|
}
|
||||||
}
|
}
|
107
packages/tl/scripts/build-package.ts
Normal file
107
packages/tl/scripts/build-package.ts
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import * as fsp from 'node:fs/promises'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
|
||||||
|
import { parsePackageJsonFile, processPackageJson } from '@fuman/build'
|
||||||
|
import { packageJsonToDeno } from '@fuman/build/jsr'
|
||||||
|
|
||||||
|
async function transformFile(file: string, transform: (content: string, file: string) => string) {
|
||||||
|
const content = await fsp.readFile(file, 'utf8')
|
||||||
|
const res = transform(content, file)
|
||||||
|
if (res != null) await fsp.writeFile(file, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create package by copying all the needed files
|
||||||
|
const packageDir = fileURLToPath(new URL('../', import.meta.url))
|
||||||
|
const outDir = process.env.FUMAN_BUILD_OUT ?? fileURLToPath(new URL('../dist', import.meta.url))
|
||||||
|
|
||||||
|
await fsp.rm(outDir, { recursive: true, force: true })
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
'binary/reader.d.ts',
|
||||||
|
'binary/reader.js',
|
||||||
|
'binary/rsa-keys.d.ts',
|
||||||
|
'binary/rsa-keys.js',
|
||||||
|
'binary/writer.d.ts',
|
||||||
|
'binary/writer.js',
|
||||||
|
'index.d.ts',
|
||||||
|
'index.js',
|
||||||
|
'raw-errors.json',
|
||||||
|
'mtp-schema.json',
|
||||||
|
'api-schema.json',
|
||||||
|
'app-config.json',
|
||||||
|
'README.md',
|
||||||
|
]
|
||||||
|
|
||||||
|
await fsp.mkdir(resolve(outDir, 'binary'), { recursive: true })
|
||||||
|
|
||||||
|
for (const f of files) {
|
||||||
|
await fsp.copyFile(resolve(packageDir, f), resolve(outDir, f))
|
||||||
|
}
|
||||||
|
|
||||||
|
await fsp.cp(new URL('../../../LICENSE', import.meta.url), resolve(outDir, 'LICENSE'), { recursive: true })
|
||||||
|
|
||||||
|
const { packageJson, packageJsonOrig } = processPackageJson({
|
||||||
|
packageJson: await parsePackageJsonFile(resolve(packageDir, 'package.json')),
|
||||||
|
workspaceVersions: {},
|
||||||
|
rootPackageJson: await parsePackageJsonFile(resolve(packageDir, '../../package.json')),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (process.env.JSR) {
|
||||||
|
// jsr doesn't support cjs, so we'll need to add some shims
|
||||||
|
// todo: remove this god awfulness when tl esm rewrite
|
||||||
|
await transformFile(resolve(outDir, 'index.js'), (content) => {
|
||||||
|
return [
|
||||||
|
'/// <reference types="./index.d.ts" />',
|
||||||
|
'const exports = {};',
|
||||||
|
content,
|
||||||
|
'export const tl = exports.tl;',
|
||||||
|
'export const mtp = exports.mtp;',
|
||||||
|
].join('\n')
|
||||||
|
})
|
||||||
|
await transformFile(resolve(outDir, 'binary/reader.js'), (content) => {
|
||||||
|
return [
|
||||||
|
'/// <reference types="./reader.d.ts" />',
|
||||||
|
'const exports = {};',
|
||||||
|
content,
|
||||||
|
'export const __tlReaderMap = exports.__tlReaderMap;',
|
||||||
|
].join('\n')
|
||||||
|
})
|
||||||
|
await transformFile(resolve(outDir, 'binary/writer.js'), (content) => {
|
||||||
|
return [
|
||||||
|
'/// <reference types="./writer.d.ts" />',
|
||||||
|
'const exports = {};',
|
||||||
|
content,
|
||||||
|
'export const __tlWriterMap = exports.__tlWriterMap;',
|
||||||
|
].join('\n')
|
||||||
|
})
|
||||||
|
await transformFile(resolve(outDir, 'binary/rsa-keys.js'), (content) => {
|
||||||
|
return [
|
||||||
|
'/// <reference types="./rsa-keys.d.ts" />',
|
||||||
|
'const exports = {};',
|
||||||
|
content,
|
||||||
|
'export const __publicKeyIndex = exports.__publicKeyIndex;',
|
||||||
|
].join('\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
// patch deno.json to add some export maps
|
||||||
|
const denoJson = packageJsonToDeno({
|
||||||
|
packageJson,
|
||||||
|
packageJsonOrig,
|
||||||
|
workspaceVersions: {},
|
||||||
|
buildDirName: 'dist',
|
||||||
|
})
|
||||||
|
denoJson.exports = {}
|
||||||
|
|
||||||
|
for (const f of files) {
|
||||||
|
if (!f.match(/\.js(?:on)?$/)) continue
|
||||||
|
if (f === 'index.js') {
|
||||||
|
denoJson.exports['.'] = './index.js'
|
||||||
|
} else {
|
||||||
|
denoJson.exports[`./${f}`] = `./${f}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await fsp.writeFile(resolve(outDir, 'deno.json'), JSON.stringify(denoJson, null, 2))
|
||||||
|
} else {
|
||||||
|
await fsp.writeFile(resolve(outDir, 'package.json'), JSON.stringify(packageJson, null, 2))
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
|
|
||||||
|
/** @type {import('@fuman/build/vite').CustomBuildConfig} */
|
||||||
export default () => ({
|
export default () => ({
|
||||||
finalPackageJson(pkg) {
|
finalizePackageJson({ packageJson }) {
|
||||||
pkg.exports['./mtcute.wasm'] = './mtcute.wasm'
|
packageJson.exports['./mtcute.wasm'] = './mtcute.wasm'
|
||||||
},
|
},
|
||||||
final({ packageDir, outDir }) {
|
finalize({ packageDir, outDir }) {
|
||||||
fs.cpSync(resolve(packageDir, 'src/mtcute.wasm'), resolve(outDir, 'mtcute.wasm'))
|
fs.cpSync(resolve(packageDir, 'src/mtcute.wasm'), resolve(outDir, 'mtcute.wasm'))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,17 +12,12 @@
|
||||||
"./mtcute.wasm": "./src/mtcute.wasm"
|
"./mtcute.wasm": "./src/mtcute.wasm"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package wasm",
|
|
||||||
"build:wasm": "docker build --output=lib --target=binaries lib"
|
"build:wasm": "docker build --output=lib --target=binaries lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/node": "workspace:^",
|
"@mtcute/node": "workspace:^",
|
||||||
"@mtcute/web": "workspace:^",
|
"@mtcute/web": "workspace:^",
|
||||||
"@fuman/utils": "workspace:^"
|
"@fuman/utils": "0.0.1"
|
||||||
},
|
|
||||||
"jsrOnlyFields": {
|
|
||||||
"exports": "./src/index.ts"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@ export function initSync(module: SyncInitInput): void {
|
||||||
module = new WebAssembly.Instance(module)
|
module = new WebAssembly.Instance(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
wasm = (module as unknown as WebAssembly.Instance).exports as unknown as MtcuteWasmModule
|
wasm = (module as unknown as WebAssembly.Instance).exports as unknown as MtcuteWasmModule
|
||||||
initCommon()
|
initCommon()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,10 @@
|
||||||
"./utils.js": "./src/utils.ts",
|
"./utils.js": "./src/utils.ts",
|
||||||
"./methods.js": "./src/methods.ts"
|
"./methods.js": "./src/methods.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"docs": "typedoc",
|
|
||||||
"build": "pnpm run -w build-package web"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/wasm": "workspace:^",
|
"@mtcute/wasm": "workspace:^",
|
||||||
"@fuman/net": "workspace:^",
|
"@fuman/net": "0.0.1",
|
||||||
"events": "3.2.0"
|
"events": "3.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
3028
pnpm-lock.yaml
3028
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,3 @@
|
||||||
packages:
|
packages:
|
||||||
- packages/*
|
- packages/*
|
||||||
- private/fuman/packages/*
|
|
||||||
- '!e2e/*'
|
- '!e2e/*'
|
||||||
|
|
|
@ -1,210 +0,0 @@
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
import * as fs from 'node:fs'
|
|
||||||
import * as cp from 'node:child_process'
|
|
||||||
import { resolve } from 'node:path'
|
|
||||||
|
|
||||||
import { populateFromUpstream } from '@fuman/jsr'
|
|
||||||
import * as glob from 'glob'
|
|
||||||
import ts from 'typescript'
|
|
||||||
|
|
||||||
import { processPackageJson } from '../.config/vite-utils/package-json.js'
|
|
||||||
|
|
||||||
export function packageJsonToDeno({ packageJson, packageJsonOrig }) {
|
|
||||||
// https://jsr.io/docs/package-configuration
|
|
||||||
|
|
||||||
const importMap = {}
|
|
||||||
const exports = {}
|
|
||||||
|
|
||||||
if (packageJson.dependencies) {
|
|
||||||
for (const [name, version] of Object.entries(packageJson.dependencies)) {
|
|
||||||
if (name.startsWith('@mtcute/')) {
|
|
||||||
importMap[name] = `jsr:${name}@${version}`
|
|
||||||
} else if (version.startsWith('npm:@jsr/')) {
|
|
||||||
const jsrName = version.slice(9).split('@')[0].replace('__', '/')
|
|
||||||
const jsrVersion = version.slice(9).split('@')[1]
|
|
||||||
importMap[name] = `jsr:@${jsrName}@${jsrVersion}`
|
|
||||||
} else {
|
|
||||||
importMap[name] = `npm:${name}@${version}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageJsonOrig.exports) {
|
|
||||||
let tmpExports
|
|
||||||
if (typeof packageJsonOrig.exports === 'string') {
|
|
||||||
tmpExports = { '.': packageJsonOrig.exports }
|
|
||||||
} else if (typeof packageJsonOrig.exports !== 'object') {
|
|
||||||
throw new TypeError('package.json exports must be an object')
|
|
||||||
} else {
|
|
||||||
tmpExports = packageJsonOrig.exports
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [name, value] of Object.entries(tmpExports)) {
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
throw new TypeError(`package.json exports value must be a string: ${name}`)
|
|
||||||
}
|
|
||||||
if (value.endsWith('.wasm')) continue
|
|
||||||
|
|
||||||
exports[name] = value
|
|
||||||
.replace(/^\.\/src\//, './')
|
|
||||||
.replace(/\.js$/, '.ts')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: packageJson.name,
|
|
||||||
version: packageJson.version,
|
|
||||||
exports,
|
|
||||||
exclude: ['**/*.test.ts', '**/*.test-utils.ts', '**/__fixtures__/**'],
|
|
||||||
imports: importMap,
|
|
||||||
publish: {
|
|
||||||
exclude: ['!../dist'], // lol
|
|
||||||
},
|
|
||||||
...packageJson.denoJson,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function runJsrBuildSync(packageName) {
|
|
||||||
const packageDir = fileURLToPath(new URL(`../packages/${packageName}`, import.meta.url))
|
|
||||||
const outDir = fileURLToPath(new URL(`../packages/${packageName}/dist/jsr`, import.meta.url))
|
|
||||||
fs.rmSync(outDir, { recursive: true, force: true })
|
|
||||||
fs.mkdirSync(outDir, { recursive: true })
|
|
||||||
|
|
||||||
console.log('[i] Copying sources...')
|
|
||||||
fs.cpSync(resolve(packageDir, 'src'), outDir, { recursive: true })
|
|
||||||
|
|
||||||
const printer = ts.createPrinter()
|
|
||||||
|
|
||||||
for (const f of glob.sync(resolve(outDir, '**/*.ts'))) {
|
|
||||||
let fileContent = fs.readFileSync(f, 'utf8')
|
|
||||||
let changed = false
|
|
||||||
|
|
||||||
// replace .js imports with .ts
|
|
||||||
const file = ts.createSourceFile(f, fileContent, ts.ScriptTarget.ESNext, true)
|
|
||||||
let changedTs = false
|
|
||||||
|
|
||||||
for (const imp of file.statements) {
|
|
||||||
if (imp.kind !== ts.SyntaxKind.ImportDeclaration && imp.kind !== ts.SyntaxKind.ExportDeclaration) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (imp.kind === ts.SyntaxKind.ExportDeclaration && !imp.moduleSpecifier) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const mod = imp.moduleSpecifier.text
|
|
||||||
|
|
||||||
if (mod[0] === '.' && mod.endsWith('.js')) {
|
|
||||||
changedTs = true
|
|
||||||
imp.moduleSpecifier = {
|
|
||||||
kind: ts.SyntaxKind.StringLiteral,
|
|
||||||
text: `${mod.slice(0, -3)}.ts`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedTs) {
|
|
||||||
fileContent = printer.printFile(file)
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// add shims for node-specific APIs and replace NodeJS.* types
|
|
||||||
// pretty fragile, but it works for now
|
|
||||||
const typesToReplace = {
|
|
||||||
'NodeJS\\.Timeout': 'number',
|
|
||||||
'NodeJS\\.Immediate': 'number',
|
|
||||||
}
|
|
||||||
const nodeSpecificApis = {
|
|
||||||
setImmediate: '(cb: (...args: any[]) => void, ...args: any[]) => number',
|
|
||||||
clearImmediate: '(id: number) => void',
|
|
||||||
Buffer:
|
|
||||||
'{ '
|
|
||||||
+ 'concat: (...args: any[]) => Uint8Array, '
|
|
||||||
+ 'from: (data: any, encoding?: string) => { toString(encoding?: string): string }, '
|
|
||||||
+ ' }',
|
|
||||||
SharedWorker: ['type', 'never'],
|
|
||||||
WorkerGlobalScope:
|
|
||||||
'{ '
|
|
||||||
+ ' new (): typeof WorkerGlobalScope, '
|
|
||||||
+ ' postMessage: (message: any, transfer?: Transferable[]) => void, '
|
|
||||||
+ ' addEventListener: (type: "message", listener: (ev: MessageEvent) => void) => void, '
|
|
||||||
+ ' }',
|
|
||||||
process: '{ ' + 'hrtime: { bigint: () => bigint }, ' + '}',
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [name, decl_] of Object.entries(nodeSpecificApis)) {
|
|
||||||
if (fileContent.includes(name)) {
|
|
||||||
if (name === 'Buffer' && fileContent.includes('node:buffer')) continue
|
|
||||||
|
|
||||||
changed = true
|
|
||||||
const isType = Array.isArray(decl_) && decl_[0] === 'type'
|
|
||||||
const decl = isType ? decl_[1] : decl_
|
|
||||||
|
|
||||||
if (isType) {
|
|
||||||
fileContent = `declare type ${name} = ${decl};\n${fileContent}`
|
|
||||||
} else {
|
|
||||||
fileContent = `declare const ${name}: ${decl};\n${fileContent}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [oldType, newType] of Object.entries(typesToReplace)) {
|
|
||||||
if (fileContent.match(oldType)) {
|
|
||||||
changed = true
|
|
||||||
fileContent = fileContent.replace(new RegExp(oldType, 'g'), newType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
fs.writeFileSync(f, fileContent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { packageJson, packageJsonOrig } = processPackageJson(packageDir)
|
|
||||||
const denoJson = packageJsonToDeno({ packageJson, packageJsonOrig })
|
|
||||||
|
|
||||||
fs.writeFileSync(resolve(outDir, 'deno.json'), JSON.stringify(denoJson, null, 2))
|
|
||||||
fs.cpSync(new URL('../LICENSE', import.meta.url), resolve(outDir, 'LICENSE'), { recursive: true })
|
|
||||||
|
|
||||||
if (process.env.E2E) {
|
|
||||||
// populate dependencies, if any
|
|
||||||
const depsToPopulate = []
|
|
||||||
|
|
||||||
for (const dep of Object.values(denoJson.imports)) {
|
|
||||||
if (!dep.startsWith('jsr:')) continue
|
|
||||||
if (dep.startsWith('jsr:@mtcute/')) continue
|
|
||||||
depsToPopulate.push(dep.slice(4))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depsToPopulate.length) {
|
|
||||||
console.log('[i] Populating %d dependencies...', depsToPopulate.length)
|
|
||||||
await populateFromUpstream({
|
|
||||||
downstream: process.env.JSR_URL,
|
|
||||||
token: process.env.JSR_TOKEN,
|
|
||||||
unstable_createViaApi: true,
|
|
||||||
packages: depsToPopulate,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let customConfig
|
|
||||||
try {
|
|
||||||
customConfig = await (await import(resolve(packageDir, 'build.config.js'))).default()
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (customConfig) {
|
|
||||||
await customConfig.finalJsr?.({ packageDir, outDir })
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[i] Trying to publish with --dry-run')
|
|
||||||
cp.execSync('deno publish --dry-run --allow-dirty --quiet', { cwd: outDir, stdio: 'inherit' })
|
|
||||||
console.log('[v] All good!')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
||||||
const PACKAGE_NAME = process.argv[2]
|
|
||||||
|
|
||||||
if (!PACKAGE_NAME) {
|
|
||||||
throw new Error('package name not specified')
|
|
||||||
}
|
|
||||||
|
|
||||||
await runJsrBuildSync(PACKAGE_NAME)
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/* eslint-disable node/prefer-global/process */
|
|
||||||
import * as cp from 'node:child_process'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
const configPath = fileURLToPath(new URL('../.config/vite.build.ts', import.meta.url))
|
|
||||||
|
|
||||||
export function runViteBuildSync(packageName) {
|
|
||||||
cp.execSync(`pnpm exec vite build --config "${configPath}"`, {
|
|
||||||
stdio: 'inherit',
|
|
||||||
cwd: fileURLToPath(new URL(`../packages/${packageName}`, import.meta.url)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
||||||
const PACKAGE_NAME = process.argv[2]
|
|
||||||
|
|
||||||
if (!PACKAGE_NAME) {
|
|
||||||
throw new Error('package name not specified')
|
|
||||||
}
|
|
||||||
|
|
||||||
runViteBuildSync(PACKAGE_NAME)
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
import * as fs from 'node:fs'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
import { resolve } from 'node:path'
|
|
||||||
|
|
||||||
import { processPackageJson } from '../.config/vite-utils/package-json.js'
|
|
||||||
|
|
||||||
import { packageJsonToDeno, runJsrBuildSync } from './build-package-jsr.js'
|
|
||||||
import { runViteBuildSync } from './build-package-vite.js'
|
|
||||||
|
|
||||||
if (process.argv.length < 3) {
|
|
||||||
console.log('Usage: build-package.js <package name>')
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const IS_JSR = process.env.JSR === '1'
|
|
||||||
|
|
||||||
const packageName = process.argv[2]
|
|
||||||
|
|
||||||
function transformFile(file, transform) {
|
|
||||||
const content = fs.readFileSync(file, 'utf8')
|
|
||||||
const res = transform(content, file)
|
|
||||||
if (res != null) fs.writeFileSync(file, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageName === 'tl') {
|
|
||||||
// create package by copying all the needed files
|
|
||||||
const packageDir = fileURLToPath(new URL('../packages/tl', import.meta.url))
|
|
||||||
let outDir = fileURLToPath(new URL('../packages/tl/dist', import.meta.url))
|
|
||||||
if (IS_JSR) outDir = resolve(outDir, 'jsr')
|
|
||||||
|
|
||||||
fs.rmSync(outDir, { recursive: true, force: true })
|
|
||||||
|
|
||||||
const files = [
|
|
||||||
'binary/reader.d.ts',
|
|
||||||
'binary/reader.js',
|
|
||||||
'binary/rsa-keys.d.ts',
|
|
||||||
'binary/rsa-keys.js',
|
|
||||||
'binary/writer.d.ts',
|
|
||||||
'binary/writer.js',
|
|
||||||
'index.d.ts',
|
|
||||||
'index.js',
|
|
||||||
'raw-errors.json',
|
|
||||||
'mtp-schema.json',
|
|
||||||
'api-schema.json',
|
|
||||||
'app-config.json',
|
|
||||||
'README.md',
|
|
||||||
]
|
|
||||||
|
|
||||||
fs.mkdirSync(resolve(outDir, 'binary'), { recursive: true })
|
|
||||||
|
|
||||||
for (const f of files) {
|
|
||||||
fs.copyFileSync(resolve(packageDir, f), resolve(outDir, f))
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.cpSync(new URL('../LICENSE', import.meta.url), resolve(outDir, 'LICENSE'), { recursive: true })
|
|
||||||
const { packageJson, packageJsonOrig } = processPackageJson(packageDir)
|
|
||||||
|
|
||||||
if (IS_JSR) {
|
|
||||||
// jsr doesn't support cjs, so we'll need to add some shims
|
|
||||||
// todo: remove this god awfulness when tl esm rewrite
|
|
||||||
transformFile(resolve(outDir, 'index.js'), (content) => {
|
|
||||||
return [
|
|
||||||
'/// <reference types="./index.d.ts" />',
|
|
||||||
'const exports = {};',
|
|
||||||
content,
|
|
||||||
'export const tl = exports.tl;',
|
|
||||||
'export const mtp = exports.mtp;',
|
|
||||||
].join('\n')
|
|
||||||
})
|
|
||||||
transformFile(resolve(outDir, 'binary/reader.js'), (content) => {
|
|
||||||
return [
|
|
||||||
'/// <reference types="./reader.d.ts" />',
|
|
||||||
'const exports = {};',
|
|
||||||
content,
|
|
||||||
'export const __tlReaderMap = exports.__tlReaderMap;',
|
|
||||||
].join('\n')
|
|
||||||
})
|
|
||||||
transformFile(resolve(outDir, 'binary/writer.js'), (content) => {
|
|
||||||
return [
|
|
||||||
'/// <reference types="./writer.d.ts" />',
|
|
||||||
'const exports = {};',
|
|
||||||
content,
|
|
||||||
'export const __tlWriterMap = exports.__tlWriterMap;',
|
|
||||||
].join('\n')
|
|
||||||
})
|
|
||||||
transformFile(resolve(outDir, 'binary/rsa-keys.js'), (content) => {
|
|
||||||
return [
|
|
||||||
'/// <reference types="./rsa-keys.d.ts" />',
|
|
||||||
'const exports = {};',
|
|
||||||
content,
|
|
||||||
'export const __publicKeyIndex = exports.__publicKeyIndex;',
|
|
||||||
].join('\n')
|
|
||||||
})
|
|
||||||
|
|
||||||
// patch deno.json to add some export maps
|
|
||||||
const denoJson = packageJsonToDeno({ packageJson, packageJsonOrig })
|
|
||||||
denoJson.exports = {}
|
|
||||||
|
|
||||||
for (const f of files) {
|
|
||||||
if (!f.match(/\.js(?:on)?$/)) continue
|
|
||||||
if (f === 'index.js') {
|
|
||||||
denoJson.exports['.'] = './index.js'
|
|
||||||
} else {
|
|
||||||
denoJson.exports[`./${f}`] = `./${f}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs.writeFileSync(resolve(outDir, 'deno.json'), JSON.stringify(denoJson, null, 2))
|
|
||||||
} else {
|
|
||||||
fs.writeFileSync(resolve(outDir, 'package.json'), JSON.stringify(packageJson, null, 2))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (IS_JSR) {
|
|
||||||
await runJsrBuildSync(packageName)
|
|
||||||
} else {
|
|
||||||
runViteBuildSync(packageName)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
import { appendFileSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
||||||
import { EOL } from 'node:os'
|
|
||||||
import { dirname, join } from 'node:path'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
import { inc, rcompare } from 'semver'
|
|
||||||
|
|
||||||
const __dirname = dirname(new URL(import.meta.url).pathname)
|
|
||||||
|
|
||||||
function collectPackageJsons() {
|
|
||||||
return readdirSync(join(__dirname, '../packages'))
|
|
||||||
.filter(s => !s.startsWith('.'))
|
|
||||||
.map((name) => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(readFileSync(join(__dirname, '../packages', name, 'package.json'), 'utf-8'))
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== 'ENOENT') throw e
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
function bumpVersions(packages, kind) {
|
|
||||||
const pkgJsons = collectPackageJsons()
|
|
||||||
const maxVersion = pkgJsons
|
|
||||||
.filter(it => it.name !== '@mtcute/tl')
|
|
||||||
.map(it => it.version)
|
|
||||||
.sort(rcompare)[0]
|
|
||||||
|
|
||||||
const nextVersion = inc(maxVersion, kind)
|
|
||||||
console.log('[i] Bumping versions to %s', nextVersion)
|
|
||||||
|
|
||||||
for (const pkg of packages) {
|
|
||||||
if (pkg === 'tl') continue // own versioning
|
|
||||||
const pkgJson = pkgJsons.find(it => it.name === `@mtcute/${pkg}`)
|
|
||||||
|
|
||||||
if (!pkgJson) {
|
|
||||||
console.error(`Package ${pkg} not found!`)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgJson.version = nextVersion
|
|
||||||
writeFileSync(
|
|
||||||
join(__dirname, '../packages', pkg, 'package.json'),
|
|
||||||
`${JSON.stringify(pkgJson, null, 2)}\n`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootPkgJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'))
|
|
||||||
rootPkgJson.version = nextVersion
|
|
||||||
writeFileSync(join(__dirname, '../package.json'), `${JSON.stringify(rootPkgJson, null, 2)}\n`)
|
|
||||||
|
|
||||||
return nextVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
||||||
const kind = process.argv[2]
|
|
||||||
const packages = process.argv[3]
|
|
||||||
|
|
||||||
if (!packages || !kind) {
|
|
||||||
console.log('Usage: bump-version.js <major|minor|patch> <package1,package2>')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const packagesList = packages.split(',')
|
|
||||||
|
|
||||||
if (packagesList.length === 0) {
|
|
||||||
console.error('No packages specified!')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kind === 'major' && packagesList.length !== collectPackageJsons().length) {
|
|
||||||
console.error('Cannot bump major version only for some packages!')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ver = bumpVersions(packagesList, kind)
|
|
||||||
|
|
||||||
if (process.env.GITHUB_OUTPUT) {
|
|
||||||
appendFileSync(process.env.GITHUB_OUTPUT, `version=${ver}${EOL}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { bumpVersions }
|
|
|
@ -1,111 +0,0 @@
|
||||||
import { execSync } from 'node:child_process'
|
|
||||||
import { appendFileSync, existsSync } from 'node:fs'
|
|
||||||
import { EOL } from 'node:os'
|
|
||||||
import { dirname, join } from 'node:path'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
import { findChangedFilesSince, getLatestTag } from './git-utils.js'
|
|
||||||
import { listPackages } from './publish.js'
|
|
||||||
|
|
||||||
getTsconfigFiles.cache = {}
|
|
||||||
|
|
||||||
const __dirname = dirname(new URL(import.meta.url).pathname)
|
|
||||||
|
|
||||||
function getTsconfigFiles(pkg) {
|
|
||||||
if (!existsSync(join(__dirname, `../packages/${pkg}/tsconfig.json`))) {
|
|
||||||
throw new Error(`[!] ${pkg} does not have a tsconfig.json`)
|
|
||||||
}
|
|
||||||
if (pkg in getTsconfigFiles.cache) return getTsconfigFiles.cache[pkg]
|
|
||||||
|
|
||||||
console.log('[i] Getting tsconfig files for %s', pkg)
|
|
||||||
const res = execSync('pnpm exec tsc --showConfig', {
|
|
||||||
encoding: 'utf8',
|
|
||||||
stdio: 'pipe',
|
|
||||||
cwd: join(__dirname, `../packages/${pkg}`),
|
|
||||||
})
|
|
||||||
|
|
||||||
const json = JSON.parse(res)
|
|
||||||
|
|
||||||
return (getTsconfigFiles.cache[pkg] = json.files.map(it => it.replace(/^\.\//, '')))
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMeaningfulChange(pkg, path) {
|
|
||||||
// some magic heuristics stuff
|
|
||||||
|
|
||||||
if (path.match(/\.(md|test(?:-utils)?\.ts)$/i)) return false
|
|
||||||
|
|
||||||
if (getTsconfigFiles(pkg).includes(path)) {
|
|
||||||
console.log('[i] %s: %s is in tsconfig', pkg, path)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.match(/typedoc\.cjs$/i)) return false
|
|
||||||
if (path.match(/^(scripts|dist|tests|private)\//i)) return false
|
|
||||||
|
|
||||||
console.log('[i] %s: %s is a meaningful change', pkg, path)
|
|
||||||
|
|
||||||
// to be safe
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function findChangedPackagesSince(tag, until) {
|
|
||||||
const packages = new Set(listPackages(true))
|
|
||||||
const changedFiles = findChangedFilesSince(tag, until)
|
|
||||||
|
|
||||||
const changedPackages = new Set()
|
|
||||||
|
|
||||||
for (const file of changedFiles) {
|
|
||||||
const [dir, pkgname, ...rest] = file.split('/')
|
|
||||||
if (dir !== 'packages') continue
|
|
||||||
if (!packages.has(pkgname)) continue
|
|
||||||
|
|
||||||
// already checked, no need to check again
|
|
||||||
if (changedPackages.has(pkgname)) continue
|
|
||||||
|
|
||||||
const relpath = rest.join('/')
|
|
||||||
|
|
||||||
if (isMeaningfulChange(pkgname, relpath)) {
|
|
||||||
changedPackages.add(pkgname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(changedPackages)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { findChangedPackagesSince, getLatestTag }
|
|
||||||
|
|
||||||
if (process.argv[1] === fileURLToPath(import.meta.url) && process.env.CI && process.env.GITHUB_OUTPUT) {
|
|
||||||
const kind = process.argv[2]
|
|
||||||
const input = process.argv[3]
|
|
||||||
|
|
||||||
if (!input) {
|
|
||||||
// for simpler flow, one can pass all or package list as the first argument,
|
|
||||||
// and they will be returned as is, so that we can later simply
|
|
||||||
// use the outputs of this script
|
|
||||||
console.log('Usage: find-updated-packages.js <packages>')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kind === 'major' && input !== 'all') {
|
|
||||||
throw new Error('For major releases, all packages must be published')
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[i] Determining packages to publish...')
|
|
||||||
|
|
||||||
let res
|
|
||||||
|
|
||||||
if (input === 'all') {
|
|
||||||
res = listPackages(true)
|
|
||||||
} else if (input === 'updated') {
|
|
||||||
const tag = getLatestTag()
|
|
||||||
console.log('[i] Latest tag is %s', tag)
|
|
||||||
|
|
||||||
res = findChangedPackagesSince(tag)
|
|
||||||
} else {
|
|
||||||
res = input.split(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[i] Will publish:', res)
|
|
||||||
appendFileSync(process.env.GITHUB_OUTPUT, `modified=${res.join(',')}${EOL}`)
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
// node scripts/gen-deps-graph.js | dot -Tsvg > deps.svg
|
|
||||||
import { getPackageJsons } from './utils.js'
|
|
||||||
|
|
||||||
const packageJsons = await getPackageJsons()
|
|
||||||
|
|
||||||
function getMtcuteName(name) {
|
|
||||||
if (!name.startsWith('@mtcute/')) return null
|
|
||||||
|
|
||||||
return name.slice(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = []
|
|
||||||
|
|
||||||
for (const pkg of packageJsons) {
|
|
||||||
if (!pkg) continue
|
|
||||||
|
|
||||||
const name = getMtcuteName(pkg.name)
|
|
||||||
|
|
||||||
if (!name) continue
|
|
||||||
|
|
||||||
for (const dep of Object.keys(pkg.dependencies || {})) {
|
|
||||||
const depName = getMtcuteName(dep)
|
|
||||||
if (!depName) continue
|
|
||||||
|
|
||||||
output.push(`"${name}" -> "${depName}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const dep of Object.keys(pkg.devDependencies || {})) {
|
|
||||||
const depName = getMtcuteName(dep)
|
|
||||||
if (!depName) continue
|
|
||||||
|
|
||||||
output.push(`"${name}" -> "${depName}" [style=dashed,color=grey]`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('digraph {')
|
|
||||||
console.log(output.join('\n'))
|
|
||||||
console.log('}')
|
|
|
@ -1,74 +0,0 @@
|
||||||
import { randomUUID } from 'node:crypto'
|
|
||||||
import { appendFileSync } from 'node:fs'
|
|
||||||
import { EOL } from 'node:os'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
import { findChangedFilesSince, getCommitsSince, getLatestTag, parseConventionalCommit } from './git-utils.js'
|
|
||||||
|
|
||||||
function generateChangelog(onlyPackages) {
|
|
||||||
const byPackage = {}
|
|
||||||
|
|
||||||
for (const commit of getCommitsSince(getLatestTag())) {
|
|
||||||
const parsed = parseConventionalCommit(commit.msg)
|
|
||||||
|
|
||||||
if (!parsed) {
|
|
||||||
console.warn('[warn] Failed to parse commit message: %s', commit.msg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const { type, breaking } = parsed
|
|
||||||
|
|
||||||
if ((!type || ['chore', 'ci', 'docs', 'test'].includes(type)) && !breaking) continue
|
|
||||||
|
|
||||||
const changed = findChangedFilesSince(`${commit.hash}~1`, commit.hash)
|
|
||||||
|
|
||||||
let line = `- ${commit.hash}: ${breaking ? '**❗ BREAKING** ' : ''}${commit.msg}`
|
|
||||||
|
|
||||||
if (breaking && commit.description) {
|
|
||||||
line
|
|
||||||
+= `\n${
|
|
||||||
commit.description
|
|
||||||
.trim()
|
|
||||||
.split('\n')
|
|
||||||
.map(line => ` ${line}`)
|
|
||||||
.join('\n')}`
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const file of changed) {
|
|
||||||
if (!file.startsWith('packages/')) continue
|
|
||||||
const pkg = file.split('/')[1]
|
|
||||||
if (onlyPackages && !onlyPackages.includes(pkg)) continue
|
|
||||||
|
|
||||||
if (!byPackage[pkg]) byPackage[pkg] = {}
|
|
||||||
byPackage[pkg][commit.hash] = line
|
|
||||||
// console.log('including %s in %s because of %s', commit.hash, pkg, file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = ''
|
|
||||||
|
|
||||||
for (const [pkg, lines] of Object.entries(byPackage)) {
|
|
||||||
ret += `### ${pkg}\n`
|
|
||||||
ret += Object.values(lines).join('\n')
|
|
||||||
ret += '\n\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
||||||
let onlyPackages = null
|
|
||||||
|
|
||||||
if (process.argv[2]) {
|
|
||||||
onlyPackages = process.argv[2].split(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = generateChangelog(onlyPackages)
|
|
||||||
|
|
||||||
if (process.env.CI && process.env.GITHUB_OUTPUT) {
|
|
||||||
const delim = `---${randomUUID()}---${EOL}`
|
|
||||||
appendFileSync(process.env.GITHUB_OUTPUT, `changelog<<${delim}${res}${delim}`)
|
|
||||||
} else {
|
|
||||||
console.log(res)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
import { execSync } from 'node:child_process'
|
|
||||||
|
|
||||||
function getLatestTag() {
|
|
||||||
try {
|
|
||||||
const res = execSync('git describe --abbrev=0 --tags', { encoding: 'utf8', stdio: 'pipe' }).trim()
|
|
||||||
|
|
||||||
return res
|
|
||||||
} catch (e) {
|
|
||||||
if (e.stderr.match(/^fatal: (No names found|No tags can describe)/i)) {
|
|
||||||
// no tags found, let's just return the first commit
|
|
||||||
return execSync('git rev-list --max-parents=0 HEAD', { encoding: 'utf8' }).trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findChangedFilesSince(tag, until = 'HEAD') {
|
|
||||||
return execSync(`git diff --name-only ${tag} ${until}`, { encoding: 'utf8', stdio: 'pipe' }).trim().split('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCommitsSince(tag, until = 'HEAD') {
|
|
||||||
const delim = `---${Math.random().toString(36).slice(2)}---`
|
|
||||||
|
|
||||||
const lines = execSync(`git log --pretty="format:%H %s%n%b%n${delim}" ${tag}..${until}`, { encoding: 'utf8', stdio: 'pipe' })
|
|
||||||
.trim()
|
|
||||||
.split('\n')
|
|
||||||
|
|
||||||
const items = []
|
|
||||||
|
|
||||||
let current = null
|
|
||||||
|
|
||||||
for (const line of lines) {
|
|
||||||
if (line === delim) {
|
|
||||||
if (current) items.push(current)
|
|
||||||
current = null
|
|
||||||
} else if (current) {
|
|
||||||
if (current.description) current.description += '\n'
|
|
||||||
current.description += line
|
|
||||||
} else {
|
|
||||||
const [hash, ...msg] = line.split(' ')
|
|
||||||
current = { hash, msg: msg.join(' '), description: '' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current) items.push(current)
|
|
||||||
|
|
||||||
return items.reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentCommit() {
|
|
||||||
return execSync('git rev-parse HEAD', { encoding: 'utf8', stdio: 'pipe' }).trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentBranch() {
|
|
||||||
return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8', stdio: 'pipe' }).trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseConventionalCommit(msg) {
|
|
||||||
const match = msg.match(/^(\w+)(?:\(([^)]+)\))?(!?): (.+)$/)
|
|
||||||
|
|
||||||
if (!match) return null
|
|
||||||
|
|
||||||
const [, type, scope, breaking, subject] = match
|
|
||||||
|
|
||||||
return { type, scope, breaking: Boolean(breaking), subject }
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
findChangedFilesSince,
|
|
||||||
getCommitsSince,
|
|
||||||
getCurrentBranch,
|
|
||||||
getCurrentCommit,
|
|
||||||
getLatestTag,
|
|
||||||
parseConventionalCommit,
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue