@antglobal/rlog-sdk 0.0.1755855517-dev.11 → 0.0.1755855517-dev.13
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/dist/esm/lib/api.d.ts +10 -0
- package/dist/esm/lib/api.d.ts.map +1 -1
- package/dist/esm/lib/api.js +47 -31
- package/dist/esm/lib/config.d.ts +1 -0
- package/dist/esm/lib/config.d.ts.map +1 -1
- package/dist/esm/lib/config.js +2 -0
- package/dist/esm/lib/error.d.ts.map +1 -1
- package/dist/esm/lib/error.js +65 -138
- package/dist/esm/lib/init.d.ts.map +1 -1
- package/dist/esm/lib/init.js +14 -7
- package/dist/esm/lib/net.d.ts +1 -0
- package/dist/esm/lib/net.d.ts.map +1 -1
- package/dist/esm/lib/net.js +33 -2
- package/dist/esm/lib/router-monitor.d.ts.map +1 -1
- package/dist/esm/lib/router-monitor.js +100 -0
- package/dist/esm/lib/upload-worker-manager.d.ts +37 -0
- package/dist/esm/lib/upload-worker-manager.d.ts.map +1 -0
- package/dist/esm/lib/upload-worker-manager.js +482 -0
- package/dist/esm/lib/upload-worker.d.ts +59 -0
- package/dist/esm/lib/upload-worker.d.ts.map +1 -0
- package/dist/esm/lib/upload-worker.js +26 -0
- package/dist/esm/lib/uploader.d.ts +5 -0
- package/dist/esm/lib/uploader.d.ts.map +1 -1
- package/dist/esm/lib/uploader.js +58 -10
- package/dist/lib/lib/api.d.ts +10 -0
- package/dist/lib/lib/api.d.ts.map +1 -1
- package/dist/lib/lib/api.js +48 -31
- package/dist/lib/lib/config.d.ts +1 -0
- package/dist/lib/lib/config.d.ts.map +1 -1
- package/dist/lib/lib/config.js +2 -0
- package/dist/lib/lib/error.d.ts.map +1 -1
- package/dist/lib/lib/error.js +65 -138
- package/dist/lib/lib/init.d.ts.map +1 -1
- package/dist/lib/lib/init.js +13 -6
- package/dist/lib/lib/net.d.ts +1 -0
- package/dist/lib/lib/net.d.ts.map +1 -1
- package/dist/lib/lib/net.js +33 -1
- package/dist/lib/lib/router-monitor.d.ts.map +1 -1
- package/dist/lib/lib/router-monitor.js +100 -0
- package/dist/lib/lib/upload-worker-manager.d.ts +37 -0
- package/dist/lib/lib/upload-worker-manager.d.ts.map +1 -0
- package/dist/lib/lib/upload-worker-manager.js +491 -0
- package/dist/lib/lib/upload-worker.d.ts +59 -0
- package/dist/lib/lib/upload-worker.d.ts.map +1 -0
- package/dist/lib/lib/upload-worker.js +32 -0
- package/dist/lib/lib/uploader.d.ts +5 -0
- package/dist/lib/lib/uploader.d.ts.map +1 -1
- package/dist/lib/lib/uploader.js +58 -10
- package/dist/rlog-sdk.min.js +1 -1
- package/package.json +3 -2
|
@@ -170,6 +170,91 @@ export function updateRouterConfig(config) {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* 处理页面加载事件
|
|
175
|
+
*/
|
|
176
|
+
function handlePageLoad() {
|
|
177
|
+
try {
|
|
178
|
+
record.addCustomEvent('page-lifecycle', {
|
|
179
|
+
type: 'PAGE_LOAD',
|
|
180
|
+
url: window.location.href,
|
|
181
|
+
timestamp: Date.now()
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
logger.warn('Failed to add PAGE_LOAD event (recording may have stopped)', error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 处理页面卸载事件
|
|
190
|
+
*/
|
|
191
|
+
function handlePageUnload() {
|
|
192
|
+
try {
|
|
193
|
+
record.addCustomEvent('page-lifecycle', {
|
|
194
|
+
type: 'PAGE_UNLOAD',
|
|
195
|
+
url: window.location.href,
|
|
196
|
+
timestamp: Date.now()
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
logger.warn('Failed to add PAGE_UNLOAD event (recording may have stopped)', error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 处理页面隐藏事件(移动端友好,替代 beforeunload)
|
|
205
|
+
* pagehide 在移动端浏览器中比 beforeunload 更可靠
|
|
206
|
+
*/
|
|
207
|
+
function handlePageHide(event) {
|
|
208
|
+
try {
|
|
209
|
+
record.addCustomEvent('page-lifecycle', {
|
|
210
|
+
type: 'PAGE_HIDE',
|
|
211
|
+
url: window.location.href,
|
|
212
|
+
timestamp: Date.now(),
|
|
213
|
+
persisted: event.persisted // 页面是否进入 bfcache
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.warn('Failed to add PAGE_HIDE event (recording may have stopped)', error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 处理页面显示事件(移动端友好,支持 bfcache 恢复检测)
|
|
222
|
+
* pageshow 在页面从 bfcache 恢复时也会触发
|
|
223
|
+
*/
|
|
224
|
+
function handlePageShow(event) {
|
|
225
|
+
try {
|
|
226
|
+
record.addCustomEvent('page-lifecycle', {
|
|
227
|
+
type: 'PAGE_SHOW',
|
|
228
|
+
url: window.location.href,
|
|
229
|
+
timestamp: Date.now(),
|
|
230
|
+
persisted: event.persisted // 是否从 bfcache 恢复
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// 如果是从 bfcache 恢复,重置路由计时器
|
|
234
|
+
if (event.persisted) {
|
|
235
|
+
routeStartTime = Date.now();
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
logger.warn('Failed to add PAGE_SHOW event (recording may have stopped)', error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 处理页面可见性变化事件(移动端切换应用/标签页)
|
|
244
|
+
*/
|
|
245
|
+
function handleVisibilityChange() {
|
|
246
|
+
try {
|
|
247
|
+
var isHidden = document.visibilityState === 'hidden';
|
|
248
|
+
record.addCustomEvent('page-lifecycle', {
|
|
249
|
+
type: isHidden ? 'PAGE_VISIBILITY_HIDDEN' : 'PAGE_VISIBILITY_VISIBLE',
|
|
250
|
+
url: window.location.href,
|
|
251
|
+
timestamp: Date.now()
|
|
252
|
+
});
|
|
253
|
+
} catch (error) {
|
|
254
|
+
logger.warn('Failed to add visibility change event (recording may have stopped)', error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
173
258
|
/**
|
|
174
259
|
* 初始化路由监控
|
|
175
260
|
*/
|
|
@@ -205,6 +290,16 @@ export function initRouterMonitor(config) {
|
|
|
205
290
|
if (routerConfig.trackHashChange) {
|
|
206
291
|
window.addEventListener('hashchange', handleHashChange);
|
|
207
292
|
}
|
|
293
|
+
|
|
294
|
+
// 监听页面生命周期事件
|
|
295
|
+
window.addEventListener('load', handlePageLoad);
|
|
296
|
+
window.addEventListener('beforeunload', handlePageUnload);
|
|
297
|
+
|
|
298
|
+
// 监听移动端页面生命周期事件
|
|
299
|
+
// pagehide/pageshow 在移动端比 beforeunload 更可靠
|
|
300
|
+
window.addEventListener('pagehide', handlePageHide);
|
|
301
|
+
window.addEventListener('pageshow', handlePageShow);
|
|
302
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
208
303
|
isInitialized = true;
|
|
209
304
|
}
|
|
210
305
|
|
|
@@ -243,6 +338,11 @@ export function stopRouterMonitor() {
|
|
|
243
338
|
if (typeof window !== 'undefined') {
|
|
244
339
|
window.removeEventListener('popstate', handlePopState);
|
|
245
340
|
window.removeEventListener('hashchange', handleHashChange);
|
|
341
|
+
window.removeEventListener('load', handlePageLoad);
|
|
342
|
+
window.removeEventListener('beforeunload', handlePageUnload);
|
|
343
|
+
window.removeEventListener('pagehide', handlePageHide);
|
|
344
|
+
window.removeEventListener('pageshow', handlePageShow);
|
|
345
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
246
346
|
}
|
|
247
347
|
|
|
248
348
|
// 重置状态
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload Worker 管理器
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - Worker 生命周期管理(创建、销毁、降级)
|
|
6
|
+
* - 将 Worker 脚本以 inline Blob 方式加载
|
|
7
|
+
* - 封装 postMessage/onmessage 通信
|
|
8
|
+
* - 处理 Worker 回传的消息,调用对应的主线程逻辑
|
|
9
|
+
* - 提供与现有 uploader.ts 相同的对外 API
|
|
10
|
+
*/
|
|
11
|
+
import type { WorkerConfig } from './upload-worker';
|
|
12
|
+
/**
|
|
13
|
+
* 尝试初始化 Worker 模式
|
|
14
|
+
* @returns 是否成功启用 Worker 模式
|
|
15
|
+
*/
|
|
16
|
+
export declare function initWorkerUpload(serv: string, appId: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 销毁 Worker
|
|
19
|
+
*/
|
|
20
|
+
export declare function destroyWorker(): void;
|
|
21
|
+
/**
|
|
22
|
+
* 是否正在使用 Worker 模式
|
|
23
|
+
*/
|
|
24
|
+
export declare function isUsingWorkerMode(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 设置错误模式上传状态(Worker 模式)
|
|
27
|
+
*/
|
|
28
|
+
export declare function setWorkerErrorModeUploading(uploading: boolean): void;
|
|
29
|
+
/**
|
|
30
|
+
* 设置错误模式上传时间窗口起点(Worker 模式)
|
|
31
|
+
*/
|
|
32
|
+
export declare function setWorkerErrorModeWindowStart(startTime: number): void;
|
|
33
|
+
/**
|
|
34
|
+
* 更新 Worker 配置
|
|
35
|
+
*/
|
|
36
|
+
export declare function updateWorkerConfig(config: Partial<WorkerConfig>): void;
|
|
37
|
+
//# sourceMappingURL=upload-worker-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-worker-manager.d.ts","sourceRoot":"","sources":["../../../src/lib/upload-worker-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAgC,MAAM,iBAAiB,CAAC;AAkTlF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CA8BrE;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAkBpC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAQpE;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAErE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAKtE"}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
|
|
3
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
4
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
5
|
+
/**
|
|
6
|
+
* Upload Worker 管理器
|
|
7
|
+
*
|
|
8
|
+
* 职责:
|
|
9
|
+
* - Worker 生命周期管理(创建、销毁、降级)
|
|
10
|
+
* - 将 Worker 脚本以 inline Blob 方式加载
|
|
11
|
+
* - 封装 postMessage/onmessage 通信
|
|
12
|
+
* - 处理 Worker 回传的消息,调用对应的主线程逻辑
|
|
13
|
+
* - 提供与现有 uploader.ts 相同的对外 API
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { getUploadWorkerCode } from "./upload-worker";
|
|
17
|
+
import { storage } from "./storage-manager";
|
|
18
|
+
import { getDeviceId } from "./utils";
|
|
19
|
+
import { getConfig } from "./config";
|
|
20
|
+
import { buildQueryParams } from "./api";
|
|
21
|
+
import { getCustomHeaders } from "./net";
|
|
22
|
+
import { record } from 'rrweb';
|
|
23
|
+
import { cancelRlog } from "./init";
|
|
24
|
+
import logger from "./logger";
|
|
25
|
+
|
|
26
|
+
// Worker 实例
|
|
27
|
+
var worker = null;
|
|
28
|
+
|
|
29
|
+
// Worker 是否就绪
|
|
30
|
+
var workerReady = false;
|
|
31
|
+
|
|
32
|
+
// 是否正在使用 Worker 模式
|
|
33
|
+
var isWorkerMode = false;
|
|
34
|
+
|
|
35
|
+
// 当前配置缓存
|
|
36
|
+
var currentServ = '';
|
|
37
|
+
var currentAppId = '';
|
|
38
|
+
|
|
39
|
+
// 错误模式状态
|
|
40
|
+
var errorModeUploading = false;
|
|
41
|
+
var errorModeWindowStart = 0;
|
|
42
|
+
|
|
43
|
+
// 上传锁(Worker 模式下用于防止并发数据读取)
|
|
44
|
+
var isFetchingBatch = false;
|
|
45
|
+
|
|
46
|
+
// batchId 计数器
|
|
47
|
+
var batchIdCounter = 0;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 生成唯一的 batchId
|
|
51
|
+
*/
|
|
52
|
+
function generateBatchId() {
|
|
53
|
+
return "batch_".concat(Date.now(), "_").concat(++batchIdCounter);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 创建内联 Worker
|
|
58
|
+
* 使用 Blob URL 方式,避免额外的文件加载问题
|
|
59
|
+
*/
|
|
60
|
+
function createInlineWorker() {
|
|
61
|
+
try {
|
|
62
|
+
// 检查 Worker 是否可用
|
|
63
|
+
if (typeof Worker === 'undefined') {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
var workerCode = getUploadWorkerCode();
|
|
67
|
+
var blob = new Blob([workerCode], {
|
|
68
|
+
type: 'application/javascript'
|
|
69
|
+
});
|
|
70
|
+
var url = URL.createObjectURL(blob);
|
|
71
|
+
var w = new Worker(url);
|
|
72
|
+
// 创建后立即释放 Blob URL,Worker 已经加载了代码
|
|
73
|
+
URL.revokeObjectURL(url);
|
|
74
|
+
return w;
|
|
75
|
+
} catch (e) {
|
|
76
|
+
logger.warn('Failed to create inline Worker, will fallback to main thread:', e);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 处理 Worker 发来的消息
|
|
83
|
+
*/
|
|
84
|
+
function handleWorkerMessage(e) {
|
|
85
|
+
var msg = e.data;
|
|
86
|
+
if (!msg || !msg.type) return;
|
|
87
|
+
switch (msg.type) {
|
|
88
|
+
case 'READY':
|
|
89
|
+
workerReady = true;
|
|
90
|
+
logger.log('Upload Worker is ready');
|
|
91
|
+
break;
|
|
92
|
+
case 'REQUEST_BATCH':
|
|
93
|
+
handleRequestBatch(msg.mode);
|
|
94
|
+
break;
|
|
95
|
+
case 'UPLOAD_SUCCESS':
|
|
96
|
+
handleUploadSuccess(msg.batchId);
|
|
97
|
+
break;
|
|
98
|
+
case 'UPLOAD_FAILURE':
|
|
99
|
+
handleUploadFailure(msg.batchId, msg.error, msg.failureCount);
|
|
100
|
+
break;
|
|
101
|
+
case 'MAX_RETRY_REACHED':
|
|
102
|
+
handleMaxRetryReached(msg.batchId, msg.error);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 处理 Worker 请求数据批次
|
|
109
|
+
* Worker 内部定时器触发,主线程从 storage 读取数据后发给 Worker
|
|
110
|
+
*/
|
|
111
|
+
function handleRequestBatch(_x) {
|
|
112
|
+
return _handleRequestBatch.apply(this, arguments);
|
|
113
|
+
} // 待处理的批次元数据
|
|
114
|
+
function _handleRequestBatch() {
|
|
115
|
+
_handleRequestBatch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(mode) {
|
|
116
|
+
var currentConfig, isErrorMode, events, windowStart, config, consumeOnly, queryParams, customHeaders, batchId;
|
|
117
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
118
|
+
while (1) switch (_context.prev = _context.next) {
|
|
119
|
+
case 0:
|
|
120
|
+
if (!isFetchingBatch) {
|
|
121
|
+
_context.next = 2;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
return _context.abrupt("return");
|
|
125
|
+
case 2:
|
|
126
|
+
// 检查采集总开关
|
|
127
|
+
currentConfig = getConfig();
|
|
128
|
+
if (currentConfig.enable) {
|
|
129
|
+
_context.next = 6;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
logger.log('SDK is disabled by config, skipping batch request');
|
|
133
|
+
return _context.abrupt("return");
|
|
134
|
+
case 6:
|
|
135
|
+
isErrorMode = currentConfig.captureMode === 'error'; // 错误模式下,如果未处于 uploading 状态,跳过
|
|
136
|
+
if (!(isErrorMode && !errorModeUploading)) {
|
|
137
|
+
_context.next = 9;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
return _context.abrupt("return");
|
|
141
|
+
case 9:
|
|
142
|
+
_context.prev = 9;
|
|
143
|
+
isFetchingBatch = true;
|
|
144
|
+
windowStart = 0;
|
|
145
|
+
if (!isErrorMode) {
|
|
146
|
+
_context.next = 20;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
// 错误模式:按时间范围提取事件
|
|
150
|
+
windowStart = errorModeWindowStart > 0 ? errorModeWindowStart : Date.now();
|
|
151
|
+
_context.next = 16;
|
|
152
|
+
return storage.pullByTimeRange(getDeviceId(), windowStart, Date.now());
|
|
153
|
+
case 16:
|
|
154
|
+
events = _context.sent;
|
|
155
|
+
errorModeWindowStart = 0;
|
|
156
|
+
_context.next = 23;
|
|
157
|
+
break;
|
|
158
|
+
case 20:
|
|
159
|
+
_context.next = 22;
|
|
160
|
+
return storage.pull(getDeviceId(), 100);
|
|
161
|
+
case 22:
|
|
162
|
+
events = _context.sent;
|
|
163
|
+
case 23:
|
|
164
|
+
if (!(!events || events.length === 0)) {
|
|
165
|
+
_context.next = 26;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
isFetchingBatch = false;
|
|
169
|
+
return _context.abrupt("return");
|
|
170
|
+
case 26:
|
|
171
|
+
// 检查是否只消费不上报
|
|
172
|
+
config = getConfig();
|
|
173
|
+
consumeOnly = sessionStorage.getItem('RLOG_CONSUME_ONLY') === 'YES' || config.consumeOnly;
|
|
174
|
+
if (!consumeOnly) {
|
|
175
|
+
_context.next = 32;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
logger.log('Consume only mode, skip upload');
|
|
179
|
+
isFetchingBatch = false;
|
|
180
|
+
return _context.abrupt("return");
|
|
181
|
+
case 32:
|
|
182
|
+
// 构建查询参数
|
|
183
|
+
queryParams = buildQueryParams({
|
|
184
|
+
appId: currentAppId,
|
|
185
|
+
deviceId: getDeviceId(),
|
|
186
|
+
data: events
|
|
187
|
+
}); // 获取自定义请求头
|
|
188
|
+
customHeaders = getCustomHeaders(); // 生成 batchId 并记录元数据(用于成功/失败回调)
|
|
189
|
+
batchId = generateBatchId(); // 将批次元数据存储起来,供回调使用
|
|
190
|
+
pendingBatches.set(batchId, {
|
|
191
|
+
events: events,
|
|
192
|
+
isErrorMode: isErrorMode,
|
|
193
|
+
windowStart: windowStart
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// 发送数据给 Worker 执行上传
|
|
197
|
+
sendToWorker({
|
|
198
|
+
type: 'UPLOAD_BATCH',
|
|
199
|
+
batchId: batchId,
|
|
200
|
+
events: events,
|
|
201
|
+
queryParams: queryParams,
|
|
202
|
+
customHeaders: customHeaders
|
|
203
|
+
});
|
|
204
|
+
isFetchingBatch = false;
|
|
205
|
+
_context.next = 44;
|
|
206
|
+
break;
|
|
207
|
+
case 40:
|
|
208
|
+
_context.prev = 40;
|
|
209
|
+
_context.t0 = _context["catch"](9);
|
|
210
|
+
isFetchingBatch = false;
|
|
211
|
+
logger.error('Error fetching batch for Worker:', _context.t0);
|
|
212
|
+
case 44:
|
|
213
|
+
case "end":
|
|
214
|
+
return _context.stop();
|
|
215
|
+
}
|
|
216
|
+
}, _callee, null, [[9, 40]]);
|
|
217
|
+
}));
|
|
218
|
+
return _handleRequestBatch.apply(this, arguments);
|
|
219
|
+
}
|
|
220
|
+
var pendingBatches = new Map();
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 处理上传成功
|
|
224
|
+
*/
|
|
225
|
+
function handleUploadSuccess(_x2) {
|
|
226
|
+
return _handleUploadSuccess.apply(this, arguments);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 处理上传失败(未达到最大重试次数)
|
|
230
|
+
*/
|
|
231
|
+
function _handleUploadSuccess() {
|
|
232
|
+
_handleUploadSuccess = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(batchId) {
|
|
233
|
+
var batch, lastEvent;
|
|
234
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
235
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
236
|
+
case 0:
|
|
237
|
+
batch = pendingBatches.get(batchId);
|
|
238
|
+
if (batch) {
|
|
239
|
+
_context2.next = 3;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
return _context2.abrupt("return");
|
|
243
|
+
case 3:
|
|
244
|
+
pendingBatches.delete(batchId);
|
|
245
|
+
|
|
246
|
+
// 错误模式:上传成功后移除已上传的事件
|
|
247
|
+
if (!(batch.isErrorMode && batch.windowStart > 0 && batch.events.length > 0)) {
|
|
248
|
+
_context2.next = 9;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
_context2.next = 7;
|
|
252
|
+
return storage.removeByTimeRange(getDeviceId(), batch.windowStart);
|
|
253
|
+
case 7:
|
|
254
|
+
// 更新窗口起点为最后一个事件的时间戳
|
|
255
|
+
lastEvent = batch.events[batch.events.length - 1];
|
|
256
|
+
if (lastEvent && lastEvent.timestamp) {
|
|
257
|
+
errorModeWindowStart = lastEvent.timestamp;
|
|
258
|
+
}
|
|
259
|
+
case 9:
|
|
260
|
+
case "end":
|
|
261
|
+
return _context2.stop();
|
|
262
|
+
}
|
|
263
|
+
}, _callee2);
|
|
264
|
+
}));
|
|
265
|
+
return _handleUploadSuccess.apply(this, arguments);
|
|
266
|
+
}
|
|
267
|
+
function handleUploadFailure(_x3, _x4, _x5) {
|
|
268
|
+
return _handleUploadFailure.apply(this, arguments);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 处理达到最大重试次数
|
|
272
|
+
*/
|
|
273
|
+
function _handleUploadFailure() {
|
|
274
|
+
_handleUploadFailure = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(batchId, error, failureCount) {
|
|
275
|
+
var batch;
|
|
276
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
277
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
278
|
+
case 0:
|
|
279
|
+
batch = pendingBatches.get(batchId);
|
|
280
|
+
if (batch) {
|
|
281
|
+
_context3.next = 3;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
return _context3.abrupt("return");
|
|
285
|
+
case 3:
|
|
286
|
+
pendingBatches.delete(batchId);
|
|
287
|
+
logger.warn("Upload failed ".concat(failureCount, " time(s), will retry in next cycle:"), error);
|
|
288
|
+
|
|
289
|
+
// 将事件放回存储头部,保持时间戳顺序
|
|
290
|
+
_context3.next = 7;
|
|
291
|
+
return storage.unshiftBatch(getDeviceId(), batch.events);
|
|
292
|
+
case 7:
|
|
293
|
+
case "end":
|
|
294
|
+
return _context3.stop();
|
|
295
|
+
}
|
|
296
|
+
}, _callee3);
|
|
297
|
+
}));
|
|
298
|
+
return _handleUploadFailure.apply(this, arguments);
|
|
299
|
+
}
|
|
300
|
+
function handleMaxRetryReached(_x6, _x7) {
|
|
301
|
+
return _handleMaxRetryReached.apply(this, arguments);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* 向 Worker 发送消息
|
|
305
|
+
*/
|
|
306
|
+
function _handleMaxRetryReached() {
|
|
307
|
+
_handleMaxRetryReached = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(batchId, error) {
|
|
308
|
+
var batch, config, maxRetryCount, customEvent;
|
|
309
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
310
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
311
|
+
case 0:
|
|
312
|
+
batch = pendingBatches.get(batchId);
|
|
313
|
+
if (batch) {
|
|
314
|
+
_context4.next = 3;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
return _context4.abrupt("return");
|
|
318
|
+
case 3:
|
|
319
|
+
pendingBatches.delete(batchId);
|
|
320
|
+
config = getConfig();
|
|
321
|
+
maxRetryCount = config.maxRetryCount || 3;
|
|
322
|
+
logger.warn("Upload failed ".concat(maxRetryCount, " times consecutively, stopping upload"));
|
|
323
|
+
|
|
324
|
+
// 将事件放回存储
|
|
325
|
+
_context4.next = 9;
|
|
326
|
+
return storage.unshiftBatch(getDeviceId(), batch.events);
|
|
327
|
+
case 9:
|
|
328
|
+
// 创建并派发自定义事件
|
|
329
|
+
customEvent = new CustomEvent('rlog-upload-failure', {
|
|
330
|
+
detail: {
|
|
331
|
+
error: error,
|
|
332
|
+
retryCount: maxRetryCount,
|
|
333
|
+
appId: currentAppId,
|
|
334
|
+
deviceId: getDeviceId()
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
if (typeof window !== 'undefined') {
|
|
338
|
+
window.dispatchEvent(customEvent);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 使用 addCustomEvent 记录上传失败事件
|
|
342
|
+
try {
|
|
343
|
+
record.addCustomEvent('rlog-upload-failure', {
|
|
344
|
+
error: error,
|
|
345
|
+
retryCount: maxRetryCount,
|
|
346
|
+
appId: currentAppId,
|
|
347
|
+
deviceId: getDeviceId(),
|
|
348
|
+
timestamp: Date.now()
|
|
349
|
+
});
|
|
350
|
+
} catch (e) {
|
|
351
|
+
logger.warn('Failed to record upload failure event via addCustomEvent:', e);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 停止 Worker
|
|
355
|
+
destroyWorker();
|
|
356
|
+
|
|
357
|
+
// 停止 rrweb 录制
|
|
358
|
+
setTimeout(function () {
|
|
359
|
+
cancelRlog();
|
|
360
|
+
}, 0);
|
|
361
|
+
logger.error('Upload and recording stopped due to repeated failures');
|
|
362
|
+
case 15:
|
|
363
|
+
case "end":
|
|
364
|
+
return _context4.stop();
|
|
365
|
+
}
|
|
366
|
+
}, _callee4);
|
|
367
|
+
}));
|
|
368
|
+
return _handleMaxRetryReached.apply(this, arguments);
|
|
369
|
+
}
|
|
370
|
+
function sendToWorker(command) {
|
|
371
|
+
if (worker && workerReady) {
|
|
372
|
+
worker.postMessage(command);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 处理 Worker 错误
|
|
378
|
+
*/
|
|
379
|
+
function handleWorkerError(e) {
|
|
380
|
+
logger.error('Upload Worker error:', e.message);
|
|
381
|
+
// Worker 出错时销毁,后续由 uploader.ts 降级到主线程
|
|
382
|
+
destroyWorker();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ==================== 对外 API ====================
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 尝试初始化 Worker 模式
|
|
389
|
+
* @returns 是否成功启用 Worker 模式
|
|
390
|
+
*/
|
|
391
|
+
export function initWorkerUpload(serv, appId) {
|
|
392
|
+
currentServ = serv;
|
|
393
|
+
currentAppId = appId;
|
|
394
|
+
|
|
395
|
+
// 尝试创建 Worker
|
|
396
|
+
var w = createInlineWorker();
|
|
397
|
+
if (!w) {
|
|
398
|
+
isWorkerMode = false;
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
worker = w;
|
|
402
|
+
worker.onmessage = handleWorkerMessage;
|
|
403
|
+
worker.onerror = handleWorkerError;
|
|
404
|
+
|
|
405
|
+
// 发送启动指令
|
|
406
|
+
var config = getConfig();
|
|
407
|
+
var startConfig = {
|
|
408
|
+
serv: serv,
|
|
409
|
+
appId: appId,
|
|
410
|
+
deviceId: getDeviceId(),
|
|
411
|
+
uploadInterval: config.uploadInterval,
|
|
412
|
+
maxRetryCount: config.maxRetryCount || 3
|
|
413
|
+
};
|
|
414
|
+
worker.postMessage({
|
|
415
|
+
type: 'START',
|
|
416
|
+
config: startConfig
|
|
417
|
+
});
|
|
418
|
+
isWorkerMode = true;
|
|
419
|
+
logger.log('Upload Worker mode initialized');
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* 销毁 Worker
|
|
425
|
+
*/
|
|
426
|
+
export function destroyWorker() {
|
|
427
|
+
if (worker) {
|
|
428
|
+
try {
|
|
429
|
+
worker.postMessage({
|
|
430
|
+
type: 'STOP'
|
|
431
|
+
});
|
|
432
|
+
worker.terminate();
|
|
433
|
+
} catch (e) {
|
|
434
|
+
// 忽略终止错误
|
|
435
|
+
}
|
|
436
|
+
worker = null;
|
|
437
|
+
}
|
|
438
|
+
workerReady = false;
|
|
439
|
+
isWorkerMode = false;
|
|
440
|
+
isFetchingBatch = false;
|
|
441
|
+
errorModeUploading = false;
|
|
442
|
+
errorModeWindowStart = 0;
|
|
443
|
+
batchIdCounter = 0;
|
|
444
|
+
pendingBatches.clear();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 是否正在使用 Worker 模式
|
|
449
|
+
*/
|
|
450
|
+
export function isUsingWorkerMode() {
|
|
451
|
+
return isWorkerMode && worker !== null;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* 设置错误模式上传状态(Worker 模式)
|
|
456
|
+
*/
|
|
457
|
+
export function setWorkerErrorModeUploading(uploading) {
|
|
458
|
+
errorModeUploading = uploading;
|
|
459
|
+
|
|
460
|
+
// 同步通知 Worker
|
|
461
|
+
sendToWorker({
|
|
462
|
+
type: 'SET_ERROR_MODE_UPLOADING',
|
|
463
|
+
uploading: uploading
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* 设置错误模式上传时间窗口起点(Worker 模式)
|
|
469
|
+
*/
|
|
470
|
+
export function setWorkerErrorModeWindowStart(startTime) {
|
|
471
|
+
errorModeWindowStart = startTime;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 更新 Worker 配置
|
|
476
|
+
*/
|
|
477
|
+
export function updateWorkerConfig(config) {
|
|
478
|
+
sendToWorker({
|
|
479
|
+
type: 'UPDATE_CONFIG',
|
|
480
|
+
config: config
|
|
481
|
+
});
|
|
482
|
+
}
|