-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
integration.ts
120 lines (96 loc) · 4.18 KB
/
integration.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import type { Integration, Options } from '@sentry/types';
import { arrayify, logger } from '@sentry/utils';
import { getCurrentHub } from './hub';
import { addGlobalEventProcessor } from './scope';
declare module '@sentry/types' {
interface Integration {
isDefaultInstance?: boolean;
}
}
export const installedIntegrations: string[] = [];
/** Map of integrations assigned to a client */
export type IntegrationIndex = {
[key: string]: Integration;
};
/**
* 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: Integration[]): Integration[] {
const integrationsByName: { [key: string]: Integration } = {};
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 */
export function getIntegrationsToSetup(options: Options): Integration[] {
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: Integration[];
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
*/
export function setupIntegrations(integrations: Integration[]): IntegrationIndex {
const integrationIndex: IntegrationIndex = {};
integrations.forEach(integration => {
// guard against empty provided integrations
if (integration) {
setupIntegration(integration, integrationIndex);
}
});
return integrationIndex;
}
/** Setup a single integration. */
export function setupIntegration(integration: Integration, integrationIndex: IntegrationIndex): void {
integrationIndex[integration.name] = integration;
if (installedIntegrations.indexOf(integration.name) === -1) {
integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
installedIntegrations.push(integration.name);
__DEBUG_BUILD__ && logger.log(`Integration installed: ${integration.name}`);
}
}
// Polyfill for Array.findIndex(), which is not supported in ES5
function findIndex<T>(arr: T[], callback: (item: T) => boolean): number {
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i]) === true) {
return i;
}
}
return -1;
}