offline.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. const localForage = require('localforage');
  4. const debugBuild = require('./debug-build.js');
  5. const WINDOW = utils.GLOBAL_OBJ ;
  6. /**
  7. * cache offline errors and send when connected
  8. * @deprecated The offline integration has been deprecated in favor of the offline transport wrapper.
  9. *
  10. * http://docs.sentry.io/platforms/javascript/configuration/transports/#offline-caching
  11. */
  12. class Offline {
  13. /**
  14. * @inheritDoc
  15. */
  16. static __initStatic() {this.id = 'Offline';}
  17. /**
  18. * @inheritDoc
  19. */
  20. /**
  21. * the current hub instance
  22. */
  23. /**
  24. * maximum number of events to store while offline
  25. */
  26. /**
  27. * event cache
  28. */
  29. /**
  30. * @inheritDoc
  31. */
  32. constructor(options = {}) {
  33. this.name = Offline.id;
  34. this.maxStoredEvents = options.maxStoredEvents || 30; // set a reasonable default
  35. // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
  36. this.offlineEventStore = localForage.default.createInstance({
  37. name: 'sentry/offlineEventStore',
  38. });
  39. }
  40. /**
  41. * @inheritDoc
  42. */
  43. setupOnce(addGlobalEventProcessor, getCurrentHub) {
  44. this.hub = getCurrentHub();
  45. if ('addEventListener' in WINDOW) {
  46. WINDOW.addEventListener('online', () => {
  47. void this._sendEvents().catch(() => {
  48. debugBuild.DEBUG_BUILD && utils.logger.warn('could not send cached events');
  49. });
  50. });
  51. }
  52. const eventProcessor = event => {
  53. // eslint-disable-next-line deprecation/deprecation
  54. if (this.hub && this.hub.getIntegration(Offline)) {
  55. // cache if we are positively offline
  56. if ('navigator' in WINDOW && 'onLine' in WINDOW.navigator && !WINDOW.navigator.onLine) {
  57. debugBuild.DEBUG_BUILD && utils.logger.log('Event dropped due to being a offline - caching instead');
  58. void this._cacheEvent(event)
  59. .then((_event) => this._enforceMaxEvents())
  60. .catch((_error) => {
  61. debugBuild.DEBUG_BUILD && utils.logger.warn('could not cache event while offline');
  62. });
  63. // return null on success or failure, because being offline will still result in an error
  64. return null;
  65. }
  66. }
  67. return event;
  68. };
  69. eventProcessor.id = this.name;
  70. addGlobalEventProcessor(eventProcessor);
  71. // if online now, send any events stored in a previous offline session
  72. if ('navigator' in WINDOW && 'onLine' in WINDOW.navigator && WINDOW.navigator.onLine) {
  73. void this._sendEvents().catch(() => {
  74. debugBuild.DEBUG_BUILD && utils.logger.warn('could not send cached events');
  75. });
  76. }
  77. }
  78. /**
  79. * cache an event to send later
  80. * @param event an event
  81. */
  82. async _cacheEvent(event) {
  83. return this.offlineEventStore.setItem(utils.uuid4(), utils.normalize(event));
  84. }
  85. /**
  86. * purge excess events if necessary
  87. */
  88. async _enforceMaxEvents() {
  89. const events = [];
  90. return this.offlineEventStore
  91. .iterate((event, cacheKey, _index) => {
  92. // aggregate events
  93. events.push({ cacheKey, event });
  94. })
  95. .then(
  96. () =>
  97. // this promise resolves when the iteration is finished
  98. this._purgeEvents(
  99. // purge all events past maxStoredEvents in reverse chronological order
  100. events
  101. .sort((a, b) => (b.event.timestamp || 0) - (a.event.timestamp || 0))
  102. .slice(this.maxStoredEvents < events.length ? this.maxStoredEvents : events.length)
  103. .map(event => event.cacheKey),
  104. ),
  105. )
  106. .catch((_error) => {
  107. debugBuild.DEBUG_BUILD && utils.logger.warn('could not enforce max events');
  108. });
  109. }
  110. /**
  111. * purge event from cache
  112. */
  113. async _purgeEvent(cacheKey) {
  114. return this.offlineEventStore.removeItem(cacheKey);
  115. }
  116. /**
  117. * purge events from cache
  118. */
  119. async _purgeEvents(cacheKeys) {
  120. // trail with .then to ensure the return type as void and not void|void[]
  121. return Promise.all(cacheKeys.map(cacheKey => this._purgeEvent(cacheKey))).then();
  122. }
  123. /**
  124. * send all events
  125. */
  126. async _sendEvents() {
  127. return this.offlineEventStore.iterate((event, cacheKey, _index) => {
  128. if (this.hub) {
  129. this.hub.captureEvent(event);
  130. void this._purgeEvent(cacheKey).catch((_error) => {
  131. debugBuild.DEBUG_BUILD && utils.logger.warn('could not purge event from cache');
  132. });
  133. } else {
  134. debugBuild.DEBUG_BUILD && utils.logger.warn('no hub found - could not send cached event');
  135. }
  136. });
  137. }
  138. } Offline.__initStatic();
  139. exports.Offline = Offline;
  140. //# sourceMappingURL=offline.js.map