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.
Files changed (46) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +88 -0
  3. package/dist/sentry-miniapp.cjs.js +142 -39
  4. package/dist/sentry-miniapp.cjs.js.map +1 -1
  5. package/dist/sentry-miniapp.esm.js +142 -39
  6. package/dist/sentry-miniapp.esm.js.map +1 -1
  7. package/dist/sentry-miniapp.umd.js +142 -39
  8. package/dist/sentry-miniapp.umd.js.map +1 -1
  9. package/dist/types/client.d.ts +4 -6
  10. package/dist/types/client.d.ts.map +1 -1
  11. package/dist/types/integrations/index.d.ts +1 -0
  12. package/dist/types/integrations/index.d.ts.map +1 -1
  13. package/dist/types/integrations/networkbreadcrumbs.d.ts +29 -0
  14. package/dist/types/integrations/networkbreadcrumbs.d.ts.map +1 -0
  15. package/dist/types/sdk.d.ts +6 -4
  16. package/dist/types/sdk.d.ts.map +1 -1
  17. package/dist/types/transports/offlineStore.d.ts +4 -1
  18. package/dist/types/transports/offlineStore.d.ts.map +1 -1
  19. package/dist/types/types.d.ts +4 -0
  20. package/dist/types/types.d.ts.map +1 -1
  21. package/dist/types/version.d.ts +1 -1
  22. package/package.json +94 -16
  23. package/src/.keep +0 -0
  24. package/src/client.ts +203 -0
  25. package/src/crossPlatform.ts +407 -0
  26. package/src/eventbuilder.ts +291 -0
  27. package/src/helpers.ts +214 -0
  28. package/src/index.ts +86 -0
  29. package/src/integrations/dedupe.ts +215 -0
  30. package/src/integrations/globalhandlers.ts +209 -0
  31. package/src/integrations/httpcontext.ts +140 -0
  32. package/src/integrations/index.ts +10 -0
  33. package/src/integrations/linkederrors.ts +107 -0
  34. package/src/integrations/networkbreadcrumbs.ts +155 -0
  35. package/src/integrations/performance.ts +622 -0
  36. package/src/integrations/rewriteframes.ts +77 -0
  37. package/src/integrations/router.ts +180 -0
  38. package/src/integrations/system.ts +135 -0
  39. package/src/integrations/trycatch.ts +233 -0
  40. package/src/polyfills.ts +242 -0
  41. package/src/sdk.ts +182 -0
  42. package/src/transports/index.ts +3 -0
  43. package/src/transports/offlineStore.ts +85 -0
  44. package/src/transports/xhr.ts +68 -0
  45. package/src/types.ts +129 -0
  46. package/src/version.ts +3 -0
@@ -0,0 +1,140 @@
1
+ import { getCurrentScope } from '@sentry/core';
2
+ import type { Event, Integration, IntegrationFn } from '@sentry/core';
3
+
4
+ import { sdk, getSystemInfo } from '../crossPlatform';
5
+
6
+ /** Add node request data to the event */
7
+ export class HttpContext implements Integration {
8
+ /**
9
+ * @inheritDoc
10
+ */
11
+ public static id: string = 'HttpContext';
12
+
13
+ /**
14
+ * @inheritDoc
15
+ */
16
+ public name: string = HttpContext.id;
17
+
18
+ /**
19
+ * @inheritDoc
20
+ */
21
+ public setupOnce(): void {
22
+ // This integration adds context during event processing
23
+ }
24
+
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ public processEvent(event: Event): Event {
29
+ const scope = getCurrentScope();
30
+
31
+ // Add miniapp specific context
32
+ const context = {
33
+ runtime: {
34
+ name: 'miniapp',
35
+ version: this._getMiniappVersion(),
36
+ },
37
+ app: {
38
+ name: this._getAppName(),
39
+ version: this._getAppVersion(),
40
+ },
41
+ device: this._getDeviceInfo(),
42
+ network: this._getNetworkInfo(),
43
+ };
44
+
45
+ scope.setContext('runtime', context.runtime);
46
+ scope.setContext('app', context.app);
47
+ scope.setContext('device', context.device);
48
+ scope.setContext('network', context.network);
49
+
50
+ return event;
51
+ }
52
+
53
+ /**
54
+ * Get miniapp version
55
+ */
56
+ private _getMiniappVersion(): string {
57
+ const sys = getSystemInfo();
58
+ return sys?.version || sys?.SDKVersion || 'unknown';
59
+ }
60
+
61
+ /**
62
+ * Get app name
63
+ */
64
+ private _getAppName(): string {
65
+ try {
66
+ if (sdk().getAccountInfoSync) {
67
+ const accountInfo = sdk().getAccountInfoSync?.();
68
+ return accountInfo.miniProgram?.appId || 'unknown';
69
+ }
70
+ } catch (e) {
71
+ // Ignore errors
72
+ }
73
+ return 'unknown';
74
+ }
75
+
76
+ /**
77
+ * Get app version
78
+ */
79
+ private _getAppVersion(): string {
80
+ try {
81
+ if (sdk().getAccountInfoSync) {
82
+ const accountInfo = sdk().getAccountInfoSync?.();
83
+ return accountInfo?.miniProgram?.version || 'unknown';
84
+ }
85
+ } catch (e) {
86
+ // Ignore errors
87
+ }
88
+ return 'unknown';
89
+ }
90
+
91
+ /**
92
+ * Get device information
93
+ */
94
+ private _getDeviceInfo(): Record<string, any> {
95
+ const sys = getSystemInfo();
96
+ if (!sys) return {};
97
+
98
+ return {
99
+ brand: sys.brand,
100
+ model: sys.model,
101
+ system: sys.system,
102
+ platform: sys.platform,
103
+ screenWidth: sys.screenWidth,
104
+ screenHeight: sys.screenHeight,
105
+ windowWidth: sys.windowWidth,
106
+ windowHeight: sys.windowHeight,
107
+ pixelRatio: sys.pixelRatio,
108
+ language: sys.language,
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Get network information
114
+ */
115
+ private _getNetworkInfo(): Record<string, any> {
116
+ try {
117
+ if ((sdk() as any).getNetworkType) {
118
+ (sdk() as any).getNetworkType({
119
+ success: (res: { networkType: string }) => {
120
+ const scope = getCurrentScope();
121
+ scope.setTag('network.type', res.networkType);
122
+ scope.setContext('network', {
123
+ type: res.networkType,
124
+ });
125
+ },
126
+ });
127
+ }
128
+ } catch (e) {
129
+ // Ignore errors
130
+ }
131
+ return {};
132
+ }
133
+ }
134
+
135
+ /**
136
+ * HttpContext integration
137
+ */
138
+ export const httpContextIntegration: IntegrationFn = () => {
139
+ return new HttpContext();
140
+ };
@@ -0,0 +1,10 @@
1
+ export { GlobalHandlers } from './globalhandlers';
2
+ export { TryCatch } from './trycatch';
3
+ export { LinkedErrors } from './linkederrors';
4
+ export { HttpContext } from './httpcontext';
5
+ export { Dedupe } from './dedupe';
6
+ export { System } from './system';
7
+ export { Router } from './router';
8
+ export { PerformanceIntegration, performanceIntegration } from './performance';
9
+ export { RewriteFrames } from './rewriteframes';
10
+ export { NetworkBreadcrumbs } from './networkbreadcrumbs';
@@ -0,0 +1,107 @@
1
+ import type { Event, EventHint, Exception, ExtendedError, Integration, IntegrationFn } from '@sentry/core';
2
+ import { exceptionFromError, getCurrentScope } from '@sentry/core';
3
+
4
+ const DEFAULT_KEY = 'cause';
5
+ const DEFAULT_LIMIT = 5;
6
+
7
+ /** Adds SDK info to the event */
8
+ export class LinkedErrors implements Integration {
9
+ /**
10
+ * @inheritDoc
11
+ */
12
+ public static id: string = 'LinkedErrors';
13
+
14
+ /**
15
+ * @inheritDoc
16
+ */
17
+ public name: string = LinkedErrors.id;
18
+
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ private readonly _key: string;
23
+
24
+ /**
25
+ * @inheritDoc
26
+ */
27
+ private readonly _limit: number;
28
+
29
+ /**
30
+ * @inheritDoc
31
+ */
32
+ public constructor(options: { key?: string; limit?: number } = {}) {
33
+ this._key = options.key || DEFAULT_KEY;
34
+ this._limit = options.limit || DEFAULT_LIMIT;
35
+ }
36
+
37
+ /**
38
+ * @inheritDoc
39
+ */
40
+ public setupOnce(): void {
41
+ // This integration doesn't need setup
42
+ }
43
+
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ public processEvent(event: Event, hint?: EventHint): Event {
48
+ const client = getCurrentScope().getClient();
49
+ if (!client) {
50
+ return event;
51
+ }
52
+
53
+ // const options = client.getOptions();
54
+ // Note: getCurrentScope() doesn't have getIntegration method, so we'll process directly
55
+ return this._handler(event, hint);
56
+ }
57
+
58
+ /**
59
+ * @inheritDoc
60
+ */
61
+ private _handler(event: Event, hint?: EventHint): Event {
62
+ if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) {
63
+ return event;
64
+ }
65
+
66
+ const linkedErrors = this._walkErrorTree(hint.originalException as ExtendedError, this._key);
67
+ event.exception.values = [...linkedErrors, ...event.exception.values];
68
+
69
+ return event;
70
+ }
71
+
72
+ /**
73
+ * @inheritDoc
74
+ */
75
+ private _walkErrorTree(error: ExtendedError, key: string, stack: Exception[] = []): Exception[] {
76
+ if (!isInstanceOf(error[key], Error) || stack.length + 1 >= this._limit) {
77
+ return stack;
78
+ }
79
+
80
+ const exception = exceptionFromError(() => [], error[key]);
81
+ return this._walkErrorTree(error[key], key, [exception, ...stack]);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Checks whether given value's type is one of a few Error or Error-like
87
+ * {@link isError}.
88
+ *
89
+ * @param wat A value to be checked.
90
+ * @returns A boolean representing the result.
91
+ */
92
+ function isInstanceOf(wat: any, base: any): boolean {
93
+ try {
94
+ return wat instanceof base;
95
+ } catch (_e) {
96
+ return false;
97
+ }
98
+ }
99
+
100
+
101
+
102
+ /**
103
+ * LinkedErrors integration
104
+ */
105
+ export const linkedErrorsIntegration: IntegrationFn = (options?: { key?: string; limit?: number }) => {
106
+ return new LinkedErrors(options);
107
+ };
@@ -0,0 +1,155 @@
1
+ import { addBreadcrumb, getClient } from '@sentry/core';
2
+ import type { Integration } from '@sentry/core';
3
+ import { fill } from '../helpers';
4
+ import { sdk } from '../crossPlatform';
5
+
6
+ /**
7
+ * Network Breadcrumbs Integration.
8
+ * Monkey patches miniapp network API (e.g. wx.request, my.httpRequest)
9
+ * to record network breadcrumbs, including request and response body if configured.
10
+ */
11
+ export class NetworkBreadcrumbs implements Integration {
12
+ /**
13
+ * @inheritDoc
14
+ */
15
+ public static id: string = 'NetworkBreadcrumbs';
16
+
17
+ /**
18
+ * @inheritDoc
19
+ */
20
+ public name: string = NetworkBreadcrumbs.id;
21
+
22
+ private readonly _traceNetworkBody: boolean;
23
+
24
+ public constructor(options: { traceNetworkBody?: boolean | undefined } = {}) {
25
+ this._traceNetworkBody = !!options.traceNetworkBody;
26
+ }
27
+
28
+ /**
29
+ * @inheritDoc
30
+ */
31
+ public setupOnce(): void {
32
+ const miniappSdk = sdk();
33
+
34
+ // Intercept standard request (WeChat, ByteDance, Swan, etc.)
35
+ if (miniappSdk && typeof miniappSdk.request === 'function') {
36
+ fill(miniappSdk, 'request', this._createRequestWrapper.bind(this));
37
+ }
38
+
39
+ // Intercept Alipay request
40
+ if (miniappSdk && typeof miniappSdk.httpRequest === 'function') {
41
+ fill(miniappSdk, 'httpRequest', this._createRequestWrapper.bind(this));
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Wraps the miniapp request API to capture breadcrumbs
47
+ */
48
+ private _createRequestWrapper(originalRequest: Function): Function {
49
+ const traceNetworkBody = this._traceNetworkBody;
50
+
51
+ return function (this: any, options: any): any {
52
+ if (!options || typeof options !== 'object') {
53
+ return originalRequest.call(this, options);
54
+ }
55
+
56
+ const url = options.url || '';
57
+
58
+ // Get the configured DSN/Transport URL from the current client
59
+ const client = getClient();
60
+ let dsnUrl = '';
61
+ if (client) {
62
+ const dsn = client.getOptions().dsn;
63
+ if (dsn) {
64
+ // A DSN looks like https://key@sentry.io/123
65
+ // We extract just the host part to filter against
66
+ try {
67
+ const dsnMatch = dsn.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?([^:/\n]+)/i);
68
+ if (dsnMatch && dsnMatch[1]) {
69
+ dsnUrl = dsnMatch[1];
70
+ }
71
+ } catch (e) {
72
+ // fallback
73
+ }
74
+ }
75
+ }
76
+
77
+ // Ignore Sentry's own requests to prevent infinite loops
78
+ // Fallback to 'sentry.io' if DSN extraction fails but it's the SaaS version
79
+ if (typeof url === 'string') {
80
+ const isSentryRequest = (dsnUrl && url.indexOf(dsnUrl) !== -1) || url.indexOf('sentry.io') !== -1;
81
+ if (isSentryRequest) {
82
+ return originalRequest.call(this, options);
83
+ }
84
+ }
85
+
86
+ const method = (options.method || 'GET').toUpperCase();
87
+ const requestData = options.data;
88
+
89
+ const breadcrumbData: Record<string, any> = {
90
+ url,
91
+ method,
92
+ };
93
+
94
+ if (traceNetworkBody && requestData) {
95
+ try {
96
+ breadcrumbData['request_body'] = typeof requestData === 'string'
97
+ ? requestData
98
+ : JSON.stringify(requestData);
99
+ } catch (e) {
100
+ breadcrumbData['request_body'] = '[Cannot serialize request body]';
101
+ }
102
+ }
103
+
104
+ const originalSuccess = options.success;
105
+ const originalFail = options.fail;
106
+
107
+ // Wrap success callback
108
+ options.success = function (this: any, ...args: any[]) {
109
+ const res = args[0] || {};
110
+ const statusCode = res.statusCode || res.status;
111
+ breadcrumbData['status_code'] = statusCode;
112
+
113
+ if (traceNetworkBody && res.data) {
114
+ try {
115
+ breadcrumbData['response_body'] = typeof res.data === 'string'
116
+ ? res.data
117
+ : JSON.stringify(res.data);
118
+ } catch (e) {
119
+ breadcrumbData['response_body'] = '[Cannot serialize response body]';
120
+ }
121
+ }
122
+
123
+ addBreadcrumb({
124
+ type: 'http',
125
+ category: 'xhr',
126
+ data: breadcrumbData,
127
+ level: statusCode >= 400 ? 'warning' : 'info',
128
+ });
129
+
130
+ if (typeof originalSuccess === 'function') {
131
+ return originalSuccess.apply(this, args);
132
+ }
133
+ };
134
+
135
+ // Wrap fail callback
136
+ options.fail = function (this: any, ...args: any[]) {
137
+ const err = args[0] || {};
138
+ breadcrumbData['error'] = err.errMsg || err.errorMessage || 'Network request failed';
139
+
140
+ addBreadcrumb({
141
+ type: 'http',
142
+ category: 'xhr',
143
+ data: breadcrumbData,
144
+ level: 'error',
145
+ });
146
+
147
+ if (typeof originalFail === 'function') {
148
+ return originalFail.apply(this, args);
149
+ }
150
+ };
151
+
152
+ return originalRequest.call(this, options);
153
+ };
154
+ }
155
+ }