192 lines
6.3 KiB
JavaScript
192 lines
6.3 KiB
JavaScript
import { arrayify, logger } from '@sentry/utils';
|
|
import { DEBUG_BUILD } from './debug-build.js';
|
|
import { addGlobalEventProcessor } from './eventProcessors.js';
|
|
import { getClient } from './exports.js';
|
|
import { getCurrentHub } from './hub.js';
|
|
|
|
const installedIntegrations = [];
|
|
|
|
/** Map of integrations assigned to a client */
|
|
|
|
/**
|
|
* Remove duplicates from the given array, preferring the last instance of any duplicate. Not guaranteed to
|
|
* preseve the order of integrations in the array.
|
|
*
|
|
* @private
|
|
*/
|
|
function filterDuplicates(integrations) {
|
|
const integrationsByName = {};
|
|
|
|
integrations.forEach(currentInstance => {
|
|
const { name } = currentInstance;
|
|
|
|
const existingInstance = integrationsByName[name];
|
|
|
|
// We want integrations later in the array to overwrite earlier ones of the same type, except that we never want a
|
|
// default instance to overwrite an existing user instance
|
|
if (existingInstance && !existingInstance.isDefaultInstance && currentInstance.isDefaultInstance) {
|
|
return;
|
|
}
|
|
|
|
integrationsByName[name] = currentInstance;
|
|
});
|
|
|
|
return Object.keys(integrationsByName).map(k => integrationsByName[k]);
|
|
}
|
|
|
|
/** Gets integrations to install */
|
|
function getIntegrationsToSetup(options) {
|
|
const defaultIntegrations = options.defaultIntegrations || [];
|
|
const userIntegrations = options.integrations;
|
|
|
|
// We flag default instances, so that later we can tell them apart from any user-created instances of the same class
|
|
defaultIntegrations.forEach(integration => {
|
|
integration.isDefaultInstance = true;
|
|
});
|
|
|
|
let integrations;
|
|
|
|
if (Array.isArray(userIntegrations)) {
|
|
integrations = [...defaultIntegrations, ...userIntegrations];
|
|
} else if (typeof userIntegrations === 'function') {
|
|
integrations = arrayify(userIntegrations(defaultIntegrations));
|
|
} else {
|
|
integrations = defaultIntegrations;
|
|
}
|
|
|
|
const finalIntegrations = filterDuplicates(integrations);
|
|
|
|
// The `Debug` integration prints copies of the `event` and `hint` which will be passed to `beforeSend` or
|
|
// `beforeSendTransaction`. It therefore has to run after all other integrations, so that the changes of all event
|
|
// processors will be reflected in the printed values. For lack of a more elegant way to guarantee that, we therefore
|
|
// locate it and, assuming it exists, pop it out of its current spot and shove it onto the end of the array.
|
|
const debugIndex = findIndex(finalIntegrations, integration => integration.name === 'Debug');
|
|
if (debugIndex !== -1) {
|
|
const [debugInstance] = finalIntegrations.splice(debugIndex, 1);
|
|
finalIntegrations.push(debugInstance);
|
|
}
|
|
|
|
return finalIntegrations;
|
|
}
|
|
|
|
/**
|
|
* Given a list of integration instances this installs them all. When `withDefaults` is set to `true` then all default
|
|
* integrations are added unless they were already provided before.
|
|
* @param integrations array of integration instances
|
|
* @param withDefault should enable default integrations
|
|
*/
|
|
function setupIntegrations(client, integrations) {
|
|
const integrationIndex = {};
|
|
|
|
integrations.forEach(integration => {
|
|
// guard against empty provided integrations
|
|
if (integration) {
|
|
setupIntegration(client, integration, integrationIndex);
|
|
}
|
|
});
|
|
|
|
return integrationIndex;
|
|
}
|
|
|
|
/**
|
|
* Execute the `afterAllSetup` hooks of the given integrations.
|
|
*/
|
|
function afterSetupIntegrations(client, integrations) {
|
|
for (const integration of integrations) {
|
|
// guard against empty provided integrations
|
|
if (integration && integration.afterAllSetup) {
|
|
integration.afterAllSetup(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Setup a single integration. */
|
|
function setupIntegration(client, integration, integrationIndex) {
|
|
if (integrationIndex[integration.name]) {
|
|
DEBUG_BUILD && logger.log(`Integration skipped because it was already installed: ${integration.name}`);
|
|
return;
|
|
}
|
|
integrationIndex[integration.name] = integration;
|
|
|
|
// `setupOnce` is only called the first time
|
|
if (installedIntegrations.indexOf(integration.name) === -1) {
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
|
|
installedIntegrations.push(integration.name);
|
|
}
|
|
|
|
// `setup` is run for each client
|
|
if (integration.setup && typeof integration.setup === 'function') {
|
|
integration.setup(client);
|
|
}
|
|
|
|
if (client.on && typeof integration.preprocessEvent === 'function') {
|
|
const callback = integration.preprocessEvent.bind(integration) ;
|
|
client.on('preprocessEvent', (event, hint) => callback(event, hint, client));
|
|
}
|
|
|
|
if (client.addEventProcessor && typeof integration.processEvent === 'function') {
|
|
const callback = integration.processEvent.bind(integration) ;
|
|
|
|
const processor = Object.assign((event, hint) => callback(event, hint, client), {
|
|
id: integration.name,
|
|
});
|
|
|
|
client.addEventProcessor(processor);
|
|
}
|
|
|
|
DEBUG_BUILD && logger.log(`Integration installed: ${integration.name}`);
|
|
}
|
|
|
|
/** Add an integration to the current hub's client. */
|
|
function addIntegration(integration) {
|
|
const client = getClient();
|
|
|
|
if (!client || !client.addIntegration) {
|
|
DEBUG_BUILD && logger.warn(`Cannot add integration "${integration.name}" because no SDK Client is available.`);
|
|
return;
|
|
}
|
|
|
|
client.addIntegration(integration);
|
|
}
|
|
|
|
// Polyfill for Array.findIndex(), which is not supported in ES5
|
|
function findIndex(arr, callback) {
|
|
for (let i = 0; i < arr.length; i++) {
|
|
if (callback(arr[i]) === true) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Convert a new integration function to the legacy class syntax.
|
|
* In v8, we can remove this and instead export the integration functions directly.
|
|
*
|
|
* @deprecated This will be removed in v8!
|
|
*/
|
|
function convertIntegrationFnToClass(
|
|
name,
|
|
fn,
|
|
) {
|
|
return Object.assign(
|
|
function ConvertedIntegration(...args) {
|
|
return fn(...args);
|
|
},
|
|
{ id: name },
|
|
) ;
|
|
}
|
|
|
|
/**
|
|
* Define an integration function that can be used to create an integration instance.
|
|
* Note that this by design hides the implementation details of the integration, as they are considered internal.
|
|
*/
|
|
function defineIntegration(fn) {
|
|
return fn;
|
|
}
|
|
|
|
export { addIntegration, afterSetupIntegrations, convertIntegrationFnToClass, defineIntegration, getIntegrationsToSetup, installedIntegrations, setupIntegration, setupIntegrations };
|
|
//# sourceMappingURL=integration.js.map
|