@arms/rum-core 0.1.6 → 0.1.8

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/es/index.d.ts CHANGED
@@ -19,5 +19,7 @@ export * from './utils/trace';
19
19
  export * from './utils/verify';
20
20
  export * from './utils/url';
21
21
  export * from './utils/events';
22
+ export * from './utils/throttle';
23
+ export * from './utils/disposable';
22
24
  export * from './monitor';
23
25
  export { Client, Context, Reporter, Shell };
package/es/index.js CHANGED
@@ -19,5 +19,7 @@ export * from './utils/trace';
19
19
  export * from './utils/verify';
20
20
  export * from './utils/url';
21
21
  export * from './utils/events';
22
+ export * from './utils/throttle';
23
+ export * from './utils/disposable';
22
24
  export * from './monitor';
23
25
  export { Client, Context, Reporter, Shell };
@@ -1,6 +1,6 @@
1
1
  import { IClient, IConfiguration, IContext, IRumSession } from '../types/client';
2
- import { IConfigManager } from "../types/config";
3
- import { RumEvent } from '../types/rum-event';
2
+ import { IConfigManager } from '../types/config';
3
+ import { RumEvent, SendEventOptions } from '../types/rum-event';
4
4
  import { IProcessor } from '../types/processor';
5
5
  import { ICollector } from '../types/collector';
6
6
  import { IReporter } from '../types/reporter';
@@ -12,7 +12,7 @@ declare class Client implements IClient {
12
12
  private reporter;
13
13
  private ctx;
14
14
  init(config: IConfiguration, rumSession?: IRumSession, configManager?: IConfigManager, monitorReporter?: IMonitorReporter): void;
15
- sendEvent: (payload: RumEvent) => void;
15
+ sendEvent: (payload: RumEvent, options?: SendEventOptions) => void;
16
16
  setContext(ctx: IContext): void;
17
17
  getContext(): IContext;
18
18
  useCollectors(collectors: ICollector[]): void;
@@ -5,7 +5,7 @@ import { EventType } from '../types/client';
5
5
  import { EventEmitter } from '../utils/events';
6
6
  import { Telemetry } from '../monitor/telemetry';
7
7
  import Context from './context';
8
- import { isFunction } from "../utils/is";
8
+ import { isFunction } from '../utils/is';
9
9
  var Client = /*#__PURE__*/function () {
10
10
  function Client() {
11
11
  var _this = this;
@@ -14,8 +14,8 @@ var Client = /*#__PURE__*/function () {
14
14
  this.processors = void 0;
15
15
  this.reporter = void 0;
16
16
  this.ctx = void 0;
17
- this.sendEvent = function (payload) {
18
- _this.emitter.emit(EventType.collect, payload);
17
+ this.sendEvent = function (payload, options) {
18
+ _this.emitter.emit(EventType.collect, payload, options);
19
19
  };
20
20
  }
21
21
  var _proto = Client.prototype;
@@ -56,7 +56,7 @@ var Client = /*#__PURE__*/function () {
56
56
  });
57
57
 
58
58
  // collector 收集数据统一 push 到各匹配 processor
59
- this.emitter.on(EventType.collect, function (payload) {
59
+ this.emitter.on(EventType.collect, function (payload, options) {
60
60
  ctx.setRumEvent(payload);
61
61
  for (var _iterator = _createForOfIteratorHelperLoose(processors), _step; !(_step = _iterator()).done;) {
62
62
  var processor = _step.value;
@@ -68,12 +68,12 @@ var Client = /*#__PURE__*/function () {
68
68
  }
69
69
  if (processor.match(ctx)) {
70
70
  var res = processor.process(ctx);
71
- if (res) {
72
- ctx.setRumEvent(res);
73
- }
71
+ // 无论返回值是否为 falsy 都写回 context,
72
+ // 确保 processor 返回 null/undefined(丢弃事件)时能正确生效
73
+ ctx.setRumEvent(res);
74
74
  }
75
75
  }
76
- reporter.report(ctx);
76
+ reporter.report(ctx, options);
77
77
  });
78
78
  collectors.forEach(function (collector) {
79
79
  collector.setup(ctx, _this2.sendEvent);
@@ -1,23 +1,46 @@
1
1
  import { IContext } from '../types/client';
2
- import { RumEvent, IViewData, RumEventBundle } from '../types/rum-event';
2
+ import { RumEvent, IViewData, RumEventBundle, SendEventOptions } from '../types/rum-event';
3
+ /**
4
+ * 抽象上报器,负责事件排队、批量打包与发送。
5
+ *
6
+ * 上报模式(通过 SendEventOptions 控制):
7
+ * - 默认:事件进入队列,等待定时器或队列满后批量 flush
8
+ * - immediate:事件进入队列后立即 flush 整个队列
9
+ * - single:事件不进队列,独立打包立即上报
10
+ */
3
11
  export default abstract class Reporter {
4
12
  name: string;
5
- /**
6
- * 新上报事件优先检测队列是否超过 50 条,如果超过立即 report。
7
- * 如果没超过则检测 FlushTime 内有无新上报事件,如果有新事件,重置 timer,无则 report
8
- */
9
13
  protected eventQueue: RumEvent[];
10
14
  protected ctx: IContext;
11
15
  private timer;
12
16
  private _init;
13
17
  getReportCfg(): import("../types/client").IReportConfig;
14
- report(ctx: IContext): void;
18
+ /**
19
+ * 上报入口,根据 options 决定上报策略:
20
+ * - single: 当前事件独立打包立即发送,不影响队列
21
+ * - immediate: 当前事件入队后立即 flush 整个队列
22
+ * - 默认: 当前事件入队,等待定时器或队列满后批量 flush
23
+ */
24
+ report(ctx: IContext, options?: SendEventOptions): void;
25
+ /**
26
+ * 当前事件独立打包上报,不进入批量队列,不重置队列定时器
27
+ */
28
+ private flushSingleEvent;
15
29
  private pushToQueue;
16
30
  /**
17
31
  * eventQueue 提取最新 View 作为公共 View,event 中不同 View Event 保留 View id
18
32
  */
19
33
  protected flushEventQueue(): void;
20
- mergeEvent(ctx: IContext, events: RumEvent[], view: IViewData): void;
34
+ /**
35
+ * 按 session_id 分组事件并分别打包上报:
36
+ * - 当前 session 的事件统一打包
37
+ * - 非当前 session 的事件按各自 session_id 单独打包
38
+ */
39
+ protected dispatchEvents(ctx: IContext, events: RumEvent[], view: IViewData): void;
40
+ /**
41
+ * 构建 bundle 并发送请求
42
+ */
43
+ private buildAndSend;
21
44
  private tryRequest;
22
45
  /**
23
46
  * 接口请求由各平台 sdk reporter 实现
@@ -4,19 +4,24 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
4
  import { logger } from '../monitor/logger';
5
5
  import { RumEventType } from '../types/rum-event';
6
6
  import { getErrorID } from '../utils/exception';
7
- import { isNumber, isObject, isString } from "../utils/is";
8
- import { verifyProperties } from "../utils/verify";
9
- import { getUrlParams } from "../utils/url";
7
+ import { isNumber, isObject, isString } from '../utils/is';
8
+ import { verifyProperties } from '../utils/verify';
9
+ import { getUrlParams } from '../utils/url';
10
10
  var FLUSH_TIME = 3000;
11
11
  var MAX_EVENT_COUNT = 20;
12
12
  var attrs = ['app', 'user', 'device', 'os', 'geo', 'isp', 'net', 'properties'];
13
+
14
+ /**
15
+ * 抽象上报器,负责事件排队、批量打包与发送。
16
+ *
17
+ * 上报模式(通过 SendEventOptions 控制):
18
+ * - 默认:事件进入队列,等待定时器或队列满后批量 flush
19
+ * - immediate:事件进入队列后立即 flush 整个队列
20
+ * - single:事件不进队列,独立打包立即上报
21
+ */
13
22
  var Reporter = /*#__PURE__*/function () {
14
23
  function Reporter() {
15
24
  this.name = 'reporter';
16
- /**
17
- * 新上报事件优先检测队列是否超过 50 条,如果超过立即 report。
18
- * 如果没超过则检测 FlushTime 内有无新上报事件,如果有新事件,重置 timer,无则 report
19
- */
20
25
  this.eventQueue = [];
21
26
  this.ctx = void 0;
22
27
  this.timer = void 0;
@@ -35,30 +40,62 @@ var Reporter = /*#__PURE__*/function () {
35
40
  cfg.maxEventCount = MAX_EVENT_COUNT;
36
41
  }
37
42
  return cfg;
38
- };
39
- _proto.report = function report(ctx) {
43
+ }
44
+
45
+ /**
46
+ * 上报入口,根据 options 决定上报策略:
47
+ * - single: 当前事件独立打包立即发送,不影响队列
48
+ * - immediate: 当前事件入队后立即 flush 整个队列
49
+ * - 默认: 当前事件入队,等待定时器或队列满后批量 flush
50
+ */;
51
+ _proto.report = function report(ctx, options) {
40
52
  var _this = this;
41
53
  this.ctx = ctx;
42
54
  if (!this._init) {
43
55
  this.init(ctx);
44
56
  this._init = true;
45
57
  }
58
+
59
+ // single 优先级最高:当前事件单独上报,不影响队列
60
+ if (options && options.single) {
61
+ this.flushSingleEvent(ctx);
62
+ return;
63
+ }
46
64
  clearTimeout(this.timer);
47
65
  this.pushToQueue();
48
66
  var reportConfig = this.getReportCfg();
49
- // 超过 MaxCount 直接 flush,否则延迟发送
50
- if (this.eventQueue.length >= reportConfig.maxEventCount) {
67
+ if (options && options.immediate || this.eventQueue.length >= reportConfig.maxEventCount) {
51
68
  this.flushEventQueue();
52
69
  } else {
53
70
  this.timer = setTimeout(function () {
54
71
  _this.flushEventQueue();
55
72
  }, reportConfig.flushTime);
56
73
  }
74
+ }
75
+
76
+ /**
77
+ * 当前事件独立打包上报,不进入批量队列,不重置队列定时器
78
+ */;
79
+ _proto.flushSingleEvent = function flushSingleEvent(ctx) {
80
+ var _event$view;
81
+ var session = ctx.session;
82
+ var sampled = session ? session.getSampled() : true;
83
+ if (!sampled) return;
84
+ var event = ctx.getRumEvent();
85
+ if (!event) return;
86
+ var views = ctx.getViews();
87
+ var curView = views[views.length - 1];
88
+ if (((_event$view = event.view) === null || _event$view === void 0 ? void 0 : _event$view.id) === curView.id) {
89
+ delete event.view;
90
+ }
91
+ this.dispatchEvents(ctx, [event], curView);
57
92
  };
58
93
  _proto.pushToQueue = function pushToQueue() {
59
94
  var ctx = this.ctx,
60
95
  eventQueue = this.eventQueue;
61
96
  var event = ctx.getRumEvent();
97
+ // processor 返回 null/undefined 表示丢弃事件,直接跳过
98
+ if (!event) return;
62
99
 
63
100
  // 针对相同 event,做 times 合并
64
101
  // todo,支持其他类型 event 合并
@@ -116,8 +153,8 @@ var Reporter = /*#__PURE__*/function () {
116
153
  views.forEach(function (view) {
117
154
  if (view.id === curView.id) {
118
155
  var events = eventQueue.filter(function (event) {
119
- var _event$view;
120
- return ((_event$view = event.view) === null || _event$view === void 0 ? void 0 : _event$view.id) === view.id;
156
+ var _event$view2;
157
+ return ((_event$view2 = event.view) === null || _event$view2 === void 0 ? void 0 : _event$view2.id) === view.id;
121
158
  });
122
159
  events.forEach(function (event) {
123
160
  delete event.view;
@@ -126,28 +163,49 @@ var Reporter = /*#__PURE__*/function () {
126
163
  });
127
164
  var session = ctx.session;
128
165
  var sampled = session ? session.getSampled() : true;
129
- sampled && this.mergeEvent(ctx, eventQueue, curView);
166
+ sampled && this.dispatchEvents(ctx, eventQueue, curView);
130
167
  this.eventQueue = [];
131
- };
132
- _proto.mergeEvent = function mergeEvent(ctx, events, view) {
168
+ }
169
+
170
+ /**
171
+ * 按 session_id 分组事件并分别打包上报:
172
+ * - 当前 session 的事件统一打包
173
+ * - 非当前 session 的事件按各自 session_id 单独打包
174
+ */;
175
+ _proto.dispatchEvents = function dispatchEvents(ctx, events, view) {
133
176
  var config = ctx.getConfig();
134
177
  var session = ctx.session;
135
178
  var sessionId = session.getSessionId();
136
- // events.forEach(event => {
137
- // if (event.session_id === sessionId) {
138
- // delete event.session_id;
139
- // }
140
- // });
179
+ var currentSessionEvents = [];
180
+ var otherSessionEventsMap = {};
141
181
  for (var i = 0; i < events.length; i++) {
142
182
  var e = events[i];
143
- if (e.session_id === sessionId) {
183
+ if (!e.session_id || e.session_id === sessionId) {
144
184
  delete e.session_id;
185
+ currentSessionEvents.push(e);
145
186
  } else {
146
- events.splice(i, 1);
147
- i--;
187
+ var sid = e.session_id;
188
+ if (!otherSessionEventsMap[sid]) {
189
+ otherSessionEventsMap[sid] = [];
190
+ }
191
+ delete e.session_id;
192
+ otherSessionEventsMap[sid].push(e);
148
193
  }
149
194
  }
150
- if (events.length === 0) return;
195
+ if (currentSessionEvents.length > 0) {
196
+ this.buildAndSend(ctx, config, session, sessionId, currentSessionEvents, view);
197
+ }
198
+ var otherSessionIds = Object.keys(otherSessionEventsMap);
199
+ for (var _i = 0; _i < otherSessionIds.length; _i++) {
200
+ var _sid = otherSessionIds[_i];
201
+ this.buildAndSend(ctx, config, session, _sid, otherSessionEventsMap[_sid], view);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * 构建 bundle 并发送请求
207
+ */;
208
+ _proto.buildAndSend = function buildAndSend(ctx, config, session, sessionId, events, view) {
151
209
  var extend = getUrlParams(config.endpoint);
152
210
  var bundle = _extends({
153
211
  app: {
@@ -1,6 +1,6 @@
1
- import { IClient, IConfiguration } from "../types/client";
2
- import { RumCustomEvent, RumEvent, RumExceptionEvent, RumResourceEvent, RumViewEvent } from "../types/rum-event";
3
- import { IShell } from "../types/shell";
1
+ import { IClient, IConfiguration } from '../types/client';
2
+ import { RumCustomEvent, RumEvent, RumExceptionEvent, RumResourceEvent, RumViewEvent, SendEventOptions } from '../types/rum-event';
3
+ import { IShell } from '../types/shell';
4
4
  export default abstract class Shell implements IShell {
5
5
  client: IClient;
6
6
  constructor(config?: IConfiguration);
@@ -8,7 +8,7 @@ export default abstract class Shell implements IShell {
8
8
  * 初始化
9
9
  */
10
10
  abstract init(configuration: IConfiguration): Promise<Shell> | Shell;
11
- sendEvent(payload: RumEvent): void;
11
+ sendEvent(payload: RumEvent, options?: SendEventOptions): void;
12
12
  /**
13
13
  * get config
14
14
  */
package/es/model/shell.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
- import { RumEventType } from "../types/rum-event";
2
+ import { RumEventType } from '../types/rum-event';
3
3
  import Client from '../model/client';
4
- import { isNumber, isObject } from "../utils/is";
4
+ import { isNumber, isObject } from '../utils/is';
5
5
  var Shell = /*#__PURE__*/function () {
6
6
  function Shell(config) {
7
7
  // private callbacks: Function[] = [];
@@ -35,9 +35,9 @@ var Shell = /*#__PURE__*/function () {
35
35
  * 初始化
36
36
  */
37
37
  var _proto = Shell.prototype;
38
- _proto.sendEvent = function sendEvent(payload) {
38
+ _proto.sendEvent = function sendEvent(payload, options) {
39
39
  if (this.client) {
40
- this.client.sendEvent(payload);
40
+ this.client.sendEvent(payload, options);
41
41
  }
42
42
  }
43
43
 
@@ -64,10 +64,11 @@ var Shell = /*#__PURE__*/function () {
64
64
  event_type: RumEventType.CUSTOM
65
65
  });
66
66
  this.sendEvent(data);
67
- };
67
+ }
68
+
68
69
  /**
69
70
  * 自定义视图上报
70
- */
71
+ */;
71
72
  _proto.sendView = function sendView(payload) {
72
73
  if (!isObject(payload)) {
73
74
  return;
@@ -89,10 +90,11 @@ var Shell = /*#__PURE__*/function () {
89
90
  event_type: RumEventType.VIEW
90
91
  });
91
92
  this.sendEvent(data);
92
- };
93
+ }
94
+
93
95
  /**
94
96
  * 自定义异常上报
95
- */
97
+ */;
96
98
  _proto.sendException = function sendException(payload) {
97
99
  if (!payload.name || !payload.message) {
98
100
  return;
@@ -111,10 +113,11 @@ var Shell = /*#__PURE__*/function () {
111
113
  source: 'custom'
112
114
  });
113
115
  this.sendEvent(data);
114
- };
116
+ }
117
+
115
118
  /**
116
119
  * 自定义资源上报
117
- */
120
+ */;
118
121
  _proto.sendResource = function sendResource(payload) {
119
122
  if (!payload.name || !payload.type || !isNumber(payload.duration)) {
120
123
  return;
@@ -1,7 +1,7 @@
1
1
  import { ICollector } from './collector';
2
2
  import { IProcessor } from './processor';
3
3
  import { IReporter } from './reporter';
4
- import { RumEvent, RumEventBundle, IViewData } from './rum-event';
4
+ import { RumEvent, RumEventBundle, IViewData, SendEventOptions } from './rum-event';
5
5
  import { MatchOption } from '../utils/match';
6
6
  import { IConfigManager } from './config';
7
7
  import { EventEmitter } from '../utils/events';
@@ -119,7 +119,7 @@ export interface IClient {
119
119
  /**
120
120
  * 业务自定义上传数据
121
121
  */
122
- sendEvent(payload: RumEvent): void;
122
+ sendEvent(payload: RumEvent, options?: SendEventOptions): void;
123
123
  /**
124
124
  * 修改运行时上下文
125
125
  */
@@ -1,7 +1,7 @@
1
1
  import { IContext } from './client';
2
- import { RumEvent } from './rum-event';
2
+ import { SendEventFn } from './rum-event';
3
3
  export interface ICollector {
4
4
  name: string;
5
- setup(ctx: IContext, sendEvent: (payload: RumEvent) => void): void;
5
+ setup(ctx: IContext, sendEvent: SendEventFn): void;
6
6
  destroy?(): void;
7
7
  }
@@ -1,8 +1,8 @@
1
1
  import { IContext } from './client';
2
- import { RumEventBundle } from './rum-event';
2
+ import { RumEventBundle, SendEventOptions } from './rum-event';
3
3
  export interface IReporter {
4
4
  name: string;
5
5
  init(ctx: IContext): void;
6
6
  request(ctx: IContext, bundle: RumEventBundle): void;
7
- report(ctx: IContext): void;
7
+ report(ctx: IContext, options?: SendEventOptions): void;
8
8
  }
@@ -7,6 +7,15 @@ export declare enum RumEventType {
7
7
  CUSTOM = "custom",
8
8
  APPLICATION = "application"
9
9
  }
10
+ /** sendEvent 方法的可选配置 */
11
+ export interface SendEventOptions {
12
+ /** 是否立即上报,将当前事件加入队列后立即 flush 整个队列 */
13
+ immediate?: boolean;
14
+ /** 是否单独上报,当前事件不入队列,独立打包发送 */
15
+ single?: boolean;
16
+ }
17
+ /** 采集器向上层发送事件的统一回调签名 */
18
+ export type SendEventFn = (payload: RumEvent, options?: SendEventOptions) => void;
10
19
  export interface BaseObject {
11
20
  [key: string]: BaseObjectValue;
12
21
  }
@@ -9,6 +9,10 @@ export var RumEventType = /*#__PURE__*/function (RumEventType) {
9
9
  return RumEventType;
10
10
  }({});
11
11
 
12
+ /** sendEvent 方法的可选配置 */
13
+
14
+ /** 采集器向上层发送事件的统一回调签名 */
15
+
12
16
  /** 电池信息 */
13
17
 
14
18
  /** 设备信息 */
@@ -1,5 +1,5 @@
1
- import { IConfiguration } from "./client";
2
- import { RumEvent } from "./rum-event";
1
+ import { IConfiguration } from './client';
2
+ import { RumEvent, SendEventOptions } from './rum-event';
3
3
  /**
4
4
  * 每个 sdk shell 导出固定需要实现的基础 api
5
5
  * 对外导出 shell 层, 所有 shell 层模型的 API 设计约定:
@@ -15,7 +15,7 @@ export interface IShell {
15
15
  /**
16
16
  * 自定义上传数据
17
17
  */
18
- sendEvent(payload: RumEvent): void;
18
+ sendEvent(payload: RumEvent, options?: SendEventOptions): void;
19
19
  /**
20
20
  * get config
21
21
  */
package/es/utils/base.js CHANGED
@@ -1,4 +1,5 @@
1
- import { isFunction } from "./is";
1
+ import { isFunction } from './is';
2
+ import { logger } from '../monitor/logger';
2
3
 
3
4
  /**
4
5
  * 劫持函数或原型
@@ -7,19 +8,74 @@ export function interceptFunction(target, name, callback, isPrototype) {
7
8
  if (isPrototype === void 0) {
8
9
  isPrototype = false;
9
10
  }
10
- var registeredMethod = target[name];
11
- var proxyMethod = function proxyMethod() {
12
- var ctx = isPrototype ? this : target;
13
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
14
- args[_key] = arguments[_key];
11
+ try {
12
+ var registeredMethod = target[name];
13
+ var proxyMethod = function proxyMethod() {
14
+ var ctx = isPrototype ? this : target;
15
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
16
+ args[_key] = arguments[_key];
17
+ }
18
+ try {
19
+ // callback 异常隔离:SDK 采集逻辑出错不能中断客户原函数执行
20
+ callback.apply(ctx, args);
21
+ } catch (e) {
22
+ logger.warn('interceptFunction', "callback error in \"" + name + "\"", e);
23
+ }
24
+ if (isFunction(registeredMethod)) {
25
+ return registeredMethod.apply(ctx, args);
26
+ }
27
+ };
28
+ disguiseProxy(proxyMethod, registeredMethod);
29
+ target[name] = proxyMethod;
30
+ } catch (e) {
31
+ // 劫持安装保护:target 属性只读 accessor 或被 freeze 时(strict mode 下抛 TypeError),
32
+ // 上报后静默返回,不中断 SDK 初始化
33
+ logger.warn('interceptFunction', "install proxy failed for \"" + name + "\"", e);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * 伪装代理函数,降低劫持对客户代码的感知:
39
+ * 1. toString 返回原函数源码(String()、模板字符串、console.log、日志序列化等路径可见原函数)
40
+ * 2. name/length 与原函数对齐
41
+ * 3. _rum_intercepted 不可枚举,避免污染 Object.keys / for...in
42
+ * 注意:defineProperty 在部分小程序运行时可能失败,需降级为直接挂载,不能中断劫持主流程
43
+ *
44
+ */
45
+ function disguiseProxy(proxyMethod, originalMethod) {
46
+ try {
47
+ if (isFunction(originalMethod)) {
48
+ Object.defineProperty(proxyMethod, 'toString', {
49
+ value: function value() {
50
+ return originalMethod.toString();
51
+ },
52
+ writable: true,
53
+ configurable: true
54
+ });
55
+ Object.defineProperty(proxyMethod, 'name', {
56
+ value: originalMethod.name,
57
+ configurable: true
58
+ });
59
+ Object.defineProperty(proxyMethod, 'length', {
60
+ value: originalMethod.length,
61
+ configurable: true
62
+ });
15
63
  }
16
- callback.apply(ctx, args);
17
- if (isFunction(registeredMethod)) {
18
- return registeredMethod.apply(ctx, args);
64
+ Object.defineProperty(proxyMethod, '_rum_intercepted', {
65
+ value: originalMethod,
66
+ enumerable: false,
67
+ writable: true,
68
+ configurable: true
69
+ });
70
+ } catch (e) {
71
+ // 降级:defineProperty 不可用时直接挂载,保证 restoreFunction 仍可用
72
+ try {
73
+ proxyMethod._rum_intercepted = originalMethod;
74
+ } catch (err) {
75
+ // 二层保护:strict mode 下对不可扩展对象直接赋值也会抛 TypeError,上报后放弃挂载
76
+ logger.warn('interceptFunction', 'disguiseProxy fallback failed', err);
19
77
  }
20
- };
21
- proxyMethod._rum_intercepted = registeredMethod;
22
- target[name] = proxyMethod;
78
+ }
23
79
  }
24
80
 
25
81
  /**
@@ -147,72 +203,6 @@ export function delay(func, wait) {
147
203
  return setTimeout.apply(void 0, [func, +wait || 0].concat(args));
148
204
  }
149
205
 
150
- // // 类型匹配正则
151
- // const TYPE_REG = /^\[object ([a-z]*)\]$/;
152
- // /**
153
- // * @desc 需要严格区分object和array
154
- // * @param obj 任意对象
155
- // */
156
- // export function getType(obj: any) {
157
- // let type = Object.prototype.toString.call(obj);
158
- // type = type.toLowerCase() || '';
159
- // const arr = type.match(TYPE_REG);
160
- // return arr?.[1];
161
- // }
162
- //
163
- // /**
164
- // * @desc 将字符串转换成正则表达式
165
- // * @param str 任意字符串
166
- // * 注意,这里只支持 /xxx/ 这种格式,不支持修饰符,例如 /xxx/ig
167
- // */
168
- // export function transStrToReg(str: string) {
169
- // if (getType(str) !== 'string') return str;
170
- // if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') {
171
- // return new RegExp(str.substr(1, str.length - 2));
172
- // }
173
- // return str;
174
- // }
175
- //
176
- // /**
177
- // *
178
- // * @param config 配置
179
- // * @param keys 需要转换的字段
180
- // */
181
- // export function toRegFormat(config: any, keys: any[]) {
182
- // if (getType(keys) === 'string') {
183
- // keys = [keys];
184
- // }
185
- // for (let i = 0, len = keys.length; i < len; i++) {
186
- // const paths = keys[i].split('.');
187
- // const lastIndex = paths.length - 1;
188
- // if (!config) break;
189
- // let tmp = config;
190
- // for (let j = 0, jlen = paths.length; j < jlen; j++) {
191
- // if (paths[j] === '[]') {
192
- // if (getType(tmp) === 'array') {
193
- // let lastPath = paths.splice(j + 1);
194
- // lastPath = lastPath.join('.');
195
- // for (let x = 0, xlen = tmp.length; x < xlen; x++) {
196
- // toRegFormat(tmp[x], lastPath);
197
- // }
198
- // }
199
- // break;
200
- // }
201
- // // 最后一层了,需要往前一层才能修改对象
202
- // if (lastIndex === j && getType(tmp[paths[j]]) === 'string') {
203
- // tmp[paths[j]] = transStrToReg(tmp[paths[j]]);
204
- // break;
205
- // }
206
- // tmp = tmp[paths[j]];
207
- // if (!tmp) break;
208
- // }
209
- // if (!tmp || getType(tmp) !== 'array') continue;
210
- // for (let j = 0, jlen = tmp.length; j < jlen; j++) {
211
- // tmp[j] = transStrToReg(tmp[j]);
212
- // }
213
- // }
214
- // }
215
-
216
206
  /**
217
207
  * 解析正则表达式字符串,支持 /pattern/flags 格式
218
208
  * @param str 正则表达式字符串,如 "/a/ig" 或 "a"
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 资源清理组:统一管理多个清理函数的生命周期
3
+ *
4
+ * 用于收敛采集器中常见的 cleanup 数组模式,
5
+ * 避免每个采集器重复实现 push + forEach 逻辑
6
+ */
7
+ export declare function createDisposableGroup(): {
8
+ /** 注册一个或多个清理函数 */
9
+ add(...fns: Array<() => void>): void;
10
+ /** 执行所有清理函数并重置 */
11
+ dispose(): void;
12
+ };