UNPKG

9.49 kBSource Map (JSON)View Raw
1{"version":3,"file":"offline.js","sources":["../../../src/offline.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\nimport type { Event, EventProcessor, Hub, Integration } from '@sentry/types';\nimport { GLOBAL_OBJ, logger, normalize, uuid4 } from '@sentry/utils';\nimport localForage from 'localforage';\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\ntype LocalForage = {\n setItem<T>(key: string, value: T, callback?: (err: any, value: T) => void): Promise<T>;\n iterate<T, U>(\n iteratee: (value: T, key: string, iterationNumber: number) => U,\n callback?: (err: any, result: U) => void,\n ): Promise<U>;\n removeItem(key: string, callback?: (err: any) => void): Promise<void>;\n length(): Promise<number>;\n};\n\nexport type Item = { key: string; value: Event };\n\n/**\n * cache offline errors and send when connected\n */\nexport class Offline implements Integration {\n /**\n * @inheritDoc\n */\n public static id: string = 'Offline';\n\n /**\n * @inheritDoc\n */\n public readonly name: string = Offline.id;\n\n /**\n * the current hub instance\n */\n public hub?: Hub;\n\n /**\n * maximum number of events to store while offline\n */\n public maxStoredEvents: number;\n\n /**\n * event cache\n */\n public offlineEventStore: LocalForage;\n\n /**\n * @inheritDoc\n */\n public constructor(options: { maxStoredEvents?: number } = {}) {\n this.maxStoredEvents = options.maxStoredEvents || 30; // set a reasonable default\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n this.offlineEventStore = localForage.createInstance({\n name: 'sentry/offlineEventStore',\n });\n }\n\n /**\n * @inheritDoc\n */\n public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {\n this.hub = getCurrentHub();\n\n if ('addEventListener' in WINDOW) {\n WINDOW.addEventListener('online', () => {\n void this._sendEvents().catch(() => {\n __DEBUG_BUILD__ && logger.warn('could not send cached events');\n });\n });\n }\n\n const eventProcessor: EventProcessor = event => {\n if (this.hub && this.hub.getIntegration(Offline)) {\n // cache if we are positively offline\n if ('navigator' in WINDOW && 'onLine' in WINDOW.navigator && !WINDOW.navigator.onLine) {\n __DEBUG_BUILD__ && logger.log('Event dropped due to being a offline - caching instead');\n\n void this._cacheEvent(event)\n .then((_event: Event): Promise<void> => this._enforceMaxEvents())\n .catch((_error): void => {\n __DEBUG_BUILD__ && logger.warn('could not cache event while offline');\n });\n\n // return null on success or failure, because being offline will still result in an error\n return null;\n }\n }\n\n return event;\n };\n\n eventProcessor.id = this.name;\n addGlobalEventProcessor(eventProcessor);\n\n // if online now, send any events stored in a previous offline session\n if ('navigator' in WINDOW && 'onLine' in WINDOW.navigator && WINDOW.navigator.onLine) {\n void this._sendEvents().catch(() => {\n __DEBUG_BUILD__ && logger.warn('could not send cached events');\n });\n }\n }\n\n /**\n * cache an event to send later\n * @param event an event\n */\n private async _cacheEvent(event: Event): Promise<Event> {\n return this.offlineEventStore.setItem<Event>(uuid4(), normalize(event));\n }\n\n /**\n * purge excess events if necessary\n */\n private async _enforceMaxEvents(): Promise<void> {\n const events: Array<{ event: Event; cacheKey: string }> = [];\n\n return this.offlineEventStore\n .iterate<Event, void>((event: Event, cacheKey: string, _index: number): void => {\n // aggregate events\n events.push({ cacheKey, event });\n })\n .then(\n (): Promise<void> =>\n // this promise resolves when the iteration is finished\n this._purgeEvents(\n // purge all events past maxStoredEvents in reverse chronological order\n events\n .sort((a, b) => (b.event.timestamp || 0) - (a.event.timestamp || 0))\n .slice(this.maxStoredEvents < events.length ? this.maxStoredEvents : events.length)\n .map(event => event.cacheKey),\n ),\n )\n .catch((_error): void => {\n __DEBUG_BUILD__ && logger.warn('could not enforce max events');\n });\n }\n\n /**\n * purge event from cache\n */\n private async _purgeEvent(cacheKey: string): Promise<void> {\n return this.offlineEventStore.removeItem(cacheKey);\n }\n\n /**\n * purge events from cache\n */\n private async _purgeEvents(cacheKeys: string[]): Promise<void> {\n // trail with .then to ensure the return type as void and not void|void[]\n return Promise.all(cacheKeys.map(cacheKey => this._purgeEvent(cacheKey))).then();\n }\n\n /**\n * send all events\n */\n private async _sendEvents(): Promise<void> {\n return this.offlineEventStore.iterate<Event, void>((event: Event, cacheKey: string, _index: number): void => {\n if (this.hub) {\n this.hub.captureEvent(event);\n\n void this._purgeEvent(cacheKey).catch((_error): void => {\n __DEBUG_BUILD__ && logger.warn('could not purge event from cache');\n });\n } else {\n __DEBUG_BUILD__ && logger.warn('no hub found - could not send cached event');\n }\n });\n }\n}\n"],"names":[],"mappings":"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"}
\No newline at end of file