summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/example/web/tdweb/src
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/example/web/tdweb/src')
-rw-r--r--protocols/Telegram/tdlib/td/example/web/tdweb/src/index.js680
-rw-r--r--protocols/Telegram/tdlib/td/example/web/tdweb/src/logger.js47
-rw-r--r--protocols/Telegram/tdlib/td/example/web/tdweb/src/wasm-utils.js136
-rw-r--r--protocols/Telegram/tdlib/td/example/web/tdweb/src/worker.js1034
4 files changed, 0 insertions, 1897 deletions
diff --git a/protocols/Telegram/tdlib/td/example/web/tdweb/src/index.js b/protocols/Telegram/tdlib/td/example/web/tdweb/src/index.js
deleted file mode 100644
index 2f159b9424..0000000000
--- a/protocols/Telegram/tdlib/td/example/web/tdweb/src/index.js
+++ /dev/null
@@ -1,680 +0,0 @@
-import MyWorker from './worker.js';
-//import localforage from 'localforage';
-import BroadcastChannel from 'broadcast-channel';
-import uuid4 from 'uuid/v4';
-import log from './logger.js';
-
-const sleep = ms => new Promise(res => setTimeout(res, ms));
-
-/**
- * TDLib in a browser
- *
- * TDLib can be compiled to WebAssembly or asm.js using Emscripten compiler and used in a browser from JavaScript.
- * This is a convenient wrapper for TDLib in a browser which controls TDLib instance creation, handles interaction
- * with TDLib and manages a filesystem for persistent TDLib data.
- * TDLib instance is created in a Web Worker to run it in a separate thread.
- * TdClient just sends queries to the Web Worker and receives updates and results from it.
- * <br>
- * <br>
- * Differences from the TDLib JSON API:<br>
- * 1. Added the update <code>updateFatalError error:string = Update;</code> which is sent whenever TDLib encounters a fatal error.<br>
- * 2. Added the method <code>setJsLogVerbosityLevel new_verbosity_level:string = Ok;</code>, which allows to change the verbosity level of tdweb logging.<br>
- * 3. Added the possibility to use blobs as input files via the constructor <code>inputFileBlob data:<JavaScript blob> = InputFile;</code>.<br>
- * 4. The class <code>filePart</code> contains data as a JavaScript blob instead of a base64-encoded string.<br>
- * 5. The methods <code>getStorageStatistics</code>, <code>getStorageStatisticsFast</code>, <code>optimizeStorage</code>, <code>addProxy</code> and <code>getFileDownloadedPrefixSize</code> are not supported.<br>
- * <br>
- */
-class TdClient {
- /**
- * @callback TdClient~updateCallback
- * @param {Object} update The update.
- */
-
- /**
- * Create TdClient.
- * @param {Object} options - Options for TDLib instance creation.
- * @param {TdClient~updateCallback} options.onUpdate - Callback for all incoming updates.
- * @param {string} [options.instanceName=tdlib] - Name of the TDLib instance. Currently only one instance of TdClient with a given name is allowed. All but one instances with the same name will be automatically closed. Usually, the newest non-background instance is kept alive. Files will be stored in an IndexedDb table with the same name.
- * @param {boolean} [options.isBackground=false] - Pass true if the instance is opened from the background.
- * @param {string} [options.jsLogVerbosityLevel=info] - The initial verbosity level of the JavaScript part of the code (one of 'error', 'warning', 'info', 'log', 'debug').
- * @param {number} [options.logVerbosityLevel=2] - The initial verbosity level for the TDLib internal logging (0-1023).
- * @param {boolean} [options.useDatabase=true] - Pass false to use TDLib without database and secret chats. It will significantly improve loading time, but some functionality will be unavailable.
- * @param {boolean} [options.readOnly=false] - For debug only. Pass true to open TDLib database in read-only mode
- * @param {string} [options.mode=auto] - For debug only. The type of the TDLib build to use. 'asmjs' for asm.js and 'wasm' for WebAssembly. If mode == 'auto' WebAbassembly will be used if supported by browser, asm.js otherwise.
- */
- constructor(options) {
- log.setVerbosity(options.jsLogVerbosityLevel);
- this.worker = new MyWorker();
- this.worker.onmessage = e => {
- this.onResponse(e.data);
- };
- this.query_id = 0;
- this.query_callbacks = new Map();
- if ('onUpdate' in options) {
- this.onUpdate = options.onUpdate;
- delete options.onUpdate;
- }
- options.instanceName = options.instanceName || 'tdlib';
- this.fileManager = new FileManager(options.instanceName, this);
- this.worker.postMessage({ '@type': 'init', options: options });
- this.closeOtherClients(options);
- }
-
- /**
- * Send a query to TDLib.
- *
- * If the query contains the field '@extra', the same field will be added into the result.
- *
- * @param {Object} query - The query for TDLib. See the [td_api.tl]{@link https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl} scheme or
- * the automatically generated [HTML documentation]{@link https://core.telegram.org/tdlib/docs/td__api_8h.html}
- * for a list of all available TDLib [methods]{@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html} and
- * [classes]{@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html}.
- * @returns {Promise} Promise object represents the result of the query.
- */
- send(query) {
- return this.doSend(query, true);
- }
-
- /** @private */
- sendInternal(query) {
- return this.doSend(query, false);
- }
- /** @private */
- doSend(query, isExternal) {
- this.query_id++;
- if (query['@extra']) {
- query['@extra'] = {
- '@old_extra': JSON.parse(JSON.stringify(query['@extra'])),
- query_id: this.query_id
- };
- } else {
- query['@extra'] = {
- query_id: this.query_id
- };
- }
- if (query['@type'] === 'setJsLogVerbosityLevel') {
- log.setVerbosity(query.new_verbosity_level);
- }
-
- log.debug('send to worker: ', query);
- const res = new Promise((resolve, reject) => {
- this.query_callbacks.set(this.query_id, [resolve, reject]);
- });
- if (isExternal) {
- this.externalPostMessage(query);
- } else {
- this.worker.postMessage(query);
- }
- return res;
- }
-
- /** @private */
- externalPostMessage(query) {
- const unsupportedMethods = [
- 'getStorageStatistics',
- 'getStorageStatisticsFast',
- 'optimizeStorage',
- 'addProxy',
- 'init',
- 'start'
- ];
- if (unsupportedMethods.includes(query['@type'])) {
- this.onResponse({
- '@type': 'error',
- '@extra': query['@extra'],
- code: 400,
- message: "Method '" + query['@type'] + "' is not supported"
- });
- return;
- }
- if (query['@type'] === 'readFile' || query['@type'] === 'readFilePart') {
- this.readFile(query);
- return;
- }
- if (query['@type'] === 'deleteFile') {
- this.deleteFile(query);
- return;
- }
- this.worker.postMessage(query);
- }
-
- /** @private */
- async readFile(query) {
- const response = await this.fileManager.readFile(query);
- this.onResponse(response);
- }
-
- /** @private */
- async deleteFile(query) {
- const response = this.fileManager.deleteFile(query);
- try {
- if (response.idb_key) {
- await this.sendInternal({
- '@type': 'deleteIdbKey',
- idb_key: response.idb_key
- });
- delete response.idb_key;
- }
- await this.sendInternal({
- '@type': 'deleteFile',
- file_id: query.file_id
- });
- } catch (e) {}
- this.onResponse(response);
- }
-
- /** @private */
- onResponse(response) {
- log.debug(
- 'receive from worker: ',
- JSON.parse(
- JSON.stringify(response, (key, value) => {
- if (key === 'arr' || key === 'data') {
- return undefined;
- }
- return value;
- })
- )
- );
-
- // for FileManager
- response = this.prepareResponse(response);
-
- if ('@extra' in response) {
- const query_id = response['@extra'].query_id;
- const [resolve, reject] = this.query_callbacks.get(query_id);
- this.query_callbacks.delete(query_id);
- if ('@old_extra' in response['@extra']) {
- response['@extra'] = response['@extra']['@old_extra'];
- }
- if (resolve) {
- if (response['@type'] === 'error') {
- reject(response);
- } else {
- resolve(response);
- }
- }
- } else {
- if (response['@type'] === 'inited') {
- this.onInited();
- return;
- }
- if (response['@type'] === 'fsInited') {
- this.onFsInited();
- return;
- }
- if (
- response['@type'] === 'updateAuthorizationState' &&
- response.authorization_state['@type'] === 'authorizationStateClosed'
- ) {
- this.onClosed();
- }
- this.onUpdate(response);
- }
- }
-
- /** @private */
- prepareFile(file) {
- return this.fileManager.registerFile(file);
- }
-
- /** @private */
- prepareResponse(response) {
- if (response['@type'] === 'file') {
- if (false && Math.random() < 0.1) {
- (async () => {
- log.warn('DELETE FILE', response.id);
- try {
- await this.send({ '@type': 'deleteFile', file_id: response.id });
- } catch (e) {}
- })();
- }
- return this.prepareFile(response);
- }
- for (const key in response) {
- const field = response[key];
- if (
- field &&
- typeof field === 'object' &&
- key !== 'data' &&
- key !== 'arr'
- ) {
- response[key] = this.prepareResponse(field);
- }
- }
- return response;
- }
-
- /** @private */
- onBroadcastMessage(e) {
- //const message = e.data;
- const message = e;
- if (message.uid === this.uid) {
- log.info('ignore self broadcast message: ', message);
- return;
- }
- log.info('got broadcast message: ', message);
- if (message.isBackground && !this.isBackground) {
- // continue
- } else if (
- (!message.isBackground && this.isBackground) ||
- message.timestamp > this.timestamp
- ) {
- this.close();
- return;
- }
- if (message.state === 'closed') {
- this.waitSet.delete(message.uid);
- if (this.waitSet.size === 0) {
- log.info('onWaitSetEmpty');
- this.onWaitSetEmpty();
- this.onWaitSetEmpty = () => {};
- }
- } else {
- this.waitSet.add(message.uid);
- if (message.state !== 'closing') {
- this.postState();
- }
- }
- }
-
- /** @private */
- postState() {
- const state = {
- uid: this.uid,
- state: this.state,
- timestamp: this.timestamp,
- isBackground: this.isBackground
- };
- log.info('Post state: ', state);
- this.channel.postMessage(state);
- }
-
- /** @private */
- onWaitSetEmpty() {
- // nop
- }
-
- /** @private */
- onFsInited() {
- this.fileManager.init();
- }
-
- /** @private */
- onInited() {
- this.isInited = true;
- this.doSendStart();
- }
-
- /** @private */
- sendStart() {
- this.wantSendStart = true;
- this.doSendStart();
- }
-
- /** @private */
- doSendStart() {
- if (!this.isInited || !this.wantSendStart || this.state !== 'start') {
- return;
- }
- this.wantSendStart = false;
- this.state = 'active';
- const query = { '@type': 'start' };
- log.info('send to worker: ', query);
- this.worker.postMessage(query);
- }
-
- /** @private */
- onClosed() {
- this.isClosing = true;
- this.worker.terminate();
- log.info('worker is terminated');
- this.state = 'closed';
- this.postState();
- }
-
- /** @private */
- close() {
- if (this.isClosing) {
- return;
- }
- this.isClosing = true;
-
- log.info('close state: ', this.state);
-
- if (this.state === 'start') {
- this.onClosed();
- this.onUpdate({
- '@type': 'updateAuthorizationState',
- authorization_state: {
- '@type': 'authorizationStateClosed'
- }
- });
- return;
- }
-
- const query = { '@type': 'close' };
- log.info('send to worker: ', query);
- this.worker.postMessage(query);
-
- this.state = 'closing';
- this.postState();
- }
-
- /** @private */
- async closeOtherClients(options) {
- this.uid = uuid4();
- this.state = 'start';
- this.isBackground = !!options.isBackground;
- this.timestamp = Date.now();
- this.waitSet = new Set();
-
- log.info('close other clients');
- this.channel = new BroadcastChannel(options.instanceName, {
- webWorkerSupport: false
- });
-
- this.postState();
-
- this.channel.onmessage = message => {
- this.onBroadcastMessage(message);
- };
-
- await sleep(300);
- if (this.waitSet.size !== 0) {
- await new Promise(resolve => {
- this.onWaitSetEmpty = resolve;
- });
- }
- this.sendStart();
- }
-
- /** @private */
- onUpdate(update) {
- log.info('ignore onUpdate');
- //nop
- }
-}
-
-/** @private */
-class ListNode {
- constructor(value) {
- this.value = value;
- this.clear();
- }
-
- erase() {
- this.prev.connect(this.next);
- this.clear();
- }
- clear() {
- this.prev = this;
- this.next = this;
- }
-
- connect(other) {
- this.next = other;
- other.prev = this;
- }
-
- onUsed(other) {
- other.usedAt = Date.now();
- other.erase();
- other.connect(this.next);
- log.debug('LRU: used file_id: ', other.value);
- this.connect(other);
- }
-
- getLru() {
- if (this === this.next) {
- throw new Error('popLru from empty list');
- }
- return this.prev;
- }
-}
-
-/** @private */
-class FileManager {
- constructor(instanceName, client) {
- this.instanceName = instanceName;
- this.cache = new Map();
- this.pending = [];
- this.transaction_id = 0;
- this.totalSize = 0;
- this.lru = new ListNode(-1);
- this.client = client;
- }
-
- init() {
- this.idb = new Promise((resolve, reject) => {
- const request = indexedDB.open(this.instanceName);
- request.onsuccess = () => resolve(request.result);
- request.onerror = () => reject(request.error);
- });
- //this.store = localforage.createInstance({
- //name: instanceName
- //});
- this.isInited = true;
- }
-
- unload(info) {
- if (info.arr) {
- log.debug(
- 'LRU: delete file_id: ',
- info.node.value,
- ' with arr.length: ',
- info.arr.length
- );
- this.totalSize -= info.arr.length;
- delete info.arr;
- }
- if (info.node) {
- info.node.erase();
- delete info.node;
- }
- }
-
- registerFile(file) {
- if (file.idb_key || file.arr) {
- file.local.is_downloading_completed = true;
- } else {
- file.local.is_downloading_completed = false;
- }
- let info = {};
- const cached_info = this.cache.get(file.id);
- if (cached_info) {
- info = cached_info;
- } else {
- this.cache.set(file.id, info);
- }
- if (file.idb_key) {
- info.idb_key = file.idb_key;
- delete file.idb_key;
- } else {
- delete info.idb_key;
- }
- if (file.arr) {
- const now = Date.now();
- while (this.totalSize > 100000000) {
- const node = this.lru.getLru();
- // immunity for 60 seconds
- if (node.usedAt + 60 * 1000 > now) {
- break;
- }
- const lru_info = this.cache.get(node.value);
- this.unload(lru_info);
- }
-
- if (info.arr) {
- log.warn('Got file.arr at least twice for the same file');
- this.totalSize -= info.arr.length;
- }
- info.arr = file.arr;
- delete file.arr;
- this.totalSize += info.arr.length;
- if (!info.node) {
- log.debug(
- 'LRU: create file_id: ',
- file.id,
- ' with arr.length: ',
- info.arr.length
- );
- info.node = new ListNode(file.id);
- }
- this.lru.onUsed(info.node);
- log.info('Total file.arr size: ', this.totalSize);
- }
- info.file = file;
- return file;
- }
-
- async flushLoad() {
- const pending = this.pending;
- this.pending = [];
- const idb = await this.idb;
- const transaction_id = this.transaction_id++;
- const read = idb
- .transaction(['keyvaluepairs'], 'readonly')
- .objectStore('keyvaluepairs');
- log.debug('Load group of files from idb', pending.length);
- for (const query of pending) {
- const request = read.get(query.key);
- request.onsuccess = event => {
- const blob = event.target.result;
- if (blob) {
- if (blob.size === 0) {
- log.error('Got empty blob from db ', query.key);
- }
- query.resolve({ data: blob, transaction_id: transaction_id });
- } else {
- query.reject();
- }
- };
- request.onerror = () => query.reject(request.error);
- }
- }
-
- load(key, resolve, reject) {
- if (this.pending.length === 0) {
- setTimeout(() => {
- this.flushLoad();
- }, 1);
- }
- this.pending.push({ key: key, resolve: resolve, reject: reject });
- }
-
- async doLoadFull(info) {
- if (info.arr) {
- return { data: new Blob([info.arr]), transaction_id: -1 };
- }
- if (info.idb_key) {
- const idb_key = info.idb_key;
- //return this.store.getItem(idb_key);
- return await new Promise((resolve, reject) => {
- this.load(idb_key, resolve, reject);
- });
- }
- throw new Error('File is not loaded');
- }
- async doLoad(info, offset, size) {
- if (!info.arr && !info.idb_key && info.file.local.path) {
- try {
- const count = await this.client.sendInternal({
- '@type': 'getFileDownloadedPrefixSize',
- file_id: info.file.id,
- offset: offset
- });
- //log.error(count, size);
- if (!size) {
- size = count.count;
- } else if (size > count.count) {
- throw new Error('File not loaded yet');
- }
- const res = await this.client.sendInternal({
- '@type': 'readFilePart',
- path: info.file.local.path,
- offset: offset,
- count: size
- });
- res.data = new Blob([res.data]);
- res.transaction_id = -2;
- //log.error(res);
- return res;
- } catch (e) {
- log.info('readFilePart failed', info, offset, size, e);
- }
- }
-
- const res = await this.doLoadFull(info);
-
- // return slice(size, offset + size)
- const data_size = res.data.size;
- if (!size) {
- size = data_size;
- }
- if (offset > data_size) {
- offset = data_size;
- }
- res.data = res.data.slice(offset, offset + size);
- return res;
- }
-
- doDelete(info) {
- this.unload(info);
- return info.idb_key;
- }
-
- async readFile(query) {
- try {
- if (!this.isInited) {
- throw new Error('FileManager is not inited');
- }
- const info = this.cache.get(query.file_id);
- if (!info) {
- throw new Error('File is not loaded');
- }
- if (info.node) {
- this.lru.onUsed(info.node);
- }
- query.offset = query.offset || 0;
- query.size = query.count || query.size || 0;
- const response = await this.doLoad(info, query.offset, query.size);
- return {
- '@type': 'filePart',
- '@extra': query['@extra'],
- data: response.data,
- transaction_id: response.transaction_id
- };
- } catch (e) {
- return {
- '@type': 'error',
- '@extra': query['@extra'],
- code: 400,
- message: e
- };
- }
- }
-
- deleteFile(query) {
- const res = {
- '@type': 'ok',
- '@extra': query['@extra']
- };
- try {
- if (!this.isInited) {
- throw new Error('FileManager is not inited');
- }
- const info = this.cache.get(query.file_id);
- if (!info) {
- throw new Error('File is not loaded');
- }
- const idb_key = this.doDelete(info);
- if (idb_key) {
- res.idb_key = idb_key;
- }
- } catch (e) {}
- return res;
- }
-}
-
-export default TdClient;
diff --git a/protocols/Telegram/tdlib/td/example/web/tdweb/src/logger.js b/protocols/Telegram/tdlib/td/example/web/tdweb/src/logger.js
deleted file mode 100644
index 95baed0318..0000000000
--- a/protocols/Telegram/tdlib/td/example/web/tdweb/src/logger.js
+++ /dev/null
@@ -1,47 +0,0 @@
-class Logger {
- constructor() {
- this.setVerbosity('WARNING');
- }
- debug(...str) {
- if (this.checkVerbosity(4)) {
- console.log(...str);
- }
- }
- log(...str) {
- if (this.checkVerbosity(4)) {
- console.log(...str);
- }
- }
- info(...str) {
- if (this.checkVerbosity(3)) {
- console.info(...str);
- }
- }
- warn(...str) {
- if (this.checkVerbosity(2)) {
- console.warn(...str);
- }
- }
- error(...str) {
- if (this.checkVerbosity(1)) {
- console.error(...str);
- }
- }
- setVerbosity(level, default_level = 'info') {
- if (level === undefined) {
- level = default_level;
- }
- if (typeof level === 'string') {
- level =
- { ERROR: 1, WARNING: 2, INFO: 3, LOG: 4, DEBUG: 4 }[
- level.toUpperCase()
- ] || 2;
- }
- this.level = level;
- }
- checkVerbosity(level) {
- return this.level >= level;
- }
-}
-let log = new Logger();
-export default log;
diff --git a/protocols/Telegram/tdlib/td/example/web/tdweb/src/wasm-utils.js b/protocols/Telegram/tdlib/td/example/web/tdweb/src/wasm-utils.js
deleted file mode 100644
index 50447d65b3..0000000000
--- a/protocols/Telegram/tdlib/td/example/web/tdweb/src/wasm-utils.js
+++ /dev/null
@@ -1,136 +0,0 @@
-// 1. +++ fetchAndInstantiate() +++ //
-
-// This library function fetches the wasm module at 'url', instantiates it with
-// the given 'importObject', and returns the instantiated object instance
-
-export async function instantiateStreaming(url, importObject) {
- let result = await WebAssembly.instantiateStreaming(fetch(url), importObject);
- return result.instance;
-}
-export function fetchAndInstantiate(url, importObject) {
- return fetch(url)
- .then(response => response.arrayBuffer())
- .then(bytes => WebAssembly.instantiate(bytes, importObject))
- .then(results => results.instance);
-}
-
-// 2. +++ instantiateCachedURL() +++ //
-
-// This library function fetches the wasm Module at 'url', instantiates it with
-// the given 'importObject', and returns a Promise resolving to the finished
-// wasm Instance. Additionally, the function attempts to cache the compiled wasm
-// Module in IndexedDB using 'url' as the key. The entire site's wasm cache (not
-// just the given URL) is versioned by dbVersion and any change in dbVersion on
-// any call to instantiateCachedURL() will conservatively clear out the entire
-// cache to avoid stale modules.
-export function instantiateCachedURL(dbVersion, url, importObject) {
- const dbName = 'wasm-cache';
- const storeName = 'wasm-cache';
-
- // This helper function Promise-ifies the operation of opening an IndexedDB
- // database and clearing out the cache when the version changes.
- function openDatabase() {
- return new Promise((resolve, reject) => {
- var request = indexedDB.open(dbName, dbVersion);
- request.onerror = reject.bind(null, 'Error opening wasm cache database');
- request.onsuccess = () => {
- resolve(request.result);
- };
- request.onupgradeneeded = event => {
- var db = request.result;
- if (db.objectStoreNames.contains(storeName)) {
- console.log(`Clearing out version ${event.oldVersion} wasm cache`);
- db.deleteObjectStore(storeName);
- }
- console.log(`Creating version ${event.newVersion} wasm cache`);
- db.createObjectStore(storeName);
- };
- });
- }
-
- // This helper function Promise-ifies the operation of looking up 'url' in the
- // given IDBDatabase.
- function lookupInDatabase(db) {
- return new Promise((resolve, reject) => {
- var store = db.transaction([storeName]).objectStore(storeName);
- var request = store.get(url);
- request.onerror = reject.bind(null, `Error getting wasm module ${url}`);
- request.onsuccess = event => {
- if (request.result) resolve(request.result);
- else reject(`Module ${url} was not found in wasm cache`);
- };
- });
- }
-
- // This helper function fires off an async operation to store the given wasm
- // Module in the given IDBDatabase.
- function storeInDatabase(db, module) {
- var store = db.transaction([storeName], 'readwrite').objectStore(storeName);
- var request = store.put(module, url);
- request.onerror = err => {
- console.log(`Failed to store in wasm cache: ${err}`);
- };
- request.onsuccess = err => {
- console.log(`Successfully stored ${url} in wasm cache`);
- };
- }
-
- // This helper function fetches 'url', compiles it into a Module,
- // instantiates the Module with the given import object.
- function fetchAndInstantiate() {
- return fetch(url)
- .then(response => response.arrayBuffer())
- .then(buffer => WebAssembly.instantiate(buffer, importObject));
- }
-
- // With all the Promise helper functions defined, we can now express the core
- // logic of an IndexedDB cache lookup. We start by trying to open a database.
- return openDatabase().then(
- db => {
- // Now see if we already have a compiled Module with key 'url' in 'db':
- return lookupInDatabase(db).then(
- module => {
- // We do! Instantiate it with the given import object.
- console.log(`Found ${url} in wasm cache`);
- return WebAssembly.instantiate(module, importObject);
- },
- errMsg => {
- // Nope! Compile from scratch and then store the compiled Module in 'db'
- // with key 'url' for next time.
- console.log(errMsg);
- return fetchAndInstantiate().then(results => {
- try {
- storeInDatabase(db, results.module);
- } catch (e) {
- console.log('Failed to store module into db');
- }
- return results.instance;
- });
- }
- );
- },
- errMsg => {
- // If opening the database failed (due to permissions or quota), fall back
- // to simply fetching and compiling the module and don't try to store the
- // results.
- console.log(errMsg);
- return fetchAndInstantiate().then(results => results.instance);
- }
- );
-}
-
-export async function instantiateAny(version, url, importObject) {
- console.log("instantiate");
- try {
- return await instantiateStreaming(url, importObject);
- } catch (e) {
- console.log("instantiateStreaming failed", e);
- }
- try {
- return await instantiateCachedURL(version, url, importObject);
- } catch (e) {
- console.log("instantiateCachedURL failed", e);
- }
- throw new Error("can't instantiate wasm");
-}
-
diff --git a/protocols/Telegram/tdlib/td/example/web/tdweb/src/worker.js b/protocols/Telegram/tdlib/td/example/web/tdweb/src/worker.js
deleted file mode 100644
index dff1845126..0000000000
--- a/protocols/Telegram/tdlib/td/example/web/tdweb/src/worker.js
+++ /dev/null
@@ -1,1034 +0,0 @@
-import localforage from 'localforage';
-import log from './logger.js';
-import { instantiateAny } from './wasm-utils.js';
-
-import td_wasm_release from './prebuilt/release/td_wasm.wasm';
-import td_asmjs_mem_release from './prebuilt/release/td_asmjs.js.mem';
-
-const tdlibVersion = 6;
-const localForageDrivers = [
- localforage.INDEXEDDB,
- localforage.LOCALSTORAGE,
- 'memoryDriver'
-];
-
-async function initLocalForage() {
- // Implement the driver here.
- const memoryDriver = {
- _driver: 'memoryDriver',
- _initStorage: function(options) {
- const dbInfo = {};
- if (options) {
- for (const i in options) {
- dbInfo[i] = options[i];
- }
- }
- this._dbInfo = dbInfo;
- this._map = new Map();
- },
- clear: async function() {
- this._map.clear();
- },
- getItem: async function(key) {
- const value = this._map.get(key);
- console.log('getItem', this._map, key, value);
- return value;
- },
- iterate: async function(iteratorCallback) {
- log.error('iterate is not supported');
- },
- key: async function(n) {
- log.error('key n is not supported');
- },
- keys: async function() {
- return this._map.keys();
- },
- length: async function() {
- return this._map.size();
- },
- removeItem: async function(key) {
- this._map.delete(key);
- },
- setItem: async function(key, value) {
- const originalValue = this._map.get(key);
- console.log('setItem', this._map, key, value);
- this._map.set(key, value);
- return originalValue;
- }
- };
-
- // Add the driver to localForage.
- localforage.defineDriver(memoryDriver);
-}
-
-async function loadTdlibWasm(onFS, wasmUrl) {
- console.log('loadTdlibWasm');
- const td_module = await import('./prebuilt/release/td_wasm.js');
- const createTdwebModule = td_module.default;
- log.info('got td_wasm.js', td_module, createTdwebModule);
- let td_wasm = td_wasm_release;
- if (wasmUrl) {
- td_wasm = wasmUrl;
- }
- let module = createTdwebModule({
- onRuntimeInitialized: () => {
- log.info('runtime intialized');
- onFS(module.FS);
- },
- instantiateWasm: (imports, successCallback) => {
- log.info('start instantiateWasm', td_wasm, imports);
- const next = instance => {
- log.info('finish instantiateWasm');
- successCallback(instance);
- };
- instantiateAny(tdlibVersion, td_wasm, imports).then(next);
- return {};
- },
- ENVIROMENT: 'WORKER'
- });
- log.info('Wait module');
- module = await module;
- log.info('Got module', module);
- //onFS(module.FS);
- return module;
-}
-
-async function loadTdlibAsmjs(onFS) {
- console.log('loadTdlibAsmjs');
- const createTdwebModule = (await import('./prebuilt/release/td_asmjs.js'))
- .default;
- console.log('got td_asm.js', createTdwebModule);
- const fromFile = 'td_asmjs.js.mem';
- const toFile = td_asmjs_mem_release;
- let module = createTdwebModule({
- onRuntimeInitialized: () => {
- console.log('runtime intialized');
- onFS(module.FS);
- },
- locateFile: name => {
- if (name === fromFile) {
- return toFile;
- }
- return name;
- },
- ENVIROMENT: 'WORKER'
- });
- log.info('Wait module');
- module = await module;
- log.info('Got module', module);
- //onFS(module.FS);
- return module;
-}
-
-async function loadTdlib(mode, onFS, wasmUrl) {
- const wasmSupported = (() => {
- try {
- if (
- typeof WebAssembly === 'object' &&
- typeof WebAssembly.instantiate === 'function'
- ) {
- const module = new WebAssembly.Module(
- Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
- );
- if (module instanceof WebAssembly.Module)
- return (
- new WebAssembly.Instance(module) instanceof WebAssembly.Instance
- );
- }
- } catch (e) {}
- return false;
- })();
- if (!wasmSupported) {
- if (mode === 'wasm') {
- log.error('WebAssembly is not supported, trying to use it anyway');
- } else {
- log.warn('WebAssembly is not supported, trying to use asm.js');
- mode = 'asmjs';
- }
- }
-
- if (mode === 'asmjs') {
- return loadTdlibAsmjs(onFS);
- }
- return loadTdlibWasm(onFS, wasmUrl);
-}
-
-class OutboundFileSystem {
- constructor(root, FS) {
- this.root = root;
- this.nextFileId = 0;
- this.FS = FS;
- this.files = new Set();
- FS.mkdir(root);
- }
- blobToPath(blob, name) {
- const dir = this.root + '/' + this.nextFileId;
- if (!name) {
- name = 'blob';
- }
- this.nextFileId++;
- this.FS.mkdir(dir);
- this.FS.mount(
- this.FS.filesystems.WORKERFS,
- {
- blobs: [{ name: name, data: blob }]
- },
- dir
- );
- const path = dir + '/' + name;
- this.files.add(path);
- return path;
- }
-
- forgetPath(path) {
- if (this.files.has(path)) {
- this.FS.unmount(path);
- this.files.delete(path);
- }
- }
-}
-
-class InboundFileSystem {
- static async create(dbName, root, FS_promise) {
- const start = performance.now();
- try {
- const ifs = new InboundFileSystem();
- ifs.pending = [];
- ifs.pendingHasTimeout = false;
- ifs.persistCount = 0;
- ifs.persistSize = 0;
- ifs.pendingI = 0;
- ifs.inPersist = false;
- ifs.totalCount = 0;
-
- ifs.root = root;
-
- //ifs.store = localforage.createInstance({
- //name: dbName,
- //driver: localForageDrivers
- //});
- log.debug('IDB name: ' + dbName);
- ifs.idb = new Promise((resolve, reject) => {
- const request = indexedDB.open(dbName);
- request.onsuccess = () => resolve(request.result);
- request.onerror = () => reject(request.error);
- request.onupgradeneeded = () => {
- request.result.createObjectStore('keyvaluepairs');
- };
- });
-
- ifs.load_pids();
-
- const FS = await FS_promise;
- await ifs.idb;
- ifs.FS = FS;
- ifs.FS.mkdir(root);
- const create_time = (performance.now() - start) / 1000;
- log.debug('InboundFileSystem::create ' + create_time);
- return ifs;
- } catch (e) {
- log.error('Failed to init Inbound FileSystem: ', e);
- }
- }
-
- async load_pids() {
- const keys_start = performance.now();
- log.debug('InboundFileSystem::create::keys start');
- //const keys = await this.store.keys();
-
- let idb = await this.idb;
- let read = idb
- .transaction(['keyvaluepairs'], 'readonly')
- .objectStore('keyvaluepairs');
- const keys = await new Promise((resolve, reject) => {
- const request = read.getAllKeys();
- request.onsuccess = () => resolve(request.result);
- request.onerror = () => reject(request.error);
- });
-
- const keys_time = (performance.now() - keys_start) / 1000;
- log.debug(
- 'InboundFileSystem::create::keys ' + keys_time + ' ' + keys.length
- );
- this.pids = new Set(keys);
- }
-
- has(pid) {
- if (!this.pids) {
- return true;
- }
-
- return this.pids.has(pid);
- }
-
- forget(pid) {
- if (this.pids) {
- this.pids.delete(pid);
- }
- }
-
- async doPersist(pid, path, arr, resolve, reject, write) {
- this.persistCount++;
- let size = arr.length;
- this.persistSize += size;
- try {
- //log.debug('persist.do start', pid, path, arr.length);
- //await this.store.setItem(pid, new Blob([arr]));
- await new Promise((resolve, reject) => {
- const request = write.put(new Blob([arr]), pid);
- request.onsuccess = () => resolve(request.result);
- request.onerror = () => reject(request.error);
- });
- if (this.pids) {
- this.pids.add(pid);
- }
- this.FS.unlink(path);
- } catch (e) {
- log.error('Failed persist ' + path + ' ', e);
- }
- //log.debug('persist.do finish', pid, path, arr.length);
- this.persistCount--;
- this.persistSize -= size;
- resolve();
-
- this.tryFinishPersist();
- }
-
- async flushPersist() {
- if (this.inPersist) {
- return;
- }
- log.debug('persist.flush');
- this.inPersist = true;
- let idb = await this.idb;
- this.writeBegin = performance.now();
- let write = idb
- .transaction(['keyvaluepairs'], 'readwrite')
- .objectStore('keyvaluepairs');
- while (
- this.pendingI < this.pending.length &&
- this.persistCount < 20 &&
- this.persistSize < 50 << 20
- ) {
- var q = this.pending[this.pendingI];
- this.pending[this.pendingI] = null;
- // TODO: add to transaction
- this.doPersist(q.pid, q.path, q.arr, q.resolve, q.reject, write);
- this.pendingI++;
- this.totalCount++;
- }
- log.debug(
- 'persist.flush transaction cnt=' +
- this.persistCount +
- ', size=' +
- this.persistSize
- );
- this.inPersist = false;
- this.tryFinishPersist();
- }
-
- async tryFinishPersist() {
- if (this.inPersist) {
- return;
- }
- if (this.persistCount !== 0) {
- return;
- }
- log.debug('persist.finish ' + (performance.now() - this.writeBegin) / 1000);
- if (this.pendingI === this.pending.length) {
- this.pending = [];
- this.pendingHasTimeout = false;
- this.pendingI = 0;
- log.debug('persist.finish done');
- return;
- }
- log.debug('persist.finish continue');
- this.flushPersist();
- }
-
- async persist(pid, path, arr) {
- if (!this.pendingHasTimeout) {
- this.pendingHasTimeout = true;
- log.debug('persist set timeout');
- setTimeout(() => {
- this.flushPersist();
- }, 1);
- }
- await new Promise((resolve, reject) => {
- this.pending.push({
- pid: pid,
- path: path,
- arr: arr,
- resolve: resolve,
- reject: reject
- });
- });
- }
-
- async unlink(pid) {
- log.debug('Unlink ' + pid);
- try {
- this.forget(pid);
- //await this.store.removeItem(pid);
- let idb = await this.idb;
- await new Promise((resolve, reject) => {
- let write = idb
- .transaction(['keyvaluepairs'], 'readwrite')
- .objectStore('keyvaluepairs');
- const request = write.delete(pid);
- request.onsuccess = () => resolve(request.result);
- request.onerror = () => reject(request.error);
- });
- } catch (e) {
- log.error('Failed unlink ' + pid + ' ', e);
- }
- }
-}
-
-class DbFileSystem {
- static async create(root, FS_promise, readOnly = false) {
- const start = performance.now();
- try {
- const dbfs = new DbFileSystem();
- dbfs.root = root;
- const FS = await FS_promise;
- dbfs.FS = FS;
- dbfs.syncfs_total_time = 0;
- dbfs.readOnly = readOnly;
- dbfs.syncActive = 0;
- FS.mkdir(root);
- FS.mount(FS.filesystems.IDBFS, {}, root);
-
- await new Promise((resolve, reject) => {
- FS.syncfs(true, err => {
- resolve();
- });
- });
-
- const rmrf = path => {
- log.debug('rmrf ', path);
- let info;
- try {
- info = FS.lookupPath(path);
- } catch (e) {
- return;
- }
- log.debug('rmrf ', path, info);
- if (info.node.isFolder) {
- for (const key in info.node.contents) {
- rmrf(info.path + '/' + info.node.contents[key].name);
- }
- log.debug('rmdir ', path);
- FS.rmdir(path);
- } else {
- log.debug('unlink ', path);
- FS.unlink(path);
- }
- };
- //const dirs = ['thumbnails', 'profile_photos', 'secret', 'stickers', 'temp', 'wallpapers', 'secret_thumbnails', 'passport'];
- const dirs = [];
- const root_dir = FS.lookupPath(root);
- for (const key in root_dir.node.contents) {
- const value = root_dir.node.contents[key];
- log.debug('node ', key, value);
- if (!value.isFolder) {
- continue;
- }
- dirs.push(root_dir.path + '/' + value.name);
- }
- for (const i in dirs) {
- const dir = dirs[i];
- rmrf(dir);
- //FS.mkdir(dir);
- //FS.mount(FS.filesystems.MEMFS, {}, dir);
- }
- dbfs.syncfsInterval = setInterval(() => {
- dbfs.sync();
- }, 5000);
- const create_time = (performance.now() - start) / 1000;
- log.debug('DbFileSystem::create ' + create_time);
- return dbfs;
- } catch (e) {
- log.error('Failed to init DbFileSystem: ', e);
- }
- }
- async sync(force) {
- if (this.readOnly) {
- return;
- }
- if (this.syncActive > 0 && !force) {
- log.debug('SYNC: skip');
- return;
- }
- this.syncActive++;
- const start = performance.now();
- await new Promise((resolve, reject) => {
- this.FS.syncfs(false, () => {
- const syncfs_time = (performance.now() - start) / 1000;
- this.syncfs_total_time += syncfs_time;
- log.debug('SYNC: ' + syncfs_time);
- log.debug('SYNC total: ' + this.syncfs_total_time);
- resolve();
- });
- });
- this.syncActive--;
- }
- async close() {
- clearInterval(this.syncfsInterval);
- await this.sync(true);
- }
- async destroy() {
- clearInterval(this.syncfsInterval);
- if (this.readOnly) {
- return;
- }
- this.FS.unmount(this.root);
- const req = indexedDB.deleteDatabase(this.root);
- await new Promise((resolve, reject) => {
- req.onsuccess = function(e) {
- log.info('SUCCESS');
- resolve(e.result);
- };
- req.onerror = function(e) {
- log.info('ONERROR');
- reject(e.error);
- };
- req.onblocked = function(e) {
- log.info('ONBLOCKED');
- reject('blocked');
- };
- });
- }
-}
-
-class TdFileSystem {
- static async init_fs(prefix, FS_promise) {
- const FS = await FS_promise;
- FS.mkdir(prefix);
- return FS;
- }
- static async create(instanceName, FS_promise, readOnly = false) {
- try {
- const tdfs = new TdFileSystem();
- const prefix = '/' + instanceName;
- tdfs.prefix = prefix;
- FS_promise = TdFileSystem.init_fs(prefix, FS_promise);
-
- //MEMFS. Store to IDB and delete files as soon as possible
- const inboundFileSystem = InboundFileSystem.create(
- instanceName,
- prefix + '/inboundfs',
- FS_promise
- );
-
- //IDBFS. MEMFS which is flushed to IDB from time to time
- const dbFileSystem = DbFileSystem.create(
- prefix + '/dbfs',
- FS_promise,
- readOnly
- );
-
- const FS = await FS_promise;
- tdfs.FS = FS;
-
- //WORKERFS. Temporary stores Blobs for outbound files
- tdfs.outboundFileSystem = new OutboundFileSystem(
- prefix + '/outboundfs',
- tdfs.FS
- );
-
- tdfs.inboundFileSystem = await inboundFileSystem;
- tdfs.dbFileSystem = await dbFileSystem;
- return tdfs;
- } catch (e) {
- log.error('Failed to init TdFileSystem: ', e);
- }
- }
- async destroy() {
- await this.dbFileSystem.destroy();
- }
-}
-
-class TdClient {
- constructor(callback) {
- log.info('Start worker');
- this.pendingQueries = [];
- this.isPending = true;
- this.callback = callback;
- this.wasInit = false;
- }
-
- async testLocalForage() {
- await initLocalForage();
- const DRIVERS = [
- localforage.INDEXEDDB,
- 'memoryDriver',
- localforage.LOCALSTORAGE,
- localforage.WEBSQL,
- localForageDrivers
- ];
- for (const driverName of DRIVERS) {
- console.log('Test ', driverName);
- try {
- await localforage.setDriver(driverName);
- console.log('A');
- await localforage.setItem('hello', 'world');
- console.log('B');
- const x = await localforage.getItem('hello');
- console.log('got ', x);
- await localforage.clear();
- console.log('C');
- } catch (error) {
- console.log('Error', error);
- }
- }
- }
-
- async init(options) {
- if (this.wasInit) {
- return;
- }
- //await this.testLocalForage();
- log.setVerbosity(options.jsLogVerbosityLevel);
- this.wasInit = true;
-
- options = options || {};
- const mode = options.mode || 'wasm';
-
- const FS_promise = new Promise(resolve => {
- this.onFS = resolve;
- });
-
- const tdfs_promise = TdFileSystem.create(
- options.instanceName,
- FS_promise,
- options.readOnly
- );
-
- this.useDatabase = true;
- if ('useDatabase' in options) {
- this.useDatabase = options.useDatabase;
- }
-
- log.info('load TdModule');
- this.TdModule = await loadTdlib(mode, this.onFS, options.wasmUrl);
- log.info('got TdModule');
- this.td_functions = {
- td_create: this.TdModule.cwrap(
- 'td_emscripten_create_client_id',
- 'number',
- []
- ),
- td_send: this.TdModule.cwrap('td_emscripten_send', null, [
- 'number',
- 'string'
- ]),
- td_execute: this.TdModule.cwrap('td_emscripten_execute', 'string', [
- 'string'
- ]),
- td_receive: this.TdModule.cwrap('td_emscripten_receive', 'string', []),
- td_set_verbosity: verbosity => {
- this.td_functions.td_execute(
- JSON.stringify({
- '@type': 'setLogVerbosityLevel',
- new_verbosity_level: verbosity
- })
- );
- },
- td_get_timeout: this.TdModule.cwrap(
- 'td_emscripten_get_timeout',
- 'number',
- []
- )
- };
- //this.onFS(this.TdModule.FS);
- this.FS = this.TdModule.FS;
- this.TdModule['websocket']['on']('error', error => {
- this.scheduleReceiveSoon();
- });
- this.TdModule['websocket']['on']('open', fd => {
- this.scheduleReceiveSoon();
- });
- this.TdModule['websocket']['on']('listen', fd => {
- this.scheduleReceiveSoon();
- });
- this.TdModule['websocket']['on']('connection', fd => {
- this.scheduleReceiveSoon();
- });
- this.TdModule['websocket']['on']('message', fd => {
- this.scheduleReceiveSoon();
- });
- this.TdModule['websocket']['on']('close', fd => {
- this.scheduleReceiveSoon();
- });
-
- // wait till it is allowed to start
- this.callback({ '@type': 'inited' });
- await new Promise(resolve => {
- this.onStart = resolve;
- });
- this.isStarted = true;
-
- log.info('may start now');
- if (this.isClosing) {
- return;
- }
- log.info('FS start init');
- this.tdfs = await tdfs_promise;
- log.info('FS inited');
- this.callback({ '@type': 'fsInited' });
-
- // no async initialization after this point
- if (options.logVerbosityLevel === undefined) {
- options.logVerbosityLevel = 2;
- }
- this.td_functions.td_set_verbosity(options.logVerbosityLevel);
- this.client_id = this.td_functions.td_create();
-
- this.savingFiles = new Map();
- this.send({
- '@type': 'setOption',
- name: 'store_all_files_in_files_directory',
- value: {
- '@type': 'optionValueBoolean',
- value: true
- }
- });
- this.send({
- '@type': 'setOption',
- name: 'language_pack_database_path',
- value: {
- '@type': 'optionValueString',
- value: this.tdfs.dbFileSystem.root + '/language'
- }
- });
- this.send({
- '@type': 'setOption',
- name: 'ignore_background_updates',
- value: {
- '@type': 'optionValueBoolean',
- value: !this.useDatabase
- }
- });
-
- this.flushPendingQueries();
-
- this.receive();
- }
-
- prepareQueryRecursive(query) {
- if (query['@type'] === 'inputFileBlob') {
- return {
- '@type': 'inputFileLocal',
- path: this.tdfs.outboundFileSystem.blobToPath(query.data, query.name)
- };
- }
- for (const key in query) {
- const field = query[key];
- if (field && typeof field === 'object') {
- query[key] = this.prepareQueryRecursive(field);
- }
- }
- return query;
- }
-
- prepareQuery(query) {
- if (query['@type'] === 'setTdlibParameters') {
- query.database_directory = this.tdfs.dbFileSystem.root;
- query.files_directory = this.tdfs.inboundFileSystem.root;
-
- const useDb = this.useDatabase;
- query.use_file_database = useDb;
- query.use_chat_info_database = useDb;
- query.use_message_database = useDb;
- query.use_secret_chats = useDb;
- }
- if (query['@type'] === 'getLanguagePackString') {
- query.language_pack_database_path =
- this.tdfs.dbFileSystem.root + '/language';
- }
- return this.prepareQueryRecursive(query);
- }
-
- onStart() {
- //nop
- log.info('ignore on_start');
- }
-
- deleteIdbKey(query) {
- try {
- } catch (e) {
- this.callback({
- '@type': 'error',
- '@extra': query['@extra'],
- code: 400,
- message: e
- });
- return;
- }
- this.callback({
- '@type': 'ok',
- '@extra': query['@extra']
- });
- }
-
- readFilePart(query) {
- let res;
- try {
- //const file_size = this.FS.stat(query.path).size;
- const stream = this.FS.open(query.path, 'r');
- const buf = new Uint8Array(query.count);
- this.FS.read(stream, buf, 0, query.count, query.offset);
- this.FS.close(stream);
- res = buf;
- } catch (e) {
- this.callback({
- '@type': 'error',
- '@extra': query['@extra'],
- code: 400,
- message: e.toString()
- });
- return;
- }
- this.callback(
- {
- '@type': 'filePart',
- '@extra': query['@extra'],
- data: res
- },
- [res.buffer]
- );
- }
-
- send(query) {
- if (this.isClosing) {
- return;
- }
- if (this.wasFatalError) {
- if (query['@type'] === 'destroy') {
- this.destroy({ '@type': 'ok', '@extra': query['@extra'] });
- }
- return;
- }
- if (query['@type'] === 'init') {
- this.init(query.options);
- return;
- }
- if (query['@type'] === 'start') {
- log.info('on_start');
- this.onStart();
- return;
- }
- if (query['@type'] === 'setJsLogVerbosityLevel') {
- log.setVerbosity(query.new_verbosity_level);
- return;
- }
- if (this.isPending) {
- this.pendingQueries.push(query);
- return;
- }
- if (
- query['@type'] === 'setLogVerbosityLevel' ||
- query['@type'] === 'getLogVerbosityLevel' ||
- query['@type'] === 'setLogTagVerbosityLevel' ||
- query['@type'] === 'getLogTagVerbosityLevel' ||
- query['@type'] === 'getLogTags'
- ) {
- this.execute(query);
- return;
- }
- if (query['@type'] === 'readFilePart') {
- this.readFilePart(query);
- return;
- }
- if (query['@type'] === 'deleteIdbKey') {
- this.deleteIdbKey(query);
- return;
- }
- query = this.prepareQuery(query);
- this.td_functions.td_send(this.client_id, JSON.stringify(query));
- this.scheduleReceiveSoon();
- }
-
- execute(query) {
- try {
- const res = this.td_functions.td_execute(JSON.stringify(query));
- const response = JSON.parse(res);
- this.callback(response);
- } catch (error) {
- this.onFatalError(error);
- }
- }
- receive() {
- this.cancelReceive();
- if (this.wasFatalError) {
- return;
- }
- try {
- while (true) {
- const msg = this.td_functions.td_receive();
- if (!msg) {
- break;
- }
- const response = this.prepareResponse(JSON.parse(msg));
- if (
- response['@type'] === 'updateAuthorizationState' &&
- response.authorization_state['@type'] === 'authorizationStateClosed'
- ) {
- this.close(response);
- break;
- }
- this.callback(response);
- }
-
- this.scheduleReceive();
- } catch (error) {
- this.onFatalError(error);
- }
- }
-
- cancelReceive() {
- if (this.receiveTimeout) {
- clearTimeout(this.receiveTimeout);
- delete this.receiveTimeout;
- }
- delete this.receiveSoon;
- }
- scheduleReceiveSoon() {
- if (this.receiveSoon) {
- return;
- }
- this.cancelReceive();
- this.receiveSoon = true;
- this.scheduleReceiveIn(0.001);
- }
- scheduleReceive() {
- if (this.receiveSoon) {
- return;
- }
- this.cancelReceive();
- const timeout = this.td_functions.td_get_timeout();
- this.scheduleReceiveIn(timeout);
- }
- scheduleReceiveIn(timeout) {
- //return;
- log.debug('Scheduler receive in ' + timeout + 's');
- this.receiveTimeout = setTimeout(() => this.receive(), timeout * 1000);
- }
-
- onFatalError(error) {
- this.wasFatalError = true;
- this.asyncOnFatalError(error);
- }
-
- async close(last_update) {
- // close db and cancell all timers
- this.isClosing = true;
- if (this.isStarted) {
- log.debug('close worker: start');
- await this.tdfs.dbFileSystem.close();
- this.cancelReceive();
- log.debug('close worker: finish');
- }
- this.callback(last_update);
- }
-
- async destroy(result) {
- try {
- log.info('destroy tdfs ...');
- await this.tdfs.destroy();
- log.info('destroy tdfs ok');
- } catch (e) {
- log.error('Failed destroy', e);
- }
- this.callback(result);
- this.callback({
- '@type': 'updateAuthorizationState',
- authorization_state: {
- '@type': 'authorizationStateClosed'
- }
- });
- }
-
- async asyncOnFatalError(error) {
- await this.tdfs.dbFileSystem.sync();
- this.callback({ '@type': 'updateFatalError', error: error });
- }
-
- saveFile(pid, file) {
- const isSaving = this.savingFiles.has(pid);
- this.savingFiles.set(pid, file);
- if (isSaving) {
- return file;
- }
- try {
- const arr = this.FS.readFile(file.local.path);
- if (arr) {
- file = Object.assign({}, file);
- file.arr = arr;
- this.doSaveFile(pid, file, arr);
- }
- } catch (e) {
- log.error('Failed to readFile: ', e);
- }
- return file;
- }
-
- async doSaveFile(pid, file, arr) {
- await this.tdfs.inboundFileSystem.persist(pid, file.local.path, arr);
- file = this.savingFiles.get(pid);
- file.idb_key = pid;
- this.callback({ '@type': 'updateFile', file: file });
-
- this.savingFiles.delete(pid);
- }
-
- prepareFile(file) {
- const pid = file.remote.unique_id ? file.remote.unique_id : file.remote.id;
- if (!pid) {
- return file;
- }
-
- if (file.local.is_downloading_active) {
- this.tdfs.inboundFileSystem.forget(pid);
- } else if (this.tdfs.inboundFileSystem.has(pid)) {
- file.idb_key = pid;
- return file;
- }
-
- if (file.local.is_downloading_completed) {
- file = this.saveFile(pid, file);
- }
- return file;
- }
-
- prepareResponse(response) {
- if (response['@type'] === 'file') {
- return this.prepareFile(response);
- }
- for (const key in response) {
- const field = response[key];
- if (field && typeof field === 'object') {
- response[key] = this.prepareResponse(field);
- }
- }
- return response;
- }
-
- flushPendingQueries() {
- this.isPending = false;
- for (const query of this.pendingQueries) {
- this.send(query);
- }
- }
-}
-
-const client = new TdClient((e, t = []) => postMessage(e, t));
-
-onmessage = function(e) {
- try {
- client.send(e.data);
- } catch (error) {
- client.onFatalError(error);
- }
-};