1 line
99 KiB
Plaintext
1 line
99 KiB
Plaintext
{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/config/modes.ts","webpack:///./src/config/debug.ts","webpack:///./src/environment/ctx.ts","webpack:///./src/environment/userAgent.ts","webpack:///./src/helpers/context.ts","webpack:///./src/lib/logger.ts","webpack:///./src/lib/serviceWorker/cache.ts","webpack:///./src/helpers/schedulers/pause.ts","webpack:///./src/helpers/blob.ts","webpack:///./src/helpers/noop.ts","webpack:///./src/lib/filemanager.ts","webpack:///./src/lib/cacheStorage.ts","webpack:///./src/lib/serviceWorker/stream.ts","webpack:///./src/helpers/schedulers/debounce.ts","webpack:///./src/helpers/cancellablePromise.ts","webpack:///./src/config/databases/state.ts","webpack:///./src/lib/idb.ts","webpack:///./src/helpers/object.ts","webpack:///./src/lib/serviceWorker/push.ts","webpack:///./src/lib/serviceWorker/index.service.ts","webpack:///./src/lib/serviceWorker/timeout.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","Modes","test","location","search","indexOf","debug","http","ssl","multipleConnections","asServiceWorker","transport","DEBUG","window","self","USER_AGENT","navigator","userAgent","IS_SAFARI","toLowerCase","vendor","platform","maxTouchPoints","MSStream","match","IS_FIREFOX","IS_SERVICE_WORKER","ServiceWorkerGlobalScope","IS_WEB_WORKER","WorkerGlobalScope","getWindowClients","clients","matchAll","includeUncontrolled","type","notifyServiceWorker","all","args","then","listeners","length","slice","forEach","listener","postMessage","notifyWorker","noop","LogTypes","LOG_LEVELS","None","Error","Warn","Log","Debug","_logTimer","Date","now","dT","toFixed","getCallerFunctionNameFromLine","IS_WEBKIT","line","split","splitted","trim","lastIndexOf","STYLES_SUPPORTED","LINE_INDEX","getCallerFunctionName","lines","stack","LOGGER_STYLES","black","red","green","yellow","blue","magenta","cyan","white","methods","logger","prefix","ignoreDebugReset","style","originalPrefix","originalStyle","log","console","method","logType","setPrefix","newPrefix","setLevel","level","reduce","acc","v","bindPrefix","isCorrectResponse","response","ok","status","timeoutRace","promise","Promise","race","ms","resolve","setTimeout","reject","readBlobAs","blob","reader","FileReader","addEventListener","e","target","result","readBlobAsUint8Array","readBlobAsArrayBuffer","buffer","Uint8Array","blobConstruct","blobParts","mimeType","safeMimeType","blobSafeMimeType","Blob","bb","BlobBuilder","blobPart","append","getBlob","blobSupported","this","fileWriter","bytes","arr","write","saveFileCallback","part","push","truncate","finalize","saveToStorage","dbName","useStorage","STORAGES","openDatabase","openDbPromise","caches","open","entryName","timeoutOperation","cache","delete","put","fileName","Response","headers","size","save","callback","rejected","timeout","undefined","res","err","clearTimeout","fakeWriter","getFakeFileWriter","saveFile","catch","enabled","map","storage","deleteAll","cacheStorage","setInterval","keys","requests","filtered","Map","timestamp","request","url","has","set","promises","id","ignoreSearch","ignoreVary","clientId","deferredPromises","find","client","taskId","streams","info","loadedOffsets","Set","destroy","getId","limitPart","STREAM_CHUNK_UPPER_LIMIT","STREAM_CHUNK_MIDDLE_LIMIT","destroyDebounced","fn","shouldRunFirst","shouldRunLast","waitingTimeout","waitingPromise","hadNewCall","_resolve","_reject","debounce","alignedOffset","limit","fromPreload","task","payload","dcId","JSON","stringify","windowClient","deferred","uploadFile","add","deferredHelper","isFulfilled","isRejected","notify","notifyAll","lastNotify","addNotifyListener","finally","cancel","assign","deferredPromise","bytesPromise","saveChunkToCache","preloadChunks","getChunkKey","getFile","error","requestFilePartFromCache","requestFilePartFromWorker","offset","requestFilePart","end","preloadChunk","alignOffset","range","possibleResponse","statusText","responseForSafariFirstRange","Math","ceil","alignLimit","min","ab","byteLength","base","version","stores","db","storeName","storageIsAvailable","fromObject","safeAssign","preserve","onclose","close","createNew","indexedDB","message","finished","onerror","onsuccess","event","calledNew","onabort","transaction","onversionchange","onupgradeneeded","warn","oldVersion","newVersion","store","objectStoreNames","contains","os","createObjectStore","indexes","index","createIndex","indexName","keyPath","objectParameters","Array","isArray","concat","getObjectStore","objectStore","clear","idx","perf","performance","oncomplete","results","getAll","defaultBaseUrl","protocol","hostname","pathname","join","defaults","push_mute_until","push_last_alive","push_lang","push_settings","obj","data","json","hasActiveWindows","checksPromise","muteUntil","lastAliveTime","clientList","nowTime","userInvisibleIsSupported","badge","reason","closePromise","settings","lang","icon","peerId","title","body","description","custom","channel_id","chat_id","from_id","tag","nopreview","push_message_nopreview","actions","action","push_action_mute1d","registration","showNotification","notification","notifications","onCloseNotification","fireNotification","closeAllNotifications","waitUntil","pendingNotification","focus","openWindow","baseUrl","getNotifications","len","taskListeners","notifications_clear","ping","ports","source","localNotifications","onPing","toggleStorage","onFetch","origin","respondWith","file","fetch","clone","replace","random","requestCache","scope","params","exec","header","chunks","ranges","parseRange","parse","decodeURIComponent","stream","delay","requestRange","onStreamFetch","onChangeState","onfetch","skipWaiting","claim","onunhandledrejection","onoffline","ononline"],"mappings":"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","file":"sw.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport type { TransportType } from \"../lib/mtproto/dcConfigurator\";\r\n\r\nconst Modes = {\r\n test: location.search.indexOf('test=1') > 0/* || true */,\r\n debug: location.search.indexOf('debug=1') > 0,\r\n http: false,\r\n ssl: true, // location.search.indexOf('ssl=1') > 0 || location.protocol === 'https:' && location.search.indexOf('ssl=0') === -1,\r\n multipleConnections: true,\r\n asServiceWorker: false,\r\n transport: 'websocket' as TransportType\r\n};\r\n\r\n \r\nModes.http = location.search.indexOf('http=1') > 0;\r\n \r\n\r\n \r\n \r\n \r\n\r\nif(Modes.http) {\r\n Modes.transport = 'https';\r\n}\r\n\r\n \r\n \r\n \r\n\r\nexport default Modes;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport Modes from \"./modes\";\r\n\r\nexport const DEBUG = process.env.NODE_ENV !== 'production' || Modes.debug;\r\nconst ctx: any = typeof(window) !== 'undefined' ? window : self;\r\nexport const MOUNT_CLASS_TO: any = DEBUG || true/* && false */ ? ctx : {};\r\nexport default DEBUG;\r\n\r\n//let m = DEBUG;\r\n/* if(!DEBUG) {\r\n ctx.sandpitTurtle = () => {\r\n //if(!m) {\r\n for(let i in MOUNT_CLASS_TO) {\r\n ctx[i] = MOUNT_CLASS_TO[i];\r\n }\r\n //m = true;\r\n //}\r\n \r\n //DEBUG = !DEBUG;\r\n };\r\n} */\r\n\r\n/* export const superDebug = (object: any, key: string) => {\r\n var d = object[key];\r\n var beforeStr = '', afterStr = '';\r\n for(var r of d) {\r\n beforeStr += r.before.hex + '\\n';\r\n afterStr += r.after.hex + '\\n';\r\n }\r\n\r\n beforeStr = beforeStr.trim();\r\n afterStr = afterStr.trim();\r\n //var beforeStr = d.map(r => r.before.hex).join('\\n');\r\n //var afterStr = d.map(r => r.after.hex).join('\\n');\r\n\r\n var dada = (name: string, str: string) => {\r\n var a = document.createElement('a');\r\n a.target = '_blank';\r\n a.download = name + '.txt';\r\n a.href = URL.createObjectURL(new Blob([str], {\r\n type: 'text/plain'\r\n }));\r\n document.body.append(a);\r\n a.click();\r\n };\r\n\r\n dada(key + '_' + 'before', beforeStr);\r\n dada(key + '_' + 'after', afterStr);\r\n}\r\n\r\nMOUNT_CLASS_TO.superDebug = superDebug; */\r\n","const ctx = typeof(window) !== 'undefined' ? window : self;\n\nexport default ctx;\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport ctx from './ctx';\r\n\r\nexport const USER_AGENT = navigator ? navigator.userAgent : null;\r\nexport const IS_APPLE = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) !== -1;\r\nexport const IS_ANDROID = navigator.userAgent.toLowerCase().indexOf('android') !== -1;\r\nexport const IS_CHROMIUM = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);\r\n\r\n// https://stackoverflow.com/a/58065241\r\nexport const IS_APPLE_MOBILE = (/iPad|iPhone|iPod/.test(navigator.platform) ||\r\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&\r\n !(ctx as any).MSStream;\r\n\r\nexport const IS_SAFARI = !!('safari' in ctx) || !!(USER_AGENT && (/\\b(iPad|iPhone|iPod)\\b/.test(USER_AGENT) || (!!USER_AGENT.match('Safari') && !USER_AGENT.match('Chrome'))))/* || true */;\r\nexport const IS_FIREFOX = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;\r\n\r\nexport const IS_MOBILE_SAFARI = IS_SAFARI && IS_APPLE_MOBILE;\r\n\r\nexport const IS_MOBILE = /* screen.width && screen.width < 480 || */navigator.maxTouchPoints > 0 && navigator.userAgent.search(/iOS|iPhone OS|Android|BlackBerry|BB10|Series ?[64]0|J2ME|MIDP|opera mini|opera mobi|mobi.+Gecko|Windows Phone/i) != -1;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\n// в SW может быть сразу две переменных TRUE\r\nexport const IS_SERVICE_WORKER = typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope;\r\nexport const IS_WEB_WORKER = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope && !IS_SERVICE_WORKER;\r\nexport const IS_WORKER = IS_WEB_WORKER || IS_SERVICE_WORKER;\r\n\r\nexport const getWindowClients = () => {\r\n return (self as any as ServiceWorkerGlobalScope)\r\n .clients\r\n .matchAll({ includeUncontrolled: false, type: 'window' });\r\n};\r\n\r\nconst notifyServiceWorker = (all: boolean, ...args: any[]) => {\r\n (self as any as ServiceWorkerGlobalScope)\r\n .clients\r\n .matchAll({ includeUncontrolled: false, type: 'window' })\r\n .then((listeners) => {\r\n if(!listeners.length) {\r\n //console.trace('no listeners?', self, listeners);\r\n return;\r\n }\r\n\r\n listeners.slice(all ? 0 : -1).forEach(listener => {\r\n // @ts-ignore\r\n listener.postMessage(...args);\r\n });\r\n });\r\n};\r\n\r\nconst notifyWorker = (...args: any[]) => {\r\n // @ts-ignore\r\n (self as any as DedicatedWorkerGlobalScope).postMessage(...args);\r\n};\r\n\r\nconst noop = () => {};\r\n\r\nexport const notifySomeone = IS_SERVICE_WORKER ? notifyServiceWorker.bind(null, false) : (IS_WEB_WORKER ? notifyWorker : noop);\r\nexport const notifyAll = IS_SERVICE_WORKER ? notifyServiceWorker.bind(null, true) : (IS_WEB_WORKER ? notifyWorker : noop);\r\n","/*\n * https://github.com/morethanwords/tweb\n * Copyright (C) 2019-2021 Eduard Kuzmenko\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\n */\n\nimport DEBUG from \"../config/debug\";\nimport { IS_FIREFOX, IS_SAFARI } from \"../environment/userAgent\";\nimport { IS_SERVICE_WORKER, IS_WEB_WORKER } from \"../helpers/context\";\n\nexport enum LogTypes {\n None = 0,\n Error = 1,\n Warn = 2,\n Log = 4,\n Debug = 8\n};\n\nexport const LOG_LEVELS = [LogTypes.None, LogTypes.Error, LogTypes.Warn, LogTypes.Log, LogTypes.Debug];\n\nconst _logTimer = Date.now();\nfunction dT() {\n return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']';\n}\n\nlet getCallerFunctionNameFromLine: (line: string) => string;\n\nconst IS_WEBKIT = IS_SAFARI || IS_FIREFOX;\n\nif(IS_WEBKIT) {\n getCallerFunctionNameFromLine = (line) => {\n const splitted = line.split('@');\n return splitted[0];\n };\n} else {\n getCallerFunctionNameFromLine = (line: string) => {\n const splitted = line.trim().split(' ');\n if(splitted.length === 3) {\n return splitted[1].slice(splitted[1].lastIndexOf('.') + 1);\n }\n };\n}\n\nconst STYLES_SUPPORTED = !IS_WEBKIT;\nconst LINE_INDEX = IS_WEBKIT ? 2 : 3;\n\nfunction getCallerFunctionName() {\n const stack = new Error().stack;\n const lines = stack.split('\\n');\n const line = lines[LINE_INDEX] || lines[lines.length - 1];\n // const match = line.match(/\\.([^\\.]+?)\\s/);\n // line = match ? match[1] : line.trim();\n const caller = getCallerFunctionNameFromLine(line) || '<anonymous>';\n return '[' + caller + ']';\n}\n\nexport const LOGGER_STYLES = {\n reset: \"\\x1b[0m\",\n bright: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n underscore: \"\\x1b[4m\",\n blink: \"\\x1b[5m\",\n reverse: \"\\x1b[7m\",\n hidden: \"\\x1b[8m\",\n // Foreground (text) colors\n fg: {\n black: \"\\x1b[30m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\"\n },\n // Background colors\n bg: {\n black: \"\\x1b[40m\",\n red: \"\\x1b[41m\",\n green: \"\\x1b[42m\",\n yellow: \"\\x1b[43m\",\n blue: \"\\x1b[44m\",\n magenta: \"\\x1b[45m\",\n cyan: \"\\x1b[46m\",\n white: \"\\x1b[47m\"\n }\n};\n\nexport type Logger = {\n (...args: any[]): void;\n warn(...args: any[]): void;\n info(...args: any[]): void;\n error(...args: any[]): void;\n trace(...args: any[]): void;\n debug(...args: any[]): void;\n assert(...args: any[]): void;\n // log(...args: any[]): void;\n setPrefix(newPrefix: string): void;\n setLevel(level: 0 | 1 | 2 | 3 | 4): void;\n bindPrefix(prefix: string): Logger;\n};\n\nconst methods: ['debug' | 'info' | 'warn' | 'error' | 'assert' | 'trace'/* | 'log' */, LogTypes][] = [\n [\"debug\", LogTypes.Debug], \n [\"info\", LogTypes.Log], \n [\"warn\", LogTypes.Warn], \n [\"error\", LogTypes.Error], \n [\"assert\", LogTypes.Error],\n [\"trace\", LogTypes.Log],\n // [\"log\", LogTypes.Log]\n];\n\nexport function logger(prefix: string, type: LogTypes = LogTypes.Log | LogTypes.Warn | LogTypes.Error, ignoreDebugReset = false, style = ''): Logger {\n let originalPrefix: string;\n if(!DEBUG && !ignoreDebugReset/* || true */) {\n type = LogTypes.Error;\n }\n\n if(!STYLES_SUPPORTED) {\n style = '';\n } else if(!style) {\n if(IS_SERVICE_WORKER) style = LOGGER_STYLES.fg.yellow;\n else if(IS_WEB_WORKER) style = LOGGER_STYLES.fg.cyan;\n }\n\n let originalStyle = style;\n if(style) style = `%s ${style}%s`;\n else style = '%s';\n\n //level = LogLevels.log | LogLevels.warn | LogLevels.error | LogLevels.debug\n\n const log: Logger = function(...args: any[]) {\n return type & LogTypes.Log && console.log(style, dT(), prefix, getCallerFunctionName(), ...args);\n } as any;\n\n methods.forEach(([method, logType]) => {\n log[method] = function(...args: any[]) {\n return type & logType && console[method](style, dT(), prefix, getCallerFunctionName(), ...args);\n };\n });\n\n log.setPrefix = function(newPrefix: string) {\n originalPrefix = newPrefix;\n prefix = '[' + newPrefix + ']';\n };\n\n log.setPrefix(prefix);\n\n log.setLevel = function(level: 0 | 1 | 2 | 3 | 4) {\n type = LOG_LEVELS.slice(0, level + 1).reduce((acc, v) => acc | v, 0) as any;\n };\n\n log.bindPrefix = function(prefix: string) {\n return logger(`${originalPrefix}] [${prefix}`, type, ignoreDebugReset, originalStyle);\n };\n\n return log;\n};\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { pause } from \"../../helpers/schedulers/pause\";\r\n\r\nconst ctx = self as any as ServiceWorkerGlobalScope;\r\nexport const CACHE_ASSETS_NAME = 'cachedAssets';\r\n\r\nfunction isCorrectResponse(response: Response) {\r\n return response.ok && response.status === 200;\r\n}\r\n\r\nfunction timeoutRace<T extends Promise<any>>(promise: T) {\r\n return Promise.race([\r\n promise,\r\n pause(10000).then(() => Promise.reject())\r\n ]);\r\n}\r\n\r\nexport async function requestCache(event: FetchEvent) {\r\n try {\r\n // const cache = await ctx.caches.open(CACHE_ASSETS_NAME);\r\n const cache = await timeoutRace(ctx.caches.open(CACHE_ASSETS_NAME));\r\n const file = await timeoutRace(cache.match(event.request, {ignoreVary: true}));\r\n \r\n if(file && isCorrectResponse(file)) {\r\n return file;\r\n }\r\n \r\n const headers: HeadersInit = {'Vary': '*'};\r\n let response = await fetch(event.request, {headers});\r\n if(isCorrectResponse(response)) {\r\n cache.put(event.request, response.clone());\r\n } else if(response.status === 304) { // possible fix for 304 in Safari\r\n const url = event.request.url.replace(/\\?.+$/, '') + '?' + (Math.random() * 100000 | 0);\r\n response = await fetch(url, {headers});\r\n if(isCorrectResponse(response)) {\r\n cache.put(event.request, response.clone());\r\n }\r\n }\r\n \r\n return response;\r\n } catch(err) {\r\n return fetch(event.request);\r\n }\r\n}\r\n","export const pause = (ms: number) => new Promise<void>((resolve) => {\r\n setTimeout(resolve, ms);\r\n});\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nexport function readBlobAs(blob: Blob, method: 'readAsText'): Promise<string>;\r\nexport function readBlobAs(blob: Blob, method: 'readAsDataURL'): Promise<string>;\r\nexport function readBlobAs(blob: Blob, method: 'readAsArrayBuffer'): Promise<ArrayBuffer>;\r\nexport function readBlobAs(blob: Blob, method: 'readAsArrayBuffer' | 'readAsText' | 'readAsDataURL'): Promise<any> {\r\n return new Promise<any>((resolve) => {\r\n const reader = new FileReader();\r\n reader.addEventListener('loadend', (e) => resolve(e.target.result));\r\n reader[method](blob);\r\n });\r\n}\r\n\r\nexport function readBlobAsText(blob: Blob) {\r\n return readBlobAs(blob, 'readAsText');\r\n}\r\n\r\nexport function readBlobAsDataURL(blob: Blob) {\r\n return readBlobAs(blob, 'readAsDataURL');\r\n}\r\n\r\nexport function readBlobAsArrayBuffer(blob: Blob) {\r\n return readBlobAs(blob, 'readAsArrayBuffer');\r\n}\r\n\r\nexport function readBlobAsUint8Array(blob: Blob) {\r\n return readBlobAsArrayBuffer(blob).then(buffer => new Uint8Array(buffer));\r\n}\r\n\r\nexport function blobConstruct(blobParts: any, mimeType: string = ''): Blob {\r\n let blob;\r\n const safeMimeType = blobSafeMimeType(mimeType);\r\n try {\r\n blob = new Blob(blobParts, {type: safeMimeType});\r\n } catch(e) {\r\n // @ts-ignore\r\n let bb = new BlobBuilder;\r\n blobParts.forEach((blobPart: any) => {\r\n bb.append(blobPart);\r\n });\r\n blob = bb.getBlob(safeMimeType);\r\n }\r\n return blob;\r\n}\r\n\r\n// https://www.iana.org/assignments/media-types/media-types.xhtml\r\nexport function blobSafeMimeType(mimeType: string) {\r\n if([\r\n 'image/jpeg',\r\n 'image/png',\r\n 'image/gif',\r\n 'image/webp',\r\n 'image/bmp',\r\n 'video/mp4',\r\n 'video/webm',\r\n 'video/quicktime',\r\n 'audio/ogg',\r\n 'audio/mpeg',\r\n 'audio/mp4',\r\n 'application/json',\r\n 'application/pdf'\r\n ].indexOf(mimeType) === -1) {\r\n return 'application/octet-stream';\r\n }\r\n\r\n return mimeType;\r\n}\r\n","export default function noop() {}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { blobConstruct, readBlobAsUint8Array } from \"../helpers/blob\";\r\n\r\nexport class FileManager {\r\n public blobSupported = true;\r\n \r\n constructor() {\r\n try {\r\n blobConstruct([], '');\r\n } catch(e) {\r\n this.blobSupported = false;\r\n }\r\n }\r\n \r\n public isAvailable() {\r\n return this.blobSupported;\r\n }\r\n \r\n public write(fileWriter: ReturnType<FileManager['getFakeFileWriter']>, bytes: Uint8Array | Blob | string): Promise<void> {\r\n if(bytes instanceof Blob) { // is file bytes\r\n return readBlobAsUint8Array(bytes).then(arr => {\r\n return fileWriter.write(arr);\r\n });\r\n } else {\r\n return fileWriter.write(bytes);\r\n }\r\n }\r\n\r\n public getFakeFileWriter(mimeType: string, saveFileCallback?: (blob: Blob) => Promise<Blob>) {\r\n const blobParts: Array<Uint8Array | string> = [];\r\n const fakeFileWriter = {\r\n write: async(part: Uint8Array | string) => {\r\n if(!this.blobSupported) {\r\n throw false;\r\n }\r\n \r\n blobParts.push(part);\r\n },\r\n truncate: () => {\r\n blobParts.length = 0;\r\n },\r\n finalize: (saveToStorage = true) => {\r\n const blob = blobConstruct(blobParts, mimeType);\r\n\r\n if(saveToStorage && saveFileCallback) {\r\n saveFileCallback(blob);\r\n }\r\n \r\n return blob;\r\n }\r\n };\r\n \r\n return fakeFileWriter;\r\n }\r\n}\r\n\r\nexport default new FileManager();\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport Modes from '../config/modes';\r\nimport { blobConstruct } from '../helpers/blob';\r\nimport FileManager from './filemanager';\r\n//import { MOUNT_CLASS_TO } from './mtproto/mtproto_config';\r\n//import { logger } from './polyfill';\r\n\r\nexport type CacheStorageDbName = 'cachedFiles' | 'cachedStreamChunks' | 'cachedAssets';\r\n\r\nexport default class CacheStorageController {\r\n private static STORAGES: CacheStorageController[] = [];\r\n private openDbPromise: Promise<Cache>;\r\n\r\n private useStorage = true;\r\n\r\n //private log: ReturnType<typeof logger> = logger('CS');\r\n\r\n constructor(private dbName: CacheStorageDbName) {\r\n if(Modes.test) {\r\n this.dbName += '_test';\r\n }\r\n\r\n if(CacheStorageController.STORAGES.length) {\r\n this.useStorage = CacheStorageController.STORAGES[0].useStorage;\r\n }\r\n \r\n this.openDatabase();\r\n CacheStorageController.STORAGES.push(this);\r\n }\r\n\r\n private openDatabase(): Promise<Cache> {\r\n return this.openDbPromise ?? (this.openDbPromise = caches.open(this.dbName));\r\n }\r\n\r\n public delete(entryName: string) {\r\n return this.timeoutOperation((cache) => cache.delete('/' + entryName));\r\n }\r\n\r\n public deleteAll() {\r\n return caches.delete(this.dbName);\r\n }\r\n\r\n public get(entryName: string) {\r\n return this.timeoutOperation((cache) => cache.match('/' + entryName));\r\n }\r\n\r\n public save(entryName: string, response: Response) {\r\n // return new Promise((resolve) => {}); // DEBUG\r\n return this.timeoutOperation((cache) => cache.put('/' + entryName, response));\r\n }\r\n\r\n public getFile(fileName: string, method: 'blob' | 'json' | 'text' = 'blob'): Promise<any> {\r\n /* if(method === 'blob') {\r\n return Promise.reject();\r\n } */\r\n\r\n // const str = `get fileName: ${fileName}`;\r\n // console.time(str);\r\n return this.get(fileName).then((response) => {\r\n if(!response) {\r\n //console.warn('getFile:', response, fileName);\r\n throw 'NO_ENTRY_FOUND';\r\n }\r\n\r\n const promise = response[method]();\r\n // promise.then(() => {\r\n // console.timeEnd(str);\r\n // });\r\n return promise;\r\n });\r\n }\r\n\r\n public saveFile(fileName: string, blob: Blob | Uint8Array) {\r\n //return Promise.resolve(blobConstruct([blob]));\r\n if(!(blob instanceof Blob)) {\r\n blob = blobConstruct(blob) as Blob;\r\n }\r\n\r\n const response = new Response(blob, {\r\n headers: {\r\n 'Content-Length': '' + blob.size\r\n }\r\n });\r\n \r\n return this.save(fileName, response).then(() => blob as Blob);\r\n }\r\n\r\n public timeoutOperation<T>(callback: (cache: Cache) => Promise<T>) {\r\n if(!this.useStorage) {\r\n return Promise.reject('STORAGE_OFFLINE');\r\n }\r\n\r\n return new Promise<T>(async(resolve, reject) => {\r\n let rejected = false;\r\n const timeout = setTimeout(() => {\r\n reject();\r\n //console.warn('CACHESTORAGE TIMEOUT');\r\n rejected = true;\r\n }, 15e3);\r\n\r\n try {\r\n const cache = await this.openDatabase();\r\n if(!cache) {\r\n this.useStorage = false;\r\n this.openDbPromise = undefined;\r\n throw 'no cache?';\r\n }\r\n\r\n const res = await callback(cache);\r\n\r\n if(rejected) return;\r\n resolve(res);\r\n } catch(err) {\r\n reject(err);\r\n }\r\n\r\n clearTimeout(timeout);\r\n });\r\n }\r\n\r\n public getFileWriter(fileName: string, mimeType: string) {\r\n const fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => {\r\n return this.saveFile(fileName, blob).catch(() => blob);\r\n });\r\n\r\n return Promise.resolve(fakeWriter);\r\n }\r\n\r\n public static toggleStorage(enabled: boolean) {\r\n return Promise.all(this.STORAGES.map(storage => {\r\n storage.useStorage = enabled;\r\n \r\n if(!enabled) {\r\n return storage.deleteAll();\r\n }\r\n }));\r\n }\r\n}\r\n\r\n//const cacheStorage = new CacheStorageController(); \r\n//MOUNT_CLASS_TO.cacheStorage = cacheStorage;\r\n//export default cacheStorage;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { readBlobAsUint8Array } from \"../../helpers/blob\";\r\nimport { CancellablePromise, deferredPromise } from \"../../helpers/cancellablePromise\";\r\nimport { getWindowClients } from \"../../helpers/context\";\r\nimport debounce from \"../../helpers/schedulers/debounce\";\r\nimport { InputFileLocation, UploadFile } from \"../../layer\";\r\nimport CacheStorageController from \"../cacheStorage\";\r\nimport { DownloadOptions } from \"../mtproto/apiFileManager\";\r\nimport { RequestFilePartTask, deferredPromises, log } from \"./index.service\";\r\nimport timeout from \"./timeout\";\r\n\r\nconst cacheStorage = new CacheStorageController('cachedStreamChunks');\r\nconst CHUNK_TTL = 86400;\r\nconst CHUNK_CACHED_TIME_HEADER = 'Time-Cached';\r\n\r\nconst clearOldChunks = () => {\r\n return cacheStorage.timeoutOperation((cache) => {\r\n return cache.keys().then(requests => {\r\n const filtered: Map<StreamId, Request> = new Map();\r\n const timestamp = Date.now() / 1000 | 0;\r\n for(const request of requests) {\r\n const match = request.url.match(/\\/(\\d+?)\\?/);\r\n if(match && !filtered.has(match[1])) {\r\n filtered.set(match[1], request);\r\n }\r\n }\r\n\r\n const promises: Promise<any>[] = [];\r\n for(const [id, request] of filtered) {\r\n const promise = cache.match(request).then((response) => {\r\n if((+response.headers.get(CHUNK_CACHED_TIME_HEADER) + CHUNK_TTL) <= timestamp) {\r\n log('will delete stream chunk:', id);\r\n return cache.delete(request, {ignoreSearch: true, ignoreVary: true});\r\n }\r\n });\r\n\r\n promises.push(promise);\r\n }\r\n\r\n return Promise.all(promises);\r\n });\r\n });\r\n};\r\n\r\nsetInterval(clearOldChunks, 1800e3);\r\nsetInterval(() => {\r\n getWindowClients().then((clients) => {\r\n for(const [clientId, promises] of deferredPromises) {\r\n if(!clients.find(client => client.id === clientId)) {\r\n for(const taskId in promises) {\r\n const promise = promises[taskId];\r\n promise.reject();\r\n }\r\n\r\n deferredPromises.delete(clientId);\r\n }\r\n }\r\n });\r\n}, 120e3);\r\n\r\ntype StreamRange = [number, number];\r\ntype StreamId = DocId;\r\nconst streams: Map<StreamId, Stream> = new Map();\r\nclass Stream {\r\n private destroyDebounced: () => void;\r\n private id: StreamId;\r\n private limitPart: number;\r\n private loadedOffsets: Set<number> = new Set();\r\n\r\n constructor(private info: DownloadOptions) {\r\n this.id = Stream.getId(info);\r\n streams.set(this.id, this);\r\n\r\n // ! если грузить очень большое видео чанками по 512Кб в мобильном Safari, то стрим не запустится\r\n this.limitPart = info.size > (75 * 1024 * 1024) ? STREAM_CHUNK_UPPER_LIMIT : STREAM_CHUNK_MIDDLE_LIMIT;\r\n this.destroyDebounced = debounce(this.destroy, 150000, false, true);\r\n }\r\n\r\n private destroy = () => {\r\n streams.delete(this.id);\r\n };\r\n\r\n private async requestFilePartFromWorker(alignedOffset: number, limit: number, fromPreload = false) {\r\n const task: Omit<RequestFilePartTask, 'id'> = {\r\n type: 'requestFilePart',\r\n payload: [this.info.dcId, this.info.location, alignedOffset, limit]\r\n };\r\n\r\n const taskId = JSON.stringify(task);\r\n (task as RequestFilePartTask).id = taskId;\r\n\r\n const windowClient = await getWindowClients().then((clients) => {\r\n if(!clients.length) {\r\n return;\r\n }\r\n\r\n return clients.find(client => deferredPromises.has(client.id)) || clients[0];\r\n });\r\n\r\n if(!windowClient) {\r\n throw new Error('no window');\r\n }\r\n\r\n let promises = deferredPromises.get(windowClient.id);\r\n if(!promises) {\r\n deferredPromises.set(windowClient.id, promises = {});\r\n }\r\n \r\n let deferred = promises[taskId] as CancellablePromise<UploadFile.uploadFile>;\r\n if(deferred) {\r\n return deferred.then(uploadFile => uploadFile.bytes);\r\n }\r\n \r\n windowClient.postMessage(task);\r\n this.loadedOffsets.add(alignedOffset);\r\n \r\n deferred = promises[taskId] = deferredPromise<UploadFile.uploadFile>();\r\n const bytesPromise = deferred.then(uploadFile => uploadFile.bytes);\r\n\r\n this.saveChunkToCache(bytesPromise, alignedOffset, limit);\r\n !fromPreload && this.preloadChunks(alignedOffset, alignedOffset + (this.limitPart * 15));\r\n\r\n return bytesPromise;\r\n }\r\n\r\n private requestFilePartFromCache(alignedOffset: number, limit: number, fromPreload?: boolean) {\r\n const key = this.getChunkKey(alignedOffset, limit);\r\n return cacheStorage.getFile(key).then((blob: Blob) => {\r\n return fromPreload ? new Uint8Array() : readBlobAsUint8Array(blob);\r\n }, (error) => {\r\n if(error === 'NO_ENTRY_FOUND') {\r\n return;\r\n }\r\n });\r\n }\r\n\r\n private requestFilePart(alignedOffset: number, limit: number, fromPreload?: boolean) {\r\n return this.requestFilePartFromCache(alignedOffset, limit, fromPreload).then(bytes => {\r\n return bytes || this.requestFilePartFromWorker(alignedOffset, limit, fromPreload);\r\n });\r\n }\r\n\r\n private saveChunkToCache(deferred: Promise<Uint8Array>, alignedOffset: number, limit: number) {\r\n return deferred.then(bytes => {\r\n const key = this.getChunkKey(alignedOffset, limit);\r\n const response = new Response(bytes, {\r\n headers: {\r\n 'Content-Length': '' + bytes.length,\r\n 'Content-Type': 'application/octet-stream',\r\n [CHUNK_CACHED_TIME_HEADER]: '' + (Date.now() / 1000 | 0)\r\n }\r\n });\r\n\r\n return cacheStorage.save(key, response);\r\n });\r\n }\r\n\r\n private preloadChunk(offset: number) {\r\n if(this.loadedOffsets.has(offset)) {\r\n return;\r\n }\r\n\r\n this.loadedOffsets.add(offset);\r\n this.requestFilePart(offset, this.limitPart, true);\r\n }\r\n\r\n private preloadChunks(offset: number, end: number) {\r\n if(end > this.info.size) {\r\n end = this.info.size;\r\n }\r\n\r\n if(!offset) { // load last chunk for bounds\r\n this.preloadChunk(alignOffset(offset, this.limitPart));\r\n } else { // don't preload next chunks before the start\r\n for(; offset < end; offset += this.limitPart) {\r\n this.preloadChunk(offset);\r\n }\r\n }\r\n }\r\n\r\n public requestRange(range: StreamRange) {\r\n this.destroyDebounced();\r\n\r\n const possibleResponse = responseForSafariFirstRange(range, this.info.mimeType, this.info.size);\r\n if(possibleResponse) {\r\n return possibleResponse;\r\n }\r\n\r\n let [offset, end] = range;\r\n\r\n /* if(info.size > limitPart && isSafari && offset === limitPart) {\r\n //end = info.size - 1;\r\n //offset = info.size - 1 - limitPart;\r\n offset = info.size - (info.size % limitPart);\r\n } */\r\n\r\n const limit = end && end < this.limitPart ? alignLimit(end - offset + 1) : this.limitPart;\r\n const alignedOffset = alignOffset(offset, limit);\r\n\r\n if(!end) {\r\n end = Math.min(offset + limit, this.info.size - 1);\r\n }\r\n\r\n return this.requestFilePart(alignedOffset, limit).then(ab => {\r\n //log.debug('[stream] requestFilePart result:', result);\r\n\r\n // if(isSafari) {\r\n if(offset !== alignedOffset || end !== (alignedOffset + limit)) {\r\n ab = ab.slice(offset - alignedOffset, end - alignedOffset + 1);\r\n }\r\n \r\n const headers: Record<string, string> = {\r\n 'Accept-Ranges': 'bytes',\r\n 'Content-Range': `bytes ${offset}-${offset + ab.byteLength - 1}/${this.info.size || '*'}`,\r\n 'Content-Length': `${ab.byteLength}`\r\n };\r\n\r\n if(this.info.mimeType) {\r\n headers['Content-Type'] = this.info.mimeType;\r\n }\r\n\r\n // simulate slow connection\r\n //setTimeout(() => {\r\n return new Response(ab, {\r\n status: 206,\r\n statusText: 'Partial Content',\r\n headers,\r\n });\r\n //}, 2.5e3);\r\n });\r\n }\r\n\r\n private getChunkKey(alignedOffset: number, limit: number) {\r\n return this.id + '?offset=' + alignedOffset + '&limit=' + limit;\r\n }\r\n\r\n public static get(info: DownloadOptions) {\r\n return streams.get(this.getId(info)) ?? new Stream(info);\r\n }\r\n\r\n private static getId(info: DownloadOptions) {\r\n return (info.location as InputFileLocation.inputDocumentFileLocation).id;\r\n }\r\n}\r\n\r\nexport default function onStreamFetch(event: FetchEvent, params: string) {\r\n const range = parseRange(event.request.headers.get('Range'));\r\n const info: DownloadOptions = JSON.parse(decodeURIComponent(params));\r\n const stream = Stream.get(info);\r\n\r\n //log.debug('[stream]', url, offset, end);\r\n\r\n event.respondWith(Promise.race([\r\n timeout(45 * 1000),\r\n stream.requestRange(range)\r\n ]));\r\n}\r\n\r\nfunction responseForSafariFirstRange(range: StreamRange, mimeType: string, size: number): Response {\r\n if(range[0] === 0 && range[1] === 1) {\r\n return new Response(new Uint8Array(2).buffer, {\r\n status: 206,\r\n statusText: 'Partial Content',\r\n headers: {\r\n 'Accept-Ranges': 'bytes',\r\n 'Content-Range': `bytes 0-1/${size || '*'}`,\r\n 'Content-Length': '2',\r\n 'Content-Type': mimeType || 'video/mp4',\r\n },\r\n });\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/* const STREAM_CHUNK_UPPER_LIMIT = 256 * 1024;\r\nconst SMALLEST_CHUNK_LIMIT = 256 * 4; */\r\n/* const STREAM_CHUNK_UPPER_LIMIT = 1024 * 1024;\r\nconst SMALLEST_CHUNK_LIMIT = 1024 * 4; */\r\nconst STREAM_CHUNK_MIDDLE_LIMIT = 512 * 1024;\r\nconst STREAM_CHUNK_UPPER_LIMIT = 1024 * 1024;\r\nconst SMALLEST_CHUNK_LIMIT = 512 * 4;\r\n\r\nfunction parseRange(header: string): StreamRange {\r\n if(!header) return [0, 0];\r\n const [, chunks] = header.split('=');\r\n const ranges = chunks.split(', ');\r\n const [offset, end] = ranges[0].split('-');\r\n\r\n return [+offset, +end || 0];\r\n}\r\n\r\nfunction alignOffset(offset: number, base = SMALLEST_CHUNK_LIMIT) {\r\n return offset - (offset % base);\r\n}\r\n\r\nfunction alignLimit(limit: number) {\r\n return 2 ** Math.ceil(Math.log(limit) / Math.log(2));\r\n}\r\n","// * Jolly Cobra's schedulers\r\n\r\nimport { AnyFunction, Awaited } from \"../../types\";\r\n\r\nexport default function debounce<F extends AnyFunction>(\r\n fn: F,\r\n ms: number,\r\n shouldRunFirst = true,\r\n shouldRunLast = true,\r\n) {\r\n let waitingTimeout: number;\r\n let waitingPromise: Promise<Awaited<ReturnType<F>>>, resolve: (result: any) => void, reject: () => void;\r\n let hadNewCall = false;\r\n\r\n return (...args: Parameters<F>): typeof waitingPromise => {\r\n if(!waitingPromise) waitingPromise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));\r\n\r\n if(waitingTimeout) {\r\n clearTimeout(waitingTimeout);\r\n hadNewCall = true;\r\n reject();\r\n waitingPromise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));\r\n } else if(shouldRunFirst) {\r\n // @ts-ignore\r\n resolve(fn(...args));\r\n hadNewCall = false;\r\n }\r\n\r\n waitingTimeout = setTimeout(() => {\r\n // will run if should run last or first but with new call\r\n if(shouldRunLast && (!shouldRunFirst || hadNewCall)) {\r\n // @ts-ignore\r\n resolve(fn(...args));\r\n }\r\n\r\n waitingTimeout = waitingPromise = resolve = reject = undefined;\r\n hadNewCall = false;\r\n }, ms) as any;\r\n\r\n waitingPromise.catch(() => {});\r\n return waitingPromise;\r\n };\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport noop from \"./noop\";\r\n\r\nexport interface CancellablePromise<T> extends Promise<T> {\r\n resolve?: (value: T) => void,\r\n reject?: (...args: any[]) => void,\r\n cancel?: () => void,\r\n\r\n notify?: (...args: any[]) => void,\r\n notifyAll?: (...args: any[]) => void,\r\n lastNotify?: any,\r\n listeners?: Array<(...args: any[]) => void>,\r\n addNotifyListener?: (callback: (...args: any[]) => void) => void,\r\n\r\n isFulfilled?: boolean,\r\n isRejected?: boolean\r\n}\r\n\r\nexport function deferredPromise<T>() {\r\n let deferredHelper: any = {\r\n isFulfilled: false, \r\n isRejected: false,\r\n\r\n notify: () => {}, \r\n notifyAll: (...args: any[]) => {\r\n deferredHelper.lastNotify = args;\r\n deferredHelper.listeners.forEach((callback: any) => callback(...args));\r\n }, \r\n\r\n listeners: [],\r\n addNotifyListener: (callback: (...args: any[]) => void) => {\r\n if(deferredHelper.lastNotify) {\r\n callback(...deferredHelper.lastNotify);\r\n }\r\n\r\n deferredHelper.listeners.push(callback);\r\n }\r\n };\r\n\r\n let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {\r\n deferredHelper.resolve = (value: T) => {\r\n if(deferred.isFulfilled || deferred.isRejected) return;\r\n\r\n deferred.isFulfilled = true;\r\n resolve(value);\r\n };\r\n \r\n deferredHelper.reject = (...args: any[]) => {\r\n if(deferred.isRejected || deferred.isFulfilled) return;\r\n \r\n deferred.isRejected = true;\r\n reject(...args);\r\n };\r\n });\r\n\r\n // @ts-ignore\r\n /* deferred.then = (resolve: (value: T) => any, reject: (...args: any[]) => any) => {\r\n const n = deferredPromise<ReturnType<typeof resolve>>();\r\n \r\n }; */\r\n\r\n deferred.catch(noop).finally(() => {\r\n deferred.notify = deferred.notifyAll = deferred.lastNotify = null;\r\n deferred.listeners.length = 0;\r\n\r\n if(deferred.cancel) {\r\n deferred.cancel = () => {};\r\n }\r\n });\r\n\r\n Object.assign(deferred, deferredHelper);\r\n\r\n return deferred;\r\n}","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { Database } from '.';\r\n\r\nconst DATABASE_STATE: Database<'session' | 'stickerSets' | 'users' | 'chats' | 'messages' | 'dialogs'> = {\r\n name: 'tweb',\r\n version: 7,\r\n stores: [{\r\n name: 'session'\r\n }, {\r\n name: 'stickerSets'\r\n }, {\r\n name: 'users'\r\n }, {\r\n name: 'chats'\r\n }, {\r\n name: 'dialogs'\r\n }, {\r\n name: 'messages'\r\n }]\r\n};\r\n\r\nexport default DATABASE_STATE;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { Database } from '../config/databases';\r\nimport Modes from '../config/modes';\r\nimport { blobConstruct } from '../helpers/blob';\r\nimport { safeAssign } from '../helpers/object';\r\nimport { logger } from './logger';\r\n\r\n/**\r\n * https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/createIndex\r\n */\r\nexport type IDBIndex = {\r\n indexName: string,\r\n keyPath: string,\r\n objectParameters: IDBIndexParameters\r\n};\r\n\r\nexport type IDBStore = {\r\n name: string, \r\n indexes?: IDBIndex[]\r\n};\r\n\r\nexport type IDBOptions = {\r\n name?: string,\r\n storeName: string,\r\n stores?: IDBStore[],\r\n version?: number\r\n};\r\n\r\nconst DEBUG = false;\r\n\r\nexport default class IDBStorage<T extends Database<any>> {\r\n private static STORAGES: IDBStorage<Database<any>>[] = [];\r\n private openDbPromise: Promise<IDBDatabase>;\r\n private db: IDBDatabase;\r\n private storageIsAvailable = true;\r\n\r\n private log: ReturnType<typeof logger>;\r\n \r\n private name: string;\r\n private version: number;\r\n private stores: IDBStore[];\r\n private storeName: T['stores'][0]['name'];\r\n\r\n constructor(db: T, storeName: typeof db['stores'][0]['name']) {\r\n safeAssign(this, db);\r\n\r\n if(Modes.test) {\r\n this.name += '_test';\r\n }\r\n\r\n this.storeName = storeName;\r\n\r\n this.log = logger('IDB-' + this.storeName);\r\n\r\n this.openDatabase(true);\r\n\r\n IDBStorage.STORAGES.push(this);\r\n }\r\n\r\n public static closeDatabases(preserve?: IDBStorage<Database<any>>) {\r\n this.STORAGES.forEach(storage => {\r\n if(preserve && preserve === storage) {\r\n return;\r\n }\r\n\r\n const db = storage.db;\r\n if(db) {\r\n db.onclose = () => {};\r\n db.close();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * ! WARNING ! function requires at least one opened connection\r\n */\r\n /* public static clearObjectStores() {\r\n const storage = this.STORAGES[0];\r\n this.closeDatabases(storage);\r\n\r\n const names = Array.from(storage.db.objectStoreNames);\r\n const promises = names.map(name => storage.clear(name));\r\n return Promise.all(promises);\r\n } */\r\n\r\n /* public static deleteDatabase() {\r\n this.closeDatabases();\r\n\r\n const storages = this.STORAGES;\r\n const dbNames = Array.from(new Set(storages.map(storage => storage.name)));\r\n const promises = dbNames.map(dbName => {\r\n return new Promise<void>((resolve, reject) => {\r\n const deleteRequest = indexedDB.deleteDatabase(dbName);\r\n \r\n deleteRequest.onerror = () => {\r\n reject();\r\n };\r\n \r\n deleteRequest.onsuccess = () => {\r\n resolve();\r\n };\r\n });\r\n });\r\n\r\n return Promise.all(promises);\r\n } */\r\n\r\n public isAvailable() {\r\n return this.storageIsAvailable;\r\n }\r\n\r\n public openDatabase(createNew = false): Promise<IDBDatabase> {\r\n if(this.openDbPromise && !createNew) {\r\n return this.openDbPromise;\r\n }\r\n\r\n const createObjectStore = (db: IDBDatabase, store: IDBStore) => {\r\n const os = db.createObjectStore(store.name);\r\n\r\n if(store.indexes?.length) {\r\n for(const index of store.indexes) {\r\n os.createIndex(index.indexName, index.keyPath, index.objectParameters);\r\n }\r\n }\r\n };\r\n\r\n try {\r\n var request = indexedDB.open(this.name, this.version);\r\n\r\n if(!request) {\r\n return Promise.reject();\r\n }\r\n } catch(error) {\r\n this.log.error('error opening db', (error as Error).message);\r\n this.storageIsAvailable = false;\r\n return Promise.reject(error);\r\n }\r\n\r\n let finished = false;\r\n setTimeout(() => {\r\n if(!finished) {\r\n request.onerror({type: 'IDB_CREATE_TIMEOUT'} as Event);\r\n }\r\n }, 3000);\r\n\r\n return this.openDbPromise = new Promise<IDBDatabase>((resolve, reject) => {\r\n request.onsuccess = (event) => {\r\n finished = true;\r\n const db = request.result;\r\n let calledNew = false;\r\n\r\n this.log('Opened');\r\n \r\n db.onerror = (error) => {\r\n this.storageIsAvailable = false;\r\n this.log.error('Error creating/accessing IndexedDB database', error);\r\n reject(error);\r\n };\r\n\r\n db.onclose = (e) => {\r\n this.log.error('closed:', e);\r\n !calledNew && this.openDatabase();\r\n };\r\n\r\n db.onabort = (e) => {\r\n this.log.error('abort:', e);\r\n const transaction = e.target as IDBTransaction;\r\n \r\n this.openDatabase(calledNew = true);\r\n\r\n if(transaction.onerror) {\r\n transaction.onerror(e);\r\n }\r\n\r\n db.close();\r\n };\r\n\r\n db.onversionchange = (e) => {\r\n this.log.error('onversionchange, lol?');\r\n };\r\n\r\n resolve(this.db = db);\r\n };\r\n \r\n request.onerror = (event) => {\r\n finished = true;\r\n this.storageIsAvailable = false;\r\n this.log.error('Error creating/accessing IndexedDB database', event);\r\n reject(event);\r\n };\r\n \r\n request.onupgradeneeded = (event) => {\r\n finished = true;\r\n this.log.warn('performing idb upgrade from', event.oldVersion, 'to', event.newVersion);\r\n\r\n // @ts-ignore\r\n var db = event.target.result as IDBDatabase;\r\n this.stores.forEach((store) => {\r\n /* if(db.objectStoreNames.contains(store.name)) {\r\n //if(event.oldVersion === 1) {\r\n db.deleteObjectStore(store.name);\r\n //}\r\n } */\r\n \r\n if(!db.objectStoreNames.contains(store.name)) {\r\n createObjectStore(db, store);\r\n }\r\n });\r\n };\r\n });\r\n }\r\n\r\n public delete(entryName: string | string[]): Promise<void> {\r\n //return Promise.resolve();\r\n if(!Array.isArray(entryName)) {\r\n entryName = [].concat(entryName);\r\n }\r\n\r\n return this.getObjectStore('readwrite', (objectStore) => {\r\n return (entryName as string[]).map((entryName) => objectStore.delete(entryName));\r\n }, DEBUG ? 'delete: ' + entryName.join(', ') : '');\r\n }\r\n\r\n public clear(storeName?: IDBStorage<T>['storeName']) {\r\n return this.getObjectStore('readwrite', (objectStore) => objectStore.clear(), DEBUG ? 'clear' : '', storeName);\r\n }\r\n\r\n public save(entryName: string | string[], value: any | any[]) {\r\n // const handleError = (error: Error) => {\r\n // this.log.error('save: transaction error:', entryName, value, db, error, error && error.name);\r\n // if((!error || error.name === 'InvalidStateError')/* && false */) {\r\n // setTimeout(() => {\r\n // this.save(entryName, value);\r\n // }, 2e3);\r\n // } else {\r\n // //console.error('IndexedDB saveFile transaction error:', error, error && error.name);\r\n // }\r\n // };\r\n\r\n if(!Array.isArray(entryName)) {\r\n entryName = [].concat(entryName);\r\n value = [].concat(value);\r\n }\r\n \r\n return this.getObjectStore('readwrite', (objectStore) => {\r\n return (entryName as string[]).map((entryName, idx) => objectStore.put(value[idx], entryName));\r\n }, DEBUG ? 'save: ' + entryName.join(', ') : '');\r\n }\r\n\r\n public saveFile(fileName: string, blob: Blob | Uint8Array) {\r\n //return Promise.resolve(blobConstruct([blob]));\r\n if(!(blob instanceof Blob)) {\r\n blob = blobConstruct([blob]) as Blob;\r\n }\r\n\r\n return this.save(fileName, blob);\r\n }\r\n\r\n /* public saveFileBase64(db: IDBDatabase, fileName: string, blob: Blob | any): Promise<Blob> {\r\n if(this.getBlobSize(blob) > 10 * 1024 * 1024) {\r\n return Promise.reject();\r\n }\r\n\r\n if(!(blob instanceof Blob)) {\r\n var safeMimeType = blobSafeMimeType(blob.type || 'image/jpeg');\r\n var address = 'data:' + safeMimeType + ';base64,' + bytesToBase64(blob);\r\n return this.storagePutB64String(db, fileName, address).then(() => {\r\n return blob;\r\n });\r\n }\r\n\r\n try {\r\n var reader = new FileReader();\r\n } catch (e) {\r\n this.storageIsAvailable = false;\r\n return Promise.reject();\r\n }\r\n\r\n let promise = new Promise<Blob>((resolve, reject) => {\r\n reader.onloadend = () => {\r\n this.storagePutB64String(db, fileName, reader.result as string).then(() => {\r\n resolve(blob);\r\n }, reject);\r\n }\r\n \r\n reader.onerror = reject;\r\n });\r\n \r\n\r\n try {\r\n reader.readAsDataURL(blob);\r\n } catch (e) {\r\n this.storageIsAvailable = false;\r\n return Promise.reject();\r\n }\r\n\r\n return promise;\r\n }\r\n\r\n public storagePutB64String(db: IDBDatabase, fileName: string, b64string: string) {\r\n try {\r\n var objectStore = db.transaction([this.storeName], 'readwrite')\r\n .objectStore(this.storeName);\r\n var request = objectStore.put(b64string, fileName);\r\n } catch(error) {\r\n this.storageIsAvailable = false;\r\n return Promise.reject(error);\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = function(event) {\r\n resolve();\r\n };\r\n \r\n request.onerror = reject;\r\n });\r\n }\r\n\r\n public getBlobSize(blob: any) {\r\n return blob.size || blob.byteLength || blob.length;\r\n } */\r\n\r\n public get<T>(entryName: string[]): Promise<T[]>;\r\n public get<T>(entryName: string): Promise<T>;\r\n public get<T>(entryName: string | string[]): Promise<T> | Promise<T[]> {\r\n //return Promise.reject();\r\n\r\n if(!Array.isArray(entryName)) {\r\n entryName = [].concat(entryName);\r\n }\r\n\r\n return this.getObjectStore<T>('readonly', (objectStore) => {\r\n return (entryName as string[]).map((entryName) => objectStore.get(entryName));\r\n }, DEBUG ? 'get: ' + entryName.join(', ') : '');\r\n }\r\n\r\n private getObjectStore<T>(mode: IDBTransactionMode, objectStore: (objectStore: IDBObjectStore) => IDBRequest | IDBRequest[], log?: string, storeName = this.storeName) {\r\n let perf: number;\r\n\r\n if(log) {\r\n perf = performance.now();\r\n this.log(log + ': start');\r\n }\r\n\r\n return this.openDatabase().then((db) => {\r\n return new Promise<T>((resolve, reject) => {\r\n /* if(mode === 'readwrite') {\r\n return;\r\n } */\r\n\r\n const transaction = db.transaction([storeName], mode);\r\n\r\n transaction.onerror = (e) => {\r\n clearTimeout(timeout);\r\n reject(transaction.error);\r\n };\r\n \r\n transaction.oncomplete = (e) => {\r\n clearTimeout(timeout);\r\n\r\n if(log) {\r\n this.log(log + ': end', performance.now() - perf);\r\n }\r\n\r\n const results = r.map(r => r.result);\r\n resolve(isArray ? results : results[0]);\r\n };\r\n \r\n const timeout = setTimeout(() => {\r\n this.log.error('transaction not finished', transaction);\r\n }, 10000);\r\n \r\n /* transaction.addEventListener('abort', (e) => {\r\n //handleError();\r\n this.log.error('IndexedDB: transaction abort!', transaction.error);\r\n }); */\r\n \r\n const requests = objectStore(transaction.objectStore(storeName));\r\n\r\n const isArray = Array.isArray(requests);\r\n const r: IDBRequest[] = isArray ? requests : [].concat(requests) as any;\r\n\r\n // const length = r.length;\r\n // /* let left = length;\r\n\r\n // const onRequestFinished = (error?: Error) => {\r\n // if(!--left) {\r\n // resolve(result);\r\n // clearTimeout(timeout);\r\n // }\r\n // }; */\r\n\r\n // for(let i = 0; i < length; ++i) {\r\n // const request = r[i];\r\n // request.onsuccess = () => {\r\n // onRequestFinished();\r\n // };\r\n\r\n // request.onerror = (e) => {\r\n // onRequestFinished(transaction.error);\r\n // };\r\n // }\r\n });\r\n });\r\n }\r\n\r\n public getAll<T>(): Promise<T[]> {\r\n return this.getObjectStore<T[]>('readonly', (objectStore) => objectStore.getAll(), DEBUG ? 'getAll' : '');\r\n }\r\n\r\n /* public getAllKeys(): Promise<Array<string>> {\r\n console.time('getAllEntries');\r\n return this.openDatabase().then((db) => {\r\n var objectStore = db.transaction([this.storeName], 'readonly')\r\n .objectStore(this.storeName);\r\n var request = objectStore.getAllKeys();\r\n\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = function(event) {\r\n // @ts-ignore\r\n var result = event.target.result;\r\n resolve(result);\r\n console.timeEnd('getAllEntries');\r\n }\r\n \r\n request.onerror = reject;\r\n });\r\n });\r\n } */\r\n\r\n /* public isFileExists(fileName: string): Promise<boolean> {\r\n console.time('isFileExists');\r\n return this.openDatabase().then((db) => {\r\n var objectStore = db.transaction([this.storeName], 'readonly')\r\n .objectStore(this.storeName);\r\n var request = objectStore.openCursor(fileName);\r\n\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = function(event) {\r\n // @ts-ignore\r\n var cursor = event.target.result;\r\n resolve(!!cursor);\r\n console.timeEnd('isFileExists');\r\n }\r\n \r\n request.onerror = reject;\r\n });\r\n });\r\n } */\r\n\r\n /* public getFileWriter(fileName: string, mimeType: string) {\r\n var fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => {\r\n return this.saveFile(fileName, blob);\r\n });\r\n\r\n return Promise.resolve(fakeWriter);\r\n } */\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nexport function copy<T>(obj: T): T {\r\n //in case of premitives\r\n if(obj === null || typeof(obj) !== \"object\") {\r\n return obj;\r\n }\r\n \r\n //date objects should be \r\n if(obj instanceof Date) {\r\n return new Date(obj.getTime()) as any;\r\n }\r\n \r\n //handle Array\r\n if(Array.isArray(obj)) {\r\n // @ts-ignore\r\n const clonedArr: T = obj.map(el => copy(el)) as any as T;\r\n return clonedArr;\r\n }\r\n \r\n //lastly, handle objects\r\n // @ts-ignore\r\n let clonedObj = new obj.constructor();\r\n for(var prop in obj) {\r\n if(obj.hasOwnProperty(prop)) {\r\n clonedObj[prop] = copy(obj[prop]);\r\n }\r\n }\r\n return clonedObj;\r\n}\r\n\r\nexport function deepEqual(x: any, y: any): boolean {\r\n const ok = Object.keys, tx = typeof x, ty = typeof y;\r\n return x && y && tx === 'object' && tx === ty ? (\r\n ok(x).length === ok(y).length &&\r\n ok(x).every(key => deepEqual(x[key], y[key]))\r\n ) : (x === y);\r\n}\r\n\r\nexport function defineNotNumerableProperties<T extends any>(obj: T, names: (keyof T)[]) {\r\n //const perf = performance.now();\r\n const props = {writable: true, configurable: true};\r\n const out: {[name in keyof T]?: typeof props} = {};\r\n names.forEach(name => {\r\n if(!obj.hasOwnProperty(name)) {\r\n out[name] = props;\r\n }\r\n });\r\n Object.defineProperties(obj, out);\r\n //console.log('defineNotNumerableProperties time:', performance.now() - perf);\r\n}\r\n\r\nexport function getObjectKeysAndSort(object: {[key: string]: any}, sort: 'asc' | 'desc' = 'asc') {\r\n if(!object) return [];\r\n const ids = object instanceof Map ? [...object.keys()] : Object.keys(object).map(i => +i);\r\n if(sort === 'asc') return ids.sort((a, b) => a - b);\r\n else return ids.sort((a, b) => b - a);\r\n}\r\n\r\nexport function safeReplaceObject(wasObject: any, newObject: any) {\r\n if(!wasObject) {\r\n return newObject;\r\n }\r\n\r\n for(var key in wasObject) {\r\n if(!newObject.hasOwnProperty(key)) {\r\n delete wasObject[key];\r\n }\r\n }\r\n\r\n for(var key in newObject) {\r\n //if (newObject.hasOwnProperty(key)) { // useless\r\n wasObject[key] = newObject[key];\r\n //}\r\n }\r\n \r\n return wasObject;\r\n}\r\n\r\n/**\r\n * Will be used for FILE_REFERENCE_EXPIRED\r\n * @param key \r\n * @param wasObject \r\n * @param newObject \r\n */\r\nexport function safeReplaceArrayInObject<K>(key: K, wasObject: any, newObject: any) {\r\n if('byteLength' in newObject[key]) { // Uint8Array\r\n newObject[key] = [...newObject[key]];\r\n }\r\n\r\n if(wasObject && wasObject[key] !== newObject[key]) {\r\n wasObject[key].length = newObject[key].length;\r\n (newObject[key] as any[]).forEach((v, i) => {\r\n wasObject[key][i] = v;\r\n });\r\n\r\n /* wasObject[key].set(newObject[key]); */\r\n newObject[key] = wasObject[key];\r\n }\r\n}\r\n\r\nexport function isObject<T extends Record<any, any>>(object: any): object is T {\r\n return typeof(object) === 'object' && object !== null;\r\n}\r\n\r\nexport function getDeepProperty(object: any, key: string) {\r\n const splitted = key.split('.');\r\n let o: any = object;\r\n splitted.forEach(key => {\r\n if(!key) {\r\n return;\r\n }\r\n \r\n // @ts-ignore\r\n o = o[key];\r\n });\r\n \r\n return o;\r\n}\r\n\r\nexport function setDeepProperty(object: any, key: string, value: any) {\r\n const splitted = key.split('.');\r\n getDeepProperty(object, splitted.slice(0, -1).join('.'))[splitted.pop()] = value;\r\n}\r\n\r\nexport function validateInitObject(initObject: any, currentObject: any, onReplace?: (key: string) => void, previousKey?: string) {\r\n for(const key in initObject) {\r\n if(typeof(currentObject[key]) !== typeof(initObject[key])) {\r\n currentObject[key] = copy(initObject[key]);\r\n onReplace && onReplace(previousKey || key);\r\n } else if(isObject(initObject[key])) {\r\n validateInitObject(initObject[key], currentObject[key], onReplace, previousKey || key);\r\n }\r\n }\r\n}\r\n\r\nexport function safeAssign<T>(object: T, fromObject: any) {\r\n if(fromObject) {\r\n for(let i in fromObject) {\r\n if(fromObject[i] !== undefined) {\r\n // @ts-ignore\r\n object[i] = fromObject[i];\r\n }\r\n }\r\n }\r\n\r\n return object;\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { Database } from \"../../config/databases\";\r\nimport DATABASE_STATE from \"../../config/databases/state\";\r\nimport { IS_FIREFOX } from \"../../environment/userAgent\";\r\nimport IDBStorage from \"../idb\";\r\nimport { log, ServiceWorkerPingTask, ServiceWorkerPushClickTask } from \"./index.service\";\r\n\r\nconst ctx = self as any as ServiceWorkerGlobalScope;\r\nconst defaultBaseUrl = location.protocol + '//' + location.hostname + location.pathname.split('/').slice(0, -1).join('/') + '/';\r\n\r\nexport type PushNotificationObject = {\r\n loc_key: string,\r\n loc_args: string[],\r\n //user_id: number, // should be number\r\n custom: {\r\n channel_id?: string, // should be number\r\n chat_id?: string, // should be number\r\n from_id?: string, // should be number\r\n msg_id: string,\r\n peerId?: string // should be number\r\n },\r\n sound?: string,\r\n random_id: number,\r\n badge?: string, // should be number\r\n description: string,\r\n mute: string, // should be number\r\n title: string,\r\n\r\n action?: 'mute1d' | 'push_settings', // will be set before postMessage to main thread\r\n};\r\n\r\nclass SomethingGetter<T extends Database<any>, Storage extends Record<string, any>> {\r\n private cache: Partial<Storage> = {};\r\n private storage: IDBStorage<T>;\r\n\r\n constructor(\r\n db: T, \r\n storeName: typeof db['stores'][number]['name'], \r\n private defaults: {\r\n [Property in keyof Storage]: ((value: Storage[Property]) => Storage[Property]) | Storage[Property]\r\n }\r\n ) {\r\n this.storage = new IDBStorage<T>(db, storeName);\r\n }\r\n\r\n public async get<T extends keyof Storage>(key: T) {\r\n if(this.cache[key] !== undefined) {\r\n return this.cache[key];\r\n }\r\n\r\n let value: Storage[T];\r\n try {\r\n value = await this.storage.get(key as string);\r\n } catch(err) {\r\n\r\n }\r\n\r\n if(this.cache[key] !== undefined) {\r\n return this.cache[key];\r\n }\r\n\r\n if(value === undefined) {\r\n const callback = this.defaults[key];\r\n value = typeof(callback) === 'function' ? callback() : callback;\r\n }\r\n\r\n return this.cache[key] = value;\r\n }\r\n\r\n public async set<T extends keyof Storage>(key: T, value: Storage[T]) {\r\n this.cache[key] = value;\r\n\r\n try {\r\n this.storage.save(key as string, value);\r\n } catch(err) {\r\n\r\n }\r\n }\r\n}\r\n\r\ntype PushStorage = {\r\n push_mute_until: number,\r\n push_last_alive: number,\r\n push_lang: Partial<ServiceWorkerPingTask['payload']['lang']>\r\n push_settings: Partial<ServiceWorkerPingTask['payload']['settings']>\r\n};\r\n\r\nconst getter = new SomethingGetter<typeof DATABASE_STATE, PushStorage>(DATABASE_STATE, 'session', {\r\n push_mute_until: 0,\r\n push_last_alive: 0,\r\n push_lang: {},\r\n push_settings: {}\r\n});\r\n\r\nctx.addEventListener('push', (event) => {\r\n const obj: PushNotificationObject = event.data.json();\r\n log('push', obj);\r\n\r\n let hasActiveWindows = false;\r\n const checksPromise = Promise.all([\r\n getter.get('push_mute_until'), \r\n getter.get('push_last_alive'), \r\n ctx.clients.matchAll({type: 'window'})\r\n ]).then((result) => {\r\n const [muteUntil, lastAliveTime, clientList] = result;\r\n \r\n log('matched clients', clientList);\r\n hasActiveWindows = clientList.length > 0;\r\n if(hasActiveWindows) {\r\n throw 'Supress notification because some instance is alive';\r\n }\r\n \r\n const nowTime = Date.now();\r\n if(userInvisibleIsSupported() &&\r\n muteUntil &&\r\n nowTime < muteUntil) {\r\n throw `Supress notification because mute for ${Math.ceil((muteUntil - nowTime) / 60000)} min`;\r\n }\r\n\r\n if(!obj.badge) {\r\n throw 'No badge?';\r\n }\r\n });\r\n\r\n checksPromise.catch(reason => {\r\n log(reason);\r\n });\r\n\r\n const notificationPromise = checksPromise.then(() => {\r\n return Promise.all([getter.get('push_settings'), getter.get('push_lang')])\r\n }).then((result) => {\r\n return fireNotification(obj, result[0], result[1]);\r\n });\r\n\r\n const closePromise = notificationPromise.catch(() => {\r\n log('Closing all notifications on push', hasActiveWindows);\r\n if(userInvisibleIsSupported() || hasActiveWindows) {\r\n return closeAllNotifications();\r\n }\r\n\r\n return ctx.registration.showNotification('Telegram', {\r\n tag: 'unknown_peer'\r\n }).then(() => {\r\n if(hasActiveWindows) {\r\n return closeAllNotifications();\r\n }\r\n\r\n setTimeout(() => closeAllNotifications(), hasActiveWindows ? 0 : 100);\r\n }).catch((error) => {\r\n log.error('Show notification error', error);\r\n });\r\n });\r\n\r\n event.waitUntil(closePromise);\r\n});\r\n\r\nctx.addEventListener('notificationclick', (event) => {\r\n const notification = event.notification;\r\n log('On notification click: ', notification.tag);\r\n notification.close();\r\n\r\n const action = event.action as PushNotificationObject['action'];\r\n if(action === 'mute1d' && userInvisibleIsSupported()) {\r\n log('[SW] mute for 1d');\r\n getter.set('push_mute_until', Date.now() + 86400e3);\r\n return;\r\n }\r\n\r\n const data: PushNotificationObject = notification.data;\r\n if(!data) {\r\n return;\r\n }\r\n\r\n const promise = ctx.clients.matchAll({\r\n type: 'window'\r\n }).then((clientList) => {\r\n data.action = action;\r\n pendingNotification = {type: 'push_click', payload: data};\r\n for(let i = 0; i < clientList.length; i++) {\r\n const client = clientList[i];\r\n if('focus' in client) {\r\n client.focus();\r\n client.postMessage(pendingNotification);\r\n pendingNotification = undefined;\r\n return;\r\n }\r\n }\r\n\r\n if(ctx.clients.openWindow) {\r\n return getter.get('push_settings').then((settings) => {\r\n return ctx.clients.openWindow(settings.baseUrl || defaultBaseUrl);\r\n });\r\n }\r\n }).catch((error) => {\r\n log.error('Clients.matchAll error', error);\r\n })\r\n\r\n event.waitUntil(promise);\r\n});\r\n\r\nctx.addEventListener('notificationclose', onCloseNotification);\r\n\r\nlet notifications: Set<Notification> = new Set();\r\nlet pendingNotification: ServiceWorkerPushClickTask;\r\nfunction pushToNotifications(notification: Notification) {\r\n if(!notifications.has(notification)) {\r\n notifications.add(notification);\r\n // @ts-ignore\r\n notification.onclose = onCloseNotification;\r\n }\r\n}\r\n\r\nfunction onCloseNotification(event: NotificationEvent) {\r\n removeFromNotifications(event.notification)\r\n}\r\n\r\nfunction removeFromNotifications(notification: Notification) {\r\n notifications.delete(notification);\r\n}\r\n\r\nexport function closeAllNotifications() {\r\n for(const notification of notifications) {\r\n try {\r\n notification.close();\r\n } catch(e) {}\r\n }\r\n\r\n let promise: Promise<void>;\r\n if('getNotifications' in ctx.registration) {\r\n promise = ctx.registration.getNotifications({}).then((notifications) => {\r\n for(let i = 0, len = notifications.length; i < len; ++i) {\r\n try {\r\n notifications[i].close();\r\n } catch(e) {}\r\n }\r\n }).catch((error) => {\r\n log.error('Offline register SW error', error);\r\n });\r\n } else {\r\n promise = Promise.resolve();\r\n }\r\n\r\n notifications.clear();\r\n\r\n return promise;\r\n}\r\n\r\nfunction userInvisibleIsSupported() {\r\n return IS_FIREFOX;\r\n}\r\n\r\nfunction fireNotification(obj: PushNotificationObject, settings: PushStorage['push_settings'], lang: PushStorage['push_lang']) {\r\n const icon = 'assets/img/logo_filled_rounded.png';\r\n let title = obj.title || 'Telegram';\r\n let body = obj.description || '';\r\n let peerId: string;\r\n\r\n if(obj.custom) {\r\n if(obj.custom.channel_id) {\r\n peerId = '' + -obj.custom.channel_id;\r\n } else if(obj.custom.chat_id) {\r\n peerId = '' + -obj.custom.chat_id;\r\n } else {\r\n peerId = obj.custom.from_id || '';\r\n }\r\n }\r\n\r\n obj.custom.peerId = '' + peerId;\r\n let tag = 'peer' + peerId;\r\n\r\n if(settings && settings.nopreview) {\r\n title = 'Telegram';\r\n body = lang.push_message_nopreview || 'You have a new message';\r\n tag = 'unknown_peer';\r\n }\r\n\r\n log('show notify', title, body, icon, obj);\r\n\r\n const actions: (Omit<NotificationAction, 'action'> & {action: PushNotificationObject['action']})[] = [{\r\n action: 'mute1d',\r\n title: lang.push_action_mute1d || 'Mute for 24H'\r\n }/* , {\r\n action: 'push_settings',\r\n title: lang.push_action_settings || 'Settings'\r\n } */];\r\n\r\n const notificationPromise = ctx.registration.showNotification(title, {\r\n body,\r\n icon,\r\n tag,\r\n data: obj,\r\n actions\r\n });\r\n\r\n return notificationPromise.then((event) => {\r\n // @ts-ignore\r\n if(event && event.notification) {\r\n // @ts-ignore\r\n pushToNotifications(event.notification);\r\n }\r\n }).catch((error) => {\r\n log.error('Show notification promise', error);\r\n });\r\n}\r\n\r\nexport function onPing(task: ServiceWorkerPingTask, event: ExtendableMessageEvent) {\r\n const client = event.ports && event.ports[0] || event.source;\r\n const payload = task.payload;\r\n\r\n if(payload.localNotifications) {\r\n getter.set('push_last_alive', Date.now());\r\n }\r\n\r\n if(pendingNotification &&\r\n client &&\r\n 'postMessage' in client) {\r\n client.postMessage(pendingNotification, []);\r\n pendingNotification = undefined;\r\n }\r\n\r\n if(payload.lang) {\r\n getter.set('push_lang', payload.lang);\r\n }\r\n\r\n if(payload.settings) {\r\n getter.set('push_settings', payload.settings);\r\n }\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\n \r\n \r\n \r\n//import CacheStorageController from '../cacheStorage';\r\nimport type { Modify, WorkerTaskTemplate, WorkerTaskVoidTemplate } from '../../types';\r\nimport type { InputFileLocation, InputWebFileLocation, UploadFile } from '../../layer';\r\nimport type { WebPushApiManager } from '../mtproto/webPushApiManager';\r\nimport type { PushNotificationObject } from './push';\r\nimport type { ToggleStorageTask } from '../mtproto/mtprotoworker';\r\nimport type { MyUploadFile } from '../mtproto/apiFileManager';\r\nimport { logger, LogTypes } from '../logger';\r\nimport { CancellablePromise } from '../../helpers/cancellablePromise';\r\nimport { CACHE_ASSETS_NAME, requestCache } from './cache';\r\nimport onStreamFetch from './stream';\r\nimport { closeAllNotifications, onPing } from './push';\r\nimport CacheStorageController from '../cacheStorage';\r\n\r\nexport const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);\r\nconst ctx = self as any as ServiceWorkerGlobalScope;\r\nexport const deferredPromises: Map<WindowClient['id'], {[taskId: string]: CancellablePromise<any>}> = new Map();\r\n\r\nexport interface RequestFilePartTask extends Modify<WorkerTaskTemplate, {id: string}> {\r\n type: 'requestFilePart',\r\n payload: [number, InputFileLocation | InputWebFileLocation, number, number]\r\n};\r\n\r\nexport interface RequestFilePartTaskResponse extends Modify<WorkerTaskTemplate, {id: string}> {\r\n type: 'requestFilePart',\r\n payload?: MyUploadFile,\r\n originalPayload?: RequestFilePartTask['payload']\r\n};\r\n\r\nexport interface ServiceWorkerPingTask extends WorkerTaskVoidTemplate {\r\n type: 'ping',\r\n payload: {\r\n localNotifications: boolean,\r\n lang: {\r\n push_action_mute1d: string\r\n push_action_settings: string\r\n push_message_nopreview: string\r\n },\r\n settings: WebPushApiManager['settings']\r\n }\r\n};\r\n\r\nexport interface ServiceWorkerNotificationsClearTask extends WorkerTaskVoidTemplate {\r\n type: 'notifications_clear'\r\n};\r\n\r\nexport interface ServiceWorkerPushClickTask extends WorkerTaskVoidTemplate {\r\n type: 'push_click',\r\n payload: PushNotificationObject\r\n};\r\n\r\nexport type ServiceWorkerTask = RequestFilePartTaskResponse | ServiceWorkerPingTask | ServiceWorkerNotificationsClearTask | ToggleStorageTask;\r\n\r\n \r\nconst taskListeners: {\r\n [type in ServiceWorkerTask['type']]: (task: any, event: ExtendableMessageEvent) => void\r\n} = {\r\n notifications_clear: () => {\r\n closeAllNotifications();\r\n },\r\n ping: (task: ServiceWorkerPingTask, event) => {\r\n onPing(task, event);\r\n },\r\n requestFilePart: (task: RequestFilePartTaskResponse, e: ExtendableMessageEvent) => {\r\n const windowClient = e.source as WindowClient;\r\n const promises = deferredPromises.get(windowClient.id);\r\n if(!promises) {\r\n return;\r\n }\r\n\r\n const promise = promises[task.id];\r\n if(promise) {\r\n if(task.error) {\r\n promise.reject(task.error);\r\n } else {\r\n promise.resolve(task.payload);\r\n }\r\n \r\n delete promises[task.id];\r\n }\r\n },\r\n toggleStorage: (task: ToggleStorageTask) => {\r\n CacheStorageController.toggleStorage(task.payload);\r\n }\r\n};\r\nctx.addEventListener('message', (e) => {\r\n const task = e.data as ServiceWorkerTask;\r\n const callback = taskListeners[task.type];\r\n if(callback) {\r\n callback(task, e);\r\n }\r\n});\r\n \r\n\r\n//const cacheStorage = new CacheStorageController('cachedAssets');\r\n/* let taskId = 0;\r\n\r\nexport function getTaskId() {\r\n return taskId;\r\n}\r\n\r\nexport function incrementTaskId() {\r\n return taskId++;\r\n} */\r\n\r\nconst onFetch = (event: FetchEvent): void => {\r\n if(event.request.url.indexOf(location.origin + '/') === 0 && event.request.url.match(/\\.(js|css|jpe?g|json|wasm|png|mp3|svg|tgs|ico|woff2?|ttf|webmanifest?)(?:\\?.*)?$/)) {\r\n return event.respondWith(requestCache(event));\r\n }\r\n\r\n try {\r\n const [, url, scope, params] = /http[:s]+\\/\\/.*?(\\/(.*?)(?:$|\\/(.*)$))/.exec(event.request.url) || [];\r\n\r\n //log.debug('[fetch]:', event);\r\n \r\n switch(scope) {\r\n case 'stream': {\r\n onStreamFetch(event, params);\r\n break;\r\n }\r\n }\r\n } catch(err) {\r\n event.respondWith(new Response('', {\r\n status: 500,\r\n statusText: 'Internal Server Error',\r\n }));\r\n }\r\n};\r\n\r\nconst onChangeState = () => {\r\n ctx.onfetch = onFetch;\r\n};\r\n\r\nctx.addEventListener('install', (event) => {\r\n log('installing');\r\n event.waitUntil(ctx.skipWaiting()); // Activate worker immediately\r\n});\r\n\r\nctx.addEventListener('activate', (event) => {\r\n log('activating', ctx);\r\n event.waitUntil(ctx.caches.delete(CACHE_ASSETS_NAME));\r\n event.waitUntil(ctx.clients.claim());\r\n});\r\n\r\nctx.onerror = (error) => {\r\n log.error('error:', error);\r\n};\r\n\r\nctx.onunhandledrejection = (error) => {\r\n log.error('onunhandledrejection:', error);\r\n};\r\n\r\nctx.onoffline = ctx.ononline = onChangeState;\r\n\r\nonChangeState();\r\n","export default function timeout(delay: number): Promise<Response> {\r\n return new Promise(((resolve) => {\r\n setTimeout(() => {\r\n resolve(new Response('', {\r\n status: 408,\r\n statusText: 'Request timed out.',\r\n }));\r\n }, delay);\r\n }));\r\n}\r\n"],"sourceRoot":""} |