integration.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. const debugBuild = require('./debug-build.js');
  4. const eventProcessors = require('./eventProcessors.js');
  5. const exports$1 = require('./exports.js');
  6. const hub = require('./hub.js');
  7. const installedIntegrations = [];
  8. /** Map of integrations assigned to a client */
  9. /**
  10. * Remove duplicates from the given array, preferring the last instance of any duplicate. Not guaranteed to
  11. * preseve the order of integrations in the array.
  12. *
  13. * @private
  14. */
  15. function filterDuplicates(integrations) {
  16. const integrationsByName = {};
  17. integrations.forEach(currentInstance => {
  18. const { name } = currentInstance;
  19. const existingInstance = integrationsByName[name];
  20. // We want integrations later in the array to overwrite earlier ones of the same type, except that we never want a
  21. // default instance to overwrite an existing user instance
  22. if (existingInstance && !existingInstance.isDefaultInstance && currentInstance.isDefaultInstance) {
  23. return;
  24. }
  25. integrationsByName[name] = currentInstance;
  26. });
  27. return Object.keys(integrationsByName).map(k => integrationsByName[k]);
  28. }
  29. /** Gets integrations to install */
  30. function getIntegrationsToSetup(options) {
  31. const defaultIntegrations = options.defaultIntegrations || [];
  32. const userIntegrations = options.integrations;
  33. // We flag default instances, so that later we can tell them apart from any user-created instances of the same class
  34. defaultIntegrations.forEach(integration => {
  35. integration.isDefaultInstance = true;
  36. });
  37. let integrations;
  38. if (Array.isArray(userIntegrations)) {
  39. integrations = [...defaultIntegrations, ...userIntegrations];
  40. } else if (typeof userIntegrations === 'function') {
  41. integrations = utils.arrayify(userIntegrations(defaultIntegrations));
  42. } else {
  43. integrations = defaultIntegrations;
  44. }
  45. const finalIntegrations = filterDuplicates(integrations);
  46. // The `Debug` integration prints copies of the `event` and `hint` which will be passed to `beforeSend` or
  47. // `beforeSendTransaction`. It therefore has to run after all other integrations, so that the changes of all event
  48. // processors will be reflected in the printed values. For lack of a more elegant way to guarantee that, we therefore
  49. // locate it and, assuming it exists, pop it out of its current spot and shove it onto the end of the array.
  50. const debugIndex = findIndex(finalIntegrations, integration => integration.name === 'Debug');
  51. if (debugIndex !== -1) {
  52. const [debugInstance] = finalIntegrations.splice(debugIndex, 1);
  53. finalIntegrations.push(debugInstance);
  54. }
  55. return finalIntegrations;
  56. }
  57. /**
  58. * Given a list of integration instances this installs them all. When `withDefaults` is set to `true` then all default
  59. * integrations are added unless they were already provided before.
  60. * @param integrations array of integration instances
  61. * @param withDefault should enable default integrations
  62. */
  63. function setupIntegrations(client, integrations) {
  64. const integrationIndex = {};
  65. integrations.forEach(integration => {
  66. // guard against empty provided integrations
  67. if (integration) {
  68. setupIntegration(client, integration, integrationIndex);
  69. }
  70. });
  71. return integrationIndex;
  72. }
  73. /**
  74. * Execute the `afterAllSetup` hooks of the given integrations.
  75. */
  76. function afterSetupIntegrations(client, integrations) {
  77. for (const integration of integrations) {
  78. // guard against empty provided integrations
  79. if (integration && integration.afterAllSetup) {
  80. integration.afterAllSetup(client);
  81. }
  82. }
  83. }
  84. /** Setup a single integration. */
  85. function setupIntegration(client, integration, integrationIndex) {
  86. if (integrationIndex[integration.name]) {
  87. debugBuild.DEBUG_BUILD && utils.logger.log(`Integration skipped because it was already installed: ${integration.name}`);
  88. return;
  89. }
  90. integrationIndex[integration.name] = integration;
  91. // `setupOnce` is only called the first time
  92. if (installedIntegrations.indexOf(integration.name) === -1) {
  93. // eslint-disable-next-line deprecation/deprecation
  94. integration.setupOnce(eventProcessors.addGlobalEventProcessor, hub.getCurrentHub);
  95. installedIntegrations.push(integration.name);
  96. }
  97. // `setup` is run for each client
  98. if (integration.setup && typeof integration.setup === 'function') {
  99. integration.setup(client);
  100. }
  101. if (client.on && typeof integration.preprocessEvent === 'function') {
  102. const callback = integration.preprocessEvent.bind(integration) ;
  103. client.on('preprocessEvent', (event, hint) => callback(event, hint, client));
  104. }
  105. if (client.addEventProcessor && typeof integration.processEvent === 'function') {
  106. const callback = integration.processEvent.bind(integration) ;
  107. const processor = Object.assign((event, hint) => callback(event, hint, client), {
  108. id: integration.name,
  109. });
  110. client.addEventProcessor(processor);
  111. }
  112. debugBuild.DEBUG_BUILD && utils.logger.log(`Integration installed: ${integration.name}`);
  113. }
  114. /** Add an integration to the current hub's client. */
  115. function addIntegration(integration) {
  116. const client = exports$1.getClient();
  117. if (!client || !client.addIntegration) {
  118. debugBuild.DEBUG_BUILD && utils.logger.warn(`Cannot add integration "${integration.name}" because no SDK Client is available.`);
  119. return;
  120. }
  121. client.addIntegration(integration);
  122. }
  123. // Polyfill for Array.findIndex(), which is not supported in ES5
  124. function findIndex(arr, callback) {
  125. for (let i = 0; i < arr.length; i++) {
  126. if (callback(arr[i]) === true) {
  127. return i;
  128. }
  129. }
  130. return -1;
  131. }
  132. /**
  133. * Convert a new integration function to the legacy class syntax.
  134. * In v8, we can remove this and instead export the integration functions directly.
  135. *
  136. * @deprecated This will be removed in v8!
  137. */
  138. function convertIntegrationFnToClass(
  139. name,
  140. fn,
  141. ) {
  142. return Object.assign(
  143. function ConvertedIntegration(...args) {
  144. return fn(...args);
  145. },
  146. { id: name },
  147. ) ;
  148. }
  149. /**
  150. * Define an integration function that can be used to create an integration instance.
  151. * Note that this by design hides the implementation details of the integration, as they are considered internal.
  152. */
  153. function defineIntegration(fn) {
  154. return fn;
  155. }
  156. exports.addIntegration = addIntegration;
  157. exports.afterSetupIntegrations = afterSetupIntegrations;
  158. exports.convertIntegrationFnToClass = convertIntegrationFnToClass;
  159. exports.defineIntegration = defineIntegration;
  160. exports.getIntegrationsToSetup = getIntegrationsToSetup;
  161. exports.installedIntegrations = installedIntegrations;
  162. exports.setupIntegration = setupIntegration;
  163. exports.setupIntegrations = setupIntegrations;
  164. //# sourceMappingURL=integration.js.map