diff options
| author | George Hazan <ghazan@miranda.im> | 2022-12-08 12:33:42 +0300 | 
|---|---|---|
| committer | George Hazan <ghazan@miranda.im> | 2022-12-08 12:33:42 +0300 | 
| commit | e4a555d8e146994b7fc99c8f0c0f6b4ca8af1495 (patch) | |
| tree | 310ece548498c4f7862f4ca2bb6fb060f6c5071e /protocols/Telegram/tdlib/td/example/web/tdweb/src | |
| parent | baa88cd35d210debe6af6e784466c813e2ebcf58 (diff) | |
we don't need this code
Diffstat (limited to 'protocols/Telegram/tdlib/td/example/web/tdweb/src')
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); -  } -};  | 
