@arms/rum-reactnative 0.1.3-beta.5 → 0.1.4-beta.2

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 (89) hide show
  1. package/README.md +9 -2
  2. package/android/build.gradle +12 -1
  3. package/android/gradle.properties +3 -1
  4. package/android/src/main/AndroidManifest.xml +4 -1
  5. package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativeModule.kt +67 -14
  6. package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativePackage.kt +9 -2
  7. package/docs/ReactNative SDK/351/205/215/347/275/256/345/217/202/350/200/203.md" +25 -0
  8. package/docs/SDK /347/211/210/346/234/254/350/257/264/346/230/216.md" +20 -0
  9. package/docs//346/216/245/345/205/245ReactNative/345/272/224/347/224/250.md +2 -1
  10. package/ios/RumAppStateRecorder.h +69 -0
  11. package/ios/RumAppStateRecorder.mm +547 -0
  12. package/ios/RumCrashReporter.h +32 -0
  13. package/ios/RumCrashReporter.mm +97 -11
  14. package/ios/RumSdkReactnative.h +11 -1
  15. package/ios/RumSdkReactnative.mm +115 -29
  16. package/lib/commonjs/NativeRumSdkReactnative.js +1 -54
  17. package/lib/commonjs/collector/action/index.js +1 -108
  18. package/lib/commonjs/collector/exception/crash.js +1 -148
  19. package/lib/commonjs/collector/exception/index.js +1 -238
  20. package/lib/commonjs/collector/exception/oom.js +1 -0
  21. package/lib/commonjs/collector/index.js +1 -42
  22. package/lib/commonjs/collector/resource/api.js +1 -292
  23. package/lib/commonjs/collector/view/perf.js +1 -79
  24. package/lib/commonjs/collector/view/pv.js +1 -88
  25. package/lib/commonjs/expoConfigPlugin.js +1 -37
  26. package/lib/commonjs/index.js +1 -29
  27. package/lib/commonjs/plugin.js +1 -47
  28. package/lib/commonjs/processor/default-processor.js +1 -27
  29. package/lib/commonjs/processor/session-processor.js +1 -47
  30. package/lib/commonjs/reporter/index.js +1 -148
  31. package/lib/commonjs/reporter/monitor-reporter.js +1 -43
  32. package/lib/commonjs/shell.js +1 -91
  33. package/lib/commonjs/types/client.js +1 -6
  34. package/lib/commonjs/types/crash.js +1 -37
  35. package/lib/commonjs/types/index.js +1 -2
  36. package/lib/commonjs/types/oom.js +1 -0
  37. package/lib/commonjs/utils/action.js +1 -305
  38. package/lib/commonjs/utils/api.js +1 -84
  39. package/lib/commonjs/utils/base.js +1 -95
  40. package/lib/commonjs/utils/crash.js +1 -149
  41. package/lib/commonjs/utils/platform.js +1 -162
  42. package/lib/commonjs/utils/session.js +1 -158
  43. package/lib/commonjs/utils/url.js +1 -59
  44. package/lib/commonjs/utils/view.js +1 -93
  45. package/lib/module/NativeRumSdkReactnative.js +1 -51
  46. package/lib/module/collector/action/index.js +1 -103
  47. package/lib/module/collector/exception/crash.js +1 -143
  48. package/lib/module/collector/exception/index.js +1 -233
  49. package/lib/module/collector/exception/oom.js +1 -0
  50. package/lib/module/collector/index.js +1 -8
  51. package/lib/module/collector/resource/api.js +1 -287
  52. package/lib/module/collector/view/perf.js +1 -79
  53. package/lib/module/collector/view/pv.js +1 -83
  54. package/lib/module/expoConfigPlugin.js +1 -33
  55. package/lib/module/index.js +1 -7
  56. package/lib/module/plugin.js +1 -42
  57. package/lib/module/processor/default-processor.js +1 -22
  58. package/lib/module/processor/session-processor.js +1 -42
  59. package/lib/module/reporter/index.js +1 -144
  60. package/lib/module/reporter/monitor-reporter.js +1 -38
  61. package/lib/module/shell.js +1 -84
  62. package/lib/module/types/client.js +1 -4
  63. package/lib/module/types/crash.js +1 -33
  64. package/lib/module/types/index.js +1 -2
  65. package/lib/module/types/oom.js +1 -0
  66. package/lib/module/utils/action.js +1 -299
  67. package/lib/module/utils/api.js +1 -76
  68. package/lib/module/utils/base.js +1 -90
  69. package/lib/module/utils/crash.js +1 -144
  70. package/lib/module/utils/platform.js +1 -150
  71. package/lib/module/utils/session.js +1 -154
  72. package/lib/module/utils/url.js +1 -54
  73. package/lib/module/utils/view.js +1 -88
  74. package/lib/typescript/commonjs/src/NativeRumSdkReactnative.d.ts +10 -0
  75. package/lib/typescript/commonjs/src/collector/exception/oom.d.ts +34 -0
  76. package/lib/typescript/commonjs/src/collector/index.d.ts +1 -0
  77. package/lib/typescript/commonjs/src/shell.d.ts +1 -1
  78. package/lib/typescript/commonjs/src/types/client.d.ts +27 -0
  79. package/lib/typescript/commonjs/src/types/oom.d.ts +58 -0
  80. package/lib/typescript/commonjs/src/utils/crash.d.ts +13 -0
  81. package/lib/typescript/module/src/NativeRumSdkReactnative.d.ts +10 -0
  82. package/lib/typescript/module/src/collector/exception/oom.d.ts +34 -0
  83. package/lib/typescript/module/src/collector/index.d.ts +1 -0
  84. package/lib/typescript/module/src/shell.d.ts +1 -1
  85. package/lib/typescript/module/src/types/client.d.ts +27 -0
  86. package/lib/typescript/module/src/types/oom.d.ts +58 -0
  87. package/lib/typescript/module/src/utils/crash.d.ts +13 -0
  88. package/package.json +1 -2
  89. package/src/NativeRumSdkReactnative.ts +18 -0
@@ -1,148 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _rumCore = require("@arms/rum-core");
8
- var _platform = require("../utils/platform");
9
- var _crash = require("../types/crash");
10
- var _reactNative = require("react-native");
11
- // @ts-ignore
12
-
13
- /**
14
- * 从 bundle.events 的 snapshots 中提取携带的 crash_id(瞬态字段)。
15
- * crash_id 由 CrashCollector 注入到 snapshots JSON 字符串中,
16
- * reporter 提取后用于 ack,并在序列化前从 snapshots 中剥离,不会发送到后端。
17
- * 兼容旧版降级路径:crash_id 可能残留在事件根级。
18
- */
19
- function extractCrashIds(bundle) {
20
- const ids = [];
21
- const events = bundle?.events;
22
- if (Array.isArray(events)) {
23
- for (const ev of events) {
24
- if (!ev) continue;
25
- // 优先从 snapshots 提取
26
- let cid;
27
- try {
28
- const snapshotsObj = JSON.parse(ev.snapshots);
29
- cid = snapshotsObj?.[_crash.CRASH_ID_FIELD];
30
- } catch {
31
- // snapshots 解析失败
32
- }
33
- // 兼容旧版降级路径:检查根级
34
- if (!cid) {
35
- cid = ev[_crash.CRASH_ID_FIELD];
36
- }
37
- if (typeof cid === 'string' && cid) ids.push(cid);
38
- }
39
- }
40
- return ids;
41
- }
42
-
43
- /**
44
- * 序列化前剥离事件上的 crash_id 瞬态字段:
45
- * - 从 snapshots JSON 字符串中移除;
46
- * - 兼容旧版降级路径:从事件根级移除。
47
- */
48
- function stripCrashIds(bundle) {
49
- const events = bundle?.events;
50
- if (!Array.isArray(events)) return;
51
- for (const ev of events) {
52
- if (!ev) continue;
53
- // 从 snapshots 中移除
54
- if (typeof ev.snapshots === 'string') {
55
- try {
56
- const snapshotsObj = JSON.parse(ev.snapshots);
57
- if (snapshotsObj && _crash.CRASH_ID_FIELD in snapshotsObj) {
58
- delete snapshotsObj[_crash.CRASH_ID_FIELD];
59
- ev.snapshots = JSON.stringify(snapshotsObj);
60
- }
61
- } catch {
62
- // snapshots 解析失败,跳过
63
- }
64
- }
65
- // 兼容旧版降级路径:从根级移除
66
- if (_crash.CRASH_ID_FIELD in ev) {
67
- delete ev[_crash.CRASH_ID_FIELD];
68
- }
69
- }
70
- }
71
-
72
- /**
73
- * rn-app reporter
74
- */
75
- class RNReporter extends _rumCore.Reporter {
76
- name = 'rn-reporter';
77
- constructor() {
78
- super();
79
- // 离开之前 flushEventQueue
80
- try {
81
- this.appStateListener = _reactNative.AppState.addEventListener('change', this.onAppStateChanged);
82
- } catch (error) {
83
- _rumCore.logger.warn('reporter', 'AppState listener binding failed', error);
84
- }
85
- }
86
- init(ctx) {
87
- this.ctx = ctx;
88
- }
89
- onAppStateChanged = nextAppState => {
90
- if (nextAppState === 'background') {
91
- // 执行后台操作:暂停动画、保存数据、停止网络请求等
92
- if (typeof this.flushEventQueue === 'function') {
93
- this.flushEventQueue();
94
- }
95
- // 后台前 flush 自监控数据
96
- _rumCore.Telemetry.get()?.flush();
97
- }
98
- // else if (nextAppState === 'active') {
99
- // console.warn('RUM:应用回到前台');
100
- // }
101
- };
102
- async request(ctx, bundle) {
103
- // 先从事件中提取崩溃 crash_id(瞬态字段),用于上报后 ack 删文件
104
- const crashIds = extractCrashIds(bundle);
105
- try {
106
- const batchedEvents = await (0, _platform.fixAttrs)(bundle);
107
- // 序列化前剥离瞬态字段,避免发送到后端
108
- stripCrashIds(batchedEvents);
109
- const config = ctx.getConfig();
110
- fetch(config.endpoint, {
111
- method: 'POST',
112
- headers: {
113
- 'Content-Type': 'text/plain'
114
- },
115
- body: JSON.stringify(batchedEvents)
116
- }).then(res => {
117
- this.ackCrashes(ctx, crashIds, !!(res && res.ok));
118
- }).catch(err => {
119
- this.catchHandle(err);
120
- this.ackCrashes(ctx, crashIds, false);
121
- });
122
- } catch (err) {
123
- this.catchHandle(err);
124
- this.ackCrashes(ctx, crashIds, false);
125
- }
126
- }
127
-
128
- /** 通过共享 emitter 通知崩溃采集器上报结果(成功则删文件,失败保留重试) */
129
- ackCrashes(ctx, crashIds, success) {
130
- if (!crashIds.length) return;
131
- try {
132
- ctx.emitter?.emit(_crash.CRASH_REPORT_ACK_EVENT, {
133
- crashIds,
134
- success
135
- });
136
- } catch (e) {
137
- _rumCore.logger.warn('reporter', 'emit crash ack failed', e);
138
- }
139
- }
140
- catchHandle = err => {
141
- _rumCore.logger.warn('reporter', 'sendRequest failed', err);
142
- };
143
- destroy() {
144
- this.appStateListener?.remove();
145
- }
146
- }
147
- var _default = exports.default = RNReporter;
148
- //# sourceMappingURL=index.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _rumCore=require("@arms/rum-core"),_platform=require("../utils/platform"),_crash=require("../types/crash"),_reactNative=require("react-native");function extractCrashIds(e){const t=[],r=e?.events;if(Array.isArray(r))for(const e of r){if(!e)continue;let r;try{const t=JSON.parse(e.snapshots);r=t?.[_crash.CRASH_ID_FIELD]}catch{}r||(r=e[_crash.CRASH_ID_FIELD]),"string"==typeof r&&r&&t.push(r)}return t}function stripCrashIds(e){const t=e?.events;if(Array.isArray(t))for(const e of t)if(e){if("string"==typeof e.snapshots)try{const t=JSON.parse(e.snapshots);t&&_crash.CRASH_ID_FIELD in t&&(delete t[_crash.CRASH_ID_FIELD],e.snapshots=JSON.stringify(t))}catch{}_crash.CRASH_ID_FIELD in e&&delete e[_crash.CRASH_ID_FIELD]}}class RNReporter extends _rumCore.Reporter{name="rn-reporter";constructor(){super();try{this.appStateListener=_reactNative.AppState.addEventListener("change",this.onAppStateChanged)}catch(e){_rumCore.logger.warn("reporter","AppState listener binding failed",e)}}init(e){this.ctx=e}onAppStateChanged=e=>{"background"===e&&("function"==typeof this.flushEventQueue&&this.flushEventQueue(),_rumCore.Telemetry.get()?.flush())};async request(e,t){const r=extractCrashIds(t);try{const s=await(0,_platform.fixAttrs)(t);stripCrashIds(s);const a=e.getConfig();fetch(a.endpoint,{method:"POST",headers:{"Content-Type":"text/plain"},body:JSON.stringify(s)}).then(t=>{this.ackCrashes(e,r,!(!t||!t.ok))}).catch(t=>{this.catchHandle(t),this.ackCrashes(e,r,!1)})}catch(t){this.catchHandle(t),this.ackCrashes(e,r,!1)}}ackCrashes(e,t,r){if(t.length)try{e.emitter?.emit(_crash.CRASH_REPORT_ACK_EVENT,{crashIds:t,success:r})}catch(e){_rumCore.logger.warn("reporter","emit crash ack failed",e)}}catchHandle=e=>{_rumCore.logger.warn("reporter","sendRequest failed",e)};destroy(){this.appStateListener?.remove()}}var _default=exports.default=RNReporter;
@@ -1,43 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.RNMonitorReporter = void 0;
7
- var _rumCore = require("@arms/rum-core");
8
- class RNMonitorReporter {
9
- constructor(endpoint) {
10
- this.endpoint = this.buildMonitorEndpoint(endpoint);
11
- }
12
- send(data) {
13
- if (!data || data.length === 0) return;
14
- try {
15
- // 使用 rum-core 的 Protobuf 编码器将数据编码为 SLS LogGroup 二进制格式
16
- const protobufBody = (0, _rumCore.convertBundlesToLogGroup)(data);
17
- fetch(this.endpoint, {
18
- method: 'POST',
19
- headers: {
20
- 'Content-Type': 'application/x-protobuf',
21
- 'x-log-apiversion': '0.6.0',
22
- 'x-log-compresstype': '',
23
- 'x-log-date': new Date().toUTCString()
24
- },
25
- body: protobufBody
26
- }).catch(() => {
27
- // 静默处理,避免反馈循环(不调用 logger)
28
- });
29
- } catch (_) {
30
- // 吞掉异常,防止自监控影响宿主应用
31
- }
32
- }
33
- buildMonitorEndpoint(endpoint) {
34
- try {
35
- const url = new URL(endpoint);
36
- return `${url.origin}/rum/monitor`;
37
- } catch (_) {
38
- return endpoint;
39
- }
40
- }
41
- }
42
- exports.RNMonitorReporter = RNMonitorReporter;
43
- //# sourceMappingURL=monitor-reporter.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.RNMonitorReporter=void 0;var _rumCore=require("@arms/rum-core");class RNMonitorReporter{constructor(o){this.endpoint=this.buildMonitorEndpoint(o)}send(o){if(o&&0!==o.length)try{const r=(0,_rumCore.convertBundlesToLogGroup)(o);fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/x-protobuf","x-log-apiversion":"0.6.0","x-log-compresstype":"","x-log-date":(new Date).toUTCString()},body:r}).catch(()=>{})}catch(o){}}buildMonitorEndpoint(o){try{return`${new URL(o).origin}/rum/monitor`}catch(r){return o}}}exports.RNMonitorReporter=RNMonitorReporter;
@@ -1,91 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = exports.ArmsRum = void 0;
7
- var _rumCore = require("@arms/rum-core");
8
- var collectors = _interopRequireWildcard(require("./collector"));
9
- var _defaultProcessor = _interopRequireDefault(require("./processor/default-processor"));
10
- var _sessionProcessor = _interopRequireDefault(require("./processor/session-processor"));
11
- var _reporter = _interopRequireDefault(require("./reporter"));
12
- var _monitorReporter = require("./reporter/monitor-reporter");
13
- var _platform = require("./utils/platform");
14
- var _session = require("./utils/session");
15
- var _view = require("./utils/view");
16
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
18
- const builtInCollectors = [...Object.values(collectors).filter(Collector => (0, _rumCore.isFunction)(Collector))];
19
- /**
20
- * 对外导出 shell 层, 所有 shell 层模型的 API 设计约定:
21
- * 1. API 命名空间按照 variables / functions / events 来组织
22
- * 2. 事件(events)的命名格式为:on[Will|Did]VerbNoun?,参考 https://code.visualstudio.com/api/references/vscode-api#events
23
- * 3. 基于 Disposable 模式,对于事件的绑定、快捷键的绑定函数,返回值则是解绑函数
24
- */
25
- class ArmsRum extends _rumCore.Shell {
26
- version = _platform.VERSION;
27
-
28
- /**
29
- * 初始化
30
- */
31
- init(config) {
32
- this.client.useCollectors(builtInCollectors.map(Collector => (0, _rumCore.callMonitored)(() => new Collector(), undefined, [], 'shell', `init ${Collector.name}`)).filter(Boolean));
33
- this.client.useProcessors([new _defaultProcessor.default(), new _sessionProcessor.default()]);
34
- this.client.useReporter(new _reporter.default());
35
- // 传入 MonitorReporter 激活自监控
36
- const monitorReporter = config.endpoint ? new _monitorReporter.RNMonitorReporter(config.endpoint) : undefined;
37
- this.client.init(config, new _session.RumSession(), undefined, monitorReporter);
38
- return this;
39
- }
40
-
41
- /**
42
- * set config
43
- */
44
-
45
- setConfig(...params) {
46
- const ctx = this.client.getContext();
47
- if (params.length === 2) {
48
- const oldConfig = ctx.getConfig();
49
- ctx.setConfig({
50
- ...oldConfig,
51
- [params[0]]: params[1]
52
- });
53
- } else {
54
- ctx.setConfig(params[0]);
55
- }
56
- }
57
-
58
- /**
59
- * 开启路由变化监听,自动采集 PV 数据
60
- *
61
- * 必须在 NavigationContainer 的 `onReady` 回调中调用,以确保
62
- * navigationRef 已正确初始化。
63
- *
64
- * @example
65
- * ```tsx
66
- * <NavigationContainer
67
- * ref={navigationRef}
68
- * onReady={() => {
69
- * ArmsRum.startTrackingViews(navigationRef);
70
- * }}
71
- * >
72
- * ...
73
- * </NavigationContainer>
74
- * ```
75
- *
76
- * @param navigationRef - NavigationContainer 的 ref 引用
77
- */
78
- startTrackingViews(navigationRef) {
79
- if (!navigationRef) return;
80
- (0, _view.startTrackingViews)(navigationRef);
81
- }
82
- stopTrackingViews() {
83
- (0, _view.stopTrackingViews)();
84
- }
85
- getCollector(name) {
86
- return this.client.getCollectors().find(collector => collector.name === name);
87
- }
88
- }
89
- exports.ArmsRum = ArmsRum;
90
- var _default = exports.default = new ArmsRum();
91
- //# sourceMappingURL=shell.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=exports.ArmsRum=void 0;var _rumCore=require("@arms/rum-core"),collectors=_interopRequireWildcard(require("./collector")),_defaultProcessor=_interopRequireDefault(require("./processor/default-processor")),_sessionProcessor=_interopRequireDefault(require("./processor/session-processor")),_reporter=_interopRequireDefault(require("./reporter")),_monitorReporter=require("./reporter/monitor-reporter"),_platform=require("./utils/platform"),_session=require("./utils/session"),_view=require("./utils/view");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var t=new WeakMap,o=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var i,s,n={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return n;if(i=r?o:t){if(i.has(e))return i.get(e);i.set(e,n)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((s=(i=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(s.get||s.set)?i(n,r,s):n[r]=e[r]);return n})(e,r)}const builtInCollectors=[...Object.values(collectors).filter(e=>(0,_rumCore.isFunction)(e))];class ArmsRum extends _rumCore.Shell{version=_platform.VERSION;init(e){this.client.useCollectors(builtInCollectors.map(e=>(0,_rumCore.callMonitored)(()=>new e,void 0,[],"shell",`init ${e.name}`)).filter(Boolean)),this.client.useProcessors([new _defaultProcessor.default,new _sessionProcessor.default]),this.client.useReporter(new _reporter.default);const r=e.endpoint?new _monitorReporter.RNMonitorReporter(e.endpoint):void 0;return this.client.init(e,new _session.RumSession,void 0,r),this}setConfig(...e){const r=this.client.getContext();if(2===e.length){const t=r.getConfig();r.setConfig({...t,[e[0]]:e[1]})}else r.setConfig(e[0])}startTrackingViews(e){e&&(0,_view.startTrackingViews)(e)}stopTrackingViews(){(0,_view.stopTrackingViews)()}getCollector(e){return this.client.getCollectors().find(r=>r.name===e)}}exports.ArmsRum=ArmsRum;var _default=exports.default=new ArmsRum;
@@ -1,6 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- //# sourceMappingURL=client.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});
@@ -1,37 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.CRASH_REPORT_ACK_EVENT = exports.CRASH_ID_FIELD = exports.CRASH_CONTEXT_EVENT = void 0;
7
- /**
8
- * 崩溃相关类型定义
9
- */
10
-
11
- /** 崩溃上下文同步事件名 */
12
- const CRASH_CONTEXT_EVENT = exports.CRASH_CONTEXT_EVENT = 'crash_context_change';
13
-
14
- /** 崩溃上报确认事件名(reporter -> crash collector,用于 ack 后删除文件) */
15
- const CRASH_REPORT_ACK_EVENT = exports.CRASH_REPORT_ACK_EVENT = 'crash_report_ack';
16
-
17
- /**
18
- * 挂在崩溃 RumEvent 上的瞬态字段名,携带 crash_id。
19
- * reporter 上报前提取用于 ack、并在序列化前剥离,不会发送到后端。
20
- */
21
- const CRASH_ID_FIELD = exports.CRASH_ID_FIELD = '__rum_crash_id';
22
-
23
- /** 崩溃上报确认事件负载 */
24
-
25
- /**
26
- * 崩溃上下文快照(session / view 变更时同步到原生侧)。
27
- */
28
-
29
- /**
30
- * 原生崩溃栈帧数据结构。
31
- */
32
-
33
- /**
34
- * 原生崩溃数据结构(与原生侧 RumCrashReporter / CrashContextManager 写入的 JSON 对齐)。
35
- * 仅声明 JS 层会用到的字段,未列出的字段会通过 snapshots 透传给后端。
36
- */
37
- //# sourceMappingURL=crash.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.CRASH_REPORT_ACK_EVENT=exports.CRASH_ID_FIELD=exports.CRASH_CONTEXT_EVENT=void 0;const CRASH_CONTEXT_EVENT=exports.CRASH_CONTEXT_EVENT="crash_context_change",CRASH_REPORT_ACK_EVENT=exports.CRASH_REPORT_ACK_EVENT="crash_report_ack",CRASH_ID_FIELD=exports.CRASH_ID_FIELD="__rum_crash_id";
@@ -1,2 +1 @@
1
- "use strict";
2
- //# sourceMappingURL=index.js.map
1
+ "use strict";
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1,305 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.addActionListener = addActionListener;
7
- exports.removeActionListener = removeActionListener;
8
- exports.startTrackingActions = startTrackingActions;
9
- exports.stopTrackingActions = stopTrackingActions;
10
- var _rumCore = require("@arms/rum-core");
11
- /**
12
- * 行为事件监听 & React Native 触摸自动采集
13
- *
14
- * 通过 patch React 的元素创建函数(createElement 及自动 JSX 运行时
15
- * jsx/jsxs/jsxDEV),自动拦截 Pressable / TouchableOpacity 等可触摸组件的
16
- * onPress,并从触摸事件(GestureResponderEvent)、onLayout、props/children 中
17
- * 提取丰富的行为数据(位置、尺寸、持续时间、语义化名称、快照等),发射标准事件
18
- * 给 ActionCollector。
19
- *
20
- * 说明:现代 RN/Expo(babel-preset-expo)默认使用「automatic」JSX 运行时,
21
- * JSX 会被编译为 react/jsx-runtime 的 jsx()/jsxs()(生产)或
22
- * react/jsx-dev-runtime 的 jsxDEV()(开发),而非 React.createElement。
23
- * 因此必须同时 patch 这三者才能覆盖所有场景。
24
- */
25
-
26
- const actionListeners = [];
27
- let patched = false;
28
-
29
- /** 语义化名称中截取的子元素文本最大长度 */
30
- const MAX_TEXT_LENGTH = 20;
31
-
32
- /** 已 patch 的目标:{ module, key, original } 用于还原 */
33
-
34
- let patchTargets = [];
35
- function addActionListener(listener) {
36
- if ((0, _rumCore.isFunction)(listener)) {
37
- actionListeners.push(listener);
38
- }
39
- }
40
- function removeActionListener(listener) {
41
- const idx = actionListeners.indexOf(listener);
42
- if (idx > -1) {
43
- actionListeners.splice(idx, 1);
44
- }
45
- }
46
-
47
- /** 采集到的原始触摸数据,交由 emitAction 组装为标准 ActionEvent */
48
-
49
- function emitAction(raw) {
50
- const {
51
- id,
52
- componentName,
53
- props,
54
- children,
55
- nativeEvent,
56
- pressInTs,
57
- layout
58
- } = raw;
59
-
60
- // 关键属性快照
61
- const dataset = {};
62
- if (props.id) dataset.id = props.id;
63
- if (props.testID) dataset.testID = props.testID;
64
- if (props.accessibilityLabel) {
65
- dataset.accessibilityLabel = props.accessibilityLabel;
66
- }
67
- const text = getElementText(children);
68
- const selector = componentName ? `${componentName}#${id}` : `#${id}`;
69
- const label = `tap ${selector}`;
70
- const name = text ? `${label}: ${text}` : label;
71
- const position = getPosition(nativeEvent);
72
- const duration = pressInTs && nativeEvent && typeof nativeEvent.timestamp === 'number' ? Math.max(0, Math.round(nativeEvent.timestamp - pressInTs)) : undefined;
73
- const event = {
74
- type: 'tap',
75
- name,
76
- targetName: selector,
77
- duration,
78
- position,
79
- target: {
80
- id,
81
- dataset,
82
- selector,
83
- width: layout?.width,
84
- height: layout?.height,
85
- // 带 onPress 的 touchable 本身即为可交互元素
86
- reaction: 1,
87
- // 存在原生触摸事件视为可信交互,纯代码调用视为不可信
88
- trust: nativeEvent ? 1 : 0
89
- }
90
- };
91
- actionListeners.forEach(listener => {
92
- listener(event);
93
- });
94
- }
95
-
96
- /** 获取组件的显示名称,如 Pressable / TouchableOpacity */
97
- function getComponentName(type) {
98
- if (!type) return undefined;
99
- if (typeof type === 'string') return type;
100
- return type.displayName || type.name || undefined;
101
- }
102
-
103
- /** 递归提取 children 中的文本内容,用于生成语义化名称 */
104
- function getElementText(children) {
105
- const acc = {
106
- text: ''
107
- };
108
- collectText(children, acc);
109
- const t = acc.text.trim();
110
- if (!t) return '';
111
- return t.length > MAX_TEXT_LENGTH ? t.slice(0, MAX_TEXT_LENGTH) + '...' : t;
112
- }
113
- function collectText(node, acc) {
114
- if (node == null || typeof node === 'boolean') return;
115
- if (acc.text.length > MAX_TEXT_LENGTH) return;
116
- if (typeof node === 'string' || typeof node === 'number') {
117
- acc.text += String(node);
118
- return;
119
- }
120
- if (Array.isArray(node)) {
121
- node.forEach(c => collectText(c, acc));
122
- return;
123
- }
124
- if (node.props && node.props.children != null) {
125
- collectText(node.props.children, acc);
126
- }
127
- }
128
-
129
- /** 从原生触摸事件中提取点击位置(屏幕坐标 + 元素内相对坐标) */
130
- function getPosition(nativeEvent) {
131
- if (!nativeEvent) return undefined;
132
- const {
133
- pageX,
134
- pageY,
135
- locationX,
136
- locationY
137
- } = nativeEvent;
138
- if (pageX == null && pageY == null && locationX == null && locationY == null) {
139
- return undefined;
140
- }
141
- return {
142
- x: round(pageX),
143
- y: round(pageY),
144
- ex: round(locationX),
145
- ey: round(locationY)
146
- };
147
- }
148
- function round(n) {
149
- return typeof n === 'number' ? Math.round(n) : undefined;
150
- }
151
-
152
- /**
153
- * 标记位:用于在已包装的 onPress 函数上做去重判断,避免重复包装
154
- */
155
- const RUM_WRAPPED = '__rumWrapped';
156
-
157
- /**
158
- * 生成一个「元素创建函数」的包装器。
159
- *
160
- * createElement / jsx / jsxs / jsxDEV 的前两个参数都是 (type, props),
161
- * 其余参数(children / key / source 等)原样透传即可,因此可用统一逻辑处理。
162
- *
163
- * 除包装 onPress 采集点击外,还会顺带包装 onPressIn(记录按下时间以计算
164
- * duration)与 onLayout(记录元素尺寸 width/height),以补全行为数据。
165
- *
166
- * childrenInRest:经典 createElement 的 children 以可变参数传入(在 rest 中),
167
- * 而 automatic 运行时 jsx/jsxs/jsxDEV 的 children 在 props.children 中。
168
- */
169
- function makeWrapper(original, touchableTypes, childrenInRest) {
170
- return function patchedElementFactory(type, props, ...rest) {
171
- if (props && touchableTypes.has(type) && (0, _rumCore.isFunction)(props.onPress)) {
172
- const id = props.id || props.testID || props.accessibilityLabel;
173
- if (id && !props.onPress[RUM_WRAPPED]) {
174
- const componentName = getComponentName(type);
175
- const children = childrenInRest ? rest.length <= 1 ? rest[0] : rest : props.children;
176
- const originalOnPress = props.onPress;
177
- const originalOnPressIn = props.onPressIn;
178
- const originalOnLayout = props.onLayout;
179
-
180
- // 闭包内保存按下时间与布局尺寸,供 onPress 采集时读取
181
- let pressInTs = 0;
182
- let layout;
183
- const wrappedOnPressIn = (...args) => {
184
- const ne = args[0] && args[0].nativeEvent;
185
- pressInTs = ne && typeof ne.timestamp === 'number' ? ne.timestamp : 0;
186
- if ((0, _rumCore.isFunction)(originalOnPressIn)) return originalOnPressIn(...args);
187
- };
188
- const wrappedOnLayout = (...args) => {
189
- const ne = args[0] && args[0].nativeEvent;
190
- const l = ne && ne.layout;
191
- if (l) {
192
- layout = {
193
- width: round(l.width),
194
- height: round(l.height)
195
- };
196
- }
197
- if ((0, _rumCore.isFunction)(originalOnLayout)) return originalOnLayout(...args);
198
- };
199
- const wrappedOnPress = (...args) => {
200
- emitAction({
201
- id,
202
- componentName,
203
- props,
204
- children,
205
- nativeEvent: args[0] && args[0].nativeEvent,
206
- pressInTs,
207
- layout
208
- });
209
- return originalOnPress(...args);
210
- };
211
- wrappedOnPress[RUM_WRAPPED] = true;
212
- return original(type, {
213
- ...props,
214
- onPress: wrappedOnPress,
215
- onPressIn: wrappedOnPressIn,
216
- onLayout: wrappedOnLayout
217
- }, ...rest);
218
- }
219
- }
220
- return original(type, props, ...rest);
221
- };
222
- }
223
-
224
- /**
225
- * 安全地按字面量 require 一个模块,失败返回 undefined。
226
- *
227
- * 注意:Metro 的 babel 插件禁止动态 `require(变量)`,因此这里必须用
228
- * 字面量字符串分别 require,不能抽成 `safeRequire(name)` 之类的通用函数。
229
- */
230
- function safeRequireJsxRuntime() {
231
- try {
232
- return require('react/jsx-runtime');
233
- } catch {
234
- return undefined;
235
- }
236
- }
237
- function safeRequireJsxDevRuntime() {
238
- try {
239
- return require('react/jsx-dev-runtime');
240
- } catch {
241
- return undefined;
242
- }
243
- }
244
- function patchModuleFn(mod, key, touchableTypes) {
245
- if (!mod || !(0, _rumCore.isFunction)(mod[key])) return;
246
- const original = mod[key];
247
- // 经典 createElement 的 children 以可变参数传入;jsx 运行时则在 props.children
248
- const childrenInRest = key === 'createElement';
249
- try {
250
- mod[key] = makeWrapper(original, touchableTypes, childrenInRest);
251
- patchTargets.push({
252
- mod,
253
- key,
254
- original
255
- });
256
- } catch {
257
- // 某些环境下属性只读,忽略
258
- }
259
- }
260
-
261
- /**
262
- * 开始自动采集用户点击行为
263
- *
264
- * 原理:patch React.createElement 以及 automatic JSX 运行时的 jsx/jsxs/jsxDEV,
265
- * 当组件为可触摸类型且含 onPress + id/testID/accessibilityLabel 时,
266
- * 包装 onPress 使其在调用时自动 emit action 事件。
267
- */
268
- function startTrackingActions() {
269
- if (patched) return;
270
- const React = require('react');
271
- const RN = require('react-native');
272
- const touchableTypes = new Set([RN.Pressable, RN.TouchableOpacity, RN.TouchableHighlight, RN.TouchableWithoutFeedback, RN.TouchableNativeFeedback, RN.Button].filter(Boolean));
273
-
274
- // 经典运行时
275
- patchModuleFn(React, 'createElement', touchableTypes);
276
- // automatic 运行时(生产)
277
- const jsxRuntime = safeRequireJsxRuntime();
278
- patchModuleFn(jsxRuntime, 'jsx', touchableTypes);
279
- patchModuleFn(jsxRuntime, 'jsxs', touchableTypes);
280
- // automatic 运行时(开发)
281
- const jsxDevRuntime = safeRequireJsxDevRuntime();
282
- patchModuleFn(jsxDevRuntime, 'jsxDEV', touchableTypes);
283
- patched = patchTargets.length > 0;
284
- }
285
-
286
- /**
287
- * 停止自动采集
288
- */
289
- function stopTrackingActions() {
290
- if (!patched) return;
291
- patchTargets.forEach(({
292
- mod,
293
- key,
294
- original
295
- }) => {
296
- try {
297
- mod[key] = original;
298
- } catch {
299
- // 忽略还原失败
300
- }
301
- });
302
- patchTargets = [];
303
- patched = false;
304
- }
305
- //# sourceMappingURL=action.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.addActionListener=addActionListener,exports.removeActionListener=removeActionListener,exports.startTrackingActions=startTrackingActions,exports.stopTrackingActions=stopTrackingActions;var _rumCore=require("@arms/rum-core");const actionListeners=[];let patched=!1;const MAX_TEXT_LENGTH=20;let patchTargets=[];function addActionListener(t){(0,_rumCore.isFunction)(t)&&actionListeners.push(t)}function removeActionListener(t){const e=actionListeners.indexOf(t);e>-1&&actionListeners.splice(e,1)}function emitAction(t){const{id:e,componentName:n,props:r,children:i,nativeEvent:o,pressInTs:s,layout:c}=t,a={};r.id&&(a.id=r.id),r.testID&&(a.testID=r.testID),r.accessibilityLabel&&(a.accessibilityLabel=r.accessibilityLabel);const u=getElementText(i),l=n?`${n}#${e}`:`#${e}`,p=`tap ${l}`,d=u?`${p}: ${u}`:p,h=getPosition(o),m={type:"tap",name:d,targetName:l,duration:s&&o&&"number"==typeof o.timestamp?Math.max(0,Math.round(o.timestamp-s)):void 0,position:h,target:{id:e,dataset:a,selector:l,width:c?.width,height:c?.height,reaction:1,trust:o?1:0}};actionListeners.forEach(t=>{t(m)})}function getComponentName(t){if(t)return"string"==typeof t?t:t.displayName||t.name||void 0}function getElementText(t){const e={text:""};collectText(t,e);const n=e.text.trim();return n?n.length>20?n.slice(0,20)+"...":n:""}function collectText(t,e){null!=t&&"boolean"!=typeof t&&(e.text.length>20||("string"!=typeof t&&"number"!=typeof t?Array.isArray(t)?t.forEach(t=>collectText(t,e)):t.props&&null!=t.props.children&&collectText(t.props.children,e):e.text+=String(t)))}function getPosition(t){if(!t)return;const{pageX:e,pageY:n,locationX:r,locationY:i}=t;return null!=e||null!=n||null!=r||null!=i?{x:round(e),y:round(n),ex:round(r),ey:round(i)}:void 0}function round(t){return"number"==typeof t?Math.round(t):void 0}const RUM_WRAPPED="__rumWrapped";function makeWrapper(t,e,n){return function(r,i,...o){if(i&&e.has(r)&&(0,_rumCore.isFunction)(i.onPress)){const e=i.id||i.testID||i.accessibilityLabel;if(e&&!i.onPress[RUM_WRAPPED]){const s=getComponentName(r),c=n?o.length<=1?o[0]:o:i.children,a=i.onPress,u=i.onPressIn,l=i.onLayout;let p,d=0;const h=(...t)=>{const e=t[0]&&t[0].nativeEvent;if(d=e&&"number"==typeof e.timestamp?e.timestamp:0,(0,_rumCore.isFunction)(u))return u(...t)},m=(...t)=>{const e=t[0]&&t[0].nativeEvent,n=e&&e.layout;if(n&&(p={width:round(n.width),height:round(n.height)}),(0,_rumCore.isFunction)(l))return l(...t)},f=(...t)=>(emitAction({id:e,componentName:s,props:i,children:c,nativeEvent:t[0]&&t[0].nativeEvent,pressInTs:d,layout:p}),a(...t));return f[RUM_WRAPPED]=!0,t(r,{...i,onPress:f,onPressIn:h,onLayout:m},...o)}}return t(r,i,...o)}}function safeRequireJsxRuntime(){try{return require("react/jsx-runtime")}catch{return}}function safeRequireJsxDevRuntime(){try{return require("react/jsx-dev-runtime")}catch{return}}function patchModuleFn(t,e,n){if(!t||!(0,_rumCore.isFunction)(t[e]))return;const r=t[e],i="createElement"===e;try{t[e]=makeWrapper(r,n,i),patchTargets.push({mod:t,key:e,original:r})}catch{}}function startTrackingActions(){if(patched)return;const t=require("react"),e=require("react-native"),n=new Set([e.Pressable,e.TouchableOpacity,e.TouchableHighlight,e.TouchableWithoutFeedback,e.TouchableNativeFeedback,e.Button].filter(Boolean));patchModuleFn(t,"createElement",n);const r=safeRequireJsxRuntime();patchModuleFn(r,"jsx",n),patchModuleFn(r,"jsxs",n);patchModuleFn(safeRequireJsxDevRuntime(),"jsxDEV",n),patched=patchTargets.length>0}function stopTrackingActions(){patched&&(patchTargets.forEach(({mod:t,key:e,original:n})=>{try{t[e]=n}catch{}}),patchTargets=[],patched=!1)}