sentry-miniapp 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -0
- package/README.md +88 -0
- package/dist/sentry-miniapp.cjs.js +142 -39
- package/dist/sentry-miniapp.cjs.js.map +1 -1
- package/dist/sentry-miniapp.esm.js +142 -39
- package/dist/sentry-miniapp.esm.js.map +1 -1
- package/dist/sentry-miniapp.umd.js +142 -39
- package/dist/sentry-miniapp.umd.js.map +1 -1
- package/dist/types/client.d.ts +4 -6
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/integrations/index.d.ts +1 -0
- package/dist/types/integrations/index.d.ts.map +1 -1
- package/dist/types/integrations/networkbreadcrumbs.d.ts +29 -0
- package/dist/types/integrations/networkbreadcrumbs.d.ts.map +1 -0
- package/dist/types/sdk.d.ts +6 -4
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/types/transports/offlineStore.d.ts +4 -1
- package/dist/types/transports/offlineStore.d.ts.map +1 -1
- package/dist/types/types.d.ts +4 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +94 -16
- package/src/.keep +0 -0
- package/src/client.ts +203 -0
- package/src/crossPlatform.ts +407 -0
- package/src/eventbuilder.ts +291 -0
- package/src/helpers.ts +214 -0
- package/src/index.ts +86 -0
- package/src/integrations/dedupe.ts +215 -0
- package/src/integrations/globalhandlers.ts +209 -0
- package/src/integrations/httpcontext.ts +140 -0
- package/src/integrations/index.ts +10 -0
- package/src/integrations/linkederrors.ts +107 -0
- package/src/integrations/networkbreadcrumbs.ts +155 -0
- package/src/integrations/performance.ts +622 -0
- package/src/integrations/rewriteframes.ts +77 -0
- package/src/integrations/router.ts +180 -0
- package/src/integrations/system.ts +135 -0
- package/src/integrations/trycatch.ts +233 -0
- package/src/polyfills.ts +242 -0
- package/src/sdk.ts +182 -0
- package/src/transports/index.ts +3 -0
- package/src/transports/offlineStore.ts +85 -0
- package/src/transports/xhr.ts +68 -0
- package/src/types.ts +129 -0
- package/src/version.ts +3 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { addBreadcrumb, getCurrentScope } from '@sentry/core';
|
|
2
|
+
import type { Integration, IntegrationFn } from '@sentry/core';
|
|
3
|
+
|
|
4
|
+
/** Router integration for miniapp navigation */
|
|
5
|
+
export class Router implements Integration {
|
|
6
|
+
/**
|
|
7
|
+
* @inheritDoc
|
|
8
|
+
*/
|
|
9
|
+
public static id: string = 'Router';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @inheritDoc
|
|
13
|
+
*/
|
|
14
|
+
public name: string = Router.id;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
private _lastRoute: string = '';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @inheritDoc
|
|
23
|
+
*/
|
|
24
|
+
public setupOnce(): void {
|
|
25
|
+
this._instrumentNavigation();
|
|
26
|
+
this._startRouteMonitoring();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Instrument navigation functions
|
|
31
|
+
*/
|
|
32
|
+
private _instrumentNavigation(): void {
|
|
33
|
+
const global = globalThis as any;
|
|
34
|
+
|
|
35
|
+
// Instrument wx.navigateTo
|
|
36
|
+
if (global.wx && global.wx.navigateTo) {
|
|
37
|
+
const originalNavigateTo = global.wx.navigateTo;
|
|
38
|
+
global.wx.navigateTo = (options: any) => {
|
|
39
|
+
this._recordNavigation('navigateTo', options.url, this._getCurrentRoute());
|
|
40
|
+
return originalNavigateTo.call(global.wx, options);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Instrument wx.redirectTo
|
|
45
|
+
if (global.wx && global.wx.redirectTo) {
|
|
46
|
+
const originalRedirectTo = global.wx.redirectTo;
|
|
47
|
+
global.wx.redirectTo = (options: any) => {
|
|
48
|
+
this._recordNavigation('redirectTo', options.url, this._getCurrentRoute());
|
|
49
|
+
return originalRedirectTo.call(global.wx, options);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Instrument wx.switchTab
|
|
54
|
+
if (global.wx && global.wx.switchTab) {
|
|
55
|
+
const originalSwitchTab = global.wx.switchTab;
|
|
56
|
+
global.wx.switchTab = (options: any) => {
|
|
57
|
+
this._recordNavigation('switchTab', options.url, this._getCurrentRoute());
|
|
58
|
+
return originalSwitchTab.call(global.wx, options);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Instrument wx.navigateBack
|
|
63
|
+
if (global.wx && global.wx.navigateBack) {
|
|
64
|
+
const originalNavigateBack = global.wx.navigateBack;
|
|
65
|
+
global.wx.navigateBack = (options: any = {}) => {
|
|
66
|
+
this._recordNavigation('navigateBack', 'back', this._getCurrentRoute(), options.delta);
|
|
67
|
+
return originalNavigateBack.call(global.wx, options);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Instrument wx.reLaunch
|
|
72
|
+
if (global.wx && global.wx.reLaunch) {
|
|
73
|
+
const originalReLaunch = global.wx.reLaunch;
|
|
74
|
+
global.wx.reLaunch = (options: any) => {
|
|
75
|
+
this._recordNavigation('reLaunch', options.url, this._getCurrentRoute());
|
|
76
|
+
return originalReLaunch.call(global.wx, options);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Start monitoring route changes
|
|
83
|
+
*/
|
|
84
|
+
private _startRouteMonitoring(): void {
|
|
85
|
+
// Monitor route changes by checking current pages periodically
|
|
86
|
+
setInterval(() => {
|
|
87
|
+
const currentRoute = this._getCurrentRoute();
|
|
88
|
+
if (currentRoute && currentRoute !== this._lastRoute) {
|
|
89
|
+
this._recordRouteChange(this._lastRoute, currentRoute);
|
|
90
|
+
this._lastRoute = currentRoute;
|
|
91
|
+
}
|
|
92
|
+
}, 1000);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get current route
|
|
97
|
+
*/
|
|
98
|
+
private _getCurrentRoute(): string {
|
|
99
|
+
try {
|
|
100
|
+
const global = globalThis as any;
|
|
101
|
+
if (global.getCurrentPages) {
|
|
102
|
+
const pages = global.getCurrentPages();
|
|
103
|
+
if (pages && pages.length > 0) {
|
|
104
|
+
const currentPage = pages[pages.length - 1];
|
|
105
|
+
return currentPage.route || currentPage.__route__ || '';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
// Ignore errors
|
|
110
|
+
}
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Record navigation action
|
|
116
|
+
*/
|
|
117
|
+
private _recordNavigation(action: string, to: string, from: string, delta?: number): void {
|
|
118
|
+
const scope = getCurrentScope();
|
|
119
|
+
|
|
120
|
+
// Add breadcrumb
|
|
121
|
+
addBreadcrumb({
|
|
122
|
+
category: 'navigation',
|
|
123
|
+
data: {
|
|
124
|
+
action,
|
|
125
|
+
from,
|
|
126
|
+
to,
|
|
127
|
+
delta,
|
|
128
|
+
},
|
|
129
|
+
message: `Navigation ${action}: ${from} -> ${to}`,
|
|
130
|
+
type: 'navigation',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Set current route tag
|
|
134
|
+
scope.setTag('route', to === 'back' ? from : to);
|
|
135
|
+
|
|
136
|
+
// Set navigation context
|
|
137
|
+
scope.setContext('navigation', {
|
|
138
|
+
action,
|
|
139
|
+
from,
|
|
140
|
+
to,
|
|
141
|
+
delta,
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Record route change
|
|
148
|
+
*/
|
|
149
|
+
private _recordRouteChange(from: string, to: string): void {
|
|
150
|
+
const scope = getCurrentScope();
|
|
151
|
+
|
|
152
|
+
// Add breadcrumb
|
|
153
|
+
addBreadcrumb({
|
|
154
|
+
category: 'navigation',
|
|
155
|
+
data: {
|
|
156
|
+
from,
|
|
157
|
+
to,
|
|
158
|
+
},
|
|
159
|
+
message: `Route changed: ${from} -> ${to}`,
|
|
160
|
+
type: 'navigation',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Update route tag
|
|
164
|
+
scope.setTag('route', to);
|
|
165
|
+
|
|
166
|
+
// Update route context
|
|
167
|
+
scope.setContext('route', {
|
|
168
|
+
current: to,
|
|
169
|
+
previous: from,
|
|
170
|
+
timestamp: Date.now(),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Router integration
|
|
177
|
+
*/
|
|
178
|
+
export const routerIntegration: IntegrationFn = () => {
|
|
179
|
+
return new Router();
|
|
180
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { getCurrentScope } from '@sentry/core';
|
|
2
|
+
import type { Integration, IntegrationFn } from '@sentry/core';
|
|
3
|
+
|
|
4
|
+
import { getSystemInfo, sdk } from '../crossPlatform';
|
|
5
|
+
|
|
6
|
+
/** System information integration */
|
|
7
|
+
export class System implements Integration {
|
|
8
|
+
/**
|
|
9
|
+
* @inheritDoc
|
|
10
|
+
*/
|
|
11
|
+
public static id: string = 'System';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @inheritDoc
|
|
15
|
+
*/
|
|
16
|
+
public name: string = System.id;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @inheritDoc
|
|
20
|
+
*/
|
|
21
|
+
public setupOnce(): void {
|
|
22
|
+
this._addSystemContext();
|
|
23
|
+
this._addNetworkContext();
|
|
24
|
+
this._addLocationContext();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Add system information to context
|
|
29
|
+
*/
|
|
30
|
+
private _addSystemContext(): void {
|
|
31
|
+
try {
|
|
32
|
+
const systemInfo = getSystemInfo();
|
|
33
|
+
if (systemInfo) {
|
|
34
|
+
const scope = getCurrentScope();
|
|
35
|
+
|
|
36
|
+
// Set device context
|
|
37
|
+
scope.setContext('device', {
|
|
38
|
+
name: systemInfo.model,
|
|
39
|
+
model: systemInfo.model,
|
|
40
|
+
brand: systemInfo.brand,
|
|
41
|
+
family: systemInfo.platform,
|
|
42
|
+
arch: systemInfo.platform,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Set OS context
|
|
46
|
+
scope.setContext('os', {
|
|
47
|
+
name: systemInfo.system?.split(' ')[0] || 'unknown',
|
|
48
|
+
version: systemInfo.system?.split(' ')[1] || 'unknown',
|
|
49
|
+
kernel_version: systemInfo.version,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Set app context
|
|
53
|
+
scope.setContext('app', {
|
|
54
|
+
app_name: (systemInfo as any).appName || 'unknown',
|
|
55
|
+
app_version: systemInfo.version,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Set screen context
|
|
59
|
+
scope.setContext('screen', {
|
|
60
|
+
screen_width: systemInfo.screenWidth,
|
|
61
|
+
screen_height: systemInfo.screenHeight,
|
|
62
|
+
screen_density: systemInfo.pixelRatio,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Set tags
|
|
66
|
+
scope.setTag('device.model', systemInfo.model);
|
|
67
|
+
scope.setTag('device.brand', systemInfo.brand);
|
|
68
|
+
scope.setTag('os.name', systemInfo.system?.split(' ')[0] || 'unknown');
|
|
69
|
+
scope.setTag('os.version', systemInfo.system?.split(' ')[1] || 'unknown');
|
|
70
|
+
scope.setTag('app.version', systemInfo.version);
|
|
71
|
+
scope.setTag('language', systemInfo.language);
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Ignore errors when getting system info
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Add network information to context
|
|
80
|
+
*/
|
|
81
|
+
private _addNetworkContext(): void {
|
|
82
|
+
try {
|
|
83
|
+
if ((sdk() as any).getNetworkType) {
|
|
84
|
+
(sdk() as any).getNetworkType({
|
|
85
|
+
success: (res: { networkType: string; isConnected?: boolean }) => {
|
|
86
|
+
const scope = getCurrentScope();
|
|
87
|
+
scope.setContext('network', {
|
|
88
|
+
type: res.networkType,
|
|
89
|
+
connected: res.isConnected !== false,
|
|
90
|
+
});
|
|
91
|
+
scope.setTag('network.type', res.networkType);
|
|
92
|
+
},
|
|
93
|
+
fail: () => {
|
|
94
|
+
// Ignore network type fetch errors
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// Ignore errors when getting network info
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Add location information to context (if available)
|
|
105
|
+
*/
|
|
106
|
+
private _addLocationContext(): void {
|
|
107
|
+
try {
|
|
108
|
+
if ((sdk() as any).getLocation) {
|
|
109
|
+
(sdk() as any).getLocation({
|
|
110
|
+
type: 'gcj02',
|
|
111
|
+
success: (res: { latitude: number; longitude: number; accuracy: number }) => {
|
|
112
|
+
const scope = getCurrentScope();
|
|
113
|
+
scope.setContext('location', {
|
|
114
|
+
latitude: res.latitude,
|
|
115
|
+
longitude: res.longitude,
|
|
116
|
+
accuracy: res.accuracy,
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
fail: () => {
|
|
120
|
+
// Ignore location fetch errors (user might not grant permission)
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
} catch (e) {
|
|
125
|
+
// Ignore errors when getting location info
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* System integration
|
|
132
|
+
*/
|
|
133
|
+
export const systemIntegration: IntegrationFn = () => {
|
|
134
|
+
return new System();
|
|
135
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { captureException, getCurrentScope } from '@sentry/core';
|
|
2
|
+
import type { Integration, IntegrationFn, WrappedFunction } from '@sentry/core';
|
|
3
|
+
|
|
4
|
+
/** Wrap timer functions and event targets to catch errors and provide better meta data */
|
|
5
|
+
export class TryCatch implements Integration {
|
|
6
|
+
/**
|
|
7
|
+
* @inheritDoc
|
|
8
|
+
*/
|
|
9
|
+
public static id: string = 'TryCatch';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @inheritDoc
|
|
13
|
+
*/
|
|
14
|
+
public name: string = TryCatch.id;
|
|
15
|
+
|
|
16
|
+
/** JSDoc */
|
|
17
|
+
private _wrapTimeFunction(original: (...args: any[]) => any): (...args: any[]) => any {
|
|
18
|
+
return function (this: any, ...args: any[]): any {
|
|
19
|
+
const originalCallback = args[0];
|
|
20
|
+
args[0] = wrap(originalCallback, {
|
|
21
|
+
mechanism: {
|
|
22
|
+
data: { function: getFunctionName(original) },
|
|
23
|
+
handled: true,
|
|
24
|
+
type: 'instrument',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
return original.apply(this, args);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** JSDoc */
|
|
32
|
+
private _wrapRAF(original: any): (callback: () => void) => any {
|
|
33
|
+
return function (this: any, callback: () => void): any {
|
|
34
|
+
return original(
|
|
35
|
+
wrap(callback, {
|
|
36
|
+
mechanism: {
|
|
37
|
+
data: {
|
|
38
|
+
function: 'requestAnimationFrame',
|
|
39
|
+
handler: getFunctionName(original),
|
|
40
|
+
},
|
|
41
|
+
handled: true,
|
|
42
|
+
type: 'instrument',
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wrap timer functions and event targets to catch errors
|
|
51
|
+
* and provide better metadata.
|
|
52
|
+
*/
|
|
53
|
+
public setupOnce(): void {
|
|
54
|
+
|
|
55
|
+
// In miniapp environment, we mainly focus on wrapping common async functions
|
|
56
|
+
const global = globalThis as any;
|
|
57
|
+
|
|
58
|
+
if (global.setTimeout) {
|
|
59
|
+
fill(global, 'setTimeout', this._wrapTimeFunction.bind(this));
|
|
60
|
+
}
|
|
61
|
+
if (global.setInterval) {
|
|
62
|
+
fill(global, 'setInterval', this._wrapTimeFunction.bind(this));
|
|
63
|
+
}
|
|
64
|
+
if (global.requestAnimationFrame) {
|
|
65
|
+
fill(global, 'requestAnimationFrame', this._wrapRAF.bind(this));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Wrap a function to capture exceptions
|
|
72
|
+
*/
|
|
73
|
+
export function wrap(
|
|
74
|
+
fn: WrappedFunction,
|
|
75
|
+
options: {
|
|
76
|
+
mechanism?: {
|
|
77
|
+
data?: Record<string, any>;
|
|
78
|
+
handled?: boolean;
|
|
79
|
+
type?: string;
|
|
80
|
+
};
|
|
81
|
+
capture?: boolean;
|
|
82
|
+
} = {},
|
|
83
|
+
before?: WrappedFunction,
|
|
84
|
+
): any {
|
|
85
|
+
// tslint:disable-next-line:strict-type-predicates
|
|
86
|
+
if (typeof fn !== 'function') {
|
|
87
|
+
return fn;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// We don't wanna wrap it twice
|
|
92
|
+
if ((fn as any).__sentry__) {
|
|
93
|
+
return fn;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// If this has already been wrapped in the past, return that wrapped function
|
|
97
|
+
if (fn.__sentry_wrapped__) {
|
|
98
|
+
return fn.__sentry_wrapped__;
|
|
99
|
+
}
|
|
100
|
+
} catch (e) {
|
|
101
|
+
// Just accessing custom props in some environments
|
|
102
|
+
// can cause a "Permission denied" exception.
|
|
103
|
+
// Bail on wrapping and return the function as-is.
|
|
104
|
+
return fn;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const sentryWrapped: WrappedFunction = function (this: any, ...args: any[]): any {
|
|
108
|
+
// tslint:disable-next-line:strict-type-predicates
|
|
109
|
+
if (before && typeof before === 'function') {
|
|
110
|
+
before.apply(this, args);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const wrappedArguments = args.map((arg: any) => wrap(arg, options));
|
|
115
|
+
|
|
116
|
+
if ((fn as any).handleEvent) {
|
|
117
|
+
return (fn as any).handleEvent.apply(this, wrappedArguments);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return fn.apply(this, wrappedArguments);
|
|
121
|
+
} catch (ex) {
|
|
122
|
+
const scope = getCurrentScope();
|
|
123
|
+
|
|
124
|
+
scope.addEventProcessor((event) => {
|
|
125
|
+
const processedEvent = { ...event };
|
|
126
|
+
|
|
127
|
+
if (options.mechanism) {
|
|
128
|
+
processedEvent.exception = processedEvent.exception || {};
|
|
129
|
+
(processedEvent.exception as any).mechanism = options.mechanism;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
processedEvent.extra = {
|
|
133
|
+
...processedEvent.extra,
|
|
134
|
+
arguments: args,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return processedEvent;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
captureException(ex);
|
|
141
|
+
throw ex;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Accessing some objects may throw
|
|
146
|
+
try {
|
|
147
|
+
// tslint:disable-next-line: no-for-in
|
|
148
|
+
for (const property in fn) {
|
|
149
|
+
if (Object.prototype.hasOwnProperty.call(fn, property)) {
|
|
150
|
+
(sentryWrapped as any)[property] = (fn as any)[property];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch (_oO) {
|
|
154
|
+
// no-empty
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn.prototype = fn.prototype || {};
|
|
158
|
+
sentryWrapped.prototype = fn.prototype;
|
|
159
|
+
|
|
160
|
+
Object.defineProperty(fn, '__sentry_wrapped__', {
|
|
161
|
+
enumerable: false,
|
|
162
|
+
value: sentryWrapped,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Signal that this function has been wrapped/filled already
|
|
166
|
+
Object.defineProperties(sentryWrapped, {
|
|
167
|
+
__sentry__: {
|
|
168
|
+
enumerable: false,
|
|
169
|
+
value: true,
|
|
170
|
+
},
|
|
171
|
+
__sentry_original__: {
|
|
172
|
+
enumerable: false,
|
|
173
|
+
value: fn,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Restore original function name
|
|
178
|
+
try {
|
|
179
|
+
const descriptor = Object.getOwnPropertyDescriptor(sentryWrapped, 'name') as PropertyDescriptor;
|
|
180
|
+
if (descriptor.configurable) {
|
|
181
|
+
Object.defineProperty(sentryWrapped, 'name', {
|
|
182
|
+
get(): string {
|
|
183
|
+
return fn.name;
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
} catch (_oO) {
|
|
188
|
+
// no-empty
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return sentryWrapped;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Fill an object with a new value, keeping a reference to the original
|
|
196
|
+
*/
|
|
197
|
+
function fill(source: { [key: string]: any }, name: string, replacementFactory: (...args: any[]) => any): void {
|
|
198
|
+
if (!(name in source)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const original = source[name] as () => any;
|
|
203
|
+
const wrapped = replacementFactory(original);
|
|
204
|
+
|
|
205
|
+
if (typeof wrapped === 'function') {
|
|
206
|
+
try {
|
|
207
|
+
wrapped.prototype = wrapped.prototype || {};
|
|
208
|
+
wrapped.prototype.constructor = wrapped;
|
|
209
|
+
} catch (_Oo) {
|
|
210
|
+
// This can throw in some funky environments
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
source[name] = wrapped;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Safely extract function name from itself
|
|
219
|
+
*/
|
|
220
|
+
function getFunctionName(fn: any): string {
|
|
221
|
+
try {
|
|
222
|
+
return (fn && fn.name) || '<anonymous>';
|
|
223
|
+
} catch (e) {
|
|
224
|
+
return '<anonymous>';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* TryCatch integration
|
|
230
|
+
*/
|
|
231
|
+
export const tryCatchIntegration: IntegrationFn = () => {
|
|
232
|
+
return new TryCatch();
|
|
233
|
+
};
|