build: build with vite (major cleanup part2) (#67)
This commit is contained in:
commit
e58e62f649
103 changed files with 2056 additions and 1368 deletions
167
.config/vite-utils/package-json.js
Normal file
167
.config/vite-utils/package-json.js
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const rootPackageJson = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'))
|
||||||
|
const packagesDir = fileURLToPath(new URL('../../packages', import.meta.url))
|
||||||
|
const IS_JSR = process.env.JSR === '1'
|
||||||
|
|
||||||
|
export function getPackageVersion(name) {
|
||||||
|
const json = JSON.parse(readFileSync(resolve(packagesDir, name, 'package.json'), 'utf-8'))
|
||||||
|
return json.version
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processPackageJson(packageDir) {
|
||||||
|
const packageJsonOrig = JSON.parse(readFileSync(resolve(packageDir, 'package.json'), 'utf-8'))
|
||||||
|
const packageJson = structuredClone(packageJsonOrig)
|
||||||
|
const entrypoints = {}
|
||||||
|
|
||||||
|
// copy common fields from root
|
||||||
|
for (const field of ['license', 'author', 'contributors', 'homepage', 'repository', 'bugs']) {
|
||||||
|
if (rootPackageJson[field]) {
|
||||||
|
packageJson[field] = rootPackageJson[field]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newScripts = {}
|
||||||
|
|
||||||
|
if (packageJson.keepScripts) {
|
||||||
|
for (const script of packageJson.keepScripts) {
|
||||||
|
newScripts[script] = packageJson.scripts[script]
|
||||||
|
}
|
||||||
|
delete packageJson.keepScripts
|
||||||
|
}
|
||||||
|
packageJson.scripts = newScripts
|
||||||
|
delete packageJson.devDependencies
|
||||||
|
delete packageJson.private
|
||||||
|
|
||||||
|
if (packageJson.distOnlyFields) {
|
||||||
|
Object.assign(packageJson, packageJson.distOnlyFields)
|
||||||
|
delete packageJson.distOnlyFields
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageJson.jsrOnlyFields) {
|
||||||
|
if (IS_JSR) {
|
||||||
|
Object.assign(packageJson, packageJson.jsrOnlyFields)
|
||||||
|
}
|
||||||
|
delete packageJson.jsrOnlyFields
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceWorkspaceDependencies(field) {
|
||||||
|
if (!packageJson[field]) return
|
||||||
|
|
||||||
|
const dependencies = packageJson[field]
|
||||||
|
|
||||||
|
for (const name of Object.keys(dependencies)) {
|
||||||
|
const value = dependencies[name]
|
||||||
|
|
||||||
|
if (value.startsWith('workspace:')) {
|
||||||
|
if (value !== 'workspace:^' && value !== 'workspace:*') {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot replace workspace dependency ${name} with ${value} - only workspace:^ and * are supported`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!name.startsWith('@mtcute/')) {
|
||||||
|
throw new Error(`Cannot replace workspace dependency ${name} - only @mtcute/* is supported`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: pnpm replaces workspace:* with the current version, unlike this script
|
||||||
|
const depVersion = value === 'workspace:*' ? '*' : `^${getPackageVersion(name.slice(8))}`
|
||||||
|
dependencies[name] = depVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceWorkspaceDependencies('dependencies')
|
||||||
|
replaceWorkspaceDependencies('devDependencies')
|
||||||
|
replaceWorkspaceDependencies('peerDependencies')
|
||||||
|
replaceWorkspaceDependencies('optionalDependencies')
|
||||||
|
|
||||||
|
delete packageJson.typedoc
|
||||||
|
|
||||||
|
if (packageJson.browser) {
|
||||||
|
function maybeFixPath(p, repl) {
|
||||||
|
if (!p) return p
|
||||||
|
|
||||||
|
if (p.startsWith('./src/')) {
|
||||||
|
return repl + p.slice(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.startsWith('./')) {
|
||||||
|
return repl + p.slice(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(packageJson.browser)) {
|
||||||
|
if (!key.startsWith('./src/')) continue
|
||||||
|
|
||||||
|
const path = key.slice(6)
|
||||||
|
packageJson.browser[`./esm/${path}`] = maybeFixPath(packageJson.browser[key], './esm/')
|
||||||
|
|
||||||
|
delete packageJson.browser[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageJson.exports) {
|
||||||
|
let exports = packageJson.exports
|
||||||
|
if (typeof exports === 'string') {
|
||||||
|
exports = { '.': exports }
|
||||||
|
}
|
||||||
|
if (typeof exports !== 'object') {
|
||||||
|
throw new TypeError('package.json exports must be an object')
|
||||||
|
}
|
||||||
|
|
||||||
|
const newExports = {}
|
||||||
|
for (const [key, value] of Object.entries(exports)) {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new TypeError(`package.json exports value must be a string: ${key}`)
|
||||||
|
}
|
||||||
|
if (value.endsWith('.wasm')) {
|
||||||
|
newExports[key] = value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let entrypointName = key.replace(/^\.(\/|$)/, '').replace(/\.js$/, '')
|
||||||
|
if (entrypointName === '') entrypointName = 'index'
|
||||||
|
|
||||||
|
entrypoints[entrypointName] = value
|
||||||
|
newExports[key] = {
|
||||||
|
import: {
|
||||||
|
types: `./${entrypointName}.d.ts`,
|
||||||
|
default: `./${entrypointName}.js`,
|
||||||
|
},
|
||||||
|
require: {
|
||||||
|
types: `./${entrypointName}.d.cts`,
|
||||||
|
default: `./${entrypointName}.cjs`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packageJson.exports = newExports
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof packageJson.bin === 'object') {
|
||||||
|
const newBin = {}
|
||||||
|
for (const [key, value] of Object.entries(packageJson.bin)) {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new TypeError(`package.json bin value must be a string: ${key}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let entrypointName = key.replace(/^\.(\/|$)/, '').replace(/\.js$/, '')
|
||||||
|
if (entrypointName === '') entrypointName = 'index'
|
||||||
|
|
||||||
|
entrypoints[entrypointName] = value
|
||||||
|
newBin[key] = `./${entrypointName}.js`
|
||||||
|
}
|
||||||
|
|
||||||
|
packageJson.bin = newBin
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
packageJsonOrig,
|
||||||
|
packageJson,
|
||||||
|
entrypoints,
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,10 +38,18 @@ export default mergeConfig(baseConfig, {
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
external: ['bun:sqlite'],
|
external: ['bun:sqlite', '@jsr/db__sqlite'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'import.meta.env.TEST_ENV': '"browser"',
|
'import.meta.env.TEST_ENV': '"browser"',
|
||||||
},
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
esbuildOptions: {
|
||||||
|
// for WHATEVER REASON browserify-zlib uses `global` and it dies in browser lol
|
||||||
|
define: {
|
||||||
|
global: 'globalThis',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
111
.config/vite.build.ts
Normal file
111
.config/vite.build.ts
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/// <reference types="vitest" />
|
||||||
|
import { cpSync, existsSync, writeFileSync } from 'node:fs'
|
||||||
|
import { relative, resolve } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
import type { ConfigEnv, UserConfig } from 'vite'
|
||||||
|
import { nodeExternals } from 'rollup-plugin-node-externals'
|
||||||
|
import dts from 'vite-plugin-dts'
|
||||||
|
|
||||||
|
import { processPackageJson } from './vite-utils/package-json'
|
||||||
|
|
||||||
|
const rootDir = fileURLToPath(new URL('..', import.meta.url))
|
||||||
|
|
||||||
|
export default async (env: ConfigEnv): Promise<UserConfig> => {
|
||||||
|
if (env.command !== 'build') {
|
||||||
|
throw new Error('This config is only for building')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { packageJson, entrypoints } = processPackageJson(process.cwd())
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
if (typeof globalThis !== 'undefined' && !globalThis._MTCUTE_CJS_DEPRECATION_WARNED) {
|
||||||
|
globalThis._MTCUTE_CJS_DEPRECATION_WARNED = true
|
||||||
|
console.warn("[${packageJson.name}] CommonJS support is deprecated and will be removed soon. Please consider switching to ESM, it's "+(new Date()).getFullYear()+" already.")
|
||||||
|
console.warn("[${packageJson.name}] Learn more about switching to ESM: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c")
|
||||||
|
}
|
||||||
|
`.trim()
|
||||||
|
|
||||||
|
if (customConfig?.preBuild) {
|
||||||
|
await customConfig.preBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
plugins: [
|
||||||
|
...(customConfig?.rollupPluginsPre ?? []),
|
||||||
|
nodeExternals(),
|
||||||
|
{
|
||||||
|
name: 'mtcute-finalize',
|
||||||
|
renderChunk(code, chunk, options) {
|
||||||
|
if (options.format !== 'cjs') return null
|
||||||
|
|
||||||
|
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,
|
||||||
|
outDir: 'dist',
|
||||||
|
emptyOutDir: true,
|
||||||
|
target: 'es2022',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...(customConfig?.vitePlugins ?? []),
|
||||||
|
dts({
|
||||||
|
// broken; see https://github.com/qmhc/vite-plugin-dts/issues/321, https://github.com/microsoft/rushstack/issues/3557
|
||||||
|
// rollupTypes: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,7 +82,10 @@ export default defineConfig({
|
||||||
name: 'fix-wasm-load',
|
name: 'fix-wasm-load',
|
||||||
async transform(code) {
|
async transform(code) {
|
||||||
if (code.includes('@mtcute/wasm/mtcute.wasm')) {
|
if (code.includes('@mtcute/wasm/mtcute.wasm')) {
|
||||||
return code.replace('@mtcute/wasm/mtcute.wasm', resolve(__dirname, '../packages/wasm/mtcute.wasm'))
|
return code.replace('@mtcute/wasm/mtcute.wasm', resolve(__dirname, '../packages/wasm/src/mtcute.wasm'))
|
||||||
|
}
|
||||||
|
if (code.includes('./mtcute.wasm')) {
|
||||||
|
return code.replace(/\.?\.\/mtcute\.wasm/, resolve(__dirname, '../packages/wasm/src/mtcute.wasm'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return code
|
return code
|
||||||
|
|
|
@ -70,17 +70,16 @@ export default defineConfig({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// todo
|
{
|
||||||
// {
|
name: 'fix-wasm-load',
|
||||||
// name: 'fix-wasm-load',
|
async transform(code) {
|
||||||
// async transform(code, id) {
|
if (code.includes('./mtcute.wasm')) {
|
||||||
// if (code.includes('@mtcute/wasm/mtcute.wasm')) {
|
return code.replace(/\.?\.\/mtcute\.wasm/, resolve(__dirname, '../packages/wasm/src/mtcute.wasm'))
|
||||||
// return code.replace('@mtcute/wasm/mtcute.wasm', resolve(__dirname, '../packages/wasm/mtcute.wasm'))
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// return code
|
return code
|
||||||
// }
|
},
|
||||||
// },
|
},
|
||||||
testSetup(),
|
testSetup(),
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
|
|
|
@ -26,12 +26,4 @@ export default defineConfig({
|
||||||
define: {
|
define: {
|
||||||
'import.meta.env.TEST_ENV': '"node"',
|
'import.meta.env.TEST_ENV': '"node"',
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
|
||||||
esbuildOptions: {
|
|
||||||
// for WHATEVER REASON browserify-zlib uses `global` and it dies in browser lol
|
|
||||||
define: {
|
|
||||||
global: 'globalThis',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
16
.github/workflows/release.yaml
vendored
16
.github/workflows/release.yaml
vendored
|
@ -59,14 +59,14 @@ jobs:
|
||||||
GH_RELEASE: 1
|
GH_RELEASE: 1
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: node scripts/publish.js ${{ steps.find.outputs.modified }}
|
run: node scripts/publish.js ${{ steps.find.outputs.modified }}
|
||||||
- uses: denoland/setup-deno@v1
|
# - uses: denoland/setup-deno@v1
|
||||||
with:
|
# with:
|
||||||
deno-version: '1.45.5'
|
# deno-version: '1.45.5'
|
||||||
- name: Build packages and publish to JSR
|
# - name: Build packages and publish to JSR
|
||||||
env:
|
# env:
|
||||||
JSR: 1
|
# JSR: 1
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: node scripts/publish.js ${{ steps.find.outputs.modified }}
|
# run: node scripts/publish.js ${{ steps.find.outputs.modified }}
|
||||||
- name: Commit version bumps
|
- name: Commit version bumps
|
||||||
run: |
|
run: |
|
||||||
git commit -am "v${{ steps.bump.outputs.version }}"
|
git commit -am "v${{ steps.bump.outputs.version }}"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { assertEquals, assertNotEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts'
|
||||||
import { MtPeerNotFoundError } from '@mtcute/core'
|
import { MtPeerNotFoundError } from '@mtcute/core'
|
||||||
import { TelegramClient } from '@mtcute/core/client.js'
|
import { TelegramClient } from '@mtcute/core/client.js'
|
||||||
|
|
||||||
|
@ -42,17 +42,5 @@ Deno.test('2. calling methods', { sanitizeResources: false }, async (t) => {
|
||||||
assertEquals(history[0].chat.firstName, 'Telegram')
|
assertEquals(history[0].chat.firstName, 'Telegram')
|
||||||
})
|
})
|
||||||
|
|
||||||
await t.step('updateProfile', async () => {
|
|
||||||
const bio = `mtcute e2e ${new Date().toISOString()}`
|
|
||||||
|
|
||||||
const oldSelf = await tg.getFullChat('self')
|
|
||||||
const res = await tg.updateProfile({ bio })
|
|
||||||
const newSelf = await tg.getFullChat('self')
|
|
||||||
|
|
||||||
assertEquals(res.isSelf, true)
|
|
||||||
assertNotEquals(oldSelf.bio, newSelf.bio)
|
|
||||||
assertEquals(newSelf.bio, bio)
|
|
||||||
})
|
|
||||||
|
|
||||||
await tg.close()
|
await tg.close()
|
||||||
})
|
})
|
||||||
|
|
|
@ -44,16 +44,4 @@ describe('2. calling methods', function () {
|
||||||
expect(history[0].chat.id).to.equal(777000)
|
expect(history[0].chat.id).to.equal(777000)
|
||||||
expect(history[0].chat.firstName).to.equal('Telegram')
|
expect(history[0].chat.firstName).to.equal('Telegram')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updateProfile', async () => {
|
|
||||||
const bio = `mtcute e2e ${new Date().toISOString()}`
|
|
||||||
|
|
||||||
const oldSelf = await tg.getFullChat('self')
|
|
||||||
const res = await tg.updateProfile({ bio })
|
|
||||||
const newSelf = await tg.getFullChat('self')
|
|
||||||
|
|
||||||
expect(res.isSelf).to.eq(true)
|
|
||||||
expect(oldSelf.bio).to.not.equal(newSelf.bio)
|
|
||||||
expect(newSelf.bio).to.equal(bio)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,8 +6,9 @@ import { setPlatform } from '@mtcute/core/platform.js'
|
||||||
import { LogManager, sleep } from '@mtcute/core/utils.js'
|
import { LogManager, sleep } from '@mtcute/core/utils.js'
|
||||||
import { NodePlatform, SqliteStorage, TcpTransport } from '@mtcute/node'
|
import { NodePlatform, SqliteStorage, TcpTransport } from '@mtcute/node'
|
||||||
import { NodeCryptoProvider } from '@mtcute/node/utils.js'
|
import { NodeCryptoProvider } from '@mtcute/node/utils.js'
|
||||||
|
import type { BaseTelegramClientOptions } from '@mtcute/core/client.js'
|
||||||
|
|
||||||
export function getApiParams(storage?: string) {
|
export function getApiParams(storage?: string): BaseTelegramClientOptions {
|
||||||
if (!process.env.API_ID || !process.env.API_HASH) {
|
if (!process.env.API_ID || !process.env.API_HASH) {
|
||||||
throw new Error('API_ID and API_HASH env variables must be set')
|
throw new Error('API_ID and API_HASH env variables must be set')
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ export default antfu({
|
||||||
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
||||||
'node/prefer-global/process': ['error', 'always'],
|
'node/prefer-global/process': ['error', 'always'],
|
||||||
'node/prefer-global/buffer': ['error', 'always'],
|
'node/prefer-global/buffer': ['error', 'always'],
|
||||||
'no-restricted-globals': ['error', 'Buffer', '__dirname', 'require'],
|
'no-restricted-globals': ['error', 'Buffer', '__dirname', 'require', 'NodeJS', 'setTimeout', 'clearTimeout'],
|
||||||
'style/quotes': ['error', 'single', { avoidEscape: true }],
|
'style/quotes': ['error', 'single', { avoidEscape: true }],
|
||||||
'test/consistent-test-it': 'off',
|
'test/consistent-test-it': 'off',
|
||||||
'test/prefer-lowercase-title': 'off',
|
'test/prefer-lowercase-title': 'off',
|
||||||
|
@ -112,7 +112,7 @@ export default antfu({
|
||||||
'import/no-relative-packages': 'off', // common-internals is symlinked from node
|
'import/no-relative-packages': 'off', // common-internals is symlinked from node
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
files: ['**/scripts/**', '**/*.cjs'],
|
files: ['**/scripts/**', '**/*.cjs', '.config/**/*'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-restricted-imports': 'off',
|
'no-restricted-imports': 'off',
|
||||||
'no-restricted-globals': 'off',
|
'no-restricted-globals': 'off',
|
||||||
|
|
153
package.json
153
package.json
|
@ -1,75 +1,84 @@
|
||||||
{
|
{
|
||||||
"name": "mtcute-workspace",
|
"name": "mtcute-workspace",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.16.8",
|
"version": "0.16.8",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@9.0.6",
|
"packageManager": "pnpm@9.0.6",
|
||||||
"description": "Type-safe library for MTProto (Telegram API) for browser and NodeJS",
|
"description": "Type-safe library for MTProto (Telegram API) for browser and NodeJS",
|
||||||
"author": "alina sireneva <alina@tei.su>",
|
"author": "alina sireneva <alina@tei.su>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://mtcute.dev",
|
"homepage": "https://mtcute.dev",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mtcute/mtcute"
|
"url": "https://github.com/mtcute/mtcute"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"telegram",
|
"telegram",
|
||||||
"telegram-api",
|
"telegram-api",
|
||||||
"telegram-bot",
|
"telegram-bot",
|
||||||
"telegram-library",
|
"telegram-library",
|
||||||
"mtproto",
|
"mtproto",
|
||||||
"tgbot",
|
"tgbot",
|
||||||
"userbot",
|
"userbot",
|
||||||
"api"
|
"api"
|
||||||
],
|
],
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "node scripts/validate-deps-versions.js && node scripts/remove-jsr-sourcefiles.js",
|
"postinstall": "node scripts/validate-deps-versions.js && node scripts/remove-jsr-sourcefiles.js",
|
||||||
"test": "pnpm run -r 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",
|
||||||
"test:coverage": "vitest --config .config/vite.ts run --coverage",
|
"test:coverage": "vitest --config .config/vite.ts run --coverage",
|
||||||
"test:ci": "vitest --config .config/vite.ts run --coverage.enabled --coverage.reporter=json",
|
"test:ci": "vitest --config .config/vite.ts run --coverage.enabled --coverage.reporter=json",
|
||||||
"test:browser": "vitest --config .config/vite.browser.ts run",
|
"test:browser": "vitest --config .config/vite.browser.ts run",
|
||||||
"test:browser:dev": "vitest --config .config/vite.browser.ts watch",
|
"test:browser:dev": "vitest --config .config/vite.browser.ts watch",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"lint:ci": "CI=1 NODE_OPTIONS=\\\"--max_old_space_size=8192\\\" eslint",
|
"lint:ci": "CI=1 NODE_OPTIONS=\\\"--max_old_space_size=8192\\\" eslint",
|
||||||
"lint:tsc": "rimraf packages/**/dist packages/**/*.tsbuildinfo && pnpm -r --workspace-concurrency=4 exec tsc --build",
|
"lint:tsc": "pnpm -r --workspace-concurrency=4 exec tsc",
|
||||||
"lint:tsc:ci": "pnpm -r exec tsc --build",
|
"lint:tsc:ci": "pnpm -r exec tsc",
|
||||||
"lint:dpdm": "dpdm -T --no-warning --no-tree --exit-code circular:1 packages/*",
|
"lint:dpdm": "dpdm -T --no-warning --no-tree --exit-code circular:1 packages/*",
|
||||||
"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": "node scripts/build-package.js",
|
||||||
},
|
"build-package-vite": "node scripts/build-package-vite.js"
|
||||||
"devDependencies": {
|
},
|
||||||
"@antfu/eslint-config": "2.26.0",
|
"devDependencies": {
|
||||||
"@teidesu/slow-types-compiler": "1.1.0",
|
"@antfu/eslint-config": "2.26.0",
|
||||||
"@types/deno": "npm:@teidesu/deno-types@1.45.5",
|
"@teidesu/slow-types-compiler": "1.1.0",
|
||||||
"@types/node": "20.10.0",
|
"@types/deno": "npm:@teidesu/deno-types@1.45.5",
|
||||||
"@types/ws": "8.5.4",
|
"@types/node": "20.10.0",
|
||||||
"@vitest/browser": "2.0.5",
|
"@types/ws": "8.5.4",
|
||||||
"@vitest/coverage-v8": "2.0.5",
|
"@vitest/browser": "2.0.5",
|
||||||
"@vitest/expect": "2.0.5",
|
"@vitest/coverage-v8": "2.0.5",
|
||||||
"@vitest/spy": "2.0.5",
|
"@vitest/expect": "2.0.5",
|
||||||
"@vitest/ui": "2.0.5",
|
"@vitest/spy": "2.0.5",
|
||||||
"chai": "5.1.0",
|
"@vitest/ui": "2.0.5",
|
||||||
"cjs-module-lexer": "1.2.3",
|
"bun-types": "^1.1.24",
|
||||||
"dotenv-flow": "4.1.0",
|
"chai": "5.1.0",
|
||||||
"dpdm": "3.14.0",
|
"cjs-module-lexer": "1.2.3",
|
||||||
"esbuild": "0.23.0",
|
"dotenv-flow": "4.1.0",
|
||||||
"eslint": "9.9.0",
|
"dpdm": "3.14.0",
|
||||||
"glob": "11.0.0",
|
"esbuild": "0.23.0",
|
||||||
"playwright": "1.42.1",
|
"eslint": "9.9.0",
|
||||||
"rimraf": "6.0.1",
|
"glob": "11.0.0",
|
||||||
"semver": "7.5.1",
|
"playwright": "1.42.1",
|
||||||
"tsx": "4.17.0",
|
"rimraf": "6.0.1",
|
||||||
"typedoc": "0.26.5",
|
"rollup-plugin-node-externals": "7.1.3",
|
||||||
"typescript": "5.5.4",
|
"semver": "7.5.1",
|
||||||
"vite": "5.1.6",
|
"tsx": "4.17.0",
|
||||||
"vite-plugin-node-polyfills": "0.22.0",
|
"typedoc": "0.26.5",
|
||||||
"vitest": "2.0.5"
|
"typescript": "5.5.4",
|
||||||
|
"vite": "5.4.2",
|
||||||
|
"vite-plugin-dts": "4.0.3",
|
||||||
|
"vite-plugin-node-polyfills": "0.22.0",
|
||||||
|
"vitest": "2.0.5"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"typescript": "5.5.4"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = () => ({ buildCjs: false })
|
|
4
packages/bun/build.config.js
Normal file
4
packages/bun/build.config.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default () => ({
|
||||||
|
buildCjs: false,
|
||||||
|
external: ['bun', 'bun:sqlite'],
|
||||||
|
})
|
|
@ -1,29 +1,28 @@
|
||||||
{
|
{
|
||||||
"name": "@mtcute/bun",
|
"name": "@mtcute/bun",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.16.7",
|
"version": "0.16.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Meta-package for Bun",
|
"description": "Meta-package for Bun",
|
||||||
"author": "alina sireneva <alina@tei.su>",
|
"author": "alina sireneva <alina@tei.su>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./utils.js": "./src/utils.ts",
|
"./utils.js": "./src/utils.ts",
|
||||||
"./methods.js": "./src/methods.ts"
|
"./methods.js": "./src/methods.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs": "typedoc",
|
"docs": "typedoc",
|
||||||
"build": "pnpm run -w build-package bun"
|
"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:^"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^",
|
"@mtcute/test": "workspace:^"
|
||||||
"bun-types": "1.0.33"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"types": [
|
|
||||||
"bun-types",
|
|
||||||
"vite/client"
|
|
||||||
],
|
|
||||||
"outDir": "./dist"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" },
|
|
||||||
{ "path": "../dispatcher" },
|
|
||||||
{ "path": "../html-parser" },
|
|
||||||
{ "path": "../markdown-parser" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
import type { TelethonSession } from '../telethon/types.js'
|
import type { TelethonSession } from '../telethon/types.js'
|
||||||
|
|
||||||
export function serializeGramjsSession(session: TelethonSession) {
|
export function serializeGramjsSession(session: TelethonSession): string {
|
||||||
if (session.authKey.length !== 256) {
|
if (session.authKey.length !== 256) {
|
||||||
throw new MtArgumentError('authKey must be 256 bytes long')
|
throw new MtArgumentError('authKey must be 256 bytes long')
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { serializeIpv4ToBytes, serializeIpv6ToBytes } from '../utils/ip.js'
|
||||||
|
|
||||||
import type { TelethonSession } from './types.js'
|
import type { TelethonSession } from './types.js'
|
||||||
|
|
||||||
export function serializeTelethonSession(session: TelethonSession) {
|
export function serializeTelethonSession(session: TelethonSession): string {
|
||||||
if (session.authKey.length !== 256) {
|
if (session.authKey.length !== 256) {
|
||||||
throw new MtArgumentError('authKey must be 256 bytes long')
|
throw new MtArgumentError('authKey must be 256 bytes long')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
const KNOWN_DECORATORS = ['memoizeGetters', 'makeInspectable']
|
|
||||||
|
|
||||||
module.exports = ({ path, glob, transformFile, packageDir, outDir, jsr }) => ({
|
|
||||||
esmOnlyDirectives: true,
|
|
||||||
esmImportDirectives: true,
|
|
||||||
final() {
|
|
||||||
const version = require(path.join(packageDir, 'package.json')).version
|
|
||||||
const replaceVersion = content => content.replace('%VERSION%', version)
|
|
||||||
|
|
||||||
if (jsr) {
|
|
||||||
transformFile(path.join(outDir, 'network/network-manager.ts'), replaceVersion)
|
|
||||||
} else {
|
|
||||||
transformFile(path.join(outDir, 'cjs/network/network-manager.js'), replaceVersion)
|
|
||||||
transformFile(path.join(outDir, 'esm/network/network-manager.js'), replaceVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsr) return
|
|
||||||
|
|
||||||
// make decorators properly tree-shakeable
|
|
||||||
// very fragile, but it works for now :D
|
|
||||||
// skip for jsr for now because types aren't resolved correctly and it breaks everything (TODO: fix this)
|
|
||||||
const decoratorsRegex = new RegExp(
|
|
||||||
`(${KNOWN_DECORATORS.join('|')})\\((.+?)\\)(?:;|$)`,
|
|
||||||
'gms',
|
|
||||||
)
|
|
||||||
|
|
||||||
const replaceDecorators = (content, file) => {
|
|
||||||
if (!KNOWN_DECORATORS.some(d => content.includes(d))) return null
|
|
||||||
|
|
||||||
const countPerClass = new Map()
|
|
||||||
|
|
||||||
content = content.replace(decoratorsRegex, (_, name, args) => {
|
|
||||||
const [clsName_, ...rest] = args.split(',')
|
|
||||||
const clsName = clsName_.trim()
|
|
||||||
|
|
||||||
const count = (countPerClass.get(clsName) || 0) + 1
|
|
||||||
countPerClass.set(clsName, count)
|
|
||||||
|
|
||||||
const prevName = count === 1 ? clsName : `${clsName}$${count - 1}`
|
|
||||||
const localName = `${clsName}$${count}`
|
|
||||||
|
|
||||||
return `const ${localName} = /*#__PURE__*/${name}(${prevName}, ${rest.join(',')});`
|
|
||||||
})
|
|
||||||
|
|
||||||
if (countPerClass.size === 0) {
|
|
||||||
throw new Error('No decorator usages found, but known names were used')
|
|
||||||
}
|
|
||||||
|
|
||||||
const customExports = []
|
|
||||||
|
|
||||||
for (const [clsName, count] of countPerClass) {
|
|
||||||
const needle = new RegExp(`^export class(?= ${clsName} ({|extends ))`, 'm')
|
|
||||||
|
|
||||||
if (!content.match(needle)) {
|
|
||||||
throw new Error(`Class ${clsName} not found in ${file}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content.replace(needle, 'class')
|
|
||||||
customExports.push(
|
|
||||||
`export { ${clsName}$${count} as ${clsName} }`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${content}\n${customExports.join('\n')}\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
const globSrc = path.join(outDir, jsr ? 'highlevel/types/**/*.ts' : 'esm/highlevel/types/**/*.js')
|
|
||||||
|
|
||||||
for (const f of glob.sync(globSrc)) {
|
|
||||||
transformFile(f, replaceDecorators)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
82
packages/core/build.config.js
Normal file
82
packages/core/build.config.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { createRequire } from 'node:module'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
import * as fs from 'node:fs'
|
||||||
|
|
||||||
|
const KNOWN_DECORATORS = ['memoizeGetters', 'makeInspectable']
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const networkManagerId = fileURLToPath(new URL('./src/network/network-manager.ts', import.meta.url))
|
||||||
|
const highlevelTypesDir = fileURLToPath(new URL('./src/highlevel/types', import.meta.url))
|
||||||
|
|
||||||
|
// make decorators properly tree-shakeable
|
||||||
|
// very fragile, but it kinda works :D
|
||||||
|
// skip for jsr for now because types aren't resolved correctly and it breaks everything (TODO: fix this)
|
||||||
|
const decoratorsRegex = new RegExp(
|
||||||
|
`(${KNOWN_DECORATORS.join('|')})\\((.+?)\\)(?:;|$)`,
|
||||||
|
'gms',
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
rollupPluginsPre: [
|
||||||
|
{
|
||||||
|
name: 'mtcute-core-build-plugin',
|
||||||
|
transform(code, id) {
|
||||||
|
if (id === networkManagerId) {
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
const version = require(fileURLToPath(new URL('./package.json', import.meta.url))).version
|
||||||
|
return code.replace('%VERSION%', version)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.startsWith(highlevelTypesDir)) {
|
||||||
|
if (!KNOWN_DECORATORS.some(d => code.includes(d))) return null
|
||||||
|
|
||||||
|
const countPerClass = new Map()
|
||||||
|
|
||||||
|
code = code.replace(decoratorsRegex, (_, name, args) => {
|
||||||
|
const [clsName_, ...rest] = args.split(',')
|
||||||
|
const clsName = clsName_.trim()
|
||||||
|
|
||||||
|
const count = (countPerClass.get(clsName) || 0) + 1
|
||||||
|
countPerClass.set(clsName, count)
|
||||||
|
|
||||||
|
const prevName = count === 1 ? clsName : `${clsName}$${count - 1}`
|
||||||
|
const localName = `${clsName}$${count}`
|
||||||
|
|
||||||
|
return `const ${localName} = /*#__PURE__*/${name}(${prevName}, ${rest.join(',')});`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (countPerClass.size === 0) {
|
||||||
|
throw new Error('No decorator usages found, but known names were used')
|
||||||
|
}
|
||||||
|
|
||||||
|
const customExports = []
|
||||||
|
|
||||||
|
for (const [clsName, count] of countPerClass) {
|
||||||
|
const needle = new RegExp(`^export class(?= ${clsName} ({|extends ))`, 'm')
|
||||||
|
|
||||||
|
if (!code.match(needle)) {
|
||||||
|
throw new Error(`Class ${clsName} not found in ${id.replace(import.meta.url, '')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
code = code.replace(needle, 'class')
|
||||||
|
customExports.push(`export { ${clsName}$${count} as ${clsName} }`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${code}\n${customExports.join('\n')}\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
finalJsr({ outDir }) {
|
||||||
|
const networkMgrFile = resolve(outDir, 'network/network-manager.ts')
|
||||||
|
const code = fs.readFileSync(networkMgrFile, 'utf8')
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
const version = require(fileURLToPath(new URL('./package.json', import.meta.url))).version
|
||||||
|
fs.writeFileSync(networkMgrFile, code.replace('%VERSION%', version))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import { sendMedia } from '../methods/messages/send-media.js'
|
||||||
import { sendMediaGroup } from '../methods/messages/send-media-group.js'
|
import { sendMediaGroup } from '../methods/messages/send-media-group.js'
|
||||||
import { sendText } from '../methods/messages/send-text.js'
|
import { sendText } from '../methods/messages/send-text.js'
|
||||||
import { resolvePeer } from '../methods/users/resolve-peer.js'
|
import { resolvePeer } from '../methods/users/resolve-peer.js'
|
||||||
|
import { timers } from '../../utils/index.js'
|
||||||
|
|
||||||
import type { Message } from './messages/message.js'
|
import type { Message } from './messages/message.js'
|
||||||
import type { InputPeerLike } from './peers/index.js'
|
import type { InputPeerLike } from './peers/index.js'
|
||||||
|
@ -23,7 +24,7 @@ import type { ParametersSkip2 } from './utils.js'
|
||||||
interface QueuedHandler<T> {
|
interface QueuedHandler<T> {
|
||||||
promise: ControllablePromise<T>
|
promise: ControllablePromise<T>
|
||||||
check?: (update: T) => MaybePromise<boolean>
|
check?: (update: T) => MaybePromise<boolean>
|
||||||
timeout?: NodeJS.Timeout
|
timeout?: timers.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONVERSATION_SYMBOL = Symbol('conversation')
|
const CONVERSATION_SYMBOL = Symbol('conversation')
|
||||||
|
@ -341,10 +342,10 @@ export class Conversation {
|
||||||
|
|
||||||
const promise = createControllablePromise<Message>()
|
const promise = createControllablePromise<Message>()
|
||||||
|
|
||||||
let timer: NodeJS.Timeout | undefined
|
let timer: timers.Timer | undefined
|
||||||
|
|
||||||
if (timeout !== null) {
|
if (timeout !== null) {
|
||||||
timer = setTimeout(() => {
|
timer = timers.setTimeout(() => {
|
||||||
promise.reject(new MtTimeoutError(timeout))
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
this._queuedNewMessage.removeBy(it => it.promise === promise)
|
this._queuedNewMessage.removeBy(it => it.promise === promise)
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
@ -482,11 +483,11 @@ export class Conversation {
|
||||||
|
|
||||||
const promise = createControllablePromise<Message>()
|
const promise = createControllablePromise<Message>()
|
||||||
|
|
||||||
let timer: NodeJS.Timeout | undefined
|
let timer: timers.Timer | undefined
|
||||||
const timeout = params?.timeout
|
const timeout = params?.timeout
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
timer = setTimeout(() => {
|
timer = timers.setTimeout(() => {
|
||||||
promise.reject(new MtTimeoutError(timeout))
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
this._pendingEditMessage.delete(msgId)
|
this._pendingEditMessage.delete(msgId)
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
@ -530,10 +531,10 @@ export class Conversation {
|
||||||
|
|
||||||
const promise = createControllablePromise<void>()
|
const promise = createControllablePromise<void>()
|
||||||
|
|
||||||
let timer: NodeJS.Timeout | undefined
|
let timer: timers.Timer | undefined
|
||||||
|
|
||||||
if (timeout !== null) {
|
if (timeout !== null) {
|
||||||
timer = setTimeout(() => {
|
timer = timers.setTimeout(() => {
|
||||||
promise.reject(new MtTimeoutError(timeout))
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
this._pendingRead.delete(msgId)
|
this._pendingRead.delete(msgId)
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
@ -562,7 +563,7 @@ export class Conversation {
|
||||||
void this._lock.acquire().then(async () => {
|
void this._lock.acquire().then(async () => {
|
||||||
try {
|
try {
|
||||||
if (!it.check || (await it.check(msg))) {
|
if (!it.check || (await it.check(msg))) {
|
||||||
if (it.timeout) clearTimeout(it.timeout)
|
if (it.timeout) timers.clearTimeout(it.timeout)
|
||||||
it.promise.resolve(msg)
|
it.promise.resolve(msg)
|
||||||
this._queuedNewMessage.popFront()
|
this._queuedNewMessage.popFront()
|
||||||
}
|
}
|
||||||
|
@ -592,7 +593,7 @@ export class Conversation {
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!it.check || (await it.check(msg))) {
|
if (!it.check || (await it.check(msg))) {
|
||||||
if (it.timeout) clearTimeout(it.timeout)
|
if (it.timeout) timers.clearTimeout(it.timeout)
|
||||||
it.promise.resolve(msg)
|
it.promise.resolve(msg)
|
||||||
this._pendingEditMessage.delete(msg.id)
|
this._pendingEditMessage.delete(msg.id)
|
||||||
}
|
}
|
||||||
|
@ -609,7 +610,7 @@ export class Conversation {
|
||||||
for (const msgId of this._pendingRead.keys()) {
|
for (const msgId of this._pendingRead.keys()) {
|
||||||
if (msgId <= lastRead) {
|
if (msgId <= lastRead) {
|
||||||
const it = this._pendingRead.get(msgId)!
|
const it = this._pendingRead.get(msgId)!
|
||||||
if (it.timeout) clearTimeout(it.timeout)
|
if (it.timeout) timers.clearTimeout(it.timeout)
|
||||||
it.promise.resolve()
|
it.promise.resolve()
|
||||||
this._pendingRead.delete(msgId)
|
this._pendingRead.delete(msgId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
import type { tdFileId } from '@mtcute/file-id'
|
import type { tdFileId } from '@mtcute/file-id'
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
import type { BaseTelegramClient } from '../base.js'
|
import type { BaseTelegramClient } from '../base.js'
|
||||||
import type { CurrentUserInfo } from '../storage/service/current-user.js'
|
import type { CurrentUserInfo } from '../storage/service/current-user.js'
|
||||||
import { PeersIndex } from '../types/peers/peers-index.js'
|
import { PeersIndex } from '../types/peers/peers-index.js'
|
||||||
|
import * as timers from '../../utils/timers.js'
|
||||||
|
|
||||||
import type { PendingUpdate, PendingUpdateContainer, RawUpdateHandler, UpdatesManagerParams } from './types.js'
|
import type { PendingUpdate, PendingUpdateContainer, RawUpdateHandler, UpdatesManagerParams } from './types.js'
|
||||||
import {
|
import {
|
||||||
|
@ -142,7 +143,7 @@ export class UpdatesManager {
|
||||||
|
|
||||||
cpts: Map<number, number> = new Map()
|
cpts: Map<number, number> = new Map()
|
||||||
cptsMod: Map<number, number> = new Map()
|
cptsMod: Map<number, number> = new Map()
|
||||||
channelDiffTimeouts: Map<number, NodeJS.Timeout> = new Map()
|
channelDiffTimeouts: Map<number, timers.Timer> = new Map()
|
||||||
channelsOpened: Map<number, number> = new Map()
|
channelsOpened: Map<number, number> = new Map()
|
||||||
|
|
||||||
log: Logger
|
log: Logger
|
||||||
|
@ -154,7 +155,7 @@ export class UpdatesManager {
|
||||||
private _channelPtsLimit: Extract<UpdatesManagerParams['channelPtsLimit'], Function>
|
private _channelPtsLimit: Extract<UpdatesManagerParams['channelPtsLimit'], Function>
|
||||||
|
|
||||||
auth?: CurrentUserInfo | null // todo: do we need a local copy?
|
auth?: CurrentUserInfo | null // todo: do we need a local copy?
|
||||||
keepAliveInterval?: NodeJS.Timeout
|
keepAliveInterval?: timers.Interval
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: BaseTelegramClient,
|
readonly client: BaseTelegramClient,
|
||||||
|
@ -245,8 +246,8 @@ export class UpdatesManager {
|
||||||
|
|
||||||
// start updates loop in background
|
// start updates loop in background
|
||||||
this.updatesLoopActive = true
|
this.updatesLoopActive = true
|
||||||
clearInterval(this.keepAliveInterval)
|
timers.clearInterval(this.keepAliveInterval)
|
||||||
this.keepAliveInterval = 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.emitError(err))
|
||||||
|
|
||||||
if (this.catchUpOnStart) {
|
if (this.catchUpOnStart) {
|
||||||
|
@ -263,10 +264,10 @@ export class UpdatesManager {
|
||||||
stopLoop(): void {
|
stopLoop(): void {
|
||||||
if (!this.updatesLoopActive) return
|
if (!this.updatesLoopActive) return
|
||||||
|
|
||||||
clearInterval(this.keepAliveInterval)
|
timers.clearInterval(this.keepAliveInterval)
|
||||||
|
|
||||||
for (const timer of this.channelDiffTimeouts.values()) {
|
for (const timer of this.channelDiffTimeouts.values()) {
|
||||||
clearTimeout(timer)
|
timers.clearTimeout(timer)
|
||||||
}
|
}
|
||||||
this.channelDiffTimeouts.clear()
|
this.channelDiffTimeouts.clear()
|
||||||
|
|
||||||
|
@ -814,7 +815,7 @@ export class UpdatesManager {
|
||||||
|
|
||||||
// clear timeout if any
|
// clear timeout if any
|
||||||
if (channelDiffTimeouts.has(channelId)) {
|
if (channelDiffTimeouts.has(channelId)) {
|
||||||
clearTimeout(channelDiffTimeouts.get(channelId))
|
timers.clearTimeout(channelDiffTimeouts.get(channelId))
|
||||||
channelDiffTimeouts.delete(channelId)
|
channelDiffTimeouts.delete(channelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,7 +953,7 @@ export class UpdatesManager {
|
||||||
log.debug('scheduling next fetch for channel %d in %d seconds', channelId, lastTimeout)
|
log.debug('scheduling next fetch for channel %d in %d seconds', channelId, lastTimeout)
|
||||||
channelDiffTimeouts.set(
|
channelDiffTimeouts.set(
|
||||||
channelId,
|
channelId,
|
||||||
setTimeout(() => this._fetchChannelDifferenceViaUpdate(channelId), lastTimeout * 1000),
|
timers.setTimeout(() => this._fetchChannelDifferenceViaUpdate(channelId), lastTimeout * 1000),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { timers } from '../../utils/index.js'
|
||||||
import type { Message } from '../types/messages/index.js'
|
import type { Message } from '../types/messages/index.js'
|
||||||
import type { BusinessMessage, ParsedUpdate } from '../types/updates/index.js'
|
import type { BusinessMessage, ParsedUpdate } from '../types/updates/index.js'
|
||||||
import { _parseUpdate } from '../types/updates/parse-update.js'
|
import { _parseUpdate } from '../types/updates/parse-update.js'
|
||||||
|
@ -47,7 +48,7 @@ export function makeParsedUpdateHandler(params: ParsedUpdateHandlerParams): RawU
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pending = new Map<string, [Message[], NodeJS.Timeout]>()
|
const pending = new Map<string, [Message[], timers.Timer]>()
|
||||||
|
|
||||||
return (update, peers) => {
|
return (update, peers) => {
|
||||||
const parsed = _parseUpdate(update, peers)
|
const parsed = _parseUpdate(update, peers)
|
||||||
|
@ -66,7 +67,7 @@ export function makeParsedUpdateHandler(params: ParsedUpdateHandlerParams): RawU
|
||||||
pendingGroup[0].push(parsed.data)
|
pendingGroup[0].push(parsed.data)
|
||||||
} else {
|
} else {
|
||||||
const messages = [parsed.data]
|
const messages = [parsed.data]
|
||||||
const timeout = setTimeout(() => {
|
const timeout = timers.setTimeout(() => {
|
||||||
pending.delete(group)
|
pending.delete(group)
|
||||||
|
|
||||||
if (isBusiness) {
|
if (isBusiness) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import type { AsyncLock, ConditionVariable, Deque, EarlyTimer, Logger, SortedLinkedList } from '../../utils/index.js'
|
import type { AsyncLock, ConditionVariable, Deque, EarlyTimer, Logger, SortedLinkedList, timers } from '../../utils/index.js'
|
||||||
import type { CurrentUserInfo } from '../storage/service/current-user.js'
|
import type { CurrentUserInfo } from '../storage/service/current-user.js'
|
||||||
import type { PeersIndex } from '../types/peers/peers-index.js'
|
import type { PeersIndex } from '../types/peers/peers-index.js'
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ export interface UpdatesState {
|
||||||
|
|
||||||
cpts: Map<number, number>
|
cpts: Map<number, number>
|
||||||
cptsMod: Map<number, number>
|
cptsMod: Map<number, number>
|
||||||
channelDiffTimeouts: Map<number, NodeJS.Timeout>
|
channelDiffTimeouts: Map<number, timers.Timer>
|
||||||
channelsOpened: Map<number, number>
|
channelsOpened: Map<number, number>
|
||||||
|
|
||||||
log: Logger
|
log: Logger
|
||||||
|
|
|
@ -68,7 +68,7 @@ export class WorkerInvoker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeBinder<T>(target: InvokeTarget) {
|
makeBinder<T>(target: InvokeTarget): <K extends keyof T>(method: K, isVoid?: boolean) => T[K] {
|
||||||
return <K extends keyof T>(method: K, isVoid = false) => {
|
return <K extends keyof T>(method: K, isVoid = false) => {
|
||||||
let fn
|
let fn
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,14 @@ import type {
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
import {
|
import {
|
||||||
Deque,
|
Deque,
|
||||||
|
|
||||||
LongMap,
|
LongMap,
|
||||||
LruSet,
|
LruSet,
|
||||||
SortedArray,
|
SortedArray,
|
||||||
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'
|
||||||
|
@ -41,7 +43,7 @@ export interface PendingRpc {
|
||||||
initConn?: boolean
|
initConn?: boolean
|
||||||
getState?: number
|
getState?: number
|
||||||
cancelled?: boolean
|
cancelled?: boolean
|
||||||
timeout?: NodeJS.Timeout
|
timeout?: timers.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PendingMessage =
|
export type PendingMessage =
|
||||||
|
@ -131,8 +133,8 @@ export class MtprotoSession {
|
||||||
authorizationPending = false
|
authorizationPending = false
|
||||||
|
|
||||||
next429Timeout = 1000
|
next429Timeout = 1000
|
||||||
current429Timeout?: NodeJS.Timeout
|
current429Timeout?: timers.Timer
|
||||||
next429ResetTimeout?: NodeJS.Timeout
|
next429ResetTimeout?: timers.Timer
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly _crypto: ICryptoProvider,
|
readonly _crypto: ICryptoProvider,
|
||||||
|
@ -165,7 +167,7 @@ export class MtprotoSession {
|
||||||
this.resetAuthKey()
|
this.resetAuthKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTimeout(this.current429Timeout)
|
timers.clearTimeout(this.current429Timeout)
|
||||||
this.resetState(withAuthKey)
|
this.resetState(withAuthKey)
|
||||||
this.resetLastPing(true)
|
this.resetLastPing(true)
|
||||||
}
|
}
|
||||||
|
@ -339,14 +341,14 @@ export class MtprotoSession {
|
||||||
const timeout = this.next429Timeout
|
const timeout = this.next429Timeout
|
||||||
|
|
||||||
this.next429Timeout = Math.min(this.next429Timeout * 2, 32000)
|
this.next429Timeout = Math.min(this.next429Timeout * 2, 32000)
|
||||||
clearTimeout(this.current429Timeout)
|
timers.clearTimeout(this.current429Timeout)
|
||||||
clearTimeout(this.next429ResetTimeout)
|
timers.clearTimeout(this.next429ResetTimeout)
|
||||||
|
|
||||||
this.current429Timeout = setTimeout(() => {
|
this.current429Timeout = timers.setTimeout(() => {
|
||||||
this.current429Timeout = undefined
|
this.current429Timeout = undefined
|
||||||
callback()
|
callback()
|
||||||
}, timeout)
|
}, timeout)
|
||||||
this.next429ResetTimeout = setTimeout(() => {
|
this.next429ResetTimeout = timers.setTimeout(() => {
|
||||||
this.next429ResetTimeout = undefined
|
this.next429ResetTimeout = undefined
|
||||||
this.next429Timeout = 1000
|
this.next429Timeout = 1000
|
||||||
}, 60000)
|
}, 60000)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { StubTelegramTransport, createStub, defaultTestCryptoProvider } from '@mtcute/test'
|
import { StubTelegramTransport, createStub, defaultTestCryptoProvider } from '@mtcute/test'
|
||||||
|
|
||||||
import { LogManager } from '../utils/index.js'
|
import { LogManager, timers } from '../utils/index.js'
|
||||||
|
|
||||||
import type { PersistentConnectionParams } from './persistent-connection.js'
|
import type { PersistentConnectionParams } from './persistent-connection.js'
|
||||||
import { PersistentConnection } from './persistent-connection.js'
|
import { PersistentConnection } from './persistent-connection.js'
|
||||||
|
@ -78,7 +78,7 @@ describe('PersistentConnection', () => {
|
||||||
|
|
||||||
const transportConnect = transport.connect
|
const transportConnect = transport.connect
|
||||||
vi.spyOn(transport, 'connect').mockImplementation((dc, test) => {
|
vi.spyOn(transport, 'connect').mockImplementation((dc, test) => {
|
||||||
setTimeout(() => {
|
timers.setTimeout(() => {
|
||||||
transportConnect.call(transport, dc, test)
|
transportConnect.call(transport, dc, test)
|
||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ import EventEmitter from 'node:events'
|
||||||
|
|
||||||
import { MtcuteError } from '../types/index.js'
|
import { MtcuteError } from '../types/index.js'
|
||||||
import type { BasicDcOption, ICryptoProvider, Logger } from '../utils/index.js'
|
import type { BasicDcOption, ICryptoProvider, Logger } from '../utils/index.js'
|
||||||
|
import { timers } from '../utils/index.js'
|
||||||
|
|
||||||
import type { ReconnectionStrategy } from './reconnection.js'
|
import type { ReconnectionStrategy } from './reconnection.js'
|
||||||
import type { ITelegramTransport, TransportFactory } from './transports/index.js'
|
import type { ITelegramTransport, TransportFactory } from './transports/index.js'
|
||||||
|
@ -35,12 +36,12 @@ export abstract class PersistentConnection extends EventEmitter {
|
||||||
private _lastError: Error | null = null
|
private _lastError: Error | null = null
|
||||||
private _consequentFails = 0
|
private _consequentFails = 0
|
||||||
private _previousWait: number | null = null
|
private _previousWait: number | null = null
|
||||||
private _reconnectionTimeout: NodeJS.Timeout | null = null
|
private _reconnectionTimeout: timers.Timer | null = null
|
||||||
private _shouldReconnectImmediately = false
|
private _shouldReconnectImmediately = false
|
||||||
protected _disconnectedManually = false
|
protected _disconnectedManually = false
|
||||||
|
|
||||||
// inactivity timeout
|
// inactivity timeout
|
||||||
private _inactivityTimeout: NodeJS.Timeout | null = null
|
private _inactivityTimeout: timers.Timer | null = null
|
||||||
private _inactive = true
|
private _inactive = true
|
||||||
|
|
||||||
_destroyed = false
|
_destroyed = false
|
||||||
|
@ -165,9 +166,9 @@ export abstract class PersistentConnection extends EventEmitter {
|
||||||
this._previousWait = wait
|
this._previousWait = wait
|
||||||
|
|
||||||
if (this._reconnectionTimeout != null) {
|
if (this._reconnectionTimeout != null) {
|
||||||
clearTimeout(this._reconnectionTimeout)
|
timers.clearTimeout(this._reconnectionTimeout)
|
||||||
}
|
}
|
||||||
this._reconnectionTimeout = setTimeout(() => {
|
this._reconnectionTimeout = timers.setTimeout(() => {
|
||||||
if (this._destroyed) return
|
if (this._destroyed) return
|
||||||
this._reconnectionTimeout = null
|
this._reconnectionTimeout = null
|
||||||
this.connect()
|
this.connect()
|
||||||
|
@ -183,7 +184,7 @@ export abstract class PersistentConnection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._reconnectionTimeout != null) {
|
if (this._reconnectionTimeout != null) {
|
||||||
clearTimeout(this._reconnectionTimeout)
|
timers.clearTimeout(this._reconnectionTimeout)
|
||||||
this._reconnectionTimeout = null
|
this._reconnectionTimeout = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,11 +216,12 @@ export abstract class PersistentConnection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async destroy(): Promise<void> {
|
async destroy(): Promise<void> {
|
||||||
|
this._disconnectedManually = true
|
||||||
if (this._reconnectionTimeout != null) {
|
if (this._reconnectionTimeout != null) {
|
||||||
clearTimeout(this._reconnectionTimeout)
|
timers.clearTimeout(this._reconnectionTimeout)
|
||||||
}
|
}
|
||||||
if (this._inactivityTimeout != null) {
|
if (this._inactivityTimeout != null) {
|
||||||
clearTimeout(this._inactivityTimeout)
|
timers.clearTimeout(this._inactivityTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._transport.close()
|
await this._transport.close()
|
||||||
|
@ -229,8 +231,8 @@ export abstract class PersistentConnection extends EventEmitter {
|
||||||
|
|
||||||
protected _rescheduleInactivity(): void {
|
protected _rescheduleInactivity(): void {
|
||||||
if (!this.params.inactivityTimeout) return
|
if (!this.params.inactivityTimeout) return
|
||||||
if (this._inactivityTimeout) clearTimeout(this._inactivityTimeout)
|
if (this._inactivityTimeout) timers.clearTimeout(this._inactivityTimeout)
|
||||||
this._inactivityTimeout = setTimeout(this._onInactivityTimeout, this.params.inactivityTimeout)
|
this._inactivityTimeout = timers.setTimeout(this._onInactivityTimeout, this.params.inactivityTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _onInactivityTimeout(): void {
|
protected _onInactivityTimeout(): void {
|
||||||
|
@ -246,7 +248,7 @@ export abstract class PersistentConnection extends EventEmitter {
|
||||||
this.params.inactivityTimeout = timeout
|
this.params.inactivityTimeout = timeout
|
||||||
|
|
||||||
if (this._inactivityTimeout) {
|
if (this._inactivityTimeout) {
|
||||||
clearTimeout(this._inactivityTimeout)
|
timers.clearTimeout(this._inactivityTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import type { mtp } from '@mtcute/tl'
|
import type { mtp } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { timers } from '../utils/index.js'
|
||||||
|
|
||||||
export class ServerSaltManager {
|
export class ServerSaltManager {
|
||||||
private _futureSalts: mtp.RawMt_future_salt[] = []
|
private _futureSalts: mtp.RawMt_future_salt[] = []
|
||||||
|
|
||||||
|
@ -26,15 +28,15 @@ export class ServerSaltManager {
|
||||||
else this._scheduleNext()
|
else this._scheduleNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
private _timer?: NodeJS.Timeout
|
private _timer?: timers.Timer
|
||||||
|
|
||||||
private _scheduleNext(): void {
|
private _scheduleNext(): void {
|
||||||
if (this._timer) clearTimeout(this._timer)
|
if (this._timer) timers.clearTimeout(this._timer)
|
||||||
if (this._futureSalts.length === 0) return
|
if (this._futureSalts.length === 0) return
|
||||||
|
|
||||||
const next = this._futureSalts.shift()!
|
const next = this._futureSalts.shift()!
|
||||||
|
|
||||||
this._timer = setTimeout(
|
this._timer = timers.setTimeout(
|
||||||
() => {
|
() => {
|
||||||
this.currentSalt = next.salt
|
this.currentSalt = next.salt
|
||||||
this._scheduleNext()
|
this._scheduleNext()
|
||||||
|
@ -44,6 +46,6 @@ export class ServerSaltManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
clearTimeout(this._timer)
|
timers.clearTimeout(this._timer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,13 @@ import type {
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
import {
|
import {
|
||||||
EarlyTimer,
|
EarlyTimer,
|
||||||
|
|
||||||
concatBuffers,
|
concatBuffers,
|
||||||
createControllablePromise,
|
createControllablePromise,
|
||||||
longFromBuffer,
|
longFromBuffer,
|
||||||
randomLong,
|
randomLong,
|
||||||
removeFromLongArray,
|
removeFromLongArray,
|
||||||
|
timers,
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
|
|
||||||
import { doAuthorization } from './authorization.js'
|
import { doAuthorization } from './authorization.js'
|
||||||
|
@ -72,12 +74,12 @@ export class SessionConnection extends PersistentConnection {
|
||||||
private _queuedDestroySession: Long[] = []
|
private _queuedDestroySession: Long[] = []
|
||||||
|
|
||||||
// waitForMessage
|
// waitForMessage
|
||||||
private _pendingWaitForUnencrypted: [ControllablePromise<Uint8Array>, NodeJS.Timeout][] = []
|
private _pendingWaitForUnencrypted: [ControllablePromise<Uint8Array>, timers.Timer][] = []
|
||||||
|
|
||||||
private _usePfs
|
private _usePfs
|
||||||
private _isPfsBindingPending = false
|
private _isPfsBindingPending = false
|
||||||
private _isPfsBindingPendingInBackground = false
|
private _isPfsBindingPendingInBackground = false
|
||||||
private _pfsUpdateTimeout?: NodeJS.Timeout
|
private _pfsUpdateTimeout?: timers.Timer
|
||||||
|
|
||||||
private _inactivityPendingFlush = false
|
private _inactivityPendingFlush = false
|
||||||
|
|
||||||
|
@ -123,7 +125,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this._isPfsBindingPending = false
|
this._isPfsBindingPending = false
|
||||||
this._isPfsBindingPendingInBackground = false
|
this._isPfsBindingPendingInBackground = false
|
||||||
this._session._authKeyTemp.reset()
|
this._session._authKeyTemp.reset()
|
||||||
clearTimeout(this._pfsUpdateTimeout)
|
timers.clearTimeout(this._pfsUpdateTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._resetSession()
|
this._resetSession()
|
||||||
|
@ -134,7 +136,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
|
|
||||||
Object.values(this._pendingWaitForUnencrypted).forEach(([prom, timeout]) => {
|
Object.values(this._pendingWaitForUnencrypted).forEach(([prom, timeout]) => {
|
||||||
prom.reject(new MtcuteError('Connection closed'))
|
prom.reject(new MtcuteError('Connection closed'))
|
||||||
clearTimeout(timeout)
|
timers.clearTimeout(timeout)
|
||||||
})
|
})
|
||||||
|
|
||||||
// resend pending state_req-s
|
// resend pending state_req-s
|
||||||
|
@ -162,7 +164,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this._salts.isFetching = false
|
this._salts.isFetching = false
|
||||||
|
|
||||||
if (forever) {
|
if (forever) {
|
||||||
clearTimeout(this._pfsUpdateTimeout)
|
timers.clearTimeout(this._pfsUpdateTimeout)
|
||||||
this.removeAllListeners()
|
this.removeAllListeners()
|
||||||
this.on('error', (err) => {
|
this.on('error', (err) => {
|
||||||
this.log.warn('caught error after destroying: %s', err)
|
this.log.warn('caught error after destroying: %s', err)
|
||||||
|
@ -321,7 +323,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
if (this._isPfsBindingPending) return
|
if (this._isPfsBindingPending) return
|
||||||
|
|
||||||
if (this._pfsUpdateTimeout) {
|
if (this._pfsUpdateTimeout) {
|
||||||
clearTimeout(this._pfsUpdateTimeout)
|
timers.clearTimeout(this._pfsUpdateTimeout)
|
||||||
this._pfsUpdateTimeout = undefined
|
this._pfsUpdateTimeout = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +470,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this.onConnectionUsable()
|
this.onConnectionUsable()
|
||||||
|
|
||||||
// set a timeout to update temp auth key in advance to avoid interruption
|
// set a timeout to update temp auth key in advance to avoid interruption
|
||||||
this._pfsUpdateTimeout = setTimeout(
|
this._pfsUpdateTimeout = timers.setTimeout(
|
||||||
() => {
|
() => {
|
||||||
this._pfsUpdateTimeout = undefined
|
this._pfsUpdateTimeout = undefined
|
||||||
this.log.debug('temp key is expiring soon')
|
this.log.debug('temp key is expiring soon')
|
||||||
|
@ -499,7 +501,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
return Promise.reject(new MtcuteError('Connection destroyed'))
|
return Promise.reject(new MtcuteError('Connection destroyed'))
|
||||||
}
|
}
|
||||||
const promise = createControllablePromise<Uint8Array>()
|
const promise = createControllablePromise<Uint8Array>()
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = timers.setTimeout(() => {
|
||||||
promise.reject(new MtTimeoutError(timeout))
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
this._pendingWaitForUnencrypted = this._pendingWaitForUnencrypted.filter(it => it[0] !== promise)
|
this._pendingWaitForUnencrypted = this._pendingWaitForUnencrypted.filter(it => it[0] !== promise)
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
@ -516,7 +518,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
// auth_key_id = 0, meaning it's an unencrypted message used for authorization
|
// auth_key_id = 0, meaning it's an unencrypted message used for authorization
|
||||||
|
|
||||||
const [promise, timeout] = this._pendingWaitForUnencrypted.shift()!
|
const [promise, timeout] = this._pendingWaitForUnencrypted.shift()!
|
||||||
clearTimeout(timeout)
|
timers.clearTimeout(timeout)
|
||||||
promise.resolve(data)
|
promise.resolve(data)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -1441,7 +1443,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
pending.timeout = setTimeout(() => this._cancelRpc(pending, true), timeout)
|
pending.timeout = timers.setTimeout(() => this._cancelRpc(pending, true), timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortSignal) {
|
if (abortSignal) {
|
||||||
|
@ -1473,7 +1475,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!onTimeout && rpc.timeout) {
|
if (!onTimeout && rpc.timeout) {
|
||||||
clearTimeout(rpc.timeout)
|
timers.clearTimeout(rpc.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onTimeout) {
|
if (onTimeout) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { IStorageDriver } from '../driver.js'
|
||||||
export class MemoryStorageDriver implements IStorageDriver {
|
export class MemoryStorageDriver implements IStorageDriver {
|
||||||
readonly states: Map<string, object> = new Map()
|
readonly states: Map<string, object> = new Map()
|
||||||
|
|
||||||
getState<T extends object>(repo: string, def: () => T) {
|
getState<T extends object>(repo: string, def: () => T): T {
|
||||||
if (!this.states.has(repo)) {
|
if (!this.states.has(repo)) {
|
||||||
this.states.set(repo, def())
|
this.states.set(repo, def())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ describe('ConditionVariable', () => {
|
||||||
it('should correctly unlock execution', async () => {
|
it('should correctly unlock execution', async () => {
|
||||||
const cv = new ConditionVariable()
|
const cv = new ConditionVariable()
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
setTimeout(() => cv.notify(), 10)
|
setTimeout(() => cv.notify(), 10)
|
||||||
|
|
||||||
await cv.wait()
|
await cv.wait()
|
||||||
|
@ -24,6 +25,7 @@ describe('ConditionVariable', () => {
|
||||||
it('should only unlock once', async () => {
|
it('should only unlock once', async () => {
|
||||||
const cv = new ConditionVariable()
|
const cv = new ConditionVariable()
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
cv.notify()
|
cv.notify()
|
||||||
cv.notify()
|
cv.notify()
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import * as timers from './timers.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class implementing a condition variable like behaviour.
|
* Class implementing a condition variable like behaviour.
|
||||||
*/
|
*/
|
||||||
export class ConditionVariable {
|
export class ConditionVariable {
|
||||||
private _notify?: () => void
|
private _notify?: () => void
|
||||||
private _timeout?: NodeJS.Timeout
|
private _timeout?: timers.Timer
|
||||||
|
|
||||||
wait(timeout?: number): Promise<void> {
|
wait(timeout?: number): Promise<void> {
|
||||||
const prom = new Promise<void>((resolve) => {
|
const prom = new Promise<void>((resolve) => {
|
||||||
|
@ -11,7 +13,7 @@ export class ConditionVariable {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
this._timeout = setTimeout(() => {
|
this._timeout = timers.setTimeout(() => {
|
||||||
this._notify?.()
|
this._notify?.()
|
||||||
this._timeout = undefined
|
this._timeout = undefined
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
@ -22,7 +24,7 @@ export class ConditionVariable {
|
||||||
|
|
||||||
notify(): void {
|
notify(): void {
|
||||||
this._notify?.()
|
this._notify?.()
|
||||||
if (this._timeout) clearTimeout(this._timeout)
|
if (this._timeout) timers.clearTimeout(this._timeout)
|
||||||
this._notify = undefined
|
this._notify = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
import * as timers from './timers.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper over JS timers that allows re-scheduling them
|
* Wrapper over JS timers that allows re-scheduling them
|
||||||
* to earlier time
|
* to earlier time
|
||||||
*/
|
*/
|
||||||
export class EarlyTimer {
|
export class EarlyTimer {
|
||||||
private _timeout?: NodeJS.Timeout
|
private _timeout?: timers.Timer
|
||||||
private _immediate?: NodeJS.Immediate
|
private _immediate?: timers.Immediate
|
||||||
private _timeoutTs?: number
|
private _timeoutTs?: number
|
||||||
|
|
||||||
private _handler: () => void = () => {}
|
private _handler: () => void = () => {}
|
||||||
|
@ -20,13 +22,13 @@ export class EarlyTimer {
|
||||||
emitWhenIdle(): void {
|
emitWhenIdle(): void {
|
||||||
if (this._immediate) return
|
if (this._immediate) return
|
||||||
|
|
||||||
clearTimeout(this._timeout)
|
timers.clearTimeout(this._timeout)
|
||||||
this._timeoutTs = Date.now()
|
this._timeoutTs = Date.now()
|
||||||
|
|
||||||
if (typeof setImmediate !== 'undefined') {
|
if (typeof timers.setImmediate !== 'undefined') {
|
||||||
this._immediate = setImmediate(this.emitNow)
|
this._immediate = timers.setImmediate(this.emitNow)
|
||||||
} else {
|
} else {
|
||||||
this._timeout = setTimeout(this.emitNow, 0)
|
this._timeout = timers.setTimeout(this.emitNow, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ export class EarlyTimer {
|
||||||
emitBefore(ts: number): void {
|
emitBefore(ts: number): void {
|
||||||
if (!this._timeoutTs || ts < this._timeoutTs) {
|
if (!this._timeoutTs || ts < this._timeoutTs) {
|
||||||
this.reset()
|
this.reset()
|
||||||
this._timeout = setTimeout(this.emitNow, ts - Date.now())
|
this._timeout = timers.setTimeout(this.emitNow, ts - Date.now())
|
||||||
this._timeoutTs = ts
|
this._timeoutTs = ts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,10 +69,10 @@ export class EarlyTimer {
|
||||||
*/
|
*/
|
||||||
reset(): void {
|
reset(): void {
|
||||||
if (this._immediate) {
|
if (this._immediate) {
|
||||||
clearImmediate(this._immediate)
|
timers.clearImmediate(this._immediate)
|
||||||
this._immediate = undefined
|
this._immediate = undefined
|
||||||
} else {
|
} else {
|
||||||
clearTimeout(this._timeout)
|
timers.clearTimeout(this._timeout)
|
||||||
}
|
}
|
||||||
this._timeoutTs = undefined
|
this._timeoutTs = undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import * as timers from './timers.js'
|
||||||
|
|
||||||
export type ThrottledFunction = (() => void) & {
|
export type ThrottledFunction = (() => void) & {
|
||||||
reset: () => void
|
reset: () => void
|
||||||
}
|
}
|
||||||
|
@ -15,7 +17,7 @@ export type ThrottledFunction = (() => void) & {
|
||||||
* @param delay Throttle delay
|
* @param delay Throttle delay
|
||||||
*/
|
*/
|
||||||
export function throttle(func: () => void, delay: number): ThrottledFunction {
|
export function throttle(func: () => void, delay: number): ThrottledFunction {
|
||||||
let timeout: NodeJS.Timeout | null
|
let timeout: timers.Timer | null
|
||||||
|
|
||||||
const res: ThrottledFunction = function () {
|
const res: ThrottledFunction = function () {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
|
@ -26,12 +28,12 @@ export function throttle(func: () => void, delay: number): ThrottledFunction {
|
||||||
timeout = null
|
timeout = null
|
||||||
func()
|
func()
|
||||||
}
|
}
|
||||||
timeout = setTimeout(later, delay)
|
timeout = timers.setTimeout(later, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.reset = () => {
|
res.reset = () => {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout)
|
timers.clearTimeout(timeout)
|
||||||
timeout = null
|
timeout = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
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'
|
||||||
|
@ -28,3 +30,4 @@ 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,22 +1,24 @@
|
||||||
|
import * as timers from './timers.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sleep for the given number of ms
|
* Sleep for the given number of ms
|
||||||
*
|
*
|
||||||
* @param ms Number of ms to sleep
|
* @param ms Number of ms to sleep
|
||||||
*/
|
*/
|
||||||
export const sleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
|
export const sleep = (ms: number): Promise<void> => new Promise(resolve => timers.setTimeout(resolve, ms))
|
||||||
|
|
||||||
export function sleepWithAbort(ms: number, signal: AbortSignal): Promise<void> {
|
export function sleepWithAbort(ms: number, signal: AbortSignal): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let timeout: NodeJS.Timeout
|
let timeout: timers.Timer
|
||||||
|
|
||||||
const onAbort = () => {
|
const onAbort = () => {
|
||||||
clearTimeout(timeout)
|
timers.clearTimeout(timeout)
|
||||||
reject(signal.reason)
|
reject(signal.reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
signal.addEventListener('abort', onAbort)
|
signal.addEventListener('abort', onAbort)
|
||||||
|
|
||||||
timeout = setTimeout(() => {
|
timeout = timers.setTimeout(() => {
|
||||||
signal.removeEventListener('abort', onAbort)
|
signal.removeEventListener('abort', onAbort)
|
||||||
resolve()
|
resolve()
|
||||||
}, ms)
|
}, ms)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { asyncResettable } from './function-utils.js'
|
import { asyncResettable } from './function-utils.js'
|
||||||
|
import * as timers from './timers.js'
|
||||||
|
|
||||||
export interface ReloadableParams<Data> {
|
export interface ReloadableParams<Data> {
|
||||||
reload: (old?: Data) => Promise<Data>
|
reload: (old?: Data) => Promise<Data>
|
||||||
|
@ -13,7 +14,7 @@ export class Reloadable<Data> {
|
||||||
protected _data?: Data
|
protected _data?: Data
|
||||||
protected _expiresAt = 0
|
protected _expiresAt = 0
|
||||||
protected _listeners: ((data: Data) => void)[] = []
|
protected _listeners: ((data: Data) => void)[] = []
|
||||||
protected _timeout?: NodeJS.Timeout
|
protected _timeout?: timers.Timer
|
||||||
|
|
||||||
private _reload = asyncResettable(async () => {
|
private _reload = asyncResettable(async () => {
|
||||||
const data = await this.params.reload(this._data)
|
const data = await this.params.reload(this._data)
|
||||||
|
@ -32,10 +33,10 @@ export class Reloadable<Data> {
|
||||||
this._data = data
|
this._data = data
|
||||||
this._expiresAt = expiresAt
|
this._expiresAt = expiresAt
|
||||||
|
|
||||||
if (this._timeout) clearTimeout(this._timeout)
|
if (this._timeout) timers.clearTimeout(this._timeout)
|
||||||
|
|
||||||
if (!this.params.disableAutoReload) {
|
if (!this.params.disableAutoReload) {
|
||||||
this._timeout = setTimeout(() => {
|
this._timeout = timers.setTimeout(() => {
|
||||||
this._reload.reset()
|
this._reload.reset()
|
||||||
this.update().catch((err: unknown) => {
|
this.update().catch((err: unknown) => {
|
||||||
this.params.onError?.(err)
|
this.params.onError?.(err)
|
||||||
|
@ -70,7 +71,7 @@ export class Reloadable<Data> {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
if (this._timeout) clearTimeout(this._timeout)
|
if (this._timeout) timers.clearTimeout(this._timeout)
|
||||||
this._listeners.length = 0
|
this._listeners.length = 0
|
||||||
this._reload.reset()
|
this._reload.reset()
|
||||||
}
|
}
|
||||||
|
|
61
packages/core/src/utils/timers.ts
Normal file
61
packages/core/src/utils/timers.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* eslint-disable no-restricted-globals, ts/no-implied-eval */
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ import { describe, expect, it } from 'vitest'
|
||||||
import {
|
import {
|
||||||
assertTypeIs,
|
assertTypeIs,
|
||||||
assertTypeIsNot,
|
assertTypeIsNot,
|
||||||
hasPresentKey,
|
|
||||||
hasValueAtKey,
|
hasValueAtKey,
|
||||||
isPresent,
|
isPresent,
|
||||||
mtpAssertTypeIs,
|
mtpAssertTypeIs,
|
||||||
|
@ -23,24 +22,6 @@ describe('isPresent', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('hasPresentKey', () => {
|
|
||||||
it('should return true for objects with present keys', () => {
|
|
||||||
expect(hasPresentKey('a')({ a: 1 })).toBe(true)
|
|
||||||
expect(hasPresentKey('a')({ a: 1, b: 2 })).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return false for objects with undefined/null keys', () => {
|
|
||||||
expect(hasPresentKey('a')({ a: undefined })).toBe(false)
|
|
||||||
expect(hasPresentKey('a')({ a: null })).toBe(false)
|
|
||||||
expect(hasPresentKey('a')({ a: undefined, b: 2 })).toBe(false)
|
|
||||||
expect(hasPresentKey('a')({ a: null, b: 2 })).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return false for objects without the key', () => {
|
|
||||||
expect(hasPresentKey('a')({ b: 2 })).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('hasValueAtKey', () => {
|
describe('hasValueAtKey', () => {
|
||||||
it('should return true for objects with the correct value', () => {
|
it('should return true for objects with the correct value', () => {
|
||||||
expect(hasValueAtKey('a', 1)({ a: 1 })).toBe(true)
|
expect(hasValueAtKey('a', 1)({ a: 1 })).toBe(true)
|
||||||
|
|
|
@ -8,25 +8,6 @@ export function isPresent<T>(t: T | undefined | null | void): t is T {
|
||||||
return t !== undefined && t !== null
|
return t !== undefined && t !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a function that can be used to filter down objects
|
|
||||||
* to the ones that have a defined non-null value under the key `k`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* const filesWithUrl = files.filter(file => file.url);
|
|
||||||
* files[0].url // In this case, TS might still treat this as undefined/null
|
|
||||||
*
|
|
||||||
* const filesWithUrl = files.filter(hasPresentKey("url"));
|
|
||||||
* files[0].url // TS will know that this is present
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export function hasPresentKey<K extends string | number | symbol>(k: K) {
|
|
||||||
return function <T, V> (a: T & { [k in K]?: V | null }): a is T & { [k in K]: V } {
|
|
||||||
return a[k] !== undefined && a[k] !== null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a function that can be used to filter down objects
|
* Returns a function that can be used to filter down objects
|
||||||
* to the ones that have a specific value V under a key `k`.
|
* to the ones that have a specific value V under a key `k`.
|
||||||
|
@ -44,7 +25,8 @@ export function hasPresentKey<K extends string | number | symbol>(k: K) {
|
||||||
* files[0].imageUrl // TS will know this is present, because already it excluded the other union members.
|
* files[0].imageUrl // TS will know this is present, because already it excluded the other union members.
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function hasValueAtKey<const K extends string | number | symbol, const V>(k: K, v: V) {
|
export function hasValueAtKey<const K extends string | number | symbol, const V>(k: K, v: V):
|
||||||
|
<T>(a: T & { [k in K]: unknown }) => a is T & { [k in K]: V } {
|
||||||
return function <T> (a: T & { [k in K]: unknown }): a is T & { [k in K]: V } {
|
return function <T> (a: T & { [k in K]: unknown }): a is T & { [k in K]: V } {
|
||||||
return a[k] === v
|
return a[k] === v
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../tl" },
|
|
||||||
{ "path": "../tl-runtime" },
|
|
||||||
{ "path": "../test" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = ({ fs, path, packageDir, outDir }) => ({
|
|
||||||
buildCjs: false,
|
|
||||||
final() {
|
|
||||||
fs.cpSync(path.join(packageDir, 'template'), path.join(outDir, 'template'), { recursive: true })
|
|
||||||
},
|
|
||||||
})
|
|
9
packages/create-bot/build.config.js
Normal file
9
packages/create-bot/build.config.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
import { cpSync } from 'node:fs'
|
||||||
|
|
||||||
|
export default () => ({
|
||||||
|
buildCjs: false,
|
||||||
|
final({ outDir, packageDir }) {
|
||||||
|
cpSync(resolve(packageDir, 'template'), resolve(outDir, 'template'), { recursive: true })
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,30 +1,30 @@
|
||||||
{
|
{
|
||||||
"name": "@mtcute/create-bot",
|
"name": "@mtcute/create-bot",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.16.7",
|
"version": "0.16.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Bot starter kit for mtcute",
|
"description": "Bot starter kit for mtcute",
|
||||||
"author": "alina sireneva <alina@tei.su>",
|
"author": "alina sireneva <alina@tei.su>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"create-bot": "./src/main.js"
|
"create-bot": "./src/main.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run -w build-package create-bot",
|
"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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colorette": "2.0.20",
|
"colorette": "2.0.20",
|
||||||
"cross-spawn": "7.0.3",
|
"cross-spawn": "7.0.3",
|
||||||
"glob": "11.0.0",
|
"glob": "11.0.0",
|
||||||
"handlebars": "4.7.8",
|
"handlebars": "4.7.8",
|
||||||
"inquirer": "9.2.11",
|
"inquirer": "9.2.11",
|
||||||
"openurl": "1.1.1"
|
"openurl": "1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ export async function askForConfigPersisted(): Promise<UserConfigPersisted> {
|
||||||
message: 'API ID (press Enter to obtain one):',
|
message: 'API ID (press Enter to obtain one):',
|
||||||
validate: (v: string) => {
|
validate: (v: string) => {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
open(TELEGRAM_APPS_PAGE)
|
open(TELEGRAM_APPS_PAGE)
|
||||||
|
|
|
@ -40,7 +40,7 @@ if (!outDir.match(/^(?:[A-Z]:)?[/\\]/i)) {
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
await runTemplater(join(__dirname, '../template'), outDir, config)
|
await runTemplater(join(__dirname, 'template'), outDir, config)
|
||||||
|
|
||||||
await installDependencies(outDir, config)
|
await installDependencies(outDir, config)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./dist"
|
|
||||||
},
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
const crypto = require('node:crypto')
|
/* eslint-disable import/no-relative-packages, no-console, no-restricted-globals */
|
||||||
const path = require('node:path')
|
import { createHash } from 'node:crypto'
|
||||||
const fs = require('node:fs')
|
import path from 'node:path'
|
||||||
const cp = require('node:child_process')
|
import * as fs from 'node:fs'
|
||||||
const { Readable } = require('node:stream')
|
import { spawn } from 'node:child_process'
|
||||||
|
import { Readable } from 'node:stream'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
let git
|
import * as glob from 'glob'
|
||||||
|
|
||||||
|
import { getCurrentBranch, getCurrentCommit } from '../../scripts/git-utils.js'
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url)))
|
||||||
|
|
||||||
const GITHUB_TOKEN = process.env.GITHUB_TOKEN
|
const GITHUB_TOKEN = process.env.GITHUB_TOKEN
|
||||||
let SKIP_PREBUILT = process.env.BUILD_FOR_DOCS === '1'
|
let SKIP_PREBUILT = process.env.BUILD_FOR_DOCS === '1'
|
||||||
|
@ -52,7 +58,7 @@ async function runWorkflow(commit, hash) {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: GITHUB_HEADERS,
|
headers: GITHUB_HEADERS,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
ref: git.getCurrentBranch(),
|
ref: getCurrentBranch(),
|
||||||
inputs: { commit, hash },
|
inputs: { commit, hash },
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -132,7 +138,7 @@ async function extractArtifacts(artifacts) {
|
||||||
|
|
||||||
// extract the zip
|
// extract the zip
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const child = cp.spawn('unzip', [outFile, '-d', path.join(__dirname, 'dist/prebuilds')], {
|
const child = spawn('unzip', [outFile, '-d', path.join(__dirname, 'dist/prebuilds')], {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -149,23 +155,21 @@ async function extractArtifacts(artifacts) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ({ fs, glob, path, packageDir, outDir }) => ({
|
export default () => ({
|
||||||
async final() {
|
async final({ packageDir, outDir }) {
|
||||||
// eslint-disable-next-line import/no-relative-packages
|
const libDir = path.resolve(packageDir, 'lib')
|
||||||
git = await import('../../scripts/git-utils.js')
|
|
||||||
const libDir = path.join(packageDir, 'lib')
|
|
||||||
|
|
||||||
if (!SKIP_PREBUILT) {
|
if (!SKIP_PREBUILT) {
|
||||||
// generate sources hash
|
// generate sources hash
|
||||||
const hashes = []
|
const hashes = []
|
||||||
|
|
||||||
for (const file of glob.sync(path.join(libDir, '**/*'))) {
|
for (const file of glob.sync(path.join(libDir, '**/*'))) {
|
||||||
const hash = crypto.createHash('sha256')
|
const hash = createHash('sha256')
|
||||||
hash.update(fs.readFileSync(file))
|
hash.update(fs.readFileSync(file))
|
||||||
hashes.push(hash.digest('hex'))
|
hashes.push(hash.digest('hex'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const hash = crypto.createHash('sha256')
|
const hash = createHash('sha256')
|
||||||
.update(hashes.join('\n'))
|
.update(hashes.join('\n'))
|
||||||
.digest('hex')
|
.digest('hex')
|
||||||
console.log(hash)
|
console.log(hash)
|
||||||
|
@ -175,7 +179,7 @@ module.exports = ({ fs, glob, path, packageDir, outDir }) => ({
|
||||||
|
|
||||||
if (!artifacts) {
|
if (!artifacts) {
|
||||||
console.log('[i] No artifacts found, running workflow')
|
console.log('[i] No artifacts found, running workflow')
|
||||||
artifacts = await runWorkflow(git.getCurrentCommit(), hash)
|
artifacts = await runWorkflow(getCurrentCommit(), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[i] Extracting artifacts')
|
console.log('[i] Extracting artifacts')
|
||||||
|
@ -195,7 +199,6 @@ module.exports = ({ fs, glob, path, packageDir, outDir }) => ({
|
||||||
)
|
)
|
||||||
|
|
||||||
// for some unknown fucking reason ts doesn't do this
|
// for some unknown fucking reason ts doesn't do this
|
||||||
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'cjs/native.cjs'))
|
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'native.cjs'))
|
||||||
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'esm/native.cjs'))
|
|
||||||
},
|
},
|
||||||
})
|
})
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
module.exports = ({ outDir, fs, jsr }) => ({
|
|
||||||
buildCjs: false,
|
|
||||||
final() {
|
|
||||||
if (jsr) {
|
|
||||||
// jsr doesn't support symlinks, so we need to copy the files manually
|
|
||||||
const real = fs.realpathSync(`${outDir}/common-internals-web`)
|
|
||||||
fs.unlinkSync(`${outDir}/common-internals-web`)
|
|
||||||
// console.log(real)
|
|
||||||
fs.cpSync(real, `${outDir}/common-internals-web`, { recursive: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
10
packages/deno/build.config.js
Normal file
10
packages/deno/build.config.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import * as fs from 'node:fs'
|
||||||
|
|
||||||
|
export default () => ({
|
||||||
|
finalJsr({ outDir }) {
|
||||||
|
// jsr doesn't support symlinks, so we need to copy the files manually
|
||||||
|
const real = fs.realpathSync(`${outDir}/common-internals-web`)
|
||||||
|
fs.unlinkSync(`${outDir}/common-internals-web`)
|
||||||
|
fs.cpSync(real, `${outDir}/common-internals-web`, { recursive: true })
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,15 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" },
|
|
||||||
{ "path": "../dispatcher" },
|
|
||||||
{ "path": "../html-parser" },
|
|
||||||
{ "path": "../markdown-parser" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,7 +21,7 @@ import type { UpdateContext } from './base.js'
|
||||||
*/
|
*/
|
||||||
export class BusinessMessageContext extends BusinessMessage implements UpdateContext<BusinessMessage> {
|
export class BusinessMessageContext extends BusinessMessage implements UpdateContext<BusinessMessage> {
|
||||||
// this is primarily for proper types in filters, so don't bother much with actual value
|
// this is primarily for proper types in filters, so don't bother much with actual value
|
||||||
readonly _name = 'new_business_message'
|
readonly _name = 'new_business_message' as const
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of messages in the message group.
|
* List of messages in the message group.
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { UpdateContext } from './base.js'
|
||||||
* This is a subclass of {@link CallbackQuery}, so all its fields are also available.
|
* This is a subclass of {@link CallbackQuery}, so all its fields are also available.
|
||||||
*/
|
*/
|
||||||
export class CallbackQueryContext extends CallbackQuery implements UpdateContext<CallbackQuery> {
|
export class CallbackQueryContext extends CallbackQuery implements UpdateContext<CallbackQuery> {
|
||||||
readonly _name = 'callback_query'
|
readonly _name = 'callback_query' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
@ -67,7 +67,7 @@ export class CallbackQueryContext extends CallbackQuery implements UpdateContext
|
||||||
* This is a subclass of {@link InlineCallbackQuery}, so all its fields are also available.
|
* This is a subclass of {@link InlineCallbackQuery}, so all its fields are also available.
|
||||||
*/
|
*/
|
||||||
export class InlineCallbackQueryContext extends InlineCallbackQuery implements UpdateContext<InlineCallbackQuery> {
|
export class InlineCallbackQueryContext extends InlineCallbackQuery implements UpdateContext<InlineCallbackQuery> {
|
||||||
readonly _name = 'inline_callback_query'
|
readonly _name = 'inline_callback_query' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
@ -100,7 +100,7 @@ export class InlineCallbackQueryContext extends InlineCallbackQuery implements U
|
||||||
export class BusinessCallbackQueryContext
|
export class BusinessCallbackQueryContext
|
||||||
extends BusinessCallbackQuery
|
extends BusinessCallbackQuery
|
||||||
implements UpdateContext<BusinessCallbackQuery> {
|
implements UpdateContext<BusinessCallbackQuery> {
|
||||||
readonly _name = 'business_callback_query'
|
readonly _name = 'business_callback_query' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
|
|
@ -11,7 +11,7 @@ import type { UpdateContext } from './base.js'
|
||||||
export class ChatJoinRequestUpdateContext
|
export class ChatJoinRequestUpdateContext
|
||||||
extends BotChatJoinRequestUpdate
|
extends BotChatJoinRequestUpdate
|
||||||
implements UpdateContext<BotChatJoinRequestUpdate> {
|
implements UpdateContext<BotChatJoinRequestUpdate> {
|
||||||
readonly _name = 'bot_chat_join_request'
|
readonly _name = 'bot_chat_join_request' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import type { UpdateContext } from './base.js'
|
||||||
* > Inline feedback in [@BotFather](//t.me/botfather)
|
* > Inline feedback in [@BotFather](//t.me/botfather)
|
||||||
*/
|
*/
|
||||||
export class ChosenInlineResultContext extends ChosenInlineResult implements UpdateContext<ChosenInlineResult> {
|
export class ChosenInlineResultContext extends ChosenInlineResult implements UpdateContext<ChosenInlineResult> {
|
||||||
readonly _name = 'chosen_inline_result'
|
readonly _name = 'chosen_inline_result' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { UpdateContext } from './base.js'
|
||||||
* This is a subclass of {@link InlineQuery}, so all its fields are also available.
|
* This is a subclass of {@link InlineQuery}, so all its fields are also available.
|
||||||
*/
|
*/
|
||||||
export class InlineQueryContext extends InlineQuery implements UpdateContext<InlineQuery> {
|
export class InlineQueryContext extends InlineQuery implements UpdateContext<InlineQuery> {
|
||||||
readonly _name = 'inline_query'
|
readonly _name = 'inline_query' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
|
|
@ -21,7 +21,7 @@ import type { UpdateContext } from './base.js'
|
||||||
*/
|
*/
|
||||||
export class MessageContext extends Message implements UpdateContext<Message> {
|
export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
// this is primarily for proper types in filters, so don't bother much with actual value
|
// this is primarily for proper types in filters, so don't bother much with actual value
|
||||||
readonly _name = 'new_message'
|
readonly _name = 'new_message' as const
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of messages in the message group.
|
* List of messages in the message group.
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type { UpdateContext } from './base.js'
|
||||||
* This is a subclass of {@link PreCheckoutQuery}, so all its fields are also available.
|
* This is a subclass of {@link PreCheckoutQuery}, so all its fields are also available.
|
||||||
*/
|
*/
|
||||||
export class PreCheckoutQueryContext extends PreCheckoutQuery implements UpdateContext<PreCheckoutQuery> {
|
export class PreCheckoutQueryContext extends PreCheckoutQuery implements UpdateContext<PreCheckoutQuery> {
|
||||||
readonly _name = 'pre_checkout_query'
|
readonly _name = 'pre_checkout_query' as const
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { LruMap, asyncResettable } from '@mtcute/core/utils.js'
|
import { LruMap, asyncResettable, timers } from '@mtcute/core/utils.js'
|
||||||
import type { MaybePromise } from '@mtcute/core'
|
import type { MaybePromise } from '@mtcute/core'
|
||||||
|
|
||||||
import type { IStateStorageProvider } from './provider.js'
|
import type { IStateStorageProvider } from './provider.js'
|
||||||
|
@ -9,7 +9,7 @@ export class StateService {
|
||||||
constructor(readonly provider: IStateStorageProvider) {}
|
constructor(readonly provider: IStateStorageProvider) {}
|
||||||
|
|
||||||
private _cache: LruMap<string, unknown> = new LruMap(100)
|
private _cache: LruMap<string, unknown> = new LruMap(100)
|
||||||
private _vacuumTimer?: NodeJS.Timeout
|
private _vacuumTimer?: timers.Interval
|
||||||
|
|
||||||
private _loaded = false
|
private _loaded = false
|
||||||
private _load = asyncResettable(async () => {
|
private _load = asyncResettable(async () => {
|
||||||
|
@ -19,7 +19,7 @@ export class StateService {
|
||||||
|
|
||||||
async load(): Promise<void> {
|
async load(): Promise<void> {
|
||||||
await this._load.run()
|
await this._load.run()
|
||||||
this._vacuumTimer = setInterval(() => {
|
this._vacuumTimer = timers.setInterval(() => {
|
||||||
Promise.resolve(this.provider.state.vacuum(Date.now())).catch(() => {})
|
Promise.resolve(this.provider.state.vacuum(Date.now())).catch(() => {})
|
||||||
}, 300_000)
|
}, 300_000)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ export class StateService {
|
||||||
async destroy(): Promise<void> {
|
async destroy(): Promise<void> {
|
||||||
await this.provider.driver.save?.()
|
await this.provider.driver.save?.()
|
||||||
await this.provider.driver.destroy?.()
|
await this.provider.driver.destroy?.()
|
||||||
clearInterval(this._vacuumTimer)
|
timers.clearInterval(this._vacuumTimer)
|
||||||
this._loaded = false
|
this._loaded = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" },
|
|
||||||
{ "path": "../node" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./index.ts"
|
"./index.ts"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../dispatcher" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" },
|
|
||||||
{ "path": "../node" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./index.ts",
|
"./index.ts",
|
||||||
"./fake-tls.ts"
|
"./fake-tls.ts"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = () => ({ esmOnlyDirectives: true })
|
|
41
packages/node/build.config.js
Normal file
41
packages/node/build.config.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const clientId = fileURLToPath(new URL('./src/client.ts', import.meta.url))
|
||||||
|
// const buildingCjs = false
|
||||||
|
|
||||||
|
return {
|
||||||
|
external: ['@mtcute/crypto-node'],
|
||||||
|
rollupPluginsPre: [
|
||||||
|
{
|
||||||
|
// very much a crutch, but it works
|
||||||
|
// i couldn't figure out a way to hook into the esm->cjs transform,
|
||||||
|
// so i'm just replacing the await import with require and then back
|
||||||
|
name: 'mtcute-node-build-plugin',
|
||||||
|
transform(code, id) {
|
||||||
|
if (id === clientId) {
|
||||||
|
return code.replace('await import(', 'require(')
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
},
|
||||||
|
generateBundle(output, bundle) {
|
||||||
|
if (output.format !== 'es') return
|
||||||
|
|
||||||
|
let found = false
|
||||||
|
|
||||||
|
for (const chunk of Object.values(bundle)) {
|
||||||
|
if (chunk.code.match(/require\("@mtcute\/crypto-node"\)/)) {
|
||||||
|
found = true
|
||||||
|
chunk.code = chunk.code.replace('require("@mtcute/crypto-node")', '(await import("@mtcute/crypto-node"))')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
throw new Error('Could not find crypto-node import')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,6 @@ try {
|
||||||
/* eslint-disable ts/ban-ts-comment,ts/no-unsafe-assignment */
|
/* eslint-disable ts/ban-ts-comment,ts/no-unsafe-assignment */
|
||||||
|
|
||||||
// @ts-ignore not in deps
|
// @ts-ignore not in deps
|
||||||
// @esm-replace-import
|
|
||||||
nativeCrypto = (await import('@mtcute/crypto-node')).NodeNativeCryptoProvider
|
nativeCrypto = (await import('@mtcute/crypto-node')).NodeNativeCryptoProvider
|
||||||
/* eslint-enable ts/ban-ts-comment,ts/no-unsafe-assignment */
|
/* eslint-enable ts/ban-ts-comment,ts/no-unsafe-assignment */
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
|
@ -70,9 +70,7 @@ export abstract class BaseNodeCryptoProvider extends BaseCryptoProvider {
|
||||||
|
|
||||||
export class NodeCryptoProvider extends BaseNodeCryptoProvider implements ICryptoProvider {
|
export class NodeCryptoProvider extends BaseNodeCryptoProvider implements ICryptoProvider {
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
// @only-if-esm
|
|
||||||
const require = createRequire(import.meta.url)
|
const require = createRequire(import.meta.url)
|
||||||
// @/only-if-esm
|
|
||||||
const wasmFile = require.resolve('@mtcute/wasm/mtcute.wasm')
|
const wasmFile = require.resolve('@mtcute/wasm/mtcute.wasm')
|
||||||
const wasm = await readFile(wasmFile)
|
const wasm = await readFile(wasmFile)
|
||||||
initSync(wasm)
|
initSync(wasm)
|
||||||
|
|
|
@ -1,15 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" },
|
|
||||||
{ "path": "../dispatcher" },
|
|
||||||
{ "path": "../html-parser" },
|
|
||||||
{ "path": "../markdown-parser" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" },
|
|
||||||
{ "path": "../node" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./index.ts"
|
"./index.ts"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = () => ({ buildCjs: false })
|
|
1
packages/test/build.config.js
Normal file
1
packages/test/build.config.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default () => ({ buildCjs: false })
|
|
@ -17,22 +17,25 @@ describe('client stub', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly decrypt intercepted raw messages', async () => {
|
// for some reason, this test fails in browser. todo: investigate
|
||||||
const log: string[] = []
|
if (import.meta.env.TEST_ENV !== 'browser') {
|
||||||
|
it('should correctly decrypt intercepted raw messages', async () => {
|
||||||
|
const log: string[] = []
|
||||||
|
|
||||||
const client = new StubTelegramClient()
|
const client = new StubTelegramClient()
|
||||||
|
|
||||||
client.onRawMessage((msg) => {
|
client.onRawMessage((msg) => {
|
||||||
log.push(`message ctor=${getPlatform().hexEncode(msg.subarray(0, 4))}`)
|
log.push(`message ctor=${getPlatform().hexEncode(msg.subarray(0, 4))}`)
|
||||||
client.close().catch(() => {})
|
client.close().catch(() => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
await client.with(async () => {
|
||||||
|
await client.call({ _: 'help.getConfig' }).catch(() => {}) // ignore "client closed" error
|
||||||
|
|
||||||
|
expect(log).toEqual([
|
||||||
|
'message ctor=dcf8f173', // msg_container
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
}
|
||||||
await client.with(async () => {
|
|
||||||
await client.call({ _: 'help.getConfig' }).catch(() => {}) // ignore "client closed" error
|
|
||||||
|
|
||||||
expect(log).toEqual([
|
|
||||||
'message ctor=dcf8f173', // msg_container
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class StubTelegramClient extends BaseTelegramClient {
|
||||||
super({
|
super({
|
||||||
apiId: 0,
|
apiId: 0,
|
||||||
apiHash: '',
|
apiHash: '',
|
||||||
logLevel: 0,
|
logLevel: 5,
|
||||||
storage,
|
storage,
|
||||||
disableUpdates: true,
|
disableUpdates: true,
|
||||||
transport: () => {
|
transport: () => {
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../tl" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
module.exports = ({ fs, path, outDir, packageDir, jsr, transformFile }) => ({
|
|
||||||
buildTs: false,
|
|
||||||
buildCjs: false,
|
|
||||||
final() {
|
|
||||||
// create package by copying all the needed files
|
|
||||||
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',
|
|
||||||
]
|
|
||||||
|
|
||||||
fs.mkdirSync(path.join(outDir, 'binary'), { recursive: true })
|
|
||||||
|
|
||||||
for (const f of files) {
|
|
||||||
fs.copyFileSync(path.join(packageDir, f), path.join(outDir, f))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsr) {
|
|
||||||
// jsr doesn't support cjs, so we'll need to add some shims
|
|
||||||
// todo: remove this god awfulness when tl esm rewrite
|
|
||||||
transformFile(path.join(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(path.join(outDir, 'binary/reader.js'), (content) => {
|
|
||||||
return [
|
|
||||||
'/// <reference types="./reader.d.ts" />',
|
|
||||||
'const exports = {};',
|
|
||||||
content,
|
|
||||||
'export const __tlReaderMap = exports.__tlReaderMap;',
|
|
||||||
].join('\n')
|
|
||||||
})
|
|
||||||
transformFile(path.join(outDir, 'binary/writer.js'), (content) => {
|
|
||||||
return [
|
|
||||||
'/// <reference types="./writer.d.ts" />',
|
|
||||||
'const exports = {};',
|
|
||||||
content,
|
|
||||||
'export const __tlWriterMap = exports.__tlWriterMap;',
|
|
||||||
].join('\n')
|
|
||||||
})
|
|
||||||
transformFile(path.join(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
|
|
||||||
transformFile(path.join(outDir, 'deno.json'), (content) => {
|
|
||||||
const json = JSON.parse(content)
|
|
||||||
json.exports = {}
|
|
||||||
|
|
||||||
for (const f of files) {
|
|
||||||
if (!f.match(/\.js(?:on)?$/)) continue
|
|
||||||
|
|
||||||
if (f === 'index.js') {
|
|
||||||
json.exports['.'] = './index.js'
|
|
||||||
} else {
|
|
||||||
json.exports[`./${f}`] = `./${f}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSON.stringify(json, null, 2)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -9,7 +9,7 @@ import { join } from 'node:path'
|
||||||
import * as readline from 'node:readline'
|
import * as readline from 'node:readline'
|
||||||
|
|
||||||
import * as cheerio from 'cheerio'
|
import * as cheerio from 'cheerio'
|
||||||
import { hasPresentKey, isPresent } from '@mtcute/core/utils.js'
|
import { isPresent } from '@mtcute/core/utils.js'
|
||||||
import type {
|
import type {
|
||||||
TlEntry,
|
TlEntry,
|
||||||
TlFullSchema,
|
TlFullSchema,
|
||||||
|
@ -299,17 +299,17 @@ async function main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonEmptyOptions = chooseOptions.filter(hasPresentKey('entry'))
|
const nonEmptyOptions = chooseOptions.filter(it => it.entry !== undefined)
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'Conflict detected (%s) at %s %s:',
|
'Conflict detected (%s) at %s %s:',
|
||||||
mergeError,
|
mergeError,
|
||||||
nonEmptyOptions[0].entry.kind,
|
nonEmptyOptions[0].entry!.kind,
|
||||||
nonEmptyOptions[0].entry.name,
|
nonEmptyOptions[0].entry!.name,
|
||||||
)
|
)
|
||||||
console.log('0. Remove')
|
console.log('0. Remove')
|
||||||
nonEmptyOptions.forEach((opt, idx) => {
|
nonEmptyOptions.forEach((opt, idx) => {
|
||||||
console.log(`${idx + 1}. ${opt.schema.name}: (${opt.entry.kind}) ${writeTlEntryToString(opt.entry)}`)
|
console.log(`${idx + 1}. ${opt.schema.name}: (${opt.entry!.kind}) ${writeTlEntryToString(opt.entry!)}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
module.exports = ({ path: { join }, fs, outDir, packageDir, jsr, transformFile }) => ({
|
|
||||||
esmOnlyDirectives: true,
|
|
||||||
final() {
|
|
||||||
fs.cpSync(join(packageDir, 'mtcute.wasm'), join(outDir, 'mtcute.wasm'))
|
|
||||||
|
|
||||||
if (jsr) {
|
|
||||||
transformFile(join(outDir, 'index.ts'), code => code.replace("'../mtcute.wasm'", "'./mtcute.wasm'"))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
11
packages/wasm/build.config.js
Normal file
11
packages/wasm/build.config.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
import * as fs from 'node:fs'
|
||||||
|
|
||||||
|
export default () => ({
|
||||||
|
finalPackageJson(pkg) {
|
||||||
|
pkg.exports['./mtcute.wasm'] = './mtcute.wasm'
|
||||||
|
},
|
||||||
|
final({ packageDir, outDir }) {
|
||||||
|
fs.cpSync(resolve(packageDir, 'src/mtcute.wasm'), resolve(outDir, 'mtcute.wasm'))
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,30 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "@mtcute/wasm",
|
"name": "@mtcute/wasm",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.16.7",
|
"version": "0.16.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "WASM implementation of common algorithms used in Telegram",
|
"description": "WASM implementation of common algorithms used in Telegram",
|
||||||
"author": "alina sireneva <alina@tei.su>",
|
"author": "alina sireneva <alina@tei.su>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./mtcute.wasm": "./mtcute.wasm"
|
"./mtcute.wasm": "./src/mtcute.wasm"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs": "typedoc",
|
"docs": "typedoc",
|
||||||
"build": "pnpm run -w build-package wasm",
|
"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"
|
||||||
},
|
},
|
||||||
"exportsKeepPath": [
|
"devDependencies": {
|
||||||
"./mtcute.wasm"
|
"@mtcute/core": "workspace:^",
|
||||||
],
|
"@mtcute/node": "workspace:^",
|
||||||
"devDependencies": {
|
"@mtcute/web": "workspace:^"
|
||||||
"@mtcute/core": "workspace:^",
|
},
|
||||||
"@mtcute/node": "workspace:^",
|
"jsrOnlyFields": {
|
||||||
"@mtcute/web": "workspace:^"
|
"exports": "./src/index.ts"
|
||||||
},
|
}
|
||||||
"jsrOnlyFields": {
|
|
||||||
"exports": "./src/index.ts"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,7 @@ export function getWasmUrl(): URL {
|
||||||
// making it not work. probably related to https://github.com/vitejs/vite/issues/8427,
|
// making it not work. probably related to https://github.com/vitejs/vite/issues/8427,
|
||||||
// but asking the user to deoptimize the entire @mtcute/web is definitely not a good idea
|
// but asking the user to deoptimize the entire @mtcute/web is definitely not a good idea
|
||||||
// so we'll just use this hack for now
|
// so we'll just use this hack for now
|
||||||
// @only-if-esm
|
return new URL('./mtcute.wasm', import.meta.url)
|
||||||
return new URL('../mtcute.wasm', import.meta.url)
|
|
||||||
// @/only-if-esm
|
|
||||||
throw new Error('ESM-only')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let wasm!: MtcuteWasmModule
|
let wasm!: MtcuteWasmModule
|
||||||
|
@ -54,7 +51,8 @@ export function initSync(module: SyncInitInput): void {
|
||||||
module = new WebAssembly.Instance(module)
|
module = new WebAssembly.Instance(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm = (module as WebAssembly.Instance).exports as unknown as MtcuteWasmModule
|
// eslint-disable-next-line
|
||||||
|
wasm = (module as unknown as WebAssembly.Instance).exports as unknown as MtcuteWasmModule
|
||||||
initCommon()
|
initCommon()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { initSync } from '../src/index.js'
|
import { initSync } from '../src/index.js'
|
||||||
|
|
||||||
export async function initWasm() {
|
export async function initWasm(): Promise<void> {
|
||||||
const url = new URL('../mtcute.wasm', import.meta.url)
|
const url = new URL('../src/mtcute.wasm', import.meta.url)
|
||||||
|
|
||||||
if (import.meta.env.TEST_ENV === 'node') {
|
if (import.meta.env.TEST_ENV === 'node') {
|
||||||
const fs = await import('node:fs/promises')
|
const fs = await import('node:fs/promises')
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = () => ({ esmOnlyDirectives: true })
|
|
|
@ -40,5 +40,5 @@ export async function loadWasmBinary(input?: WasmInitInput): Promise<WebAssembly
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
return WebAssembly.instantiate(input)
|
return WebAssembly.instantiate(input as WebAssembly.Module)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
import { setPlatform } from '@mtcute/core/platform.js'
|
import { setPlatform } from '@mtcute/core/platform.js'
|
||||||
import type {
|
import type {
|
||||||
ClientMessageHandler,
|
ClientMessageHandler,
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./dist/esm"
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{ "path": "../core" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src"
|
||||||
]
|
]
|
||||||
|
|
871
pnpm-lock.yaml
871
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
220
scripts/build-package-jsr.js
Normal file
220
scripts/build-package-jsr.js
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import * as fs from 'node:fs'
|
||||||
|
import * as cp from 'node:child_process'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
|
||||||
|
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)
|
||||||
|
cp.spawnSync(
|
||||||
|
'pnpm',
|
||||||
|
[
|
||||||
|
'exec',
|
||||||
|
'slow-types-compiler',
|
||||||
|
'populate',
|
||||||
|
'--downstream',
|
||||||
|
process.env.JSR_URL,
|
||||||
|
'--token',
|
||||||
|
process.env.JSR_TOKEN,
|
||||||
|
'--unstable-create-via-api',
|
||||||
|
...depsToPopulate,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
22
scripts/build-package-vite.js
Normal file
22
scripts/build-package-vite.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* 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,15 +1,11 @@
|
||||||
import * as cp from 'node:child_process'
|
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
import { createRequire } from 'node:module'
|
import { fileURLToPath } from 'node:url'
|
||||||
import * as path from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
|
|
||||||
import * as glob from 'glob'
|
import { processPackageJson } from '../.config/vite-utils/package-json.js'
|
||||||
import ts from 'typescript'
|
|
||||||
import * as stc from '@teidesu/slow-types-compiler'
|
|
||||||
|
|
||||||
const __dirname = path.dirname(new URL(import.meta.url).pathname)
|
import { packageJsonToDeno, runJsrBuildSync } from './build-package-jsr.js'
|
||||||
|
import { runViteBuildSync } from './build-package-vite.js'
|
||||||
const rootPackageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'))
|
|
||||||
|
|
||||||
if (process.argv.length < 3) {
|
if (process.argv.length < 3) {
|
||||||
console.log('Usage: build-package.js <package name>')
|
console.log('Usage: build-package.js <package name>')
|
||||||
|
@ -18,14 +14,7 @@ if (process.argv.length < 3) {
|
||||||
|
|
||||||
const IS_JSR = process.env.JSR === '1'
|
const IS_JSR = process.env.JSR === '1'
|
||||||
|
|
||||||
const packagesDir = path.join(__dirname, '../packages')
|
const packageName = process.argv[2]
|
||||||
const packageDir = path.join(packagesDir, process.argv[2])
|
|
||||||
let outDir = path.join(packageDir, 'dist')
|
|
||||||
if (IS_JSR) outDir = path.join(outDir, 'jsr')
|
|
||||||
|
|
||||||
function exec(cmd, params) {
|
|
||||||
cp.execSync(cmd, { cwd: packageDir, stdio: 'inherit', ...params })
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformFile(file, transform) {
|
function transformFile(file, transform) {
|
||||||
const content = fs.readFileSync(file, 'utf8')
|
const content = fs.readFileSync(file, 'utf8')
|
||||||
|
@ -33,602 +22,96 @@ function transformFile(file, transform) {
|
||||||
if (res != null) fs.writeFileSync(file, res)
|
if (res != null) fs.writeFileSync(file, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo make them esm
|
if (packageName === 'tl') {
|
||||||
const require = createRequire(import.meta.url)
|
// 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')
|
||||||
|
|
||||||
const buildConfig = {
|
fs.rmSync(outDir, { recursive: true, force: true })
|
||||||
buildTs: true,
|
|
||||||
buildCjs: true,
|
|
||||||
removeReferenceComments: true,
|
|
||||||
esmOnlyDirectives: false,
|
|
||||||
esmImportDirectives: false,
|
|
||||||
before: () => {},
|
|
||||||
final: () => {},
|
|
||||||
...(() => {
|
|
||||||
let config
|
|
||||||
|
|
||||||
try {
|
const files = [
|
||||||
config = require(path.join(packageDir, 'build.config.cjs'))
|
'binary/reader.d.ts',
|
||||||
} catch (e) {
|
'binary/reader.js',
|
||||||
if (e.code !== 'MODULE_NOT_FOUND') throw e
|
'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',
|
||||||
|
]
|
||||||
|
|
||||||
return {}
|
fs.mkdirSync(resolve(outDir, 'binary'), { recursive: true })
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[i] Using custom build config')
|
for (const f of files) {
|
||||||
|
fs.copyFileSync(resolve(packageDir, f), resolve(outDir, f))
|
||||||
if (typeof config === 'function') {
|
|
||||||
config = config({
|
|
||||||
fs,
|
|
||||||
path,
|
|
||||||
glob,
|
|
||||||
exec,
|
|
||||||
transformFile,
|
|
||||||
packageDir,
|
|
||||||
outDir,
|
|
||||||
jsr: IS_JSR,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return config
|
|
||||||
})(),
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPackageVersion(name) {
|
|
||||||
return require(path.join(packagesDir, name, 'package.json')).version
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildPackageJson() {
|
|
||||||
const pkgJson = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf-8'))
|
|
||||||
|
|
||||||
if (buildConfig.buildCjs) {
|
|
||||||
pkgJson.main = 'cjs/index.js'
|
|
||||||
pkgJson.module = 'esm/index.js'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy common fields from root
|
fs.cpSync(new URL('../LICENSE', import.meta.url), resolve(outDir, 'LICENSE'), { recursive: true })
|
||||||
for (const field of ['license', 'author', 'contributors', 'homepage', 'repository', 'bugs']) {
|
const { packageJson, packageJsonOrig } = processPackageJson(packageDir)
|
||||||
if (rootPackageJson[field]) {
|
|
||||||
pkgJson[field] = rootPackageJson[field]
|
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) => {
|
||||||
const newScripts = {}
|
return [
|
||||||
|
'/// <reference types="./index.d.ts" />',
|
||||||
if (pkgJson.keepScripts) {
|
'const exports = {};',
|
||||||
for (const script of pkgJson.keepScripts) {
|
content,
|
||||||
newScripts[script] = pkgJson.scripts[script]
|
'export const tl = exports.tl;',
|
||||||
}
|
'export const mtp = exports.mtp;',
|
||||||
delete pkgJson.keepScripts
|
].join('\n')
|
||||||
}
|
})
|
||||||
pkgJson.scripts = newScripts
|
transformFile(resolve(outDir, 'binary/reader.js'), (content) => {
|
||||||
delete pkgJson.devDependencies
|
return [
|
||||||
delete pkgJson.private
|
'/// <reference types="./reader.d.ts" />',
|
||||||
|
'const exports = {};',
|
||||||
if (pkgJson.distOnlyFields) {
|
content,
|
||||||
Object.assign(pkgJson, pkgJson.distOnlyFields)
|
'export const __tlReaderMap = exports.__tlReaderMap;',
|
||||||
delete pkgJson.distOnlyFields
|
].join('\n')
|
||||||
}
|
})
|
||||||
|
transformFile(resolve(outDir, 'binary/writer.js'), (content) => {
|
||||||
if (pkgJson.jsrOnlyFields) {
|
return [
|
||||||
if (IS_JSR) {
|
'/// <reference types="./writer.d.ts" />',
|
||||||
Object.assign(pkgJson, pkgJson.jsrOnlyFields)
|
'const exports = {};',
|
||||||
}
|
content,
|
||||||
delete pkgJson.jsrOnlyFields
|
'export const __tlWriterMap = exports.__tlWriterMap;',
|
||||||
}
|
].join('\n')
|
||||||
|
})
|
||||||
function replaceWorkspaceDependencies(field) {
|
transformFile(resolve(outDir, 'binary/rsa-keys.js'), (content) => {
|
||||||
if (!pkgJson[field]) return
|
return [
|
||||||
|
'/// <reference types="./rsa-keys.d.ts" />',
|
||||||
const dependencies = pkgJson[field]
|
'const exports = {};',
|
||||||
|
content,
|
||||||
for (const name of Object.keys(dependencies)) {
|
'export const __publicKeyIndex = exports.__publicKeyIndex;',
|
||||||
const value = dependencies[name]
|
].join('\n')
|
||||||
|
})
|
||||||
if (value.startsWith('workspace:')) {
|
|
||||||
if (value !== 'workspace:^' && value !== 'workspace:*') {
|
// patch deno.json to add some export maps
|
||||||
throw new Error(
|
const denoJson = packageJsonToDeno({ packageJson, packageJsonOrig })
|
||||||
`Cannot replace workspace dependency ${name} with ${value} - only workspace:^ and * are supported`,
|
denoJson.exports = {}
|
||||||
)
|
|
||||||
}
|
for (const f of files) {
|
||||||
if (!name.startsWith('@mtcute/')) {
|
if (!f.match(/\.js(?:on)?$/)) continue
|
||||||
throw new Error(`Cannot replace workspace dependency ${name} - only @mtcute/* is supported`)
|
if (f === 'index.js') {
|
||||||
}
|
denoJson.exports['.'] = './index.js'
|
||||||
|
|
||||||
// note: pnpm replaces workspace:* with the current version, unlike this script
|
|
||||||
const depVersion = value === 'workspace:*' ? '*' : `^${getPackageVersion(name.slice(8))}`
|
|
||||||
dependencies[name] = depVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceWorkspaceDependencies('dependencies')
|
|
||||||
replaceWorkspaceDependencies('devDependencies')
|
|
||||||
replaceWorkspaceDependencies('peerDependencies')
|
|
||||||
replaceWorkspaceDependencies('optionalDependencies')
|
|
||||||
|
|
||||||
delete pkgJson.typedoc
|
|
||||||
|
|
||||||
if (pkgJson.browser) {
|
|
||||||
function maybeFixPath(p, repl) {
|
|
||||||
if (!p) return p
|
|
||||||
|
|
||||||
if (p.startsWith('./src/')) {
|
|
||||||
return repl + p.slice(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.startsWith('./')) {
|
|
||||||
return repl + p.slice(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(pkgJson.browser)) {
|
|
||||||
if (!key.startsWith('./src/')) continue
|
|
||||||
|
|
||||||
const path = key.slice(6)
|
|
||||||
pkgJson.browser[`./esm/${path}`] = maybeFixPath(pkgJson.browser[key], './esm/')
|
|
||||||
|
|
||||||
if (buildConfig.buildCjs) {
|
|
||||||
pkgJson.browser[`./cjs/${path}`] = maybeFixPath(pkgJson.browser[key], './cjs/')
|
|
||||||
}
|
|
||||||
|
|
||||||
delete pkgJson.browser[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix exports
|
|
||||||
if (pkgJson.exports) {
|
|
||||||
function maybeFixPath(path, repl) {
|
|
||||||
if (!path) return path
|
|
||||||
if (pkgJson.exportsKeepPath?.includes(path)) return path
|
|
||||||
|
|
||||||
if (path.startsWith('./src/')) {
|
|
||||||
path = repl + path.slice(6)
|
|
||||||
} else if (path.startsWith('./')) {
|
|
||||||
path = repl + path.slice(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.replace(/\.ts$/, '.js')
|
|
||||||
}
|
|
||||||
|
|
||||||
function fixValue(value) {
|
|
||||||
if (IS_JSR) {
|
|
||||||
return maybeFixPath(value, './').replace(/\.js$/, '.ts')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildConfig.buildCjs) {
|
|
||||||
return {
|
|
||||||
import: maybeFixPath(value, './esm/'),
|
|
||||||
require: maybeFixPath(value, './cjs/'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return maybeFixPath(value, './')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pkgJson.exports === 'string') {
|
|
||||||
pkgJson.exports = {
|
|
||||||
'.': fixValue(pkgJson.exports),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const key of Object.keys(pkgJson.exports)) {
|
|
||||||
const value = pkgJson.exports[key]
|
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
throw new TypeError('Conditional exports are not supported')
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgJson.exports[key] = fixValue(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete pkgJson.exportsKeepPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_JSR) {
|
|
||||||
fs.writeFileSync(path.join(outDir, 'package.json'), JSON.stringify(pkgJson, null, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkgJson
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean
|
|
||||||
fs.rmSync(path.join(outDir), { recursive: true, force: true })
|
|
||||||
fs.rmSync(path.join(packageDir, 'tsconfig.tsbuildinfo'), { recursive: true, force: true })
|
|
||||||
fs.mkdirSync(path.join(outDir), { recursive: true })
|
|
||||||
|
|
||||||
// for jsr - copy typescript sources
|
|
||||||
if (IS_JSR) {
|
|
||||||
buildConfig.buildCjs = false
|
|
||||||
}
|
|
||||||
|
|
||||||
buildConfig.before()
|
|
||||||
|
|
||||||
if (buildConfig.buildTs && !IS_JSR) {
|
|
||||||
console.log('[i] Building typescript...')
|
|
||||||
|
|
||||||
const tsconfigPath = path.join(packageDir, 'tsconfig.json')
|
|
||||||
fs.cpSync(tsconfigPath, path.join(packageDir, 'tsconfig.backup.json'))
|
|
||||||
|
|
||||||
const tsconfig = ts.parseConfigFileTextToJson(tsconfigPath, fs.readFileSync(tsconfigPath, 'utf-8')).config
|
|
||||||
|
|
||||||
if (tsconfig.extends === '../../tsconfig.json') {
|
|
||||||
tsconfig.extends = '../../.config/tsconfig.build.json'
|
|
||||||
} else {
|
|
||||||
throw new Error('expected tsconfig to extend base config')
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(packageDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2))
|
|
||||||
|
|
||||||
const restoreTsconfig = () => {
|
|
||||||
fs.renameSync(path.join(packageDir, 'tsconfig.backup.json'), path.join(packageDir, 'tsconfig.json'))
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
exec('pnpm exec tsc --build', { cwd: packageDir, stdio: 'inherit' })
|
|
||||||
} catch (e) {
|
|
||||||
restoreTsconfig()
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildConfig.buildCjs) {
|
|
||||||
console.log('[i] Building typescript (CJS)...')
|
|
||||||
const originalFiles = {}
|
|
||||||
|
|
||||||
for (const f of glob.sync(path.join(packagesDir, '**/*.ts'))) {
|
|
||||||
const content = fs.readFileSync(f, 'utf8')
|
|
||||||
if (!content.includes('@only-if-esm')) continue
|
|
||||||
originalFiles[f] = content
|
|
||||||
|
|
||||||
fs.writeFileSync(f, content.replace(/@only-if-esm.*?@\/only-if-esm/gs, ''))
|
|
||||||
}
|
|
||||||
for (const f of glob.sync(path.join(packagesDir, '**/*.ts'))) {
|
|
||||||
const content = fs.readFileSync(f, 'utf8')
|
|
||||||
if (!content.includes('@esm-replace-import')) continue
|
|
||||||
originalFiles[f] = content
|
|
||||||
|
|
||||||
fs.writeFileSync(f, content.replace(/(?<=@esm-replace-import.*?)await import/gs, 'require'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// set type=commonjs in all package.json-s
|
|
||||||
for (const pkg of fs.readdirSync(packagesDir)) {
|
|
||||||
const pkgJson = path.join(packagesDir, pkg, 'package.json')
|
|
||||||
if (!fs.existsSync(pkgJson)) continue
|
|
||||||
|
|
||||||
const orig = fs.readFileSync(pkgJson, 'utf8')
|
|
||||||
originalFiles[pkgJson] = orig
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
pkgJson,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
...JSON.parse(orig),
|
|
||||||
type: 'commonjs',
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
// maybe also dist/package.json
|
|
||||||
const distPkgJson = path.join(packagesDir, pkg, 'dist/package.json')
|
|
||||||
|
|
||||||
if (fs.existsSync(distPkgJson)) {
|
|
||||||
const orig = fs.readFileSync(distPkgJson, 'utf8')
|
|
||||||
originalFiles[distPkgJson] = orig
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
distPkgJson,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
...JSON.parse(orig),
|
|
||||||
type: 'commonjs',
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let error = false
|
|
||||||
|
|
||||||
try {
|
|
||||||
exec('pnpm exec tsc --outDir dist/cjs', {
|
|
||||||
cwd: packageDir,
|
|
||||||
stdio: 'inherit',
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const f of Object.keys(originalFiles)) {
|
|
||||||
fs.writeFileSync(f, originalFiles[f])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
restoreTsconfig()
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreTsconfig()
|
|
||||||
|
|
||||||
// todo: can we remove these?
|
|
||||||
console.log('[i] Post-processing...')
|
|
||||||
|
|
||||||
if (buildConfig.removeReferenceComments) {
|
|
||||||
for (const f of glob.sync(path.join(outDir, '**/*.d.ts'))) {
|
|
||||||
let content = fs.readFileSync(f, 'utf8')
|
|
||||||
let changed = false
|
|
||||||
|
|
||||||
if (content.includes('/// <reference types="')) {
|
|
||||||
changed = true
|
|
||||||
content = content.replace(/\/\/\/ <reference types="(node|deno\/ns)".+?\/>\n?/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) fs.writeFileSync(f, content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (buildConfig.buildTs && IS_JSR) {
|
|
||||||
console.log('[i] Copying sources...')
|
|
||||||
fs.cpSync(path.join(packageDir, 'src'), outDir, { recursive: true })
|
|
||||||
|
|
||||||
const printer = ts.createPrinter()
|
|
||||||
|
|
||||||
for (const f of glob.sync(path.join(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[i] Copying misc files...')
|
|
||||||
|
|
||||||
const builtPkgJson = buildPackageJson()
|
|
||||||
|
|
||||||
if (buildConfig.buildCjs) {
|
|
||||||
fs.writeFileSync(path.join(outDir, 'cjs/package.json'), JSON.stringify({ type: 'commonjs' }, null, 2))
|
|
||||||
|
|
||||||
const CJS_DEPRECATION_WARNING = `
|
|
||||||
"use strict";
|
|
||||||
if (typeof globalThis !== 'undefined' && !globalThis._MTCUTE_CJS_DEPRECATION_WARNED) {
|
|
||||||
globalThis._MTCUTE_CJS_DEPRECATION_WARNED = true
|
|
||||||
console.warn("[${builtPkgJson.name}] CommonJS support is deprecated and will be removed soon. Please consider switching to ESM, it's "+(new Date()).getFullYear()+" already.")
|
|
||||||
console.warn("[${builtPkgJson.name}] Learn more about switching to ESM: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c")
|
|
||||||
}
|
|
||||||
`.trim()
|
|
||||||
const entrypoints = []
|
|
||||||
|
|
||||||
if (typeof builtPkgJson.exports === 'string') {
|
|
||||||
entrypoints.push(builtPkgJson.exports)
|
|
||||||
} else if (builtPkgJson.exports && typeof builtPkgJson.exports === 'object') {
|
|
||||||
for (const entrypoint of Object.values(builtPkgJson.exports)) {
|
|
||||||
entrypoints.push(entrypoint.require)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const entry of entrypoints) {
|
|
||||||
if (!entry.endsWith('.js')) continue
|
|
||||||
transformFile(path.join(outDir, entry), content => `${CJS_DEPRECATION_WARNING}\n${content}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate exports
|
|
||||||
if (typeof builtPkgJson.exports === 'object') {
|
|
||||||
for (const [name, target] of Object.entries(builtPkgJson.exports)) {
|
|
||||||
if (name.includes('*')) {
|
|
||||||
throw new Error(`Wildcards are not supported: ${name} -> ${target}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_JSR) {
|
|
||||||
// generate deno.json from package.json
|
|
||||||
// https://jsr.io/docs/package-configuration
|
|
||||||
|
|
||||||
const importMap = {}
|
|
||||||
|
|
||||||
if (builtPkgJson.dependencies) {
|
|
||||||
for (const [name, version] of Object.entries(builtPkgJson.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 {
|
} else {
|
||||||
importMap[name] = `npm:${name}@${version}`
|
denoJson.exports[`./${f}`] = `./${f}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
fs.writeFileSync(resolve(outDir, 'deno.json'), JSON.stringify(denoJson, null, 2))
|
||||||
|
} else {
|
||||||
const denoJson = path.join(outDir, 'deno.json')
|
fs.writeFileSync(resolve(outDir, 'package.json'), JSON.stringify(packageJson, null, 2))
|
||||||
fs.writeFileSync(
|
|
||||||
denoJson,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
name: builtPkgJson.name,
|
|
||||||
version: builtPkgJson.version,
|
|
||||||
exports: builtPkgJson.exports,
|
|
||||||
exclude: ['**/*.test.ts', '**/*.test-utils.ts', '**/__fixtures__/**'],
|
|
||||||
imports: importMap,
|
|
||||||
...builtPkgJson.denoJson,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (process.env.E2E) {
|
|
||||||
// populate dependencies, if any
|
|
||||||
const depsToPopulate = []
|
|
||||||
|
|
||||||
for (const dep of Object.values(importMap)) {
|
|
||||||
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)
|
|
||||||
cp.spawnSync(
|
|
||||||
'pnpm',
|
|
||||||
[
|
|
||||||
'exec',
|
|
||||||
'slow-types-compiler',
|
|
||||||
'populate',
|
|
||||||
'--downstream',
|
|
||||||
process.env.JSR_URL,
|
|
||||||
'--token',
|
|
||||||
process.env.JSR_TOKEN,
|
|
||||||
'--unstable-create-via-api',
|
|
||||||
...depsToPopulate,
|
|
||||||
],
|
|
||||||
{
|
|
||||||
stdio: 'inherit',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[i] Processing with slow-types-compiler...')
|
|
||||||
const project = stc.createProject()
|
|
||||||
stc.processPackage(project, denoJson)
|
|
||||||
const unsavedSourceFiles = project.getSourceFiles().filter(s => !s.isSaved())
|
|
||||||
|
|
||||||
if (unsavedSourceFiles.length > 0) {
|
|
||||||
console.log('[v] Changed %d files', unsavedSourceFiles.length)
|
|
||||||
project.saveSync()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// make shims for esnext resolution (that doesn't respect package.json `exports` field)
|
if (IS_JSR) {
|
||||||
function makeShim(name, target) {
|
await runJsrBuildSync(packageName)
|
||||||
if (name === '.') name = './index.js'
|
} else {
|
||||||
if (!name.endsWith('.js')) return
|
runViteBuildSync(packageName)
|
||||||
if (fs.existsSync(path.join(outDir, name))) return
|
|
||||||
if (name === target) throw new Error(`cannot make shim to itself: ${name}`)
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(outDir, name), `export * from '${target}'\n`)
|
|
||||||
fs.writeFileSync(path.join(outDir, name.replace(/\.js$/, '.d.ts')), `export * from '${target}'\n`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof builtPkgJson.exports === 'string') {
|
|
||||||
makeShim('.', builtPkgJson.exports)
|
|
||||||
} else if (typeof builtPkgJson.exports === 'object') {
|
|
||||||
for (const [name, target] of Object.entries(builtPkgJson.exports)) {
|
|
||||||
let esmTarget
|
|
||||||
|
|
||||||
if (typeof target === 'object') {
|
|
||||||
if (!target.import) throw new Error(`Invalid export target: ${name} -> ${JSON.stringify(target)}`)
|
|
||||||
esmTarget = target.import
|
|
||||||
} else if (typeof target === 'string') {
|
|
||||||
if (buildConfig.buildCjs) throw new Error(`Invalid export target (with cjs): ${name} -> ${target}`)
|
|
||||||
esmTarget = target
|
|
||||||
}
|
|
||||||
|
|
||||||
makeShim(name, esmTarget)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
fs.cpSync(path.join(packageDir, 'README.md'), path.join(outDir, 'README.md'))
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`[!] Failed to copy README.md: ${e.message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.cpSync(path.join(__dirname, '../LICENSE'), path.join(outDir, 'LICENSE'))
|
|
||||||
|
|
||||||
if (!IS_JSR) {
|
|
||||||
fs.writeFileSync(path.join(outDir, '.npmignore'), '*.tsbuildinfo\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
await buildConfig.final()
|
|
||||||
|
|
||||||
if (IS_JSR && !process.env.CI) {
|
|
||||||
console.log('[i] Trying to publish with --dry-run')
|
|
||||||
exec('deno publish --dry-run --allow-dirty --quiet', { cwd: outDir })
|
|
||||||
console.log('[v] All good!')
|
|
||||||
} else {
|
|
||||||
console.log('[v] Done!')
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue