@mtcute/web 0.8.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 (118) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +53 -0
  3. package/cjs/client.d.ts +30 -0
  4. package/cjs/client.js +40 -0
  5. package/cjs/client.js.map +1 -0
  6. package/cjs/crypto.d.ts +21 -0
  7. package/cjs/crypto.js +71 -0
  8. package/cjs/crypto.js.map +1 -0
  9. package/cjs/encodings/base64.d.ts +2 -0
  10. package/cjs/encodings/base64.js +112 -0
  11. package/cjs/encodings/base64.js.map +1 -0
  12. package/cjs/encodings/hex.d.ts +2 -0
  13. package/cjs/encodings/hex.js +69 -0
  14. package/cjs/encodings/hex.js.map +1 -0
  15. package/cjs/encodings/utf8.d.ts +3 -0
  16. package/cjs/encodings/utf8.js +29 -0
  17. package/cjs/encodings/utf8.js.map +1 -0
  18. package/cjs/exit-hook.d.ts +1 -0
  19. package/cjs/exit-hook.js +24 -0
  20. package/cjs/exit-hook.js.map +1 -0
  21. package/cjs/idb/driver.d.ts +18 -0
  22. package/cjs/idb/driver.js +137 -0
  23. package/cjs/idb/driver.js.map +1 -0
  24. package/cjs/idb/index.d.ts +22 -0
  25. package/cjs/idb/index.js +28 -0
  26. package/cjs/idb/index.js.map +1 -0
  27. package/cjs/idb/repository/auth-keys.d.ts +14 -0
  28. package/cjs/idb/repository/auth-keys.js +70 -0
  29. package/cjs/idb/repository/auth-keys.js.map +1 -0
  30. package/cjs/idb/repository/kv.d.ts +11 -0
  31. package/cjs/idb/repository/kv.js +34 -0
  32. package/cjs/idb/repository/kv.js.map +1 -0
  33. package/cjs/idb/repository/peers.d.ts +12 -0
  34. package/cjs/idb/repository/peers.js +38 -0
  35. package/cjs/idb/repository/peers.js.map +1 -0
  36. package/cjs/idb/repository/ref-messages.d.ts +12 -0
  37. package/cjs/idb/repository/ref-messages.js +57 -0
  38. package/cjs/idb/repository/ref-messages.js.map +1 -0
  39. package/cjs/idb/utils.d.ts +3 -0
  40. package/cjs/idb/utils.js +27 -0
  41. package/cjs/idb/utils.js.map +1 -0
  42. package/cjs/index.d.ts +7 -0
  43. package/cjs/index.js +24 -0
  44. package/cjs/index.js.map +1 -0
  45. package/cjs/logging.d.ts +1 -0
  46. package/cjs/logging.js +35 -0
  47. package/cjs/logging.js.map +1 -0
  48. package/cjs/package.json +3 -0
  49. package/cjs/platform.d.ts +21 -0
  50. package/cjs/platform.js +49 -0
  51. package/cjs/platform.js.map +1 -0
  52. package/cjs/wasm.d.ts +2 -0
  53. package/cjs/wasm.js +36 -0
  54. package/cjs/wasm.js.map +1 -0
  55. package/cjs/websocket.d.ts +45 -0
  56. package/cjs/websocket.js +121 -0
  57. package/cjs/websocket.js.map +1 -0
  58. package/cjs/worker.d.ts +10 -0
  59. package/cjs/worker.js +128 -0
  60. package/cjs/worker.js.map +1 -0
  61. package/esm/client.d.ts +30 -0
  62. package/esm/client.js +35 -0
  63. package/esm/client.js.map +1 -0
  64. package/esm/crypto.d.ts +21 -0
  65. package/esm/crypto.js +67 -0
  66. package/esm/crypto.js.map +1 -0
  67. package/esm/encodings/base64.d.ts +2 -0
  68. package/esm/encodings/base64.js +107 -0
  69. package/esm/encodings/base64.js.map +1 -0
  70. package/esm/encodings/hex.d.ts +2 -0
  71. package/esm/encodings/hex.js +64 -0
  72. package/esm/encodings/hex.js.map +1 -0
  73. package/esm/encodings/utf8.d.ts +3 -0
  74. package/esm/encodings/utf8.js +23 -0
  75. package/esm/encodings/utf8.js.map +1 -0
  76. package/esm/exit-hook.d.ts +1 -0
  77. package/esm/exit-hook.js +20 -0
  78. package/esm/exit-hook.js.map +1 -0
  79. package/esm/idb/driver.d.ts +18 -0
  80. package/esm/idb/driver.js +133 -0
  81. package/esm/idb/driver.js.map +1 -0
  82. package/esm/idb/index.d.ts +22 -0
  83. package/esm/idb/index.js +23 -0
  84. package/esm/idb/index.js.map +1 -0
  85. package/esm/idb/repository/auth-keys.d.ts +14 -0
  86. package/esm/idb/repository/auth-keys.js +66 -0
  87. package/esm/idb/repository/auth-keys.js.map +1 -0
  88. package/esm/idb/repository/kv.d.ts +11 -0
  89. package/esm/idb/repository/kv.js +30 -0
  90. package/esm/idb/repository/kv.js.map +1 -0
  91. package/esm/idb/repository/peers.d.ts +12 -0
  92. package/esm/idb/repository/peers.js +34 -0
  93. package/esm/idb/repository/peers.js.map +1 -0
  94. package/esm/idb/repository/ref-messages.d.ts +12 -0
  95. package/esm/idb/repository/ref-messages.js +53 -0
  96. package/esm/idb/repository/ref-messages.js.map +1 -0
  97. package/esm/idb/utils.d.ts +3 -0
  98. package/esm/idb/utils.js +21 -0
  99. package/esm/idb/utils.js.map +1 -0
  100. package/esm/index.d.ts +7 -0
  101. package/esm/index.js +8 -0
  102. package/esm/index.js.map +1 -0
  103. package/esm/logging.d.ts +1 -0
  104. package/esm/logging.js +31 -0
  105. package/esm/logging.js.map +1 -0
  106. package/esm/platform.d.ts +21 -0
  107. package/esm/platform.js +45 -0
  108. package/esm/platform.js.map +1 -0
  109. package/esm/wasm.d.ts +2 -0
  110. package/esm/wasm.js +32 -0
  111. package/esm/wasm.js.map +1 -0
  112. package/esm/websocket.d.ts +45 -0
  113. package/esm/websocket.js +113 -0
  114. package/esm/websocket.js.map +1 -0
  115. package/esm/worker.d.ts +10 -0
  116. package/esm/worker.js +123 -0
  117. package/esm/worker.js.map +1 -0
  118. package/package.json +26 -0
@@ -0,0 +1,113 @@
1
+ import EventEmitter from 'events';
2
+ import { IntermediatePacketCodec, MtcuteError, MtUnsupportedError, ObfuscatedPacketCodec, TransportState, } from '@mtcute/core';
3
+ const subdomainsMap = {
4
+ '1': 'pluto',
5
+ '2': 'venus',
6
+ '3': 'aurora',
7
+ '4': 'vesta',
8
+ '5': 'flora',
9
+ };
10
+ /**
11
+ * Base for WebSocket transports.
12
+ * Subclasses must provide packet codec in `_packetCodec` property
13
+ */
14
+ export class BaseWebSocketTransport extends EventEmitter {
15
+ constructor({ ws = WebSocket, baseDomain = 'web.telegram.org', subdomains = subdomainsMap, } = {}) {
16
+ super();
17
+ this._currentDc = null;
18
+ this._state = TransportState.Idle;
19
+ this._socket = null;
20
+ this.packetCodecInitialized = false;
21
+ if (!ws) {
22
+ throw new MtUnsupportedError('To use WebSocket transport with NodeJS, install `ws` package and pass it to constructor');
23
+ }
24
+ // gotta love cjs/esm compat
25
+ if ('default' in ws) {
26
+ ws = ws.default;
27
+ }
28
+ this._baseDomain = baseDomain;
29
+ this._subdomains = subdomains;
30
+ this._WebSocket = ws;
31
+ this.close = this.close.bind(this);
32
+ }
33
+ _updateLogPrefix() {
34
+ if (this._currentDc) {
35
+ this.log.prefix = `[WS:${this._subdomains[this._currentDc.id]}.${this._baseDomain}] `;
36
+ }
37
+ else {
38
+ this.log.prefix = '[WS:disconnected] ';
39
+ }
40
+ }
41
+ setup(crypto, log) {
42
+ this._crypto = crypto;
43
+ this.log = log.create('ws');
44
+ }
45
+ state() {
46
+ return this._state;
47
+ }
48
+ currentDc() {
49
+ return this._currentDc;
50
+ }
51
+ connect(dc, testMode) {
52
+ if (this._state !== TransportState.Idle) {
53
+ throw new MtcuteError('Transport is not IDLE');
54
+ }
55
+ if (!this.packetCodecInitialized) {
56
+ this._packetCodec.setup?.(this._crypto, this.log);
57
+ this._packetCodec.on('error', (err) => this.emit('error', err));
58
+ this._packetCodec.on('packet', (buf) => this.emit('message', buf));
59
+ this.packetCodecInitialized = true;
60
+ }
61
+ this._state = TransportState.Connecting;
62
+ this._currentDc = dc;
63
+ this._socket = new this._WebSocket(`wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${testMode ? '_test' : ''}`, 'binary');
64
+ this._updateLogPrefix();
65
+ this.log.debug('connecting to %s (%j)', this._socket.url, dc);
66
+ this._socket.binaryType = 'arraybuffer';
67
+ this._socket.addEventListener('message', (evt) => this._packetCodec.feed(new Uint8Array(evt.data)));
68
+ this._socket.addEventListener('open', this.handleConnect.bind(this));
69
+ this._socket.addEventListener('error', this.handleError.bind(this));
70
+ this._socket.addEventListener('close', this.close);
71
+ }
72
+ close() {
73
+ if (this._state === TransportState.Idle)
74
+ return;
75
+ this.log.info('connection closed');
76
+ this.emit('close');
77
+ this._state = TransportState.Idle;
78
+ this._socket.removeEventListener('close', this.close);
79
+ this._socket.close();
80
+ this._socket = null;
81
+ this._currentDc = null;
82
+ this._packetCodec.reset();
83
+ }
84
+ handleError(event) {
85
+ const error = 'error' in event ? event.error : new Error('unknown WebSocket error');
86
+ this.log.error('error: %s', error.stack);
87
+ this.emit('error', error);
88
+ }
89
+ handleConnect() {
90
+ this.log.info('connected');
91
+ Promise.resolve(this._packetCodec.tag())
92
+ .then((initialMessage) => {
93
+ this._socket.send(initialMessage);
94
+ this._state = TransportState.Ready;
95
+ this.emit('ready');
96
+ })
97
+ .catch((err) => this.emit('error', err));
98
+ }
99
+ async send(bytes) {
100
+ if (this._state !== TransportState.Ready) {
101
+ throw new MtcuteError('Transport is not READY');
102
+ }
103
+ const framed = await this._packetCodec.encode(bytes);
104
+ this._socket.send(framed);
105
+ }
106
+ }
107
+ export class WebSocketTransport extends BaseWebSocketTransport {
108
+ constructor() {
109
+ super(...arguments);
110
+ this._packetCodec = new ObfuscatedPacketCodec(new IntermediatePacketCodec());
111
+ }
112
+ }
113
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/websocket.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'events'\n\nimport {\n IntermediatePacketCodec,\n IPacketCodec,\n ITelegramTransport,\n MtcuteError,\n MtUnsupportedError,\n ObfuscatedPacketCodec,\n TransportState,\n} from '@mtcute/core'\nimport { BasicDcOption, ICryptoProvider, Logger } from '@mtcute/core/utils.js'\n\nexport type WebSocketConstructor = {\n new (address: string, protocol?: string): WebSocket\n}\n\nconst subdomainsMap: Record<string, string> = {\n '1': 'pluto',\n '2': 'venus',\n '3': 'aurora',\n '4': 'vesta',\n '5': 'flora',\n}\n\n/**\n * Base for WebSocket transports.\n * Subclasses must provide packet codec in `_packetCodec` property\n */\nexport abstract class BaseWebSocketTransport extends EventEmitter implements ITelegramTransport {\n private _currentDc: BasicDcOption | null = null\n private _state: TransportState = TransportState.Idle\n private _socket: WebSocket | null = null\n private _crypto!: ICryptoProvider\n protected log!: Logger\n\n abstract _packetCodec: IPacketCodec\n packetCodecInitialized = false\n\n private _baseDomain: string\n private _subdomains: Record<string, string>\n private _WebSocket: WebSocketConstructor\n\n constructor({\n ws = WebSocket,\n baseDomain = 'web.telegram.org',\n subdomains = subdomainsMap,\n }: {\n /** Custom implementation of WebSocket (e.g. https://npm.im/ws) */\n ws?: WebSocketConstructor\n /** Base WebSocket domain */\n baseDomain?: string\n /** Map of sub-domains (key is DC ID, value is string) */\n subdomains?: Record<string, string>\n } = {}) {\n super()\n\n if (!ws) {\n throw new MtUnsupportedError(\n 'To use WebSocket transport with NodeJS, install `ws` package and pass it to constructor',\n )\n }\n\n // gotta love cjs/esm compat\n if ('default' in ws) {\n ws = ws.default as WebSocketConstructor\n }\n\n this._baseDomain = baseDomain\n this._subdomains = subdomains\n this._WebSocket = ws\n\n this.close = this.close.bind(this)\n }\n\n private _updateLogPrefix() {\n if (this._currentDc) {\n this.log.prefix = `[WS:${this._subdomains[this._currentDc.id]}.${this._baseDomain}] `\n } else {\n this.log.prefix = '[WS:disconnected] '\n }\n }\n\n setup(crypto: ICryptoProvider, log: Logger): void {\n this._crypto = crypto\n this.log = log.create('ws')\n }\n\n state(): TransportState {\n return this._state\n }\n\n currentDc(): BasicDcOption | null {\n return this._currentDc\n }\n\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._socket = new this._WebSocket(\n `wss://${this._subdomains[dc.id]}.${this._baseDomain}/apiws${testMode ? '_test' : ''}`,\n 'binary',\n )\n\n this._updateLogPrefix()\n this.log.debug('connecting to %s (%j)', this._socket.url, dc)\n\n this._socket.binaryType = 'arraybuffer'\n\n this._socket.addEventListener('message', (evt) =>\n this._packetCodec.feed(new Uint8Array(evt.data as ArrayBuffer)),\n )\n this._socket.addEventListener('open', this.handleConnect.bind(this))\n this._socket.addEventListener('error', this.handleError.bind(this))\n this._socket.addEventListener('close', this.close)\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!.removeEventListener('close', this.close)\n this._socket!.close()\n this._socket = null\n this._currentDc = null\n this._packetCodec.reset()\n }\n\n handleError(event: Event | { error: Error }): void {\n const error = 'error' in event ? event.error : new Error('unknown WebSocket error')\n\n this.log.error('error: %s', error.stack)\n this.emit('error', error)\n }\n\n handleConnect(): void {\n this.log.info('connected')\n\n Promise.resolve(this._packetCodec.tag())\n .then((initialMessage) => {\n this._socket!.send(initialMessage)\n this._state = TransportState.Ready\n this.emit('ready')\n })\n .catch((err) => this.emit('error', err))\n }\n\n async send(bytes: Uint8Array): Promise<void> {\n if (this._state !== TransportState.Ready) {\n throw new MtcuteError('Transport is not READY')\n }\n\n const framed = await this._packetCodec.encode(bytes)\n\n this._socket!.send(framed)\n }\n}\n\nexport class WebSocketTransport extends BaseWebSocketTransport {\n _packetCodec = new ObfuscatedPacketCodec(new IntermediatePacketCodec())\n}\n"]}
@@ -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/esm/worker.js ADDED
@@ -0,0 +1,123 @@
1
+ import { setPlatform } from '@mtcute/core/platform.js';
2
+ import { TelegramWorker as TelegramWorkerBase, TelegramWorkerPort as TelegramWorkerPortBase, } from '@mtcute/core/worker.js';
3
+ import { WebPlatform } from './platform.js';
4
+ let _registered = false;
5
+ export class TelegramWorker extends TelegramWorkerBase {
6
+ registerWorker(handler) {
7
+ if (_registered) {
8
+ throw new Error('TelegramWorker must be created only once');
9
+ }
10
+ _registered = true;
11
+ if (typeof SharedWorkerGlobalScope !== 'undefined' && self instanceof SharedWorkerGlobalScope) {
12
+ const connections = [];
13
+ const broadcast = (message) => {
14
+ for (const port of connections) {
15
+ port.postMessage(message);
16
+ }
17
+ };
18
+ self.onconnect = (event) => {
19
+ const port = event.ports[0];
20
+ connections.push(port);
21
+ const respond = port.postMessage.bind(port);
22
+ // not very reliable, but better than nothing
23
+ // SharedWorker API doesn't provide a way to detect when the client closes the connection
24
+ // so we just assume that the client is done when it sends a 'close' message
25
+ // and keep a timeout for the case when the client closes without sending a 'close' message
26
+ const onClose = () => {
27
+ port.close();
28
+ const idx = connections.indexOf(port);
29
+ if (idx >= 0) {
30
+ connections.splice(connections.indexOf(port), 1);
31
+ }
32
+ };
33
+ const onTimeout = () => {
34
+ console.warn('some connection timed out!');
35
+ respond({ __type__: 'timeout' });
36
+ onClose();
37
+ };
38
+ // 60s should be a reasonable timeout considering that the client should send a ping every 10s
39
+ // so even if the browser has suspended the timers, we should still get a ping within a minute
40
+ let timeout = setTimeout(onTimeout, 60000);
41
+ port.addEventListener('message', (message) => {
42
+ if (message.data.__type__ === 'close') {
43
+ onClose();
44
+ return;
45
+ }
46
+ if (message.data.__type__ === 'ping') {
47
+ clearTimeout(timeout);
48
+ timeout = setTimeout(onTimeout, 60000);
49
+ return;
50
+ }
51
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
52
+ handler(message.data, respond);
53
+ });
54
+ port.start();
55
+ };
56
+ return broadcast;
57
+ }
58
+ if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
59
+ const respond = self.postMessage.bind(self);
60
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
61
+ self.addEventListener('message', (message) => handler(message.data, respond));
62
+ return respond;
63
+ }
64
+ throw new Error('TelegramWorker must be created from a worker');
65
+ }
66
+ }
67
+ const platform = new WebPlatform();
68
+ export class TelegramWorkerPort extends TelegramWorkerPortBase {
69
+ constructor(options) {
70
+ setPlatform(platform);
71
+ super(options);
72
+ this.options = options;
73
+ }
74
+ connectToWorker(worker, handler) {
75
+ if (worker instanceof Worker) {
76
+ const send = worker.postMessage.bind(worker);
77
+ const messageHandler = (ev) => {
78
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
79
+ handler(ev.data);
80
+ };
81
+ worker.addEventListener('message', messageHandler);
82
+ return [
83
+ send,
84
+ () => {
85
+ worker.removeEventListener('message', messageHandler);
86
+ },
87
+ ];
88
+ }
89
+ if (worker instanceof SharedWorker) {
90
+ const send = worker.port.postMessage.bind(worker.port);
91
+ const pingInterval = setInterval(() => {
92
+ worker.port.postMessage({ __type__: 'ping' });
93
+ }, 10000);
94
+ const messageHandler = (ev) => {
95
+ if (ev.data.__type__ === 'timeout') {
96
+ // we got disconnected from the worker due to timeout
97
+ // if the page is still alive (which is unlikely), we should reconnect
98
+ // however it's not really possible with SharedWorker API without re-creating the worker
99
+ // so we just reload the page for now
100
+ location.reload();
101
+ return;
102
+ }
103
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
104
+ handler(ev.data);
105
+ };
106
+ worker.port.addEventListener('message', messageHandler);
107
+ worker.port.start();
108
+ // eslint-disable-next-line prefer-const
109
+ let cancelBeforeExit;
110
+ const close = () => {
111
+ clearInterval(pingInterval);
112
+ worker.port.postMessage({ __type__: 'close' });
113
+ worker.port.removeEventListener('message', messageHandler);
114
+ worker.port.close();
115
+ cancelBeforeExit();
116
+ };
117
+ cancelBeforeExit = platform.beforeExit(close);
118
+ return [send, close];
119
+ }
120
+ throw new Error('Only workers and shared workers are supported');
121
+ }
122
+ }
123
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { 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 { WebPlatform } from './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 (_registered) {\n throw new Error('TelegramWorker must be created only once')\n }\n\n _registered = true\n\n if (typeof SharedWorkerGlobalScope !== 'undefined' && self instanceof SharedWorkerGlobalScope) {\n const connections: MessagePort[] = []\n\n const broadcast = (message: unknown) => {\n for (const port of connections) {\n port.postMessage(message)\n }\n }\n\n self.onconnect = (event) => {\n const port = event.ports[0]\n connections.push(port)\n\n const respond = port.postMessage.bind(port)\n\n // not very reliable, but better than nothing\n // SharedWorker API doesn't provide a way to detect when the client closes the connection\n // so we just assume that the client is done when it sends a 'close' message\n // and keep a timeout for the case when the client closes without sending a 'close' message\n const onClose = () => {\n port.close()\n const idx = connections.indexOf(port)\n\n if (idx >= 0) {\n connections.splice(connections.indexOf(port), 1)\n }\n }\n\n const onTimeout = () => {\n console.warn('some connection timed out!')\n respond({ __type__: 'timeout' })\n onClose()\n }\n\n // 60s should be a reasonable timeout considering that the client should send a ping every 10s\n // so even if the browser has suspended the timers, we should still get a ping within a minute\n let timeout = setTimeout(onTimeout, 60000)\n\n port.addEventListener('message', (message) => {\n if (message.data.__type__ === 'close') {\n onClose()\n\n return\n }\n\n if (message.data.__type__ === 'ping') {\n clearTimeout(timeout)\n timeout = setTimeout(onTimeout, 60000)\n\n return\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n handler(message.data, respond)\n })\n port.start()\n }\n\n return broadcast\n }\n\n if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {\n const respond: RespondFn = self.postMessage.bind(self)\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n self.addEventListener('message', (message) => handler(message.data, respond))\n\n return respond\n }\n\n throw new Error('TelegramWorker must be created from a worker')\n }\n}\n\nconst platform = new WebPlatform()\n\nexport class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {\n constructor(readonly options: TelegramWorkerPortOptions) {\n setPlatform(platform)\n super(options)\n }\n\n connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] {\n if (worker instanceof Worker) {\n const send: SendFn = worker.postMessage.bind(worker)\n\n const messageHandler = (ev: MessageEvent) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n handler(ev.data)\n }\n\n worker.addEventListener('message', messageHandler)\n\n return [\n send,\n () => {\n worker.removeEventListener('message', messageHandler)\n },\n ]\n }\n\n if (worker instanceof SharedWorker) {\n const send: SendFn = worker.port.postMessage.bind(worker.port)\n\n const pingInterval = setInterval(() => {\n worker.port.postMessage({ __type__: 'ping' })\n }, 10000)\n\n const messageHandler = (ev: MessageEvent) => {\n if (ev.data.__type__ === 'timeout') {\n // we got disconnected from the worker due to timeout\n // if the page is still alive (which is unlikely), we should reconnect\n // however it's not really possible with SharedWorker API without re-creating the worker\n // so we just reload the page for now\n location.reload()\n\n return\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n handler(ev.data)\n }\n\n worker.port.addEventListener('message', messageHandler)\n worker.port.start()\n\n // eslint-disable-next-line prefer-const\n let cancelBeforeExit: () => void\n\n const close = () => {\n clearInterval(pingInterval)\n worker.port.postMessage({ __type__: 'close' })\n worker.port.removeEventListener('message', messageHandler)\n worker.port.close()\n cancelBeforeExit()\n }\n\n cancelBeforeExit = platform.beforeExit(close)\n\n return [send, close]\n }\n\n throw new Error('Only workers and shared workers are supported')\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@mtcute/web",
3
+ "version": "0.8.0",
4
+ "description": "Meta-package for the web platform",
5
+ "author": "Alina Sireneva <alina@tei.su>",
6
+ "license": "MIT",
7
+ "main": "cjs/index.js",
8
+ "type": "module",
9
+ "sideEffects": false,
10
+ "scripts": {},
11
+ "exports": {
12
+ ".": {
13
+ "import": "./esm/index.js",
14
+ "require": "./cjs/index.js"
15
+ },
16
+ "./utils.js": {
17
+ "import": "./esm/utils.js",
18
+ "require": "./cjs/utils.js"
19
+ }
20
+ },
21
+ "dependencies": {
22
+ "@mtcute/core": "^0.8.0",
23
+ "@mtcute/wasm": "^0.8.0"
24
+ },
25
+ "module": "esm/index.js"
26
+ }