offline.js 4.4 KB

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