@mtcute/bun 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +25 -0
  3. package/client.d.ts +47 -0
  4. package/client.js +93 -0
  5. package/client.js.map +1 -0
  6. package/common-internals-node/exit-hook.d.ts +1 -0
  7. package/common-internals-node/exit-hook.js +41 -0
  8. package/common-internals-node/exit-hook.js.map +1 -0
  9. package/common-internals-node/logging.d.ts +1 -0
  10. package/common-internals-node/logging.js +31 -0
  11. package/common-internals-node/logging.js.map +1 -0
  12. package/common-internals-node/platform.d.ts +18 -0
  13. package/common-internals-node/platform.js +58 -0
  14. package/common-internals-node/platform.js.map +1 -0
  15. package/index.d.ts +9 -0
  16. package/index.js +10 -0
  17. package/index.js.map +1 -0
  18. package/methods/download-file.d.ts +9 -0
  19. package/methods/download-file.js +29 -0
  20. package/methods/download-file.js.map +1 -0
  21. package/methods/download-node-stream.d.ts +10 -0
  22. package/methods/download-node-stream.js +13 -0
  23. package/methods/download-node-stream.js.map +1 -0
  24. package/methods.d.ts +3 -0
  25. package/methods.js +4 -0
  26. package/methods.js.map +1 -0
  27. package/package.json +21 -0
  28. package/sqlite/driver.d.ts +20 -0
  29. package/sqlite/driver.js +17 -0
  30. package/sqlite/driver.js.map +1 -0
  31. package/sqlite/index.d.ts +8 -0
  32. package/sqlite/index.js +11 -0
  33. package/sqlite/index.js.map +1 -0
  34. package/utils/crypto.d.ts +13 -0
  35. package/utils/crypto.js +76 -0
  36. package/utils/crypto.js.map +1 -0
  37. package/utils/normalize-file.d.ts +10 -0
  38. package/utils/normalize-file.js +27 -0
  39. package/utils/normalize-file.js.map +1 -0
  40. package/utils/tcp.d.ts +31 -0
  41. package/utils/tcp.js +109 -0
  42. package/utils/tcp.js.map +1 -0
  43. package/utils.d.ts +1 -0
  44. package/utils.js +2 -0
  45. package/utils.js.map +1 -0
  46. package/worker.d.ts +10 -0
  47. package/worker.js +42 -0
  48. package/worker.js.map +1 -0
package/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright 2023 alina sireneva
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @mtcute/bun
2
+
3
+ 📖 [API Reference](https://ref.mtcute.dev/modules/_mtcute_node.html)
4
+
5
+ ‼️ **Experimental** Bun support package for mtcute. Includes:
6
+ - SQLite storage (based on `bun:sqlite`)
7
+ - TCP transport (based on Bun-native APIs)
8
+ - `TelegramClient` implementation using the above
9
+ - HTML and Markdown parsers
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { TelegramClient } from '@mtcute/bun'
15
+
16
+ const tg = new TelegramClient({
17
+ apiId: 12345,
18
+ apiHash: 'abcdef',
19
+ storage: 'my-account'
20
+ })
21
+
22
+ tg.run(async (user) => {
23
+ console.log(`✨ logged in as ${user.displayName}`)
24
+ })
25
+ ```
package/client.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core';
3
+ import { BaseTelegramClient as BaseTelegramClientBase, BaseTelegramClientOptions as BaseTelegramClientOptionsBase, TelegramClient as TelegramClientBase, TelegramClientOptions } from '@mtcute/core/client.js';
4
+ export type { TelegramClientOptions };
5
+ export interface BaseTelegramClientOptions extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> {
6
+ /**
7
+ * Storage to use for this client.
8
+ *
9
+ * If a string is passed, it will be used as
10
+ * a name for an SQLite database file.
11
+ *
12
+ * @default `"client.session"`
13
+ */
14
+ storage?: string | ITelegramStorageProvider;
15
+ /**
16
+ * **ADVANCED USE ONLY**
17
+ *
18
+ * Whether to not set up the platform.
19
+ * This is useful if you call `setPlatform` yourself.
20
+ */
21
+ platformless?: boolean;
22
+ }
23
+ export declare class BaseTelegramClient extends BaseTelegramClientBase {
24
+ constructor(opts: BaseTelegramClientOptions);
25
+ }
26
+ /**
27
+ * Telegram client for use in Node.js
28
+ */
29
+ export declare class TelegramClient extends TelegramClientBase {
30
+ constructor(opts: TelegramClientOptions);
31
+ private _rl?;
32
+ /**
33
+ * Tiny wrapper over Node `readline` package
34
+ * for simpler user input for `.run()` method.
35
+ *
36
+ * Associated `readline` interface is closed
37
+ * after `run()` returns, or with the client.
38
+ *
39
+ * @param text Text of the question
40
+ */
41
+ input(text: string): Promise<string>;
42
+ close(): Promise<void>;
43
+ start(params?: Parameters<TelegramClientBase['start']>[0]): Promise<User>;
44
+ run(params: Parameters<TelegramClient['start']>[0] | ((user: User) => void | Promise<void>), then?: (user: User) => void | Promise<void>): void;
45
+ downloadToFile(filename: string, location: FileDownloadLocation, params?: FileDownloadParameters | undefined): Promise<void>;
46
+ downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters | undefined): import("stream").Readable;
47
+ }
package/client.js ADDED
@@ -0,0 +1,93 @@
1
+ import { createInterface } from 'readline';
2
+ import { BaseTelegramClient as BaseTelegramClientBase, TelegramClient as TelegramClientBase, } from '@mtcute/core/client.js';
3
+ import { setPlatform } from '@mtcute/core/platform.js';
4
+ import { NodePlatform } from './common-internals-node/platform.js';
5
+ import { downloadToFile } from './methods/download-file.js';
6
+ import { downloadAsNodeStream } from './methods/download-node-stream.js';
7
+ import { SqliteStorage } from './sqlite/index.js';
8
+ import { BunCryptoProvider } from './utils/crypto.js';
9
+ import { TcpTransport } from './utils/tcp.js';
10
+ export class BaseTelegramClient extends BaseTelegramClientBase {
11
+ constructor(opts) {
12
+ if (!opts.platformless)
13
+ setPlatform(new NodePlatform());
14
+ super({
15
+ crypto: new BunCryptoProvider(),
16
+ transport: () => new TcpTransport(),
17
+ ...opts,
18
+ storage: typeof opts.storage === 'string' ?
19
+ new SqliteStorage(opts.storage) :
20
+ opts.storage ?? new SqliteStorage('client.session'),
21
+ });
22
+ }
23
+ }
24
+ /**
25
+ * Telegram client for use in Node.js
26
+ */
27
+ export class TelegramClient extends TelegramClientBase {
28
+ constructor(opts) {
29
+ if ('client' in opts) {
30
+ super(opts);
31
+ return;
32
+ }
33
+ super({
34
+ client: new BaseTelegramClient(opts),
35
+ });
36
+ }
37
+ /**
38
+ * Tiny wrapper over Node `readline` package
39
+ * for simpler user input for `.run()` method.
40
+ *
41
+ * Associated `readline` interface is closed
42
+ * after `run()` returns, or with the client.
43
+ *
44
+ * @param text Text of the question
45
+ */
46
+ input(text) {
47
+ if (!this._rl) {
48
+ this._rl = createInterface({
49
+ input: process.stdin,
50
+ output: process.stdout,
51
+ });
52
+ }
53
+ return new Promise((res) => this._rl?.question(text, res));
54
+ }
55
+ close() {
56
+ this._rl?.close();
57
+ return super.close();
58
+ }
59
+ start(params = {}) {
60
+ if (!params.botToken) {
61
+ if (!params.phone)
62
+ params.phone = () => this.input('phone > ');
63
+ if (!params.code)
64
+ params.code = () => this.input('code > ');
65
+ if (!params.password) {
66
+ params.password = () => this.input('2fa password > ');
67
+ }
68
+ }
69
+ return super.start(params).then((user) => {
70
+ if (this._rl) {
71
+ this._rl.close();
72
+ delete this._rl;
73
+ }
74
+ return user;
75
+ });
76
+ }
77
+ run(params, then) {
78
+ if (typeof params === 'function') {
79
+ then = params;
80
+ params = {};
81
+ }
82
+ this.start(params)
83
+ .then(then)
84
+ .catch((err) => this.emitError(err));
85
+ }
86
+ downloadToFile(filename, location, params) {
87
+ return downloadToFile(this, filename, location, params);
88
+ }
89
+ downloadAsNodeStream(location, params) {
90
+ return downloadAsNodeStream(this, location, params);
91
+ }
92
+ }
93
+ //# sourceMappingURL=client.js.map
package/client.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { createInterface, Interface as RlInterface } from 'readline'\n\nimport { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core'\nimport {\n BaseTelegramClient as BaseTelegramClientBase,\n BaseTelegramClientOptions as BaseTelegramClientOptionsBase,\n TelegramClient as TelegramClientBase,\n TelegramClientOptions,\n} from '@mtcute/core/client.js'\nimport { setPlatform } from '@mtcute/core/platform.js'\n\nimport { NodePlatform } from './common-internals-node/platform.js'\nimport { downloadToFile } from './methods/download-file.js'\nimport { downloadAsNodeStream } from './methods/download-node-stream.js'\nimport { SqliteStorage } from './sqlite/index.js'\nimport { BunCryptoProvider } from './utils/crypto.js'\nimport { TcpTransport } from './utils/tcp.js'\n\nexport type { TelegramClientOptions }\n\nexport interface BaseTelegramClientOptions\n extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> {\n /**\n * Storage to use for this client.\n *\n * If a string is passed, it will be used as\n * a name for an SQLite database file.\n *\n * @default `\"client.session\"`\n */\n storage?: string | ITelegramStorageProvider\n\n /**\n * **ADVANCED USE ONLY**\n *\n * Whether to not set up the platform.\n * This is useful if you call `setPlatform` yourself.\n */\n platformless?: boolean\n}\n\nexport class BaseTelegramClient extends BaseTelegramClientBase {\n constructor(opts: BaseTelegramClientOptions) {\n if (!opts.platformless) setPlatform(new NodePlatform())\n\n super({\n crypto: new BunCryptoProvider(),\n transport: () => new TcpTransport(),\n ...opts,\n storage:\n typeof opts.storage === 'string' ?\n new SqliteStorage(opts.storage) :\n opts.storage ?? new SqliteStorage('client.session'),\n })\n }\n}\n\n/**\n * Telegram client for use in Node.js\n */\nexport class TelegramClient extends TelegramClientBase {\n constructor(opts: TelegramClientOptions) {\n if ('client' in opts) {\n super(opts)\n\n return\n }\n\n super({\n client: new BaseTelegramClient(opts),\n })\n }\n\n private _rl?: RlInterface\n\n /**\n * Tiny wrapper over Node `readline` package\n * for simpler user input for `.run()` method.\n *\n * Associated `readline` interface is closed\n * after `run()` returns, or with the client.\n *\n * @param text Text of the question\n */\n input(text: string): Promise<string> {\n if (!this._rl) {\n this._rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n }\n\n return new Promise((res) => this._rl?.question(text, res))\n }\n\n close(): Promise<void> {\n this._rl?.close()\n\n return super.close()\n }\n\n start(params: Parameters<TelegramClientBase['start']>[0] = {}): Promise<User> {\n if (!params.botToken) {\n if (!params.phone) params.phone = () => this.input('phone > ')\n if (!params.code) params.code = () => this.input('code > ')\n\n if (!params.password) {\n params.password = () => this.input('2fa password > ')\n }\n }\n\n return super.start(params).then((user) => {\n if (this._rl) {\n this._rl.close()\n delete this._rl\n }\n\n return user\n })\n }\n\n run(\n params: Parameters<TelegramClient['start']>[0] | ((user: User) => void | Promise<void>),\n then?: (user: User) => void | Promise<void>,\n ): void {\n if (typeof params === 'function') {\n then = params\n params = {}\n }\n\n this.start(params)\n .then(then)\n .catch((err) => this.emitError(err))\n }\n\n downloadToFile(\n filename: string,\n location: FileDownloadLocation,\n params?: FileDownloadParameters | undefined,\n ): Promise<void> {\n return downloadToFile(this, filename, location, params)\n }\n\n downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters | undefined) {\n return downloadAsNodeStream(this, location, params)\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function beforeExit(fn: () => void): () => void;
@@ -0,0 +1,41 @@
1
+ // roughly based on https://github.com/sindresorhus/exit-hook/blob/main/index.js, MIT license
2
+ let installed = false;
3
+ let handled = false;
4
+ const callbacks = new Set();
5
+ function exit(shouldManuallyExit, signal, event) {
6
+ return function eventHandler() {
7
+ if (handled) {
8
+ return;
9
+ }
10
+ handled = true;
11
+ const exitCode = 128 + signal;
12
+ for (const callback of callbacks) {
13
+ callback();
14
+ }
15
+ if (shouldManuallyExit) {
16
+ // if the user has some custom handlers after us, we don't want to exit the process
17
+ const listeners = process.rawListeners(event);
18
+ const idx = listeners.indexOf(eventHandler);
19
+ if (idx === listeners.length - 1) {
20
+ process.exit(exitCode);
21
+ }
22
+ }
23
+ };
24
+ }
25
+ export function beforeExit(fn) {
26
+ // unsupported platform
27
+ if (typeof process === 'undefined')
28
+ return () => { };
29
+ if (!installed) {
30
+ installed = true;
31
+ process.on('beforeExit', exit(true, -128, 'beforeExit'));
32
+ process.on('SIGINT', exit(true, 2, 'SIGINT'));
33
+ process.on('SIGTERM', exit(true, 15, 'SIGINT'));
34
+ process.on('exit', exit(false, 15, 'exit'));
35
+ }
36
+ callbacks.add(fn);
37
+ return () => {
38
+ callbacks.delete(fn);
39
+ };
40
+ }
41
+ //# sourceMappingURL=exit-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exit-hook.js","sourceRoot":"","sources":["../../src/common-internals-node/exit-hook.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAE7F,IAAI,SAAS,GAAG,KAAK,CAAA;AACrB,IAAI,OAAO,GAAG,KAAK,CAAA;AAEnB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;AAEvC,SAAS,IAAI,CAAC,kBAA2B,EAAE,MAAc,EAAE,KAAa;IACpE,OAAO,SAAS,YAAY;QACxB,IAAI,OAAO,EAAE;YACT,OAAM;SACT;QAED,OAAO,GAAG,IAAI,CAAA;QAEd,MAAM,QAAQ,GAAG,GAAG,GAAG,MAAM,CAAA;QAE7B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;YAC9B,QAAQ,EAAE,CAAA;SACb;QAED,IAAI,kBAAkB,EAAE;YACpB,mFAAmF;YAEnF,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;YAC7C,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;YAE3C,IAAI,GAAG,KAAK,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC9B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;aACzB;SACJ;IACL,CAAC,CAAA;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAc;IACrC,uBAAuB;IACvB,IAAI,OAAO,OAAO,KAAK,WAAW;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IAEnD,IAAI,CAAC,SAAS,EAAE;QACZ,SAAS,GAAG,IAAI,CAAA;QAEhB,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAA;QACxD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC7C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC/C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAA;KAC9C;IAED,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAEjB,OAAO,GAAG,EAAE;QACR,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACxB,CAAC,CAAA;AACL,CAAC","sourcesContent":["// roughly based on https://github.com/sindresorhus/exit-hook/blob/main/index.js, MIT license\n\nlet installed = false\nlet handled = false\n\nconst callbacks = new Set<() => void>()\n\nfunction exit(shouldManuallyExit: boolean, signal: number, event: string) {\n return function eventHandler() {\n if (handled) {\n return\n }\n\n handled = true\n\n const exitCode = 128 + signal\n\n for (const callback of callbacks) {\n callback()\n }\n\n if (shouldManuallyExit) {\n // if the user has some custom handlers after us, we don't want to exit the process\n\n const listeners = process.rawListeners(event)\n const idx = listeners.indexOf(eventHandler)\n\n if (idx === listeners.length - 1) {\n process.exit(exitCode)\n }\n }\n }\n}\n\nexport function beforeExit(fn: () => void): () => void {\n // unsupported platform\n if (typeof process === 'undefined') return () => {}\n\n if (!installed) {\n installed = true\n\n process.on('beforeExit', exit(true, -128, 'beforeExit'))\n process.on('SIGINT', exit(true, 2, 'SIGINT'))\n process.on('SIGTERM', exit(true, 15, 'SIGINT'))\n process.on('exit', exit(false, 15, 'exit'))\n }\n\n callbacks.add(fn)\n\n return () => {\n callbacks.delete(fn)\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ const isTty = typeof process === 'object' && Boolean(process.stdout?.isTTY);
2
+ const BASE_FORMAT = isTty ? '%s [%s] [%s%s\x1b[0m] ' : '%s [%s] [%s] ';
3
+ const LEVEL_NAMES = isTty ?
4
+ [
5
+ '',
6
+ '\x1b[31mERR\x1b[0m',
7
+ '\x1b[33mWRN\x1b[0m',
8
+ '\x1b[34mINF\x1b[0m',
9
+ '\x1b[36mDBG\x1b[0m',
10
+ '\x1b[35mVRB\x1b[0m',
11
+ ] :
12
+ [
13
+ '',
14
+ 'ERR',
15
+ 'WRN',
16
+ 'INF',
17
+ 'DBG',
18
+ 'VRB',
19
+ ];
20
+ const TAG_COLORS = [6, 2, 3, 4, 5, 1].map((i) => `\x1b[3${i};1m`);
21
+ /** @internal */
22
+ export const defaultLoggingHandler = isTty ?
23
+ (color, level, tag, fmt, args) => {
24
+ // eslint-disable-next-line no-console
25
+ console.log(BASE_FORMAT + fmt, new Date().toISOString(), LEVEL_NAMES[level], TAG_COLORS[color], tag, ...args);
26
+ } :
27
+ (color, level, tag, fmt, args) => {
28
+ // eslint-disable-next-line no-console
29
+ console.log(BASE_FORMAT + fmt, new Date().toISOString(), LEVEL_NAMES[level], tag, ...args);
30
+ };
31
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/common-internals-node/logging.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAE3E,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,eAAe,CAAA;AACtE,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC;IACvB;QACI,EAAE;QACF,oBAAoB;QACpB,oBAAoB;QACpB,oBAAoB;QACpB,oBAAoB;QACpB,oBAAoB;KACvB,CAAC,CAAC;IACH;QACI,EAAE;QACF,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;KACR,CAAA;AACL,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAEjE,gBAAgB;AAChB,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC,CAAC;IACxC,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW,EAAE,GAAW,EAAE,IAAe,EAAQ,EAAE;QAC9E,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACjH,CAAC,CAAC,CAAC;IACH,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW,EAAE,GAAW,EAAE,IAAe,EAAQ,EAAE;QAC9E,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAC9F,CAAC,CAAA","sourcesContent":["const isTty = typeof process === 'object' && Boolean(process.stdout?.isTTY)\n\nconst BASE_FORMAT = isTty ? '%s [%s] [%s%s\\x1b[0m] ' : '%s [%s] [%s] '\nconst LEVEL_NAMES = isTty ?\n [\n '', // OFF\n '\\x1b[31mERR\\x1b[0m',\n '\\x1b[33mWRN\\x1b[0m',\n '\\x1b[34mINF\\x1b[0m',\n '\\x1b[36mDBG\\x1b[0m',\n '\\x1b[35mVRB\\x1b[0m',\n ] :\n [\n '', // OFF\n 'ERR',\n 'WRN',\n 'INF',\n 'DBG',\n 'VRB',\n ]\nconst TAG_COLORS = [6, 2, 3, 4, 5, 1].map((i) => `\\x1b[3${i};1m`)\n\n/** @internal */\nexport const defaultLoggingHandler = isTty ?\n (color: number, level: number, tag: string, fmt: string, args: unknown[]): void => {\n // eslint-disable-next-line no-console\n console.log(BASE_FORMAT + fmt, new Date().toISOString(), LEVEL_NAMES[level], TAG_COLORS[color], tag, ...args)\n } :\n (color: number, level: number, tag: string, fmt: string, args: unknown[]): void => {\n // eslint-disable-next-line no-console\n console.log(BASE_FORMAT + fmt, new Date().toISOString(), LEVEL_NAMES[level], tag, ...args)\n }\n"]}
@@ -0,0 +1,18 @@
1
+ import { ICorePlatform } from '@mtcute/core/platform.js';
2
+ import { normalizeFile } from '../utils/normalize-file.js';
3
+ import { beforeExit } from './exit-hook.js';
4
+ import { defaultLoggingHandler } from './logging.js';
5
+ export declare class NodePlatform implements ICorePlatform {
6
+ log: typeof defaultLoggingHandler;
7
+ beforeExit: typeof beforeExit;
8
+ normalizeFile: typeof normalizeFile;
9
+ getDeviceModel(): string;
10
+ getDefaultLogLevel(): number | null;
11
+ utf8ByteLength(str: string): number;
12
+ utf8Encode(str: string): Uint8Array;
13
+ utf8Decode(buf: Uint8Array): string;
14
+ hexEncode(buf: Uint8Array): string;
15
+ hexDecode(str: string): Uint8Array;
16
+ base64Encode(buf: Uint8Array, url?: boolean): string;
17
+ base64Decode(string: string, url?: boolean): Uint8Array;
18
+ }
@@ -0,0 +1,58 @@
1
+ import * as os from 'os';
2
+ import { normalizeFile } from '../utils/normalize-file.js';
3
+ import { beforeExit } from './exit-hook.js';
4
+ import { defaultLoggingHandler } from './logging.js';
5
+ const BUFFER_BASE64_URL_AVAILABLE = typeof Buffer.isEncoding === 'function' && Buffer.isEncoding('base64url');
6
+ const toBuffer = (buf) => Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
7
+ export class NodePlatform {
8
+ getDeviceModel() {
9
+ return `${os.type()} ${os.arch()} ${os.release()}`;
10
+ }
11
+ getDefaultLogLevel() {
12
+ const envLogLevel = parseInt(process.env.MTCUTE_LOG_LEVEL ?? '');
13
+ if (!isNaN(envLogLevel)) {
14
+ return envLogLevel;
15
+ }
16
+ return null;
17
+ }
18
+ // ITlPlatform
19
+ utf8ByteLength(str) {
20
+ return Buffer.byteLength(str, 'utf8');
21
+ }
22
+ utf8Encode(str) {
23
+ return Buffer.from(str, 'utf8');
24
+ }
25
+ utf8Decode(buf) {
26
+ return toBuffer(buf).toString('utf8');
27
+ }
28
+ hexEncode(buf) {
29
+ return toBuffer(buf).toString('hex');
30
+ }
31
+ hexDecode(str) {
32
+ return Buffer.from(str, 'hex');
33
+ }
34
+ base64Encode(buf, url = false) {
35
+ const nodeBuffer = toBuffer(buf);
36
+ if (url && BUFFER_BASE64_URL_AVAILABLE)
37
+ return nodeBuffer.toString('base64url');
38
+ const str = nodeBuffer.toString('base64');
39
+ if (url)
40
+ return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
41
+ return str;
42
+ }
43
+ base64Decode(string, url = false) {
44
+ if (url && BUFFER_BASE64_URL_AVAILABLE) {
45
+ return Buffer.from(string, 'base64url');
46
+ }
47
+ if (url) {
48
+ string = string.replace(/-/g, '+').replace(/_/g, '/');
49
+ while (string.length % 4)
50
+ string += '=';
51
+ }
52
+ return Buffer.from(string, 'base64');
53
+ }
54
+ }
55
+ NodePlatform.prototype.log = defaultLoggingHandler;
56
+ NodePlatform.prototype.beforeExit = beforeExit;
57
+ NodePlatform.prototype.normalizeFile = normalizeFile;
58
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/common-internals-node/platform.ts"],"names":[],"mappings":"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","sourcesContent":["import * as os from 'os'\n\nimport { ICorePlatform } from '@mtcute/core/platform.js'\n\nimport { normalizeFile } from '../utils/normalize-file.js'\nimport { beforeExit } from './exit-hook.js'\nimport { defaultLoggingHandler } from './logging.js'\n\nconst BUFFER_BASE64_URL_AVAILABLE = typeof Buffer.isEncoding === 'function' && Buffer.isEncoding('base64url')\n\nconst toBuffer = (buf: Uint8Array): Buffer => Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)\n\nexport class NodePlatform implements ICorePlatform {\n // ICorePlatform\n log!: typeof defaultLoggingHandler\n beforeExit!: typeof beforeExit\n normalizeFile!: typeof normalizeFile\n\n getDeviceModel(): string {\n return `${os.type()} ${os.arch()} ${os.release()}`\n }\n\n getDefaultLogLevel(): number | null {\n const envLogLevel = parseInt(process.env.MTCUTE_LOG_LEVEL ?? '')\n\n if (!isNaN(envLogLevel)) {\n return envLogLevel\n }\n\n return null\n }\n\n // ITlPlatform\n utf8ByteLength(str: string): number {\n return Buffer.byteLength(str, 'utf8')\n }\n utf8Encode(str: string): Uint8Array {\n return Buffer.from(str, 'utf8')\n }\n utf8Decode(buf: Uint8Array): string {\n return toBuffer(buf).toString('utf8')\n }\n\n hexEncode(buf: Uint8Array): string {\n return toBuffer(buf).toString('hex')\n }\n hexDecode(str: string): Uint8Array {\n return Buffer.from(str, 'hex')\n }\n\n base64Encode(buf: Uint8Array, url = false): string {\n const nodeBuffer = toBuffer(buf)\n\n if (url && BUFFER_BASE64_URL_AVAILABLE) return nodeBuffer.toString('base64url')\n\n const str = nodeBuffer.toString('base64')\n if (url) return str.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n\n return str\n }\n base64Decode(string: string, url = false): Uint8Array {\n if (url && BUFFER_BASE64_URL_AVAILABLE) {\n return Buffer.from(string, 'base64url')\n }\n\n if (url) {\n string = string.replace(/-/g, '+').replace(/_/g, '/')\n while (string.length % 4) string += '='\n }\n\n return Buffer.from(string, 'base64')\n }\n}\n\nNodePlatform.prototype.log = defaultLoggingHandler\nNodePlatform.prototype.beforeExit = beforeExit\nNodePlatform.prototype.normalizeFile = normalizeFile\n"]}
package/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export * from './client.js';
2
+ export * from './common-internals-node/platform.js';
3
+ export * from './sqlite/index.js';
4
+ export * from './utils/crypto.js';
5
+ export * from './utils/tcp.js';
6
+ export * from './worker.js';
7
+ export * from '@mtcute/core';
8
+ export * from '@mtcute/html-parser';
9
+ export * from '@mtcute/markdown-parser';
package/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export * from './client.js';
2
+ export * from './common-internals-node/platform.js';
3
+ export * from './sqlite/index.js';
4
+ export * from './utils/crypto.js';
5
+ export * from './utils/tcp.js';
6
+ export * from './worker.js';
7
+ export * from '@mtcute/core';
8
+ export * from '@mtcute/html-parser';
9
+ export * from '@mtcute/markdown-parser';
10
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,qCAAqC,CAAA;AACnD,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA","sourcesContent":["export * from './client.js'\nexport * from './common-internals-node/platform.js'\nexport * from './sqlite/index.js'\nexport * from './utils/crypto.js'\nexport * from './utils/tcp.js'\nexport * from './worker.js'\nexport * from '@mtcute/core'\nexport * from '@mtcute/html-parser'\nexport * from '@mtcute/markdown-parser'\n"]}
@@ -0,0 +1,9 @@
1
+ import { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core';
2
+ /**
3
+ * Download a remote file to a local file (only for NodeJS).
4
+ * Promise will resolve once the download is complete.
5
+ *
6
+ * @param filename Local file name to which the remote file will be downloaded
7
+ * @param params File download parameters
8
+ */
9
+ export declare function downloadToFile(client: ITelegramClient, filename: string, location: FileDownloadLocation, params?: FileDownloadParameters): Promise<void>;
@@ -0,0 +1,29 @@
1
+ import { unlinkSync } from 'node:fs';
2
+ import { FileLocation } from '@mtcute/core';
3
+ import { downloadAsIterable } from '@mtcute/core/methods.js';
4
+ /**
5
+ * Download a remote file to a local file (only for NodeJS).
6
+ * Promise will resolve once the download is complete.
7
+ *
8
+ * @param filename Local file name to which the remote file will be downloaded
9
+ * @param params File download parameters
10
+ */
11
+ export async function downloadToFile(client, filename, location, params) {
12
+ if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) {
13
+ // early return for inline files
14
+ await Bun.write(filename, location.location);
15
+ }
16
+ const output = Bun.file(filename).writer();
17
+ if (params?.abortSignal) {
18
+ params.abortSignal.addEventListener('abort', () => {
19
+ client.log.debug('aborting file download %s - cleaning up', filename);
20
+ Promise.resolve(output.end()).catch(() => { });
21
+ unlinkSync(filename);
22
+ });
23
+ }
24
+ for await (const chunk of downloadAsIterable(client, location, params)) {
25
+ output.write(chunk);
26
+ }
27
+ await output.end();
28
+ }
29
+ //# sourceMappingURL=download-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-file.js","sourceRoot":"","sources":["../../src/methods/download-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEpC,OAAO,EAAgD,YAAY,EAAmB,MAAM,cAAc,CAAA;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAChC,MAAuB,EACvB,QAAgB,EAChB,QAA8B,EAC9B,MAA+B;IAE/B,IAAI,QAAQ,YAAY,YAAY,IAAI,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAC3E,gCAAgC;QAChC,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;KAC/C;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAA;IAE1C,IAAI,MAAM,EAAE,WAAW,EAAE;QACrB,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YAC9C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,yCAAyC,EAAE,QAAQ,CAAC,CAAA;YACrE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YAC7C,UAAU,CAAC,QAAQ,CAAC,CAAA;QACxB,CAAC,CAAC,CAAA;KACL;IAED,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE;QACpE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;KACtB;IAED,MAAM,MAAM,CAAC,GAAG,EAAE,CAAA;AACtB,CAAC","sourcesContent":["import { unlinkSync } from 'node:fs'\n\nimport { FileDownloadLocation, FileDownloadParameters, FileLocation, ITelegramClient } from '@mtcute/core'\nimport { downloadAsIterable } from '@mtcute/core/methods.js'\n\n/**\n * Download a remote file to a local file (only for NodeJS).\n * Promise will resolve once the download is complete.\n *\n * @param filename Local file name to which the remote file will be downloaded\n * @param params File download parameters\n */\nexport async function downloadToFile(\n client: ITelegramClient,\n filename: string,\n location: FileDownloadLocation,\n params?: FileDownloadParameters,\n): Promise<void> {\n if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) {\n // early return for inline files\n await Bun.write(filename, location.location)\n }\n\n const output = Bun.file(filename).writer()\n\n if (params?.abortSignal) {\n params.abortSignal.addEventListener('abort', () => {\n client.log.debug('aborting file download %s - cleaning up', filename)\n Promise.resolve(output.end()).catch(() => {})\n unlinkSync(filename)\n })\n }\n\n for await (const chunk of downloadAsIterable(client, location, params)) {\n output.write(chunk)\n }\n\n await output.end()\n}\n"]}
@@ -0,0 +1,10 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Readable } from 'stream';
3
+ import { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core';
4
+ /**
5
+ * Download a remote file as a Node.js Readable stream
6
+ * (discouraged under Bun, since Web Streams are first-class).
7
+ *
8
+ * @param params File download parameters
9
+ */
10
+ export declare function downloadAsNodeStream(client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters): Readable;
@@ -0,0 +1,13 @@
1
+ import { Readable } from 'stream';
2
+ import { downloadAsStream } from '@mtcute/core/methods.js';
3
+ /**
4
+ * Download a remote file as a Node.js Readable stream
5
+ * (discouraged under Bun, since Web Streams are first-class).
6
+ *
7
+ * @param params File download parameters
8
+ */
9
+ export function downloadAsNodeStream(client, location, params) {
10
+ // @ts-expect-error typings are wrong
11
+ return Readable.fromWeb(downloadAsStream(client, location, params));
12
+ }
13
+ //# sourceMappingURL=download-node-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-node-stream.js","sourceRoot":"","sources":["../../src/methods/download-node-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAGjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAChC,MAAuB,EACvB,QAA8B,EAC9B,MAA+B;IAE/B,qCAAqC;IACrC,OAAO,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAA;AACvE,CAAC","sourcesContent":["import { Readable } from 'stream'\n\nimport { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core'\nimport { downloadAsStream } from '@mtcute/core/methods.js'\n\n/**\n * Download a remote file as a Node.js Readable stream\n * (discouraged under Bun, since Web Streams are first-class).\n *\n * @param params File download parameters\n */\nexport function downloadAsNodeStream(\n client: ITelegramClient,\n location: FileDownloadLocation,\n params?: FileDownloadParameters,\n): Readable {\n // @ts-expect-error typings are wrong\n return Readable.fromWeb(downloadAsStream(client, location, params))\n}\n"]}
package/methods.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { downloadToFile } from './methods/download-file.js';
2
+ export { downloadAsNodeStream } from './methods/download-node-stream.js';
3
+ export * from '@mtcute/core/methods.js';
package/methods.js ADDED
@@ -0,0 +1,4 @@
1
+ export { downloadToFile } from './methods/download-file.js';
2
+ export { downloadAsNodeStream } from './methods/download-node-stream.js';
3
+ export * from '@mtcute/core/methods.js';
4
+ //# sourceMappingURL=methods.js.map
package/methods.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"methods.js","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AACxE,cAAc,yBAAyB,CAAA","sourcesContent":["export { downloadToFile } from './methods/download-file.js'\nexport { downloadAsNodeStream } from './methods/download-node-stream.js'\nexport * from '@mtcute/core/methods.js'\n"]}
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@mtcute/bun",
3
+ "version": "0.9.0",
4
+ "description": "Meta-package for Bun",
5
+ "author": "alina sireneva <alina@tei.su>",
6
+ "license": "MIT",
7
+ "main": "src/index.ts",
8
+ "type": "module",
9
+ "sideEffects": false,
10
+ "scripts": {},
11
+ "exports": {
12
+ ".": "./index.js",
13
+ "./utils.js": "./utils.js"
14
+ },
15
+ "dependencies": {
16
+ "@mtcute/core": "^0.9.0",
17
+ "@mtcute/wasm": "^0.9.0",
18
+ "@mtcute/markdown-parser": "^0.9.0",
19
+ "@mtcute/html-parser": "^0.9.0"
20
+ }
21
+ }
@@ -0,0 +1,20 @@
1
+ import { BaseSqliteStorageDriver, ISqliteDatabase } from '@mtcute/core';
2
+ export interface SqliteStorageDriverOptions {
3
+ /**
4
+ * By default, WAL mode is enabled, which
5
+ * significantly improves performance.
6
+ * [Learn more](https://bun.sh/docs/api/sqlite#wal-mode)
7
+ *
8
+ * However, you might encounter some issues,
9
+ * and if you do, you can disable WAL by passing `true`
10
+ *
11
+ * @default false
12
+ */
13
+ disableWal?: boolean;
14
+ }
15
+ export declare class SqliteStorageDriver extends BaseSqliteStorageDriver {
16
+ readonly filename: string;
17
+ readonly params?: SqliteStorageDriverOptions | undefined;
18
+ constructor(filename?: string, params?: SqliteStorageDriverOptions | undefined);
19
+ _createDatabase(): ISqliteDatabase;
20
+ }
@@ -0,0 +1,17 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { BaseSqliteStorageDriver } from '@mtcute/core';
3
+ export class SqliteStorageDriver extends BaseSqliteStorageDriver {
4
+ constructor(filename = ':memory:', params) {
5
+ super();
6
+ this.filename = filename;
7
+ this.params = params;
8
+ }
9
+ _createDatabase() {
10
+ const db = new Database(this.filename);
11
+ if (!this.params?.disableWal) {
12
+ db.exec('PRAGMA journal_mode = WAL;');
13
+ }
14
+ return db;
15
+ }
16
+ }
17
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/sqlite/driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,EAAE,uBAAuB,EAAmB,MAAM,cAAc,CAAA;AAgBvE,MAAM,OAAO,mBAAoB,SAAQ,uBAAuB;IAC5D,YACa,WAAW,UAAU,EACrB,MAAmC;QAE5C,KAAK,EAAE,CAAA;QAHE,aAAQ,GAAR,QAAQ,CAAa;QACrB,WAAM,GAAN,MAAM,CAA6B;IAGhD,CAAC;IAED,eAAe;QACX,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEtC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE;YAC1B,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;SACxC;QAED,OAAO,EAAqB,CAAA;IAChC,CAAC;CACJ","sourcesContent":["import { Database } from 'bun:sqlite'\n\nimport { BaseSqliteStorageDriver, ISqliteDatabase } from '@mtcute/core'\n\nexport interface SqliteStorageDriverOptions {\n /**\n * By default, WAL mode is enabled, which\n * significantly improves performance.\n * [Learn more](https://bun.sh/docs/api/sqlite#wal-mode)\n *\n * However, you might encounter some issues,\n * and if you do, you can disable WAL by passing `true`\n *\n * @default false\n */\n disableWal?: boolean\n}\n\nexport class SqliteStorageDriver extends BaseSqliteStorageDriver {\n constructor(\n readonly filename = ':memory:',\n readonly params?: SqliteStorageDriverOptions,\n ) {\n super()\n }\n\n _createDatabase(): ISqliteDatabase {\n const db = new Database(this.filename)\n\n if (!this.params?.disableWal) {\n db.exec('PRAGMA journal_mode = WAL;')\n }\n\n return db as ISqliteDatabase\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { BaseSqliteStorage } from '@mtcute/core';
2
+ import { SqliteStorageDriverOptions } from './driver.js';
3
+ export { SqliteStorageDriver } from './driver.js';
4
+ export declare class SqliteStorage extends BaseSqliteStorage {
5
+ readonly filename: string;
6
+ readonly params?: SqliteStorageDriverOptions | undefined;
7
+ constructor(filename?: string, params?: SqliteStorageDriverOptions | undefined);
8
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseSqliteStorage } from '@mtcute/core';
2
+ import { SqliteStorageDriver } from './driver.js';
3
+ export { SqliteStorageDriver } from './driver.js';
4
+ export class SqliteStorage extends BaseSqliteStorage {
5
+ constructor(filename = ':memory:', params) {
6
+ super(new SqliteStorageDriver(filename, params));
7
+ this.filename = filename;
8
+ this.params = params;
9
+ }
10
+ }
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAAE,mBAAmB,EAA8B,MAAM,aAAa,CAAA;AAE7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEjD,MAAM,OAAO,aAAc,SAAQ,iBAAiB;IAChD,YACa,WAAW,UAAU,EACrB,MAAmC;QAE5C,KAAK,CAAC,IAAI,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAA;QAHvC,aAAQ,GAAR,QAAQ,CAAa;QACrB,WAAM,GAAN,MAAM,CAA6B;IAGhD,CAAC;CACJ","sourcesContent":["import { BaseSqliteStorage } from '@mtcute/core'\n\nimport { SqliteStorageDriver, SqliteStorageDriverOptions } from './driver.js'\n\nexport { SqliteStorageDriver } from './driver.js'\n\nexport class SqliteStorage extends BaseSqliteStorage {\n constructor(\n readonly filename = ':memory:',\n readonly params?: SqliteStorageDriverOptions,\n ) {\n super(new SqliteStorageDriver(filename, params))\n }\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from '@mtcute/core/utils.js';
2
+ export declare class BunCryptoProvider extends BaseCryptoProvider implements ICryptoProvider {
3
+ initialize(): Promise<void>;
4
+ createAesIge(key: Uint8Array, iv: Uint8Array): IEncryptionScheme;
5
+ createAesCtr(key: Uint8Array, iv: Uint8Array): IAesCtr;
6
+ pbkdf2(password: Uint8Array, salt: Uint8Array, iterations: number, keylen?: number, algo?: string): Promise<Uint8Array>;
7
+ sha1(data: Uint8Array): Uint8Array;
8
+ sha256(data: Uint8Array): Uint8Array;
9
+ hmacSha256(data: Uint8Array, key: Uint8Array): Promise<Uint8Array>;
10
+ gzip(data: Uint8Array, maxSize: number): Uint8Array | null;
11
+ gunzip(data: Uint8Array): Uint8Array;
12
+ randomFill(buf: Uint8Array): void;
13
+ }
@@ -0,0 +1,76 @@
1
+ // eslint-disable-next-line no-restricted-imports
2
+ import { readFile } from 'fs/promises';
3
+ import { BaseCryptoProvider } from '@mtcute/core/utils.js';
4
+ import { createCtr256, ctr256, deflateMaxSize, freeCtr256, gunzip, ige256Decrypt, ige256Encrypt, initSync, } from '@mtcute/wasm';
5
+ // we currently prefer subtle crypto and wasm for ctr because bun uses browserify polyfills for node:crypto
6
+ // which are slow AND semi-broken
7
+ // we currently prefer wasm for gzip because bun uses browserify polyfills for node:zlib too
8
+ // native node-api addon is broken on macos so we don't support it either
9
+ //
10
+ // largely just copy-pasting from @mtcute/web, todo: maybe refactor this into common-internals-web?
11
+ const ALGO_TO_SUBTLE = {
12
+ sha256: 'SHA-256',
13
+ sha1: 'SHA-1',
14
+ sha512: 'SHA-512',
15
+ };
16
+ export class BunCryptoProvider extends BaseCryptoProvider {
17
+ async initialize() {
18
+ // eslint-disable-next-line no-restricted-globals
19
+ const wasmFile = require.resolve('@mtcute/wasm/mtcute.wasm');
20
+ const wasm = await readFile(wasmFile);
21
+ initSync(wasm);
22
+ }
23
+ createAesIge(key, iv) {
24
+ return {
25
+ encrypt(data) {
26
+ return ige256Encrypt(data, key, iv);
27
+ },
28
+ decrypt(data) {
29
+ return ige256Decrypt(data, key, iv);
30
+ },
31
+ };
32
+ }
33
+ createAesCtr(key, iv) {
34
+ const ctx = createCtr256(key, iv);
35
+ return {
36
+ process: (data) => ctr256(ctx, data),
37
+ close: () => freeCtr256(ctx),
38
+ };
39
+ }
40
+ async pbkdf2(password, salt, iterations, keylen = 64, algo = 'sha512') {
41
+ const keyMaterial = await crypto.subtle.importKey('raw', password, 'PBKDF2', false, ['deriveBits']);
42
+ return crypto.subtle
43
+ .deriveBits({
44
+ name: 'PBKDF2',
45
+ salt,
46
+ iterations,
47
+ hash: algo ? ALGO_TO_SUBTLE[algo] : 'SHA-512',
48
+ }, keyMaterial, (keylen || 64) * 8)
49
+ .then((result) => new Uint8Array(result));
50
+ }
51
+ sha1(data) {
52
+ const res = new Uint8Array(Bun.SHA1.byteLength);
53
+ Bun.SHA1.hash(data, res);
54
+ return res;
55
+ }
56
+ sha256(data) {
57
+ const res = new Uint8Array(Bun.SHA256.byteLength);
58
+ Bun.SHA256.hash(data, res);
59
+ return res;
60
+ }
61
+ async hmacSha256(data, key) {
62
+ const keyMaterial = await crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign']);
63
+ const res = await crypto.subtle.sign({ name: 'HMAC' }, keyMaterial, data);
64
+ return new Uint8Array(res);
65
+ }
66
+ gzip(data, maxSize) {
67
+ return deflateMaxSize(data, maxSize);
68
+ }
69
+ gunzip(data) {
70
+ return gunzip(data);
71
+ }
72
+ randomFill(buf) {
73
+ crypto.getRandomValues(buf);
74
+ }
75
+ }
76
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"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","sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { readFile } from 'fs/promises'\n\nimport { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from '@mtcute/core/utils.js'\nimport {\n createCtr256,\n ctr256,\n deflateMaxSize,\n freeCtr256,\n gunzip,\n ige256Decrypt,\n ige256Encrypt,\n initSync,\n} from '@mtcute/wasm'\n\n// we currently prefer subtle crypto and wasm for ctr because bun uses browserify polyfills for node:crypto\n// which are slow AND semi-broken\n// we currently prefer wasm for gzip because bun uses browserify polyfills for node:zlib too\n// native node-api addon is broken on macos so we don't support it either\n//\n// largely just copy-pasting from @mtcute/web, todo: maybe refactor this into common-internals-web?\n\nconst ALGO_TO_SUBTLE: Record<string, string> = {\n sha256: 'SHA-256',\n sha1: 'SHA-1',\n sha512: 'SHA-512',\n}\n\nexport class BunCryptoProvider extends BaseCryptoProvider implements ICryptoProvider {\n async initialize(): Promise<void> {\n // eslint-disable-next-line no-restricted-globals\n const wasmFile = require.resolve('@mtcute/wasm/mtcute.wasm')\n const wasm = await readFile(wasmFile)\n initSync(wasm)\n }\n\n createAesIge(key: Uint8Array, iv: Uint8Array): IEncryptionScheme {\n return {\n encrypt(data: Uint8Array): Uint8Array {\n return ige256Encrypt(data, key, iv)\n },\n decrypt(data: Uint8Array): Uint8Array {\n return ige256Decrypt(data, key, iv)\n },\n }\n }\n\n createAesCtr(key: Uint8Array, iv: Uint8Array): IAesCtr {\n const ctx = createCtr256(key, iv)\n\n return {\n process: (data) => ctr256(ctx, data),\n close: () => freeCtr256(ctx),\n }\n }\n\n async pbkdf2(\n password: Uint8Array,\n salt: Uint8Array,\n iterations: number,\n keylen = 64,\n algo = 'sha512',\n ): Promise<Uint8Array> {\n const keyMaterial = await crypto.subtle.importKey('raw', password, 'PBKDF2', false, ['deriveBits'])\n\n return crypto.subtle\n .deriveBits(\n {\n name: 'PBKDF2',\n salt,\n iterations,\n hash: algo ? ALGO_TO_SUBTLE[algo] : 'SHA-512',\n },\n keyMaterial,\n (keylen || 64) * 8,\n )\n .then((result) => new Uint8Array(result))\n }\n\n sha1(data: Uint8Array): Uint8Array {\n const res = new Uint8Array(Bun.SHA1.byteLength)\n Bun.SHA1.hash(data, res)\n\n return res\n }\n\n sha256(data: Uint8Array): Uint8Array {\n const res = new Uint8Array(Bun.SHA256.byteLength)\n Bun.SHA256.hash(data, res)\n\n return res\n }\n\n async hmacSha256(data: Uint8Array, key: Uint8Array): Promise<Uint8Array> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n key,\n { name: 'HMAC', hash: { name: 'SHA-256' } },\n false,\n ['sign'],\n )\n\n const res = await crypto.subtle.sign({ name: 'HMAC' }, keyMaterial, data)\n\n return new Uint8Array(res)\n }\n\n gzip(data: Uint8Array, maxSize: number): Uint8Array | null {\n return deflateMaxSize(data, maxSize)\n }\n\n gunzip(data: Uint8Array): Uint8Array {\n return gunzip(data)\n }\n\n randomFill(buf: Uint8Array) {\n crypto.getRandomValues(buf)\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { UploadFileLike } from '@mtcute/core';
2
+ export declare function normalizeFile(file: UploadFileLike): Promise<{
3
+ file: ReadableStream<Uint8Array>;
4
+ fileName: string;
5
+ fileSize: number;
6
+ } | {
7
+ file: ReadableStream<Uint8Array>;
8
+ fileName?: undefined;
9
+ fileSize?: undefined;
10
+ } | null>;
@@ -0,0 +1,27 @@
1
+ import { ReadStream } from 'fs';
2
+ import { stat } from 'fs/promises';
3
+ import { basename } from 'path';
4
+ import { Readable as NodeReadable } from 'stream';
5
+ export async function normalizeFile(file) {
6
+ if (typeof file === 'string') {
7
+ file = Bun.file(file);
8
+ }
9
+ // while these are not Bun-specific, they still may happen
10
+ if (file instanceof ReadStream) {
11
+ const fileName = basename(file.path.toString());
12
+ const fileSize = await stat(file.path.toString()).then((stat) => stat.size);
13
+ return {
14
+ file: NodeReadable.toWeb(file),
15
+ fileName,
16
+ fileSize,
17
+ };
18
+ }
19
+ if (file instanceof NodeReadable) {
20
+ return {
21
+ file: NodeReadable.toWeb(file),
22
+ };
23
+ }
24
+ // string -> ReadStream, thus already handled
25
+ return null;
26
+ }
27
+ //# sourceMappingURL=normalize-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-file.js","sourceRoot":"","sources":["../../src/utils/normalize-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAC/B,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAA;AAIjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAoB;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC1B,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;KACxB;IAED,0DAA0D;IAC1D,IAAI,IAAI,YAAY,UAAU,EAAE;QAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC/C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE3E,OAAO;YACH,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,CAA0C;YACvE,QAAQ;YACR,QAAQ;SACX,CAAA;KACJ;IAED,IAAI,IAAI,YAAY,YAAY,EAAE;QAC9B,OAAO;YACH,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,CAA0C;SAC1E,CAAA;KACJ;IAED,6CAA6C;IAC7C,OAAO,IAAI,CAAA;AACf,CAAC","sourcesContent":["import { ReadStream } from 'fs'\nimport { stat } from 'fs/promises'\nimport { basename } from 'path'\nimport { Readable as NodeReadable } from 'stream'\n\nimport { UploadFileLike } from '@mtcute/core'\n\nexport async function normalizeFile(file: UploadFileLike) {\n if (typeof file === 'string') {\n file = Bun.file(file)\n }\n\n // while these are not Bun-specific, they still may happen\n if (file instanceof ReadStream) {\n const fileName = basename(file.path.toString())\n const fileSize = await stat(file.path.toString()).then((stat) => stat.size)\n\n return {\n file: NodeReadable.toWeb(file) as unknown as ReadableStream<Uint8Array>,\n fileName,\n fileSize,\n }\n }\n\n if (file instanceof NodeReadable) {\n return {\n file: NodeReadable.toWeb(file) as unknown as ReadableStream<Uint8Array>,\n }\n }\n\n // string -> ReadStream, thus already handled\n return null\n}\n"]}
package/utils/tcp.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /// <reference types="bun-types" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import { Socket } from 'bun';
4
+ import EventEmitter from 'events';
5
+ import { IntermediatePacketCodec, IPacketCodec, ITelegramTransport, TransportState } from '@mtcute/core';
6
+ import { BasicDcOption, ICryptoProvider, Logger } from '@mtcute/core/utils.js';
7
+ /**
8
+ * Base for TCP transports.
9
+ * Subclasses must provide packet codec in `_packetCodec` property
10
+ */
11
+ export declare abstract class BaseTcpTransport extends EventEmitter implements ITelegramTransport {
12
+ protected _currentDc: BasicDcOption | null;
13
+ protected _state: TransportState;
14
+ protected _socket: Socket | null;
15
+ abstract _packetCodec: IPacketCodec;
16
+ protected _crypto: ICryptoProvider;
17
+ protected log: Logger;
18
+ packetCodecInitialized: boolean;
19
+ private _updateLogPrefix;
20
+ setup(crypto: ICryptoProvider, log: Logger): void;
21
+ state(): TransportState;
22
+ currentDc(): BasicDcOption | null;
23
+ connect(dc: BasicDcOption, testMode: boolean): void;
24
+ close(): void;
25
+ handleError(socket: unknown, error: Error): void;
26
+ handleConnect(socket: Socket): void;
27
+ send(bytes: Uint8Array): Promise<void>;
28
+ }
29
+ export declare class TcpTransport extends BaseTcpTransport {
30
+ _packetCodec: IntermediatePacketCodec;
31
+ }
package/utils/tcp.js ADDED
@@ -0,0 +1,109 @@
1
+ import EventEmitter from 'events';
2
+ import { IntermediatePacketCodec, MtcuteError, TransportState } from '@mtcute/core';
3
+ /**
4
+ * Base for TCP transports.
5
+ * Subclasses must provide packet codec in `_packetCodec` property
6
+ */
7
+ export class BaseTcpTransport extends EventEmitter {
8
+ constructor() {
9
+ super(...arguments);
10
+ this._currentDc = null;
11
+ this._state = TransportState.Idle;
12
+ this._socket = null;
13
+ this.packetCodecInitialized = false;
14
+ }
15
+ _updateLogPrefix() {
16
+ if (this._currentDc) {
17
+ this.log.prefix = `[TCP:${this._currentDc.ipAddress}:${this._currentDc.port}] `;
18
+ }
19
+ else {
20
+ this.log.prefix = '[TCP:disconnected] ';
21
+ }
22
+ }
23
+ setup(crypto, log) {
24
+ this._crypto = crypto;
25
+ this.log = log.create('tcp');
26
+ this._updateLogPrefix();
27
+ }
28
+ state() {
29
+ return this._state;
30
+ }
31
+ currentDc() {
32
+ return this._currentDc;
33
+ }
34
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
+ connect(dc, testMode) {
36
+ if (this._state !== TransportState.Idle) {
37
+ throw new MtcuteError('Transport is not IDLE');
38
+ }
39
+ if (!this.packetCodecInitialized) {
40
+ this._packetCodec.setup?.(this._crypto, this.log);
41
+ this._packetCodec.on('error', (err) => this.emit('error', err));
42
+ this._packetCodec.on('packet', (buf) => this.emit('message', buf));
43
+ this.packetCodecInitialized = true;
44
+ }
45
+ this._state = TransportState.Connecting;
46
+ this._currentDc = dc;
47
+ this._updateLogPrefix();
48
+ this.log.debug('connecting to %j', dc);
49
+ Bun.connect({
50
+ hostname: dc.ipAddress,
51
+ port: dc.port,
52
+ socket: {
53
+ open: this.handleConnect.bind(this),
54
+ error: this.handleError.bind(this),
55
+ data: (socket, data) => this._packetCodec.feed(data),
56
+ close: this.close.bind(this),
57
+ },
58
+ }).catch((err) => {
59
+ this.handleError(null, err);
60
+ this.close();
61
+ });
62
+ }
63
+ close() {
64
+ if (this._state === TransportState.Idle)
65
+ return;
66
+ this.log.info('connection closed');
67
+ this.emit('close');
68
+ this._state = TransportState.Idle;
69
+ this._socket?.end();
70
+ this._socket = null;
71
+ this._currentDc = null;
72
+ this._packetCodec.reset();
73
+ }
74
+ handleError(socket, error) {
75
+ this.log.error('error: %s', error.stack);
76
+ this.emit('error', error);
77
+ }
78
+ handleConnect(socket) {
79
+ this._socket = socket;
80
+ this.log.info('connected');
81
+ Promise.resolve(this._packetCodec.tag())
82
+ .then((initialMessage) => {
83
+ if (initialMessage.length) {
84
+ this._socket.write(initialMessage);
85
+ this._state = TransportState.Ready;
86
+ this.emit('ready');
87
+ }
88
+ else {
89
+ this._state = TransportState.Ready;
90
+ this.emit('ready');
91
+ }
92
+ })
93
+ .catch((err) => this.emit('error', err));
94
+ }
95
+ async send(bytes) {
96
+ const framed = await this._packetCodec.encode(bytes);
97
+ if (this._state !== TransportState.Ready) {
98
+ throw new MtcuteError('Transport is not READY');
99
+ }
100
+ this._socket.write(framed);
101
+ }
102
+ }
103
+ export class TcpTransport extends BaseTcpTransport {
104
+ constructor() {
105
+ super(...arguments);
106
+ this._packetCodec = new IntermediatePacketCodec();
107
+ }
108
+ }
109
+ //# sourceMappingURL=tcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tcp.js","sourceRoot":"","sources":["../../src/utils/tcp.ts"],"names":[],"mappings":"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","sourcesContent":["import { Socket } from 'bun'\nimport EventEmitter from 'events'\n\nimport { IntermediatePacketCodec, IPacketCodec, ITelegramTransport, MtcuteError, TransportState } from '@mtcute/core'\nimport { BasicDcOption, ICryptoProvider, Logger } from '@mtcute/core/utils.js'\n\n/**\n * Base for TCP transports.\n * Subclasses must provide packet codec in `_packetCodec` property\n */\nexport abstract class BaseTcpTransport extends EventEmitter implements ITelegramTransport {\n protected _currentDc: BasicDcOption | null = null\n protected _state: TransportState = TransportState.Idle\n protected _socket: Socket | null = null\n\n abstract _packetCodec: IPacketCodec\n protected _crypto!: ICryptoProvider\n protected log!: Logger\n\n packetCodecInitialized = false\n\n private _updateLogPrefix() {\n if (this._currentDc) {\n this.log.prefix = `[TCP:${this._currentDc.ipAddress}:${this._currentDc.port}] `\n } else {\n this.log.prefix = '[TCP:disconnected] '\n }\n }\n\n setup(crypto: ICryptoProvider, log: Logger): void {\n this._crypto = crypto\n this.log = log.create('tcp')\n this._updateLogPrefix()\n }\n\n state(): TransportState {\n return this._state\n }\n\n currentDc(): BasicDcOption | null {\n return this._currentDc\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n connect(dc: BasicDcOption, testMode: boolean): void {\n if (this._state !== TransportState.Idle) {\n throw new MtcuteError('Transport is not IDLE')\n }\n\n if (!this.packetCodecInitialized) {\n this._packetCodec.setup?.(this._crypto, this.log)\n this._packetCodec.on('error', (err) => this.emit('error', err))\n this._packetCodec.on('packet', (buf) => this.emit('message', buf))\n this.packetCodecInitialized = true\n }\n\n this._state = TransportState.Connecting\n this._currentDc = dc\n this._updateLogPrefix()\n\n this.log.debug('connecting to %j', dc)\n\n Bun.connect({\n hostname: dc.ipAddress,\n port: dc.port,\n socket: {\n open: this.handleConnect.bind(this),\n error: this.handleError.bind(this),\n data: (socket, data) => this._packetCodec.feed(data),\n close: this.close.bind(this),\n },\n }).catch((err) => {\n this.handleError(null, err as Error)\n this.close()\n })\n }\n\n close(): void {\n if (this._state === TransportState.Idle) return\n this.log.info('connection closed')\n\n this.emit('close')\n this._state = TransportState.Idle\n this._socket?.end()\n this._socket = null\n this._currentDc = null\n this._packetCodec.reset()\n }\n\n handleError(socket: unknown, error: Error): void {\n this.log.error('error: %s', error.stack)\n this.emit('error', error)\n }\n\n handleConnect(socket: Socket): void {\n this._socket = socket\n this.log.info('connected')\n\n Promise.resolve(this._packetCodec.tag())\n .then((initialMessage) => {\n if (initialMessage.length) {\n this._socket!.write(initialMessage)\n this._state = TransportState.Ready\n this.emit('ready')\n } else {\n this._state = TransportState.Ready\n this.emit('ready')\n }\n })\n .catch((err) => this.emit('error', err))\n }\n\n async send(bytes: Uint8Array): Promise<void> {\n const framed = await this._packetCodec.encode(bytes)\n\n if (this._state !== TransportState.Ready) {\n throw new MtcuteError('Transport is not READY')\n }\n\n this._socket!.write(framed)\n }\n}\n\nexport class TcpTransport extends BaseTcpTransport {\n _packetCodec = new IntermediatePacketCodec()\n}\n"]}
package/utils.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from '@mtcute/core/utils.js';
package/utils.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from '@mtcute/core/utils.js';
2
+ //# sourceMappingURL=utils.js.map
package/utils.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA","sourcesContent":["export * from '@mtcute/core/utils.js'\n"]}
package/worker.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { ClientMessageHandler, RespondFn, SendFn, SomeWorker, TelegramWorker as TelegramWorkerBase, TelegramWorkerOptions, TelegramWorkerPort as TelegramWorkerPortBase, TelegramWorkerPortOptions, WorkerCustomMethods, WorkerMessageHandler } from '@mtcute/core/worker.js';
2
+ export type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods };
3
+ export declare class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> {
4
+ registerWorker(handler: WorkerMessageHandler): RespondFn;
5
+ }
6
+ export declare class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
7
+ readonly options: TelegramWorkerPortOptions;
8
+ constructor(options: TelegramWorkerPortOptions);
9
+ connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void];
10
+ }
package/worker.js ADDED
@@ -0,0 +1,42 @@
1
+ import { parentPort, Worker } from 'worker_threads';
2
+ import { setPlatform } from '@mtcute/core/platform.js';
3
+ import { TelegramWorker as TelegramWorkerBase, TelegramWorkerPort as TelegramWorkerPortBase, } from '@mtcute/core/worker.js';
4
+ import { NodePlatform } from './common-internals-node/platform.js';
5
+ let _registered = false;
6
+ export class TelegramWorker extends TelegramWorkerBase {
7
+ registerWorker(handler) {
8
+ if (!parentPort) {
9
+ throw new Error('TelegramWorker must be created from a worker thread');
10
+ }
11
+ if (_registered) {
12
+ throw new Error('TelegramWorker must be created only once');
13
+ }
14
+ _registered = true;
15
+ const port = parentPort;
16
+ const respond = port.postMessage.bind(port);
17
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
18
+ parentPort.on('message', (message) => handler(message, respond));
19
+ return respond;
20
+ }
21
+ }
22
+ export class TelegramWorkerPort extends TelegramWorkerPortBase {
23
+ constructor(options) {
24
+ setPlatform(new NodePlatform());
25
+ super(options);
26
+ this.options = options;
27
+ }
28
+ connectToWorker(worker, handler) {
29
+ if (!(worker instanceof Worker)) {
30
+ throw new Error('Only worker_threads are supported');
31
+ }
32
+ const send = worker.postMessage.bind(worker);
33
+ worker.on('message', handler);
34
+ return [
35
+ send,
36
+ () => {
37
+ worker.off('message', handler);
38
+ },
39
+ ];
40
+ }
41
+ }
42
+ //# sourceMappingURL=worker.js.map
package/worker.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAKH,cAAc,IAAI,kBAAkB,EAEpC,kBAAkB,IAAI,sBAAsB,GAI/C,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAA;AAIlE,IAAI,WAAW,GAAG,KAAK,CAAA;AAEvB,MAAM,OAAO,cAA8C,SAAQ,kBAAqB;IACpF,cAAc,CAAC,OAA6B;QACxC,IAAI,CAAC,UAAU,EAAE;YACb,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;SACzE;QACD,IAAI,WAAW,EAAE;YACb,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;SAC9D;QAED,WAAW,GAAG,IAAI,CAAA;QAElB,MAAM,IAAI,GAAG,UAAU,CAAA;QAEvB,MAAM,OAAO,GAAc,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEtD,iEAAiE;QACjE,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;QAEhE,OAAO,OAAO,CAAA;IAClB,CAAC;CACJ;AAED,MAAM,OAAO,kBAAkD,SAAQ,sBAAyB;IAC5F,YAAqB,OAAkC;QACnD,WAAW,CAAC,IAAI,YAAY,EAAE,CAAC,CAAA;QAC/B,KAAK,CAAC,OAAO,CAAC,CAAA;QAFG,YAAO,GAAP,OAAO,CAA2B;IAGvD,CAAC;IAED,eAAe,CAAC,MAAkB,EAAE,OAA6B;QAC7D,IAAI,CAAC,CAAC,MAAM,YAAY,MAAM,CAAC,EAAE;YAC7B,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;SACvD;QAED,MAAM,IAAI,GAAW,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAEpD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAE7B,OAAO;YACH,IAAI;YACJ,GAAG,EAAE;gBACD,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YAClC,CAAC;SACJ,CAAA;IACL,CAAC;CACJ","sourcesContent":["import { parentPort, Worker } from 'worker_threads'\n\nimport { setPlatform } from '@mtcute/core/platform.js'\nimport {\n ClientMessageHandler,\n RespondFn,\n SendFn,\n SomeWorker,\n TelegramWorker as TelegramWorkerBase,\n TelegramWorkerOptions,\n TelegramWorkerPort as TelegramWorkerPortBase,\n TelegramWorkerPortOptions,\n WorkerCustomMethods,\n WorkerMessageHandler,\n} from '@mtcute/core/worker.js'\n\nimport { NodePlatform } from './common-internals-node/platform.js'\n\nexport type { TelegramWorkerOptions, TelegramWorkerPortOptions, WorkerCustomMethods }\n\nlet _registered = false\n\nexport class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> {\n registerWorker(handler: WorkerMessageHandler): RespondFn {\n if (!parentPort) {\n throw new Error('TelegramWorker must be created from a worker thread')\n }\n if (_registered) {\n throw new Error('TelegramWorker must be created only once')\n }\n\n _registered = true\n\n const port = parentPort\n\n const respond: RespondFn = port.postMessage.bind(port)\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n parentPort.on('message', (message) => handler(message, respond))\n\n return respond\n }\n}\n\nexport class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {\n constructor(readonly options: TelegramWorkerPortOptions) {\n setPlatform(new NodePlatform())\n super(options)\n }\n\n connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {\n if (!(worker instanceof Worker)) {\n throw new Error('Only worker_threads are supported')\n }\n\n const send: SendFn = worker.postMessage.bind(worker)\n\n worker.on('message', handler)\n\n return [\n send,\n () => {\n worker.off('message', handler)\n },\n ]\n }\n}\n"]}