feat: initial deno support
This commit is contained in:
parent
2bc9f898e5
commit
a2cdc73735
50 changed files with 1013 additions and 27 deletions
|
@ -279,7 +279,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['packages/bun/**'],
|
files: ['packages/bun/**', 'packages/deno/**'],
|
||||||
rules: {
|
rules: {
|
||||||
'import/no-unresolved': 'off',
|
'import/no-unresolved': 'off',
|
||||||
'no-restricted-imports': 'off',
|
'no-restricted-imports': 'off',
|
||||||
|
@ -291,7 +291,7 @@ module.exports = {
|
||||||
rules: {
|
rules: {
|
||||||
'import/no-unresolved': 'off',
|
'import/no-unresolved': 'off',
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
|
|
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
|
@ -67,7 +67,7 @@ jobs:
|
||||||
- name: 'Build tests'
|
- name: 'Build tests'
|
||||||
run: pnpm exec vite build -c .config/vite.deno.mts
|
run: pnpm exec vite build -c .config/vite.deno.mts
|
||||||
- name: 'Run tests'
|
- name: 'Run tests'
|
||||||
run: cd dist/tests && deno test
|
run: cd dist/tests && deno test -A --unstable-ffi
|
||||||
|
|
||||||
test-web:
|
test-web:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
@jsr:registry=https://npm.jsr.io
|
|
@ -10,7 +10,7 @@
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "husky install .config/husky",
|
"prepare": "husky install .config/husky",
|
||||||
"postinstall": "node scripts/validate-deps-versions.mjs",
|
"postinstall": "node scripts/validate-deps-versions.mjs && node scripts/fetch-deno-dts.mjs && node scripts/remove-jsr-sourcefiles.mjs",
|
||||||
"test": "pnpm run -r test && vitest --config .config/vite.mts run",
|
"test": "pnpm run -r test && vitest --config .config/vite.mts run",
|
||||||
"test:dev": "vitest --config .config/vite.mts watch",
|
"test:dev": "vitest --config .config/vite.mts watch",
|
||||||
"test:ui": "vitest --config .config/vite.mts --ui",
|
"test:ui": "vitest --config .config/vite.mts --ui",
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
|
import * as os from 'os'
|
||||||
|
|
||||||
import { NodePlatform } from './common-internals-node/platform.js'
|
import { NodePlatform } from './common-internals-node/platform.js'
|
||||||
import { normalizeFile } from './utils/normalize-file.js'
|
import { normalizeFile } from './utils/normalize-file.js'
|
||||||
|
|
||||||
export class BunPlatform extends NodePlatform {
|
export class BunPlatform extends NodePlatform {
|
||||||
declare normalizeFile: typeof normalizeFile
|
declare normalizeFile: typeof normalizeFile
|
||||||
|
|
||||||
|
getDeviceModel(): string {
|
||||||
|
return `Bun/${process.version} (${os.type()} ${os.arch()})`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BunPlatform.prototype.normalizeFile = normalizeFile
|
BunPlatform.prototype.normalizeFile = normalizeFile
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
WorkerMessageHandler,
|
WorkerMessageHandler,
|
||||||
} from '@mtcute/core/worker.js'
|
} from '@mtcute/core/worker.js'
|
||||||
|
|
||||||
import { NodePlatform } from './common-internals-node/platform.js'
|
import { BunPlatform } from './platform.js'
|
||||||
|
|
||||||
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods }
|
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods }
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorke
|
||||||
|
|
||||||
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
|
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
|
||||||
constructor(readonly options: TelegramWorkerPortOptions) {
|
constructor(readonly options: TelegramWorkerPortOptions) {
|
||||||
setPlatform(new NodePlatform())
|
setPlatform(new BunPlatform())
|
||||||
super(options)
|
super(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,8 @@ export async function uploadFile(
|
||||||
throw new MtArgumentError('Fetch response contains `null` body')
|
throw new MtArgumentError('Fetch response contains `null` body')
|
||||||
}
|
}
|
||||||
|
|
||||||
file = file.body
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
file = file.body as ReadableStream<Uint8Array>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(file instanceof ReadableStream)) {
|
if (!(file instanceof ReadableStream)) {
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
/* eslint-disable no-restricted-imports */
|
|
||||||
import type { ReadStream } from 'node:fs'
|
|
||||||
|
|
||||||
import { tdFileId } from '@mtcute/file-id'
|
import { tdFileId } from '@mtcute/file-id'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { AnyToNever } from '../../../types/utils.js'
|
||||||
import { FileLocation } from './file-location.js'
|
import { FileLocation } from './file-location.js'
|
||||||
import { UploadedFile } from './uploaded-file.js'
|
import { UploadedFile } from './uploaded-file.js'
|
||||||
|
|
||||||
|
@ -14,8 +12,9 @@ import { UploadedFile } from './uploaded-file.js'
|
||||||
* - `File`, `Blob` (from the Web API)
|
* - `File`, `Blob` (from the Web API)
|
||||||
* - `string`, which will be interpreted as file path (**non-browser only!**)
|
* - `string`, which will be interpreted as file path (**non-browser only!**)
|
||||||
* - `URL` (from the Web API, will be `fetch()`-ed; `file://` URLs are not available in browsers)
|
* - `URL` (from the Web API, will be `fetch()`-ed; `file://` URLs are not available in browsers)
|
||||||
* - `ReadStream` (for Node.js/Bun, from the `fs` module)
|
* - `ReadStream` (for Node.js/Bun, from the `node:fs` module)
|
||||||
* - `BunFile` (from `Bun.file()`)
|
* - `BunFile` (from `Bun.file()`)
|
||||||
|
* - `Deno.FsFile` (from `Deno.open()` in Deno)
|
||||||
* - `ReadableStream` (Web API readable stream)
|
* - `ReadableStream` (Web API readable stream)
|
||||||
* - `Readable` (Node.js/Bun readable stream)
|
* - `Readable` (Node.js/Bun readable stream)
|
||||||
* - `Response` (from `window.fetch`)
|
* - `Response` (from `window.fetch`)
|
||||||
|
@ -26,10 +25,14 @@ export type UploadFileLike =
|
||||||
| File
|
| File
|
||||||
| Blob
|
| Blob
|
||||||
| string
|
| string
|
||||||
| ReadStream
|
| AnyToNever<import('node:fs').ReadStream>
|
||||||
| ReadableStream<Uint8Array>
|
| AnyToNever<ReadableStream<Uint8Array>>
|
||||||
| NodeJS.ReadableStream
|
| AnyToNever<NodeJS.ReadableStream>
|
||||||
| Response
|
| AnyToNever<Response>
|
||||||
|
| AnyToNever<Deno.FsFile>
|
||||||
|
|
||||||
|
// AnyToNever in the above type ensures we don't make the entire type `any`
|
||||||
|
// if some of the types are not available in the current environment
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes types that can be used as an input
|
* Describes types that can be used as an input
|
||||||
|
@ -41,6 +44,8 @@ export type UploadFileLike =
|
||||||
* - `ReadStream` (for Node.js/Bun, from the `fs` module)
|
* - `ReadStream` (for Node.js/Bun, from the `fs` module)
|
||||||
* - `ReadableStream` (from the Web API, base readable stream)
|
* - `ReadableStream` (from the Web API, base readable stream)
|
||||||
* - `Readable` (for Node.js/Bun, base readable stream)
|
* - `Readable` (for Node.js/Bun, base readable stream)
|
||||||
|
* - `BunFile` (from `Bun.file()`)
|
||||||
|
* - `Deno.FsFile` (from `Deno.open()` in Deno)
|
||||||
* - {@link UploadedFile} returned from {@link TelegramClient.uploadFile}
|
* - {@link UploadedFile} returned from {@link TelegramClient.uploadFile}
|
||||||
* - `tl.TypeInputFile` and `tl.TypeInputMedia` TL objects
|
* - `tl.TypeInputFile` and `tl.TypeInputMedia` TL objects
|
||||||
* - `string` with a path to a local file prepended with `file:` (non-browser only) (e.g. `file:image.jpg`)
|
* - `string` with a path to a local file prepended with `file:` (non-browser only) (e.g. `file:image.jpg`)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/// <reference lib="dom" />
|
||||||
import type { Worker as NodeWorker } from 'node:worker_threads'
|
import type { Worker as NodeWorker } from 'node:worker_threads'
|
||||||
|
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
|
@ -62,10 +62,9 @@ export class SqlitePeersRepository implements IPeersRepository {
|
||||||
this._driver._writeLater(this._store, [
|
this._driver._writeLater(this._store, [
|
||||||
peer.id,
|
peer.id,
|
||||||
peer.accessHash,
|
peer.accessHash,
|
||||||
// add commas to make it easier to search with LIKE
|
|
||||||
JSON.stringify(peer.usernames),
|
JSON.stringify(peer.usernames),
|
||||||
peer.updated,
|
peer.updated,
|
||||||
peer.phone,
|
peer.phone ?? null,
|
||||||
peer.complete,
|
peer.complete,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
export type MaybePromise<T> = T | Promise<T>
|
export type MaybePromise<T> = T | Promise<T>
|
||||||
export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>
|
export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>
|
||||||
export type PartialOnly<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>
|
export type PartialOnly<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export type AnyToNever<T> = any extends T ? never : T
|
||||||
|
|
||||||
export type MaybeArray<T> = T | T[]
|
export type MaybeArray<T> = T | T[]
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ export function reportUnknownError(log: Logger, error: tl.RpcError, method: stri
|
||||||
|
|
||||||
fetch(`https://rpc.pwrtelegram.xyz/?code=${error.code}&method=${method}&error=${error.text}`)
|
fetch(`https://rpc.pwrtelegram.xyz/?code=${error.code}&method=${method}&error=${error.text}`)
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.then((r) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
.then((r: any) => {
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
log.info('telerpc responded with error info for %s: %s', error.text, r.result)
|
log.info('telerpc responded with error info for %s: %s', error.text, r.result)
|
||||||
} else {
|
} else {
|
||||||
|
|
25
packages/deno/README.md
Normal file
25
packages/deno/README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# @mtcute/bun
|
||||||
|
|
||||||
|
📖 [API Reference](https://ref.mtcute.dev/modules/_mtcute_deno.html)
|
||||||
|
|
||||||
|
‼️ **Experimental** Deno support package for mtcute. Includes:
|
||||||
|
- SQLite storage (based on [`@db/sqlite`](https://jsr.io/@db/sqlite))
|
||||||
|
- TCP transport (based on Deno-native APIs)
|
||||||
|
- `TelegramClient` implementation using the above
|
||||||
|
- HTML and Markdown parsers
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { TelegramClient } from '@mtcute/deno'
|
||||||
|
|
||||||
|
const tg = new TelegramClient({
|
||||||
|
apiId: 12345,
|
||||||
|
apiHash: 'abcdef',
|
||||||
|
storage: 'my-account'
|
||||||
|
})
|
||||||
|
|
||||||
|
tg.run(async (user) => {
|
||||||
|
console.log(`✨ logged in as ${user.displayName}`)
|
||||||
|
})
|
||||||
|
```
|
1
packages/deno/build.config.cjs
Normal file
1
packages/deno/build.config.cjs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = () => ({ buildCjs: false })
|
30
packages/deno/package.json
Normal file
30
packages/deno/package.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "@mtcute/deno",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.11.0",
|
||||||
|
"description": "Meta-package for Deno",
|
||||||
|
"author": "alina sireneva <alina@tei.su>",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"scripts": {
|
||||||
|
"docs": "typedoc",
|
||||||
|
"build": "pnpm run -w build-package deno"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts",
|
||||||
|
"./utils.js": "./src/utils.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mtcute/core": "workspace:^",
|
||||||
|
"@mtcute/wasm": "workspace:^",
|
||||||
|
"@mtcute/markdown-parser": "workspace:^",
|
||||||
|
"@mtcute/html-parser": "workspace:^",
|
||||||
|
"@db/sqlite": "npm:@jsr/db__sqlite@0.11.1",
|
||||||
|
"@std/io": "npm:@jsr/std__io@0.223.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mtcute/test": "workspace:^"
|
||||||
|
}
|
||||||
|
}
|
144
packages/deno/src/client.ts
Normal file
144
packages/deno/src/client.ts
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import { createInterface, Interface as RlInterface } from 'node:readline'
|
||||||
|
import { Readable, Writable } from 'node:stream'
|
||||||
|
|
||||||
|
import { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core'
|
||||||
|
import {
|
||||||
|
BaseTelegramClient as BaseTelegramClientBase,
|
||||||
|
BaseTelegramClientOptions as BaseTelegramClientOptionsBase,
|
||||||
|
TelegramClient as TelegramClientBase,
|
||||||
|
TelegramClientOptions,
|
||||||
|
} from '@mtcute/core/client.js'
|
||||||
|
import { setPlatform } from '@mtcute/core/platform.js'
|
||||||
|
|
||||||
|
import { downloadToFile } from './methods/download-file.js'
|
||||||
|
import { DenoPlatform } from './platform.js'
|
||||||
|
import { SqliteStorage } from './sqlite/index.js'
|
||||||
|
import { DenoCryptoProvider } from './utils/crypto.js'
|
||||||
|
import { TcpTransport } from './utils/tcp.js'
|
||||||
|
|
||||||
|
export type { TelegramClientOptions }
|
||||||
|
|
||||||
|
export interface BaseTelegramClientOptions
|
||||||
|
extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> {
|
||||||
|
/**
|
||||||
|
* Storage to use for this client.
|
||||||
|
*
|
||||||
|
* If a string is passed, it will be used as
|
||||||
|
* a name for an SQLite database file.
|
||||||
|
*
|
||||||
|
* @default `"client.session"`
|
||||||
|
*/
|
||||||
|
storage?: string | ITelegramStorageProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **ADVANCED USE ONLY**
|
||||||
|
*
|
||||||
|
* Whether to not set up the platform.
|
||||||
|
* This is useful if you call `setPlatform` yourself.
|
||||||
|
*/
|
||||||
|
platformless?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BaseTelegramClient extends BaseTelegramClientBase {
|
||||||
|
constructor(opts: BaseTelegramClientOptions) {
|
||||||
|
if (!opts.platformless) setPlatform(new DenoPlatform())
|
||||||
|
|
||||||
|
super({
|
||||||
|
crypto: new DenoCryptoProvider(),
|
||||||
|
transport: () => new TcpTransport(),
|
||||||
|
...opts,
|
||||||
|
storage:
|
||||||
|
typeof opts.storage === 'string' ?
|
||||||
|
new SqliteStorage(opts.storage) :
|
||||||
|
opts.storage ?? new SqliteStorage('client.session'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telegram client for use in Node.js
|
||||||
|
*/
|
||||||
|
export class TelegramClient extends TelegramClientBase {
|
||||||
|
constructor(opts: TelegramClientOptions) {
|
||||||
|
if ('client' in opts) {
|
||||||
|
super(opts)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
super({
|
||||||
|
client: new BaseTelegramClient(opts),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private _rl?: RlInterface
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny wrapper over Node `readline` package
|
||||||
|
* for simpler user input for `.run()` method.
|
||||||
|
*
|
||||||
|
* Associated `readline` interface is closed
|
||||||
|
* after `run()` returns, or with the client.
|
||||||
|
*
|
||||||
|
* @param text Text of the question
|
||||||
|
*/
|
||||||
|
input(text: string): Promise<string> {
|
||||||
|
if (!this._rl) {
|
||||||
|
this._rl = createInterface({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
input: Readable.fromWeb(Deno.stdin.readable as any),
|
||||||
|
output: Writable.fromWeb(Deno.stdout.writable),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((res) => this._rl?.question(text, res))
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): Promise<void> {
|
||||||
|
this._rl?.close()
|
||||||
|
|
||||||
|
return super.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
start(params: Parameters<TelegramClientBase['start']>[0] = {}): Promise<User> {
|
||||||
|
if (!params.botToken) {
|
||||||
|
if (!params.phone) params.phone = () => this.input('phone > ')
|
||||||
|
if (!params.code) params.code = () => this.input('code > ')
|
||||||
|
|
||||||
|
if (!params.password) {
|
||||||
|
params.password = () => this.input('2fa password > ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.start(params).then((user) => {
|
||||||
|
if (this._rl) {
|
||||||
|
this._rl.close()
|
||||||
|
delete this._rl
|
||||||
|
}
|
||||||
|
|
||||||
|
return user
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
run(
|
||||||
|
params: Parameters<TelegramClient['start']>[0] | ((user: User) => void | Promise<void>),
|
||||||
|
then?: (user: User) => void | Promise<void>,
|
||||||
|
): void {
|
||||||
|
if (typeof params === 'function') {
|
||||||
|
then = params
|
||||||
|
params = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.start(params)
|
||||||
|
.then(then)
|
||||||
|
.catch((err) => this.emitError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadToFile(
|
||||||
|
filename: string,
|
||||||
|
location: FileDownloadLocation,
|
||||||
|
params?: FileDownloadParameters | undefined,
|
||||||
|
): Promise<void> {
|
||||||
|
return downloadToFile(this, filename, location, params)
|
||||||
|
}
|
||||||
|
}
|
1
packages/deno/src/common-internals-web
Symbolic link
1
packages/deno/src/common-internals-web
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../web/src/common-internals-web
|
9
packages/deno/src/index.ts
Normal file
9
packages/deno/src/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export * from './client.js'
|
||||||
|
export * from './platform.js'
|
||||||
|
export * from './sqlite/index.js'
|
||||||
|
export * from './utils/crypto.js'
|
||||||
|
export * from './utils/tcp.js'
|
||||||
|
export * from './worker.js'
|
||||||
|
export * from '@mtcute/core'
|
||||||
|
export * from '@mtcute/html-parser'
|
||||||
|
export * from '@mtcute/markdown-parser'
|
2
packages/deno/src/methods.ts
Normal file
2
packages/deno/src/methods.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { downloadToFile } from './methods/download-file.js'
|
||||||
|
export * from '@mtcute/core/methods.js'
|
39
packages/deno/src/methods/download-file.ts
Normal file
39
packages/deno/src/methods/download-file.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { FileDownloadLocation, FileDownloadParameters, FileLocation, ITelegramClient } from '@mtcute/core'
|
||||||
|
import { downloadAsIterable } from '@mtcute/core/methods.js'
|
||||||
|
|
||||||
|
import { writeAll } from '@std/io/write-all'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a remote file to a local file (only for NodeJS).
|
||||||
|
* Promise will resolve once the download is complete.
|
||||||
|
*
|
||||||
|
* @param filename Local file name to which the remote file will be downloaded
|
||||||
|
* @param params File download parameters
|
||||||
|
*/
|
||||||
|
export async function downloadToFile(
|
||||||
|
client: ITelegramClient,
|
||||||
|
filename: string,
|
||||||
|
location: FileDownloadLocation,
|
||||||
|
params?: FileDownloadParameters,
|
||||||
|
): Promise<void> {
|
||||||
|
if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) {
|
||||||
|
// early return for inline files
|
||||||
|
await Deno.writeFile(filename, location.location)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fd = await Deno.open(filename, { write: true, create: true, truncate: true })
|
||||||
|
|
||||||
|
if (params?.abortSignal) {
|
||||||
|
params.abortSignal.addEventListener('abort', () => {
|
||||||
|
client.log.debug('aborting file download %s - cleaning up', filename)
|
||||||
|
fd.close()
|
||||||
|
Deno.removeSync(filename)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const chunk of downloadAsIterable(client, location, params)) {
|
||||||
|
await writeAll(fd, chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd.close()
|
||||||
|
}
|
48
packages/deno/src/platform.ts
Normal file
48
packages/deno/src/platform.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { ICorePlatform } from '@mtcute/core/platform.js'
|
||||||
|
|
||||||
|
import { base64Decode, base64Encode } from './common-internals-web/base64.js'
|
||||||
|
import { hexDecode, hexEncode } from './common-internals-web/hex.js'
|
||||||
|
import { defaultLoggingHandler } from './common-internals-web/logging.js'
|
||||||
|
import { utf8ByteLength, utf8Decode, utf8Encode } from './common-internals-web/utf8.js'
|
||||||
|
import { beforeExit } from './utils/exit-hook.js'
|
||||||
|
import { normalizeFile } from './utils/normalize-file.js'
|
||||||
|
|
||||||
|
export class DenoPlatform implements ICorePlatform {
|
||||||
|
declare log: typeof defaultLoggingHandler
|
||||||
|
declare beforeExit: typeof beforeExit
|
||||||
|
declare normalizeFile: typeof normalizeFile
|
||||||
|
|
||||||
|
getDeviceModel(): string {
|
||||||
|
return `Deno/${Deno.version.deno} (${Deno.build.os} ${Deno.build.arch})`
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultLogLevel(): number | null {
|
||||||
|
const envLogLevel = parseInt(Deno.env.get('MTCUTE_LOG_LEVEL') ?? '')
|
||||||
|
|
||||||
|
if (!isNaN(envLogLevel)) {
|
||||||
|
return envLogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITlPlatform
|
||||||
|
declare utf8ByteLength: typeof utf8ByteLength
|
||||||
|
declare utf8Encode: typeof utf8Encode
|
||||||
|
declare utf8Decode: typeof utf8Decode
|
||||||
|
declare hexEncode: typeof hexEncode
|
||||||
|
declare hexDecode: typeof hexDecode
|
||||||
|
declare base64Encode: typeof base64Encode
|
||||||
|
declare base64Decode: typeof base64Decode
|
||||||
|
}
|
||||||
|
|
||||||
|
DenoPlatform.prototype.utf8ByteLength = utf8ByteLength
|
||||||
|
DenoPlatform.prototype.utf8Encode = utf8Encode
|
||||||
|
DenoPlatform.prototype.utf8Decode = utf8Decode
|
||||||
|
DenoPlatform.prototype.hexEncode = hexEncode
|
||||||
|
DenoPlatform.prototype.hexDecode = hexDecode
|
||||||
|
DenoPlatform.prototype.base64Encode = base64Encode
|
||||||
|
DenoPlatform.prototype.base64Decode = base64Decode
|
||||||
|
DenoPlatform.prototype.log = defaultLoggingHandler
|
||||||
|
DenoPlatform.prototype.beforeExit = beforeExit
|
||||||
|
DenoPlatform.prototype.normalizeFile = normalizeFile
|
38
packages/deno/src/sqlite/driver.ts
Normal file
38
packages/deno/src/sqlite/driver.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { BaseSqliteStorageDriver, ISqliteDatabase } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { Database } from '@db/sqlite'
|
||||||
|
|
||||||
|
export interface SqliteStorageDriverOptions {
|
||||||
|
/**
|
||||||
|
* By default, WAL mode is enabled, which
|
||||||
|
* significantly improves performance.
|
||||||
|
* [Learn more](https://bun.sh/docs/api/sqlite#wal-mode)
|
||||||
|
*
|
||||||
|
* However, you might encounter some issues,
|
||||||
|
* and if you do, you can disable WAL by passing `true`
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
disableWal?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
||||||
|
constructor(
|
||||||
|
readonly filename = ':memory:',
|
||||||
|
readonly params?: SqliteStorageDriverOptions,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
_createDatabase(): ISqliteDatabase {
|
||||||
|
const db = new Database(this.filename, {
|
||||||
|
int64: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!this.params?.disableWal) {
|
||||||
|
db.exec('PRAGMA journal_mode = WAL;')
|
||||||
|
}
|
||||||
|
|
||||||
|
return db as ISqliteDatabase
|
||||||
|
}
|
||||||
|
}
|
14
packages/deno/src/sqlite/index.ts
Normal file
14
packages/deno/src/sqlite/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { BaseSqliteStorage } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { SqliteStorageDriver, SqliteStorageDriverOptions } from './driver.js'
|
||||||
|
|
||||||
|
export { SqliteStorageDriver } from './driver.js'
|
||||||
|
|
||||||
|
export class SqliteStorage extends BaseSqliteStorage {
|
||||||
|
constructor(
|
||||||
|
readonly filename = ':memory:',
|
||||||
|
readonly params?: SqliteStorageDriverOptions,
|
||||||
|
) {
|
||||||
|
super(new SqliteStorageDriver(filename, params))
|
||||||
|
}
|
||||||
|
}
|
31
packages/deno/src/sqlite/sqlite.test.ts
Normal file
31
packages/deno/src/sqlite/sqlite.test.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { afterAll, beforeAll, describe } from 'vitest'
|
||||||
|
|
||||||
|
import { LogManager } from '@mtcute/core/utils.js'
|
||||||
|
import {
|
||||||
|
testAuthKeysRepository,
|
||||||
|
testKeyValueRepository,
|
||||||
|
testPeersRepository,
|
||||||
|
testRefMessagesRepository,
|
||||||
|
} from '@mtcute/test'
|
||||||
|
|
||||||
|
if (import.meta.env.TEST_ENV === 'deno') {
|
||||||
|
const { SqliteStorage } = await import('./index.js')
|
||||||
|
|
||||||
|
describe('SqliteStorage', () => {
|
||||||
|
const storage = new SqliteStorage(':memory:')
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
storage.driver.setup(new LogManager())
|
||||||
|
await storage.driver.load()
|
||||||
|
})
|
||||||
|
|
||||||
|
testAuthKeysRepository(storage.authKeys)
|
||||||
|
testKeyValueRepository(storage.kv, storage.driver)
|
||||||
|
testPeersRepository(storage.peers, storage.driver)
|
||||||
|
testRefMessagesRepository(storage.refMessages, storage.driver)
|
||||||
|
|
||||||
|
afterAll(() => storage.driver.destroy())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
describe.skip('SqliteStorage', () => {})
|
||||||
|
}
|
1
packages/deno/src/utils.ts
Normal file
1
packages/deno/src/utils.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from '@mtcute/core/utils.js'
|
13
packages/deno/src/utils/crypto.test.ts
Normal file
13
packages/deno/src/utils/crypto.test.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { describe } from 'vitest'
|
||||||
|
|
||||||
|
import { testCryptoProvider } from '@mtcute/test'
|
||||||
|
|
||||||
|
if (import.meta.env.TEST_ENV === 'deno') {
|
||||||
|
describe('DenoCryptoProvider', async () => {
|
||||||
|
const { DenoCryptoProvider } = await import('./crypto.js')
|
||||||
|
|
||||||
|
testCryptoProvider(new DenoCryptoProvider())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
describe.skip('DenoCryptoProvider', () => {})
|
||||||
|
}
|
97
packages/deno/src/utils/crypto.ts
Normal file
97
packages/deno/src/utils/crypto.ts
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
|
import { Buffer } from 'node:buffer'
|
||||||
|
import { createCipheriv, createHash, createHmac, pbkdf2 } from 'node:crypto'
|
||||||
|
import { deflateSync, gunzipSync } from 'node:zlib'
|
||||||
|
|
||||||
|
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from '@mtcute/core/utils.js'
|
||||||
|
import { getWasmUrl, ige256Decrypt, ige256Encrypt, initSync } from '@mtcute/wasm'
|
||||||
|
// node:crypto is properly implemented in deno, so we can just use it
|
||||||
|
// largely just copy-pasting from @mtcute/node
|
||||||
|
|
||||||
|
const toUint8Array = (buf: Buffer | Uint8Array): Uint8Array => {
|
||||||
|
if (!Buffer.isBuffer(buf)) return buf
|
||||||
|
|
||||||
|
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DenoCryptoProvider extends BaseCryptoProvider implements ICryptoProvider {
|
||||||
|
async initialize(): Promise<void> {
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
|
const wasm = await fetch(getWasmUrl()).then((res) => res.arrayBuffer())
|
||||||
|
initSync(wasm)
|
||||||
|
}
|
||||||
|
|
||||||
|
createAesCtr(key: Uint8Array, iv: Uint8Array): IAesCtr {
|
||||||
|
const cipher = createCipheriv(`aes-${key.length * 8}-ctr`, key, iv)
|
||||||
|
|
||||||
|
const update = (data: Uint8Array) => toUint8Array(cipher.update(data))
|
||||||
|
|
||||||
|
return {
|
||||||
|
process: update,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pbkdf2(
|
||||||
|
password: Uint8Array,
|
||||||
|
salt: Uint8Array,
|
||||||
|
iterations: number,
|
||||||
|
keylen = 64,
|
||||||
|
algo = 'sha512',
|
||||||
|
): Promise<Uint8Array> {
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
pbkdf2(password, salt, iterations, keylen, algo, (err: Error | null, buf: Uint8Array) =>
|
||||||
|
err !== null ? reject(err) : resolve(toUint8Array(buf)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sha1(data: Uint8Array): Uint8Array {
|
||||||
|
return toUint8Array(createHash('sha1').update(data).digest())
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256(data: Uint8Array): Uint8Array {
|
||||||
|
return toUint8Array(createHash('sha256').update(data).digest())
|
||||||
|
}
|
||||||
|
|
||||||
|
hmacSha256(data: Uint8Array, key: Uint8Array): Uint8Array {
|
||||||
|
return toUint8Array(createHmac('sha256', key).update(data).digest())
|
||||||
|
}
|
||||||
|
|
||||||
|
createAesIge(key: Uint8Array, iv: Uint8Array): IEncryptionScheme {
|
||||||
|
return {
|
||||||
|
encrypt(data: Uint8Array): Uint8Array {
|
||||||
|
return ige256Encrypt(data, key, iv)
|
||||||
|
},
|
||||||
|
decrypt(data: Uint8Array): Uint8Array {
|
||||||
|
return ige256Decrypt(data, key, iv)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip(data: Uint8Array, maxSize: number): Uint8Array | null {
|
||||||
|
try {
|
||||||
|
// telegram accepts both zlib and gzip, but zlib is faster and has less overhead, so we use it here
|
||||||
|
return toUint8Array(
|
||||||
|
deflateSync(data, {
|
||||||
|
maxOutputLength: maxSize,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// hot path, avoid additional runtime checks
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e.code === 'ERR_BUFFER_TOO_LARGE') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gunzip(data: Uint8Array): Uint8Array {
|
||||||
|
return toUint8Array(gunzipSync(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
randomFill(buf: Uint8Array) {
|
||||||
|
crypto.getRandomValues(buf)
|
||||||
|
}
|
||||||
|
}
|
21
packages/deno/src/utils/exit-hook.ts
Normal file
21
packages/deno/src/utils/exit-hook.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
const callbacks = new Set<() => void>()
|
||||||
|
|
||||||
|
let registered = false
|
||||||
|
|
||||||
|
export function beforeExit(fn: () => void): () => void {
|
||||||
|
if (!registered) {
|
||||||
|
registered = true
|
||||||
|
|
||||||
|
window.addEventListener('unload', () => {
|
||||||
|
for (const callback of callbacks) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.add(fn)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
callbacks.delete(fn)
|
||||||
|
}
|
||||||
|
}
|
50
packages/deno/src/utils/normalize-file.ts
Normal file
50
packages/deno/src/utils/normalize-file.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { ReadStream } from 'node:fs'
|
||||||
|
import { stat } from 'node:fs/promises'
|
||||||
|
import { basename } from 'node:path'
|
||||||
|
import { Readable as NodeReadable } from 'node:stream'
|
||||||
|
|
||||||
|
import { UploadFileLike } from '@mtcute/core'
|
||||||
|
import { extractFileName } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
|
export async function normalizeFile(file: UploadFileLike) {
|
||||||
|
if (typeof file === 'string') {
|
||||||
|
const fd = await Deno.open(file, { read: true })
|
||||||
|
|
||||||
|
return {
|
||||||
|
file: fd.readable,
|
||||||
|
fileSize: (await fd.stat()).size,
|
||||||
|
fileName: extractFileName(file),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file instanceof Deno.FsFile) {
|
||||||
|
const stat = await file.stat()
|
||||||
|
|
||||||
|
return {
|
||||||
|
file: file.readable,
|
||||||
|
// https://github.com/denoland/deno/issues/23591
|
||||||
|
// fileName: ...,
|
||||||
|
fileSize: stat.size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// while these are not Deno-specific, they still may happen
|
||||||
|
if (file instanceof ReadStream) {
|
||||||
|
const fileName = basename(file.path.toString())
|
||||||
|
const fileSize = await stat(file.path.toString()).then((stat) => stat.size)
|
||||||
|
|
||||||
|
return {
|
||||||
|
file: NodeReadable.toWeb(file) as unknown as ReadableStream<Uint8Array>,
|
||||||
|
fileName,
|
||||||
|
fileSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file instanceof NodeReadable) {
|
||||||
|
return {
|
||||||
|
file: NodeReadable.toWeb(file) as unknown as ReadableStream<Uint8Array>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
135
packages/deno/src/utils/tcp.ts
Normal file
135
packages/deno/src/utils/tcp.ts
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import EventEmitter from 'events'
|
||||||
|
|
||||||
|
import { IntermediatePacketCodec, IPacketCodec, ITelegramTransport, MtcuteError, TransportState } from '@mtcute/core'
|
||||||
|
import { BasicDcOption, ICryptoProvider, Logger } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
|
import { writeAll } from '@std/io/write-all'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base for TCP transports.
|
||||||
|
* Subclasses must provide packet codec in `_packetCodec` property
|
||||||
|
*/
|
||||||
|
export abstract class BaseTcpTransport extends EventEmitter implements ITelegramTransport {
|
||||||
|
protected _currentDc: BasicDcOption | null = null
|
||||||
|
protected _state: TransportState = TransportState.Idle
|
||||||
|
protected _socket: Deno.TcpConn | null = null
|
||||||
|
|
||||||
|
abstract _packetCodec: IPacketCodec
|
||||||
|
protected _crypto!: ICryptoProvider
|
||||||
|
protected log!: Logger
|
||||||
|
|
||||||
|
packetCodecInitialized = false
|
||||||
|
|
||||||
|
private _updateLogPrefix() {
|
||||||
|
if (this._currentDc) {
|
||||||
|
this.log.prefix = `[TCP:${this._currentDc.ipAddress}:${this._currentDc.port}] `
|
||||||
|
} else {
|
||||||
|
this.log.prefix = '[TCP:disconnected] '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(crypto: ICryptoProvider, log: Logger): void {
|
||||||
|
this._crypto = crypto
|
||||||
|
this.log = log.create('tcp')
|
||||||
|
this._updateLogPrefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
state(): TransportState {
|
||||||
|
return this._state
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDc(): BasicDcOption | null {
|
||||||
|
return this._currentDc
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
connect(dc: BasicDcOption, testMode: boolean): void {
|
||||||
|
if (this._state !== TransportState.Idle) {
|
||||||
|
throw new MtcuteError('Transport is not IDLE')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.packetCodecInitialized) {
|
||||||
|
this._packetCodec.setup?.(this._crypto, this.log)
|
||||||
|
this._packetCodec.on('error', (err) => this.emit('error', err))
|
||||||
|
this._packetCodec.on('packet', (buf) => this.emit('message', buf))
|
||||||
|
this.packetCodecInitialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = TransportState.Connecting
|
||||||
|
this._currentDc = dc
|
||||||
|
this._updateLogPrefix()
|
||||||
|
|
||||||
|
this.log.debug('connecting to %j', dc)
|
||||||
|
|
||||||
|
Deno.connect({
|
||||||
|
hostname: dc.ipAddress,
|
||||||
|
port: dc.port,
|
||||||
|
transport: 'tcp',
|
||||||
|
})
|
||||||
|
.then(this.handleConnect.bind(this))
|
||||||
|
.catch((err) => {
|
||||||
|
this.handleError(err)
|
||||||
|
this.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
if (this._state === TransportState.Idle) return
|
||||||
|
this.log.info('connection closed')
|
||||||
|
|
||||||
|
this._state = TransportState.Idle
|
||||||
|
this._socket?.close()
|
||||||
|
this._socket = null
|
||||||
|
this._currentDc = null
|
||||||
|
this._packetCodec.reset()
|
||||||
|
this.emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError(error: unknown): void {
|
||||||
|
this.log.error('error: %s', error)
|
||||||
|
this.emit('error', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleConnect(socket: Deno.TcpConn): Promise<void> {
|
||||||
|
this._socket = socket
|
||||||
|
this.log.info('connected')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const packet = await this._packetCodec.tag()
|
||||||
|
|
||||||
|
if (packet.length) {
|
||||||
|
await writeAll(this._socket, packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = TransportState.Ready
|
||||||
|
this.emit('ready')
|
||||||
|
|
||||||
|
const reader = this._socket.readable.getReader()
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read()
|
||||||
|
if (done) break
|
||||||
|
|
||||||
|
this._packetCodec.feed(value)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.handleError(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(bytes: Uint8Array): Promise<void> {
|
||||||
|
const framed = await this._packetCodec.encode(bytes)
|
||||||
|
|
||||||
|
if (this._state !== TransportState.Ready) {
|
||||||
|
throw new MtcuteError('Transport is not READY')
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeAll(this._socket!, framed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TcpTransport extends BaseTcpTransport {
|
||||||
|
_packetCodec = new IntermediatePacketCodec()
|
||||||
|
}
|
71
packages/deno/src/worker.ts
Normal file
71
packages/deno/src/worker.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/// <reference lib="WebWorker" />
|
||||||
|
import { setPlatform } from '@mtcute/core/platform.js'
|
||||||
|
import {
|
||||||
|
ClientMessageHandler,
|
||||||
|
RespondFn,
|
||||||
|
SendFn,
|
||||||
|
SomeWorker,
|
||||||
|
TelegramWorker as TelegramWorkerBase,
|
||||||
|
TelegramWorkerOptions,
|
||||||
|
TelegramWorkerPort as TelegramWorkerPortBase,
|
||||||
|
TelegramWorkerPortOptions,
|
||||||
|
WorkerCustomMethods,
|
||||||
|
WorkerMessageHandler,
|
||||||
|
} from '@mtcute/core/worker.js'
|
||||||
|
|
||||||
|
import { DenoPlatform } from './platform.js'
|
||||||
|
|
||||||
|
export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods }
|
||||||
|
|
||||||
|
let _registered = false
|
||||||
|
|
||||||
|
export class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> {
|
||||||
|
registerWorker(handler: WorkerMessageHandler): RespondFn {
|
||||||
|
if (_registered) {
|
||||||
|
throw new Error('TelegramWorker must be created only once')
|
||||||
|
}
|
||||||
|
|
||||||
|
_registered = true
|
||||||
|
|
||||||
|
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
|
||||||
|
const respond: RespondFn = self.postMessage.bind(self)
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
self.addEventListener('message', (message) => handler((message as any).data, respond))
|
||||||
|
|
||||||
|
return respond
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('TelegramWorker must be created from a worker')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const platform = new DenoPlatform()
|
||||||
|
|
||||||
|
export class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
|
||||||
|
constructor(readonly options: TelegramWorkerPortOptions) {
|
||||||
|
setPlatform(platform)
|
||||||
|
super(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {
|
||||||
|
if (worker instanceof Worker) {
|
||||||
|
const send: SendFn = worker.postMessage.bind(worker)
|
||||||
|
|
||||||
|
const messageHandler = (ev: MessageEvent) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
|
handler(ev.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.addEventListener('message', messageHandler)
|
||||||
|
|
||||||
|
return [
|
||||||
|
send,
|
||||||
|
() => {
|
||||||
|
worker.removeEventListener('message', messageHandler)
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
throw new Error('Only workers are supported')
|
||||||
|
}
|
||||||
|
}
|
16
packages/deno/tsconfig.json
Normal file
16
packages/deno/tsconfig.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src",
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{ "path": "../core" },
|
||||||
|
{ "path": "../dispatcher" },
|
||||||
|
{ "path": "../html-parser" },
|
||||||
|
{ "path": "../markdown-parser" }
|
||||||
|
]
|
||||||
|
}
|
10
packages/deno/typedoc.cjs
Normal file
10
packages/deno/typedoc.cjs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: ['../../.config/typedoc/config.base.cjs'],
|
||||||
|
entryPoints: ['./src/index.ts'],
|
||||||
|
externalPattern: [
|
||||||
|
'../core/**',
|
||||||
|
'../html-parser/**',
|
||||||
|
'../markdown-parser/**',
|
||||||
|
'../sqlite/**',
|
||||||
|
],
|
||||||
|
}
|
|
@ -6,5 +6,8 @@
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src",
|
"./src",
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{ "path": "../core" },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class NodePlatform implements ICorePlatform {
|
||||||
declare normalizeFile: typeof normalizeFile
|
declare normalizeFile: typeof normalizeFile
|
||||||
|
|
||||||
getDeviceModel(): string {
|
getDeviceModel(): string {
|
||||||
return `${os.type()} ${os.arch()} ${os.release()}`
|
return `Node.js/${process.version} (${os.type()} ${os.arch()})`
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultLogLevel(): number | null {
|
getDefaultLogLevel(): number | null {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { readFile } from 'fs/promises'
|
||||||
import { createRequire } from 'module'
|
import { createRequire } from 'module'
|
||||||
import { deflateSync, gunzipSync } from 'zlib'
|
import { deflateSync, gunzipSync } from 'zlib'
|
||||||
|
|
||||||
import { MaybePromise } from '@mtcute/core'
|
|
||||||
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from '@mtcute/core/utils.js'
|
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from '@mtcute/core/utils.js'
|
||||||
import { ige256Decrypt, ige256Encrypt, initSync } from '@mtcute/wasm'
|
import { ige256Decrypt, ige256Encrypt, initSync } from '@mtcute/wasm'
|
||||||
|
|
||||||
|
@ -25,7 +24,7 @@ export abstract class BaseNodeCryptoProvider extends BaseCryptoProvider {
|
||||||
iterations: number,
|
iterations: number,
|
||||||
keylen = 64,
|
keylen = 64,
|
||||||
algo = 'sha512',
|
algo = 'sha512',
|
||||||
): MaybePromise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
pbkdf2(password, salt, iterations, keylen, algo, (err: Error | null, buf: Uint8Array) =>
|
pbkdf2(password, salt, iterations, keylen, algo, (err: Error | null, buf: Uint8Array) =>
|
||||||
err !== null ? reject(err) : resolve(buf),
|
err !== null ? reject(err) : resolve(buf),
|
||||||
|
|
2
packages/web/src/common-internals-web/readme.md
Normal file
2
packages/web/src/common-internals-web/readme.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
this folder is for common code across `@mtcute/web` and `@mtcute/deno`.
|
||||||
|
it is symlinked into `@mtcute/deno`
|
|
@ -9,7 +9,7 @@ export function utf8ByteLength(str: string) {
|
||||||
const code = str.charCodeAt(i)
|
const code = str.charCodeAt(i)
|
||||||
if (code > 0x7f && code <= 0x7ff) s++
|
if (code > 0x7f && code <= 0x7ff) s++
|
||||||
else if (code > 0x7ff && code <= 0xffff) s += 2
|
else if (code > 0x7ff && code <= 0xffff) s += 2
|
||||||
if (code >= 0xDC00 && code <= 0xDFFF) i-- //trail surrogate
|
if (code >= 0xdc00 && code <= 0xdfff) i-- //trail surrogate
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// <reference lib="dom" />
|
||||||
|
/// <reference lib="dom.iterable" />
|
||||||
import { BaseStorageDriver, MtUnsupportedError } from '@mtcute/core'
|
import { BaseStorageDriver, MtUnsupportedError } from '@mtcute/core'
|
||||||
|
|
||||||
import { txToPromise } from './utils.js'
|
import { txToPromise } from './utils.js'
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { ICorePlatform } from '@mtcute/core/platform.js'
|
import { ICorePlatform } from '@mtcute/core/platform.js'
|
||||||
|
|
||||||
import { base64Decode, base64Encode } from './encodings/base64.js'
|
import { base64Decode, base64Encode } from './common-internals-web/base64.js'
|
||||||
import { hexDecode, hexEncode } from './encodings/hex.js'
|
import { hexDecode, hexEncode } from './common-internals-web/hex.js'
|
||||||
import { utf8ByteLength, utf8Decode, utf8Encode } from './encodings/utf8.js'
|
import { defaultLoggingHandler } from './common-internals-web/logging.js'
|
||||||
|
import { utf8ByteLength, utf8Decode, utf8Encode } from './common-internals-web/utf8.js'
|
||||||
import { beforeExit } from './exit-hook.js'
|
import { beforeExit } from './exit-hook.js'
|
||||||
import { defaultLoggingHandler } from './logging.js'
|
|
||||||
|
|
||||||
export class WebPlatform implements ICorePlatform {
|
export class WebPlatform implements ICorePlatform {
|
||||||
// ICorePlatform
|
// ICorePlatform
|
||||||
|
@ -52,7 +52,6 @@ export class WebPlatform implements ICorePlatform {
|
||||||
declare utf8Decode: typeof utf8Decode
|
declare utf8Decode: typeof utf8Decode
|
||||||
declare hexEncode: typeof hexEncode
|
declare hexEncode: typeof hexEncode
|
||||||
declare hexDecode: typeof hexDecode
|
declare hexDecode: typeof hexDecode
|
||||||
|
|
||||||
declare base64Encode: typeof base64Encode
|
declare base64Encode: typeof base64Encode
|
||||||
declare base64Decode: typeof base64Decode
|
declare base64Decode: typeof base64Decode
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// <reference lib="dom" />
|
||||||
|
/// <reference lib="webworker" />
|
||||||
import { setPlatform } from '@mtcute/core/platform.js'
|
import { setPlatform } from '@mtcute/core/platform.js'
|
||||||
import {
|
import {
|
||||||
ClientMessageHandler,
|
ClientMessageHandler,
|
||||||
|
|
105
pnpm-lock.yaml
105
pnpm-lock.yaml
|
@ -231,6 +231,31 @@ importers:
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../test
|
version: link:../test
|
||||||
|
|
||||||
|
packages/deno:
|
||||||
|
dependencies:
|
||||||
|
'@db/sqlite':
|
||||||
|
specifier: npm:@jsr/db__sqlite@0.11.1
|
||||||
|
version: /@jsr/db__sqlite@0.11.1
|
||||||
|
'@mtcute/core':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../core
|
||||||
|
'@mtcute/html-parser':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../html-parser
|
||||||
|
'@mtcute/markdown-parser':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../markdown-parser
|
||||||
|
'@mtcute/wasm':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../wasm
|
||||||
|
'@std/io':
|
||||||
|
specifier: npm:@jsr/std__io@0.223.0
|
||||||
|
version: /@jsr/std__io@0.223.0
|
||||||
|
devDependencies:
|
||||||
|
'@mtcute/test':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../test
|
||||||
|
|
||||||
packages/dispatcher:
|
packages/dispatcher:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@mtcute/core':
|
'@mtcute/core':
|
||||||
|
@ -1189,6 +1214,86 @@ packages:
|
||||||
'@jridgewell/sourcemap-codec': 1.4.11
|
'@jridgewell/sourcemap-codec': 1.4.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@jsr/db__sqlite@0.11.1:
|
||||||
|
resolution: {integrity: sha512-6IKyfi+TQan431kwOy3WrdzgKwITuDdSKfq6nkWINfNWknPQ+lQ6/R008bAUUz+AwW8e0prxi3IMMxzELUV8Lw==, tarball: https://npm.jsr.io/~/8/@jsr/db__sqlite/0.11.1.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/denosaurs__plug': 1.0.6
|
||||||
|
'@jsr/std__path': 0.217.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/denosaurs__plug@1.0.6:
|
||||||
|
resolution: {integrity: sha512-2uqvX2xpDy5W76jJVKazXvHuh5WPNg8eUV+2u+Hcn5XLwKqWGr/xj4wQFRMXrS12Xhya+ToZdUg4gxLh+XOOCg==, tarball: https://npm.jsr.io/~/8/@jsr/denosaurs__plug/1.0.6.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__encoding': 0.221.0
|
||||||
|
'@jsr/std__fmt': 0.221.0
|
||||||
|
'@jsr/std__fs': 0.221.0
|
||||||
|
'@jsr/std__path': 0.221.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__assert@0.217.0:
|
||||||
|
resolution: {integrity: sha512-RCQbXJeUVCgDGEPsrO57CI9Cgbo9NAWsJUhZ7vrHgtD//Ic32YmUQazdGKPZzao5Zn8dP6xV4Nma3HHZC5ySTw==, tarball: https://npm.jsr.io/~/8/@jsr/std__assert/0.217.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__fmt': 0.217.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__assert@0.221.0:
|
||||||
|
resolution: {integrity: sha512-2B+5fq4Rar8NmLms7sv9YfYlMukZDTNMQV5fXjtZvnaKc8ljt+59UsMtIjTXFsDwsAx7VoxkMKmmdHxlP+h5JA==, tarball: https://npm.jsr.io/~/8/@jsr/std__assert/0.221.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__fmt': 0.221.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__assert@0.223.0:
|
||||||
|
resolution: {integrity: sha512-9FWOoAQN1uF5SliWw3IgdXmk2usz5txvausX4sLAASHfQMbUSCe1akcD7HgFV01J/2Mr9TfCjPvsSUzuuASouQ==, tarball: https://npm.jsr.io/~/8/@jsr/std__assert/0.223.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__fmt': 0.223.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__bytes@0.223.0:
|
||||||
|
resolution: {integrity: sha512-BBjhj0uFlB3+AVEmaPygEwY5CL5mj3vSZlusC8xxjCRNWDYGukfQT/F5GOTTfjeaq7njduk7TYe6e5cDg659yg==, tarball: https://npm.jsr.io/~/8/@jsr/std__bytes/0.223.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__encoding@0.221.0:
|
||||||
|
resolution: {integrity: sha512-FT5i/WHNtXJvqOITDK0eOVIyyOphqtxwhzo5PiVWoYTFmUuFcRYKas39GT1UQDi4s24FcHd2deQEBbi3tPAj1Q==, tarball: https://npm.jsr.io/~/8/@jsr/std__encoding/0.221.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__fmt@0.217.0:
|
||||||
|
resolution: {integrity: sha512-L3mVYP7DsujrJ001SvPr4Fl/Fu0e3uzgHJ6NYTRUk7sgi9k7YKeLOLVwRijUX7qIsp3Ourp2DyAHHgYDgT4GcQ==, tarball: https://npm.jsr.io/~/8/@jsr/std__fmt/0.217.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__fmt@0.221.0:
|
||||||
|
resolution: {integrity: sha512-VLqM052U78LQ11p/KfqI49a2/sDbKtHFHuxO/h+3Cnvhze9beIZU4Lg3Gpu8rGYjB2YS6CfXzKXHuyAJn5FJFg==, tarball: https://npm.jsr.io/~/8/@jsr/std__fmt/0.221.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__fmt@0.223.0:
|
||||||
|
resolution: {integrity: sha512-J6SVTw/l3C4hOwEuqnZ4ZHD1jVIIZt09fb5LP9CMGyVGNnoW8/lxJvCNhIOv+3ZXC1ErGlIzW4bgYSxHwbvSaQ==, tarball: https://npm.jsr.io/~/8/@jsr/std__fmt/0.223.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__fs@0.221.0:
|
||||||
|
resolution: {integrity: sha512-2XMlO67zQlKoxbCsfGOBVlnyWhMMdOzYUWfajvggfw2p+yITd9hJj9+tpfiwLf/88CzknhlMLwSCamSYjHKloA==, tarball: https://npm.jsr.io/~/8/@jsr/std__fs/0.221.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__assert': 0.221.0
|
||||||
|
'@jsr/std__path': 0.221.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__io@0.223.0:
|
||||||
|
resolution: {integrity: sha512-K+OXJHsIf9227aYgNTaapEkpphHrI+oYVkl14UV+le+Fk9MzkJmebU0XAU6krgVS283mW7VPJsXVV3gD5JWvJw==, tarball: https://npm.jsr.io/~/8/@jsr/std__io/0.223.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__assert': 0.223.0
|
||||||
|
'@jsr/std__bytes': 0.223.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__path@0.217.0:
|
||||||
|
resolution: {integrity: sha512-OkP+yiBJpFZKTH3gHqlepYU3TQzXM/UjEQ0U1gYw8BwVr87TwKfzwAb1WT1vY/Bs8NXScvuP4Kpu/UhEsNHD3A==, tarball: https://npm.jsr.io/~/8/@jsr/std__path/0.217.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__assert': 0.217.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@jsr/std__path@0.221.0:
|
||||||
|
resolution: {integrity: sha512-uOWaY4cWp28CFBSisr8M/92FtpyjiFO0+wQSH7GgmiXQUls+vALqdCGewFkunG8RfA/25RGdot5hFXedmtPdOg==, tarball: https://npm.jsr.io/~/8/@jsr/std__path/0.221.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@jsr/std__assert': 0.221.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@ljharb/through@2.3.11:
|
/@ljharb/through@2.3.11:
|
||||||
resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==}
|
resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
|
@ -499,6 +499,10 @@ if (IS_JSR) {
|
||||||
for (const [name, version] of Object.entries(builtPkgJson.dependencies)) {
|
for (const [name, version] of Object.entries(builtPkgJson.dependencies)) {
|
||||||
if (name.startsWith('@mtcute/')) {
|
if (name.startsWith('@mtcute/')) {
|
||||||
importMap[name] = `jsr:${name}@${version}`
|
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}`
|
importMap[name] = `npm:${name}@${version}`
|
||||||
}
|
}
|
||||||
|
|
33
scripts/fetch-deno-dts.mjs
Normal file
33
scripts/fetch-deno-dts.mjs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { createHash } from 'crypto'
|
||||||
|
import * as fs from 'fs/promises'
|
||||||
|
import { dirname } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
const DTS_URL = 'https://github.com/denoland/deno/releases/download/v1.42.4/lib.deno.d.ts'
|
||||||
|
const SHA256 = '554b5da7baf05e5693ca064fcf1665b0b847743ccfd0db89cb6f2388f2de0276'
|
||||||
|
const LIB_TARGET = fileURLToPath(new URL('../node_modules/@types/deno/index.d.ts', import.meta.url))
|
||||||
|
|
||||||
|
const stat = await fs.stat(LIB_TARGET).catch(() => null)
|
||||||
|
|
||||||
|
if (stat?.isFile()) {
|
||||||
|
const sha256 = createHash('sha256').update(await fs.readFile(LIB_TARGET)).digest('hex')
|
||||||
|
|
||||||
|
if (sha256 === SHA256) {
|
||||||
|
console.log('lib.deno.d.ts is up to date')
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream = await fetch(DTS_URL)
|
||||||
|
const dts = await stream.text()
|
||||||
|
|
||||||
|
const sha256 = createHash('sha256').update(dts).digest('hex')
|
||||||
|
|
||||||
|
if (sha256 !== SHA256) {
|
||||||
|
console.error(`lib.deno.d.ts SHA256 mismatch: expected ${SHA256}, got ${sha256}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.mkdir(dirname(LIB_TARGET), { recursive: true }).catch(() => null)
|
||||||
|
await fs.writeFile(LIB_TARGET, dts)
|
||||||
|
console.log('lib.deno.d.ts updated')
|
|
@ -25,6 +25,7 @@ const JSR_EXCEPTIONS = {
|
||||||
bun: 'never',
|
bun: 'never',
|
||||||
'create-bot': 'never',
|
'create-bot': 'never',
|
||||||
'crypto-node': 'never',
|
'crypto-node': 'never',
|
||||||
|
deno: 'only',
|
||||||
node: 'never',
|
node: 'never',
|
||||||
'http-proxy': 'never',
|
'http-proxy': 'never',
|
||||||
'socks-proxy': 'never',
|
'socks-proxy': 'never',
|
||||||
|
|
23
scripts/remove-jsr-sourcefiles.mjs
Normal file
23
scripts/remove-jsr-sourcefiles.mjs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import { globSync } from 'glob'
|
||||||
|
import { join } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
// for whatever reason, jsr's npm compatibility jayer doesn't remove
|
||||||
|
// original typescript source files, which results in type errors when
|
||||||
|
// trying to build the project. this script removes all source files from @jsr/*
|
||||||
|
// https://discord.com/channels/684898665143206084/1203185670508515399/1234222204044967967
|
||||||
|
|
||||||
|
const nodeModules = fileURLToPath(new URL('../node_modules', import.meta.url))
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
for (const file of globSync(join(nodeModules, '.pnpm/**/node_modules/@jsr/**/*.ts'))) {
|
||||||
|
if (file.endsWith('.d.ts')) continue
|
||||||
|
if (!fs.existsSync(file)) continue
|
||||||
|
|
||||||
|
fs.unlinkSync(file)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[jsr] removed ${count} source files`)
|
|
@ -19,6 +19,7 @@
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"types": [
|
"types": [
|
||||||
"node",
|
"node",
|
||||||
|
"deno",
|
||||||
"vite/client"
|
"vite/client"
|
||||||
],
|
],
|
||||||
"lib": [
|
"lib": [
|
||||||
|
|
Loading…
Reference in a new issue