@antglobal/rlog-sdk 0.0.1755855517-dev.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -432
- package/dist/esm/index.d.ts +14 -41
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +92 -76
- 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 +58 -35
- package/dist/esm/lib/config.d.ts +14 -16
- package/dist/esm/lib/config.d.ts.map +1 -1
- package/dist/esm/lib/config.js +61 -33
- package/dist/esm/lib/constants.d.ts +89 -0
- package/dist/esm/lib/constants.d.ts.map +1 -0
- package/dist/esm/lib/constants.js +145 -0
- package/dist/esm/lib/drive/indexeddb-adapt.d.ts +12 -0
- package/dist/esm/lib/drive/indexeddb-adapt.d.ts.map +1 -1
- package/dist/esm/lib/drive/indexeddb-adapt.js +190 -20
- package/dist/esm/lib/drive/localstorage-adapt.d.ts +12 -0
- package/dist/esm/lib/drive/localstorage-adapt.d.ts.map +1 -1
- package/dist/esm/lib/drive/localstorage-adapt.js +179 -36
- package/dist/esm/lib/drive/memory-adapt.d.ts +10 -2
- package/dist/esm/lib/drive/memory-adapt.d.ts.map +1 -1
- package/dist/esm/lib/drive/memory-adapt.js +118 -26
- package/dist/esm/lib/drive/safe-storage.d.ts +24 -0
- package/dist/esm/lib/drive/safe-storage.d.ts.map +1 -0
- package/dist/esm/lib/drive/safe-storage.js +96 -0
- package/dist/esm/lib/drive/storage-interface.d.ts +20 -0
- package/dist/esm/lib/drive/storage-interface.d.ts.map +1 -1
- package/dist/esm/lib/error-trigger.d.ts +73 -0
- package/dist/esm/lib/error-trigger.d.ts.map +1 -0
- package/dist/esm/lib/error-trigger.js +162 -0
- package/dist/esm/lib/error.d.ts +9 -0
- package/dist/esm/lib/error.d.ts.map +1 -1
- package/dist/esm/lib/error.js +70 -118
- package/dist/esm/lib/init.d.ts +0 -4
- package/dist/esm/lib/init.d.ts.map +1 -1
- package/dist/esm/lib/init.js +148 -47
- package/dist/esm/lib/logger.d.ts +27 -0
- package/dist/esm/lib/logger.d.ts.map +1 -0
- package/dist/esm/lib/logger.js +79 -0
- package/dist/esm/lib/net.d.ts +11 -0
- package/dist/esm/lib/net.d.ts.map +1 -1
- package/dist/esm/lib/net.js +264 -116
- package/dist/esm/lib/request.d.ts.map +1 -1
- package/dist/esm/lib/request.js +2 -2
- package/dist/esm/lib/router-monitor.d.ts.map +1 -1
- package/dist/esm/lib/router-monitor.js +135 -49
- package/dist/esm/lib/rrweb.d.ts.map +1 -1
- package/dist/esm/lib/rrweb.js +31 -24
- package/dist/esm/lib/storage-manager.d.ts +12 -0
- package/dist/esm/lib/storage-manager.d.ts.map +1 -1
- package/dist/esm/lib/storage-manager.js +129 -46
- package/dist/esm/lib/upload-worker-manager.d.ts +47 -0
- package/dist/esm/lib/upload-worker-manager.d.ts.map +1 -0
- package/dist/esm/lib/upload-worker-manager.js +559 -0
- package/dist/esm/lib/upload-worker.d.ts +58 -0
- package/dist/esm/lib/upload-worker.d.ts.map +1 -0
- package/dist/esm/lib/upload-worker.js +28 -0
- package/dist/esm/lib/uploader.d.ts +43 -0
- package/dist/esm/lib/uploader.d.ts.map +1 -1
- package/dist/esm/lib/uploader.js +464 -102
- package/dist/esm/lib/utils.d.ts +75 -0
- package/dist/esm/lib/utils.d.ts.map +1 -1
- package/dist/esm/lib/utils.js +268 -5
- package/dist/lib/index.d.ts +14 -41
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +81 -63
- 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 +59 -35
- package/dist/lib/lib/config.d.ts +14 -16
- package/dist/lib/lib/config.d.ts.map +1 -1
- package/dist/lib/lib/config.js +63 -33
- package/dist/lib/lib/constants.d.ts +89 -0
- package/dist/lib/lib/constants.d.ts.map +1 -0
- package/dist/lib/lib/constants.js +151 -0
- package/dist/lib/lib/drive/indexeddb-adapt.d.ts +12 -0
- package/dist/lib/lib/drive/indexeddb-adapt.d.ts.map +1 -1
- package/dist/lib/lib/drive/indexeddb-adapt.js +191 -19
- package/dist/lib/lib/drive/localstorage-adapt.d.ts +12 -0
- package/dist/lib/lib/drive/localstorage-adapt.d.ts.map +1 -1
- package/dist/lib/lib/drive/localstorage-adapt.js +179 -36
- package/dist/lib/lib/drive/memory-adapt.d.ts +10 -2
- package/dist/lib/lib/drive/memory-adapt.d.ts.map +1 -1
- package/dist/lib/lib/drive/memory-adapt.js +117 -26
- package/dist/lib/lib/drive/safe-storage.d.ts +24 -0
- package/dist/lib/lib/drive/safe-storage.d.ts.map +1 -0
- package/dist/lib/lib/drive/safe-storage.js +102 -0
- package/dist/lib/lib/drive/storage-interface.d.ts +20 -0
- package/dist/lib/lib/drive/storage-interface.d.ts.map +1 -1
- package/dist/lib/lib/error-trigger.d.ts +73 -0
- package/dist/lib/lib/error-trigger.d.ts.map +1 -0
- package/dist/lib/lib/error-trigger.js +167 -0
- package/dist/lib/lib/error.d.ts +9 -0
- package/dist/lib/lib/error.d.ts.map +1 -1
- package/dist/lib/lib/error.js +73 -118
- package/dist/lib/lib/init.d.ts +0 -4
- package/dist/lib/lib/init.d.ts.map +1 -1
- package/dist/lib/lib/init.js +144 -43
- package/dist/lib/lib/logger.d.ts +27 -0
- package/dist/lib/lib/logger.d.ts.map +1 -0
- package/dist/lib/lib/logger.js +84 -0
- package/dist/lib/lib/net.d.ts +11 -0
- package/dist/lib/lib/net.d.ts.map +1 -1
- package/dist/lib/lib/net.js +268 -117
- package/dist/lib/lib/request.d.ts.map +1 -1
- package/dist/lib/lib/request.js +3 -3
- package/dist/lib/lib/router-monitor.d.ts.map +1 -1
- package/dist/lib/lib/router-monitor.js +136 -49
- package/dist/lib/lib/rrweb.d.ts.map +1 -1
- package/dist/lib/lib/rrweb.js +31 -23
- package/dist/lib/lib/storage-manager.d.ts +12 -0
- package/dist/lib/lib/storage-manager.d.ts.map +1 -1
- package/dist/lib/lib/storage-manager.js +130 -46
- package/dist/lib/lib/upload-worker-manager.d.ts +47 -0
- package/dist/lib/lib/upload-worker-manager.d.ts.map +1 -0
- package/dist/lib/lib/upload-worker-manager.js +570 -0
- package/dist/lib/lib/upload-worker.d.ts +58 -0
- package/dist/lib/lib/upload-worker.d.ts.map +1 -0
- package/dist/lib/lib/upload-worker.js +33 -0
- package/dist/lib/lib/uploader.d.ts +43 -0
- package/dist/lib/lib/uploader.d.ts.map +1 -1
- package/dist/lib/lib/uploader.js +468 -101
- package/dist/lib/lib/utils.d.ts +75 -0
- package/dist/lib/lib/utils.d.ts.map +1 -1
- package/dist/lib/lib/utils.js +276 -6
- package/dist/rlog-sdk.min.js +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,559 @@
|
|
|
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, getStorageKey } from "./utils";
|
|
19
|
+
import { getConfig } from "./config";
|
|
20
|
+
import { buildQueryParams } from "./api";
|
|
21
|
+
import { getCustomHeaders } from "./net";
|
|
22
|
+
import { record } from 'rrweb';
|
|
23
|
+
import logger from "./logger";
|
|
24
|
+
import { STORAGE_KEY_CONSUME_ONLY, STORAGE_VALUE_CONSUME_ONLY, EVENT_UPLOAD_FAILURE, PULL_BATCH_SIZE, WORKER_READY_TIMEOUT } from "./constants";
|
|
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 currentAppId = '';
|
|
37
|
+
|
|
38
|
+
// 错误模式状态
|
|
39
|
+
var errorModeUploading = false;
|
|
40
|
+
var errorModeWindowStart = 0;
|
|
41
|
+
|
|
42
|
+
// 上传锁(Worker 模式下用于防止并发数据读取)
|
|
43
|
+
var isFetchingBatch = false;
|
|
44
|
+
|
|
45
|
+
// batchId 计数器
|
|
46
|
+
var batchIdCounter = 0;
|
|
47
|
+
|
|
48
|
+
// Worker 就绪超时检测
|
|
49
|
+
var workerReadyTimeout = null;
|
|
50
|
+
|
|
51
|
+
// Worker 降级回调(当 Worker 模式失败时调用)
|
|
52
|
+
var onWorkerFallback = null;
|
|
53
|
+
|
|
54
|
+
// 由 init.ts 注入的取消回调,解除循环依赖
|
|
55
|
+
var onCancelCallback = null;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 设置 SDK 取消回调(由 init.ts 在启动上传循环前注入)
|
|
59
|
+
*/
|
|
60
|
+
export function setWorkerCancelCallback(callback) {
|
|
61
|
+
onCancelCallback = callback;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 生成唯一的 batchId
|
|
66
|
+
*/
|
|
67
|
+
function generateBatchId() {
|
|
68
|
+
return "batch_".concat(Date.now(), "_").concat(++batchIdCounter);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 创建内联 Worker
|
|
73
|
+
* 使用 Blob URL 方式,避免额外的文件加载问题
|
|
74
|
+
*/
|
|
75
|
+
function createInlineWorker() {
|
|
76
|
+
try {
|
|
77
|
+
// 检查 Worker 是否可用
|
|
78
|
+
if (typeof Worker === 'undefined') {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
var workerCode = getUploadWorkerCode();
|
|
82
|
+
var blob = new Blob([workerCode], {
|
|
83
|
+
type: 'application/javascript'
|
|
84
|
+
});
|
|
85
|
+
var url = URL.createObjectURL(blob);
|
|
86
|
+
var w = new Worker(url);
|
|
87
|
+
// 创建后立即释放 Blob URL,Worker 已经加载了代码
|
|
88
|
+
URL.revokeObjectURL(url);
|
|
89
|
+
return w;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
logger.warn('Failed to create inline Worker, will fallback to main thread:', e);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 处理 Worker 发来的消息
|
|
98
|
+
*/
|
|
99
|
+
function handleWorkerMessage(e) {
|
|
100
|
+
var msg = e.data;
|
|
101
|
+
if (!msg || !msg.type) {
|
|
102
|
+
logger.warn('Received invalid Worker message:', msg);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
switch (msg.type) {
|
|
106
|
+
case 'READY':
|
|
107
|
+
workerReady = true;
|
|
108
|
+
// 清除超时检测
|
|
109
|
+
if (workerReadyTimeout) {
|
|
110
|
+
clearTimeout(workerReadyTimeout);
|
|
111
|
+
workerReadyTimeout = null;
|
|
112
|
+
}
|
|
113
|
+
// Worker is ready
|
|
114
|
+
break;
|
|
115
|
+
case 'REQUEST_BATCH':
|
|
116
|
+
handleRequestBatch();
|
|
117
|
+
break;
|
|
118
|
+
case 'UPLOAD_SUCCESS':
|
|
119
|
+
handleUploadSuccess(msg.batchId);
|
|
120
|
+
break;
|
|
121
|
+
case 'UPLOAD_FAILURE':
|
|
122
|
+
handleUploadFailure(msg.batchId, msg.error, msg.failureCount);
|
|
123
|
+
break;
|
|
124
|
+
case 'MAX_RETRY_REACHED':
|
|
125
|
+
handleMaxRetryReached(msg.batchId, msg.error);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 处理 Worker 请求数据批次
|
|
132
|
+
* Worker 内部定时器触发,主线程从 storage 读取数据后发给 Worker
|
|
133
|
+
*/
|
|
134
|
+
function handleRequestBatch() {
|
|
135
|
+
return _handleRequestBatch.apply(this, arguments);
|
|
136
|
+
} // 待处理的批次元数据
|
|
137
|
+
function _handleRequestBatch() {
|
|
138
|
+
_handleRequestBatch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
|
|
139
|
+
var currentConfig, isErrorMode, events, windowStart, config, consumeOnly, queryParams, customHeaders, batchId;
|
|
140
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
141
|
+
while (1) switch (_context.prev = _context.next) {
|
|
142
|
+
case 0:
|
|
143
|
+
if (!isFetchingBatch) {
|
|
144
|
+
_context.next = 2;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
return _context.abrupt("return");
|
|
148
|
+
case 2:
|
|
149
|
+
// 检查采集总开关
|
|
150
|
+
currentConfig = getConfig();
|
|
151
|
+
if (currentConfig.enable) {
|
|
152
|
+
_context.next = 5;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
return _context.abrupt("return");
|
|
156
|
+
case 5:
|
|
157
|
+
isErrorMode = currentConfig.captureMode === 'error'; // 错误模式下,如果未处于 uploading 状态,跳过
|
|
158
|
+
if (!(isErrorMode && !errorModeUploading)) {
|
|
159
|
+
_context.next = 8;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
return _context.abrupt("return");
|
|
163
|
+
case 8:
|
|
164
|
+
_context.prev = 8;
|
|
165
|
+
isFetchingBatch = true;
|
|
166
|
+
windowStart = 0;
|
|
167
|
+
if (!isErrorMode) {
|
|
168
|
+
_context.next = 19;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
// 错误模式:按时间范围提取事件
|
|
172
|
+
windowStart = errorModeWindowStart > 0 ? errorModeWindowStart : Date.now();
|
|
173
|
+
_context.next = 15;
|
|
174
|
+
return storage.pullByTimeRange(getStorageKey(), windowStart, Date.now());
|
|
175
|
+
case 15:
|
|
176
|
+
events = _context.sent;
|
|
177
|
+
errorModeWindowStart = 0;
|
|
178
|
+
_context.next = 22;
|
|
179
|
+
break;
|
|
180
|
+
case 19:
|
|
181
|
+
_context.next = 21;
|
|
182
|
+
return storage.pull(getStorageKey(), PULL_BATCH_SIZE);
|
|
183
|
+
case 21:
|
|
184
|
+
events = _context.sent;
|
|
185
|
+
case 22:
|
|
186
|
+
if (!(!events || events.length === 0)) {
|
|
187
|
+
_context.next = 25;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
isFetchingBatch = false;
|
|
191
|
+
return _context.abrupt("return");
|
|
192
|
+
case 25:
|
|
193
|
+
// 检查是否只消费不上报
|
|
194
|
+
config = getConfig();
|
|
195
|
+
consumeOnly = sessionStorage.getItem(STORAGE_KEY_CONSUME_ONLY) === STORAGE_VALUE_CONSUME_ONLY || config.consumeOnly;
|
|
196
|
+
if (!consumeOnly) {
|
|
197
|
+
_context.next = 30;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
isFetchingBatch = false;
|
|
201
|
+
return _context.abrupt("return");
|
|
202
|
+
case 30:
|
|
203
|
+
// 构建查询参数
|
|
204
|
+
queryParams = buildQueryParams({
|
|
205
|
+
appId: currentAppId,
|
|
206
|
+
deviceId: getDeviceId(),
|
|
207
|
+
data: events
|
|
208
|
+
}); // 获取自定义请求头
|
|
209
|
+
customHeaders = getCustomHeaders(); // 生成 batchId 并记录元数据(用于成功/失败回调)
|
|
210
|
+
batchId = generateBatchId(); // 将批次元数据存储起来,供回调使用
|
|
211
|
+
pendingBatches.set(batchId, {
|
|
212
|
+
events: events,
|
|
213
|
+
isErrorMode: isErrorMode,
|
|
214
|
+
windowStart: windowStart
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// 发送数据给 Worker 执行上传
|
|
218
|
+
sendToWorker({
|
|
219
|
+
type: 'UPLOAD_BATCH',
|
|
220
|
+
batchId: batchId,
|
|
221
|
+
events: events,
|
|
222
|
+
queryParams: queryParams,
|
|
223
|
+
customHeaders: customHeaders
|
|
224
|
+
});
|
|
225
|
+
isFetchingBatch = false;
|
|
226
|
+
_context.next = 42;
|
|
227
|
+
break;
|
|
228
|
+
case 38:
|
|
229
|
+
_context.prev = 38;
|
|
230
|
+
_context.t0 = _context["catch"](8);
|
|
231
|
+
isFetchingBatch = false;
|
|
232
|
+
logger.error('Error fetching batch for Worker:', _context.t0);
|
|
233
|
+
case 42:
|
|
234
|
+
case "end":
|
|
235
|
+
return _context.stop();
|
|
236
|
+
}
|
|
237
|
+
}, _callee, null, [[8, 38]]);
|
|
238
|
+
}));
|
|
239
|
+
return _handleRequestBatch.apply(this, arguments);
|
|
240
|
+
}
|
|
241
|
+
var pendingBatches = new Map();
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* 处理上传成功
|
|
245
|
+
*/
|
|
246
|
+
function handleUploadSuccess(_x) {
|
|
247
|
+
return _handleUploadSuccess.apply(this, arguments);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 处理上传失败(未达到最大重试次数)
|
|
251
|
+
*/
|
|
252
|
+
function _handleUploadSuccess() {
|
|
253
|
+
_handleUploadSuccess = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(batchId) {
|
|
254
|
+
var batch, lastEvent;
|
|
255
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
256
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
257
|
+
case 0:
|
|
258
|
+
batch = pendingBatches.get(batchId);
|
|
259
|
+
if (batch) {
|
|
260
|
+
_context2.next = 3;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
return _context2.abrupt("return");
|
|
264
|
+
case 3:
|
|
265
|
+
pendingBatches.delete(batchId);
|
|
266
|
+
_context2.prev = 4;
|
|
267
|
+
if (!(batch.isErrorMode && batch.windowStart > 0 && batch.events.length > 0)) {
|
|
268
|
+
_context2.next = 10;
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
_context2.next = 8;
|
|
272
|
+
return storage.removeByTimeRange(getStorageKey(), batch.windowStart);
|
|
273
|
+
case 8:
|
|
274
|
+
// 更新窗口起点为最后一个事件的时间戳
|
|
275
|
+
lastEvent = batch.events[batch.events.length - 1];
|
|
276
|
+
if (lastEvent && lastEvent.timestamp) {
|
|
277
|
+
errorModeWindowStart = lastEvent.timestamp;
|
|
278
|
+
}
|
|
279
|
+
case 10:
|
|
280
|
+
_context2.next = 15;
|
|
281
|
+
break;
|
|
282
|
+
case 12:
|
|
283
|
+
_context2.prev = 12;
|
|
284
|
+
_context2.t0 = _context2["catch"](4);
|
|
285
|
+
logger.warn('Error in handleUploadSuccess:', _context2.t0);
|
|
286
|
+
case 15:
|
|
287
|
+
case "end":
|
|
288
|
+
return _context2.stop();
|
|
289
|
+
}
|
|
290
|
+
}, _callee2, null, [[4, 12]]);
|
|
291
|
+
}));
|
|
292
|
+
return _handleUploadSuccess.apply(this, arguments);
|
|
293
|
+
}
|
|
294
|
+
function handleUploadFailure(_x2, _x3, _x4) {
|
|
295
|
+
return _handleUploadFailure.apply(this, arguments);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 处理达到最大重试次数
|
|
299
|
+
*/
|
|
300
|
+
function _handleUploadFailure() {
|
|
301
|
+
_handleUploadFailure = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(batchId, error, failureCount) {
|
|
302
|
+
var batch;
|
|
303
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
304
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
305
|
+
case 0:
|
|
306
|
+
batch = pendingBatches.get(batchId);
|
|
307
|
+
if (batch) {
|
|
308
|
+
_context3.next = 3;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
return _context3.abrupt("return");
|
|
312
|
+
case 3:
|
|
313
|
+
pendingBatches.delete(batchId);
|
|
314
|
+
logger.warn("Upload failed ".concat(failureCount, " time(s), will retry in next cycle:"), error);
|
|
315
|
+
_context3.prev = 5;
|
|
316
|
+
_context3.next = 8;
|
|
317
|
+
return storage.unshiftBatch(getStorageKey(), batch.events);
|
|
318
|
+
case 8:
|
|
319
|
+
_context3.next = 13;
|
|
320
|
+
break;
|
|
321
|
+
case 10:
|
|
322
|
+
_context3.prev = 10;
|
|
323
|
+
_context3.t0 = _context3["catch"](5);
|
|
324
|
+
logger.warn('Failed to push events back to storage:', _context3.t0);
|
|
325
|
+
case 13:
|
|
326
|
+
case "end":
|
|
327
|
+
return _context3.stop();
|
|
328
|
+
}
|
|
329
|
+
}, _callee3, null, [[5, 10]]);
|
|
330
|
+
}));
|
|
331
|
+
return _handleUploadFailure.apply(this, arguments);
|
|
332
|
+
}
|
|
333
|
+
function handleMaxRetryReached(_x5, _x6) {
|
|
334
|
+
return _handleMaxRetryReached.apply(this, arguments);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* 向 Worker 发送消息
|
|
338
|
+
*/
|
|
339
|
+
function _handleMaxRetryReached() {
|
|
340
|
+
_handleMaxRetryReached = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(batchId, error) {
|
|
341
|
+
var batch, config, maxRetryCount, customEvent;
|
|
342
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
343
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
344
|
+
case 0:
|
|
345
|
+
batch = pendingBatches.get(batchId);
|
|
346
|
+
if (batch) {
|
|
347
|
+
_context4.next = 3;
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
return _context4.abrupt("return");
|
|
351
|
+
case 3:
|
|
352
|
+
pendingBatches.delete(batchId);
|
|
353
|
+
config = getConfig();
|
|
354
|
+
maxRetryCount = config.maxRetryCount || 3;
|
|
355
|
+
logger.warn("Upload failed ".concat(maxRetryCount, " times consecutively, stopping upload"));
|
|
356
|
+
|
|
357
|
+
// 将事件放回存储
|
|
358
|
+
_context4.next = 9;
|
|
359
|
+
return storage.unshiftBatch(getStorageKey(), batch.events);
|
|
360
|
+
case 9:
|
|
361
|
+
// 创建并派发自定义事件
|
|
362
|
+
customEvent = new CustomEvent(EVENT_UPLOAD_FAILURE, {
|
|
363
|
+
detail: {
|
|
364
|
+
error: error,
|
|
365
|
+
retryCount: maxRetryCount,
|
|
366
|
+
appId: currentAppId,
|
|
367
|
+
deviceId: getDeviceId()
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
if (typeof window !== 'undefined') {
|
|
371
|
+
window.dispatchEvent(customEvent);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 使用 addCustomEvent 记录上传失败事件
|
|
375
|
+
try {
|
|
376
|
+
record.addCustomEvent(EVENT_UPLOAD_FAILURE, {
|
|
377
|
+
error: error,
|
|
378
|
+
retryCount: maxRetryCount,
|
|
379
|
+
appId: currentAppId,
|
|
380
|
+
deviceId: getDeviceId(),
|
|
381
|
+
timestamp: Date.now()
|
|
382
|
+
});
|
|
383
|
+
} catch (e) {
|
|
384
|
+
logger.warn('Failed to record upload failure event via addCustomEvent:', e);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// 停止 Worker
|
|
388
|
+
destroyWorker();
|
|
389
|
+
|
|
390
|
+
// 停止 rrweb 录制(通过回调避免循环依赖)
|
|
391
|
+
if (onCancelCallback) {
|
|
392
|
+
setTimeout(function () {
|
|
393
|
+
try {
|
|
394
|
+
var _onCancelCallback;
|
|
395
|
+
(_onCancelCallback = onCancelCallback) === null || _onCancelCallback === void 0 || _onCancelCallback();
|
|
396
|
+
} catch (error) {
|
|
397
|
+
logger.error('Error in cancel callback:', error);
|
|
398
|
+
}
|
|
399
|
+
}, 0);
|
|
400
|
+
}
|
|
401
|
+
logger.error('Upload and recording stopped due to repeated failures');
|
|
402
|
+
case 15:
|
|
403
|
+
case "end":
|
|
404
|
+
return _context4.stop();
|
|
405
|
+
}
|
|
406
|
+
}, _callee4);
|
|
407
|
+
}));
|
|
408
|
+
return _handleMaxRetryReached.apply(this, arguments);
|
|
409
|
+
}
|
|
410
|
+
function sendToWorker(command) {
|
|
411
|
+
if (worker && workerReady) {
|
|
412
|
+
try {
|
|
413
|
+
worker.postMessage(command);
|
|
414
|
+
} catch (error) {
|
|
415
|
+
logger.warn('Failed to send message to Worker:', error);
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
logger.warn('Cannot send to Worker - worker:', !!worker, 'workerReady:', workerReady, 'command:', command.type);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* 处理 Worker 错误
|
|
424
|
+
*/
|
|
425
|
+
function handleWorkerError(e) {
|
|
426
|
+
logger.error('Upload Worker error:', e.message);
|
|
427
|
+
// Worker 出错时销毁,降级到主线程
|
|
428
|
+
destroyWorker(true);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ==================== 对外 API ====================
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* 设置 Worker 降级回调
|
|
435
|
+
* @param callback 降级回调函数
|
|
436
|
+
*/
|
|
437
|
+
export function setWorkerFallbackCallback(callback) {
|
|
438
|
+
onWorkerFallback = callback;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* 尝试初始化 Worker 模式
|
|
443
|
+
* @returns 是否成功启用 Worker 模式
|
|
444
|
+
*/
|
|
445
|
+
export function initWorkerUpload(serv, appId) {
|
|
446
|
+
currentAppId = appId;
|
|
447
|
+
|
|
448
|
+
// 尝试创建 Worker
|
|
449
|
+
var w = createInlineWorker();
|
|
450
|
+
if (!w) {
|
|
451
|
+
isWorkerMode = false;
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
worker = w;
|
|
455
|
+
worker.onmessage = handleWorkerMessage;
|
|
456
|
+
worker.onerror = handleWorkerError;
|
|
457
|
+
|
|
458
|
+
// 发送启动指令
|
|
459
|
+
var config = getConfig();
|
|
460
|
+
var startConfig = {
|
|
461
|
+
serv: serv,
|
|
462
|
+
appId: appId,
|
|
463
|
+
storageKey: getStorageKey(),
|
|
464
|
+
uploadInterval: config.uploadInterval,
|
|
465
|
+
maxRetryCount: config.maxRetryCount || 3
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// 注意:这里先将 isWorkerMode 设为 true
|
|
469
|
+
// 但 workerReady 仍为 false,需要等待 Worker 发送 READY 消息
|
|
470
|
+
// sendToWorker 会检查 workerReady 状态
|
|
471
|
+
worker.postMessage({
|
|
472
|
+
type: 'START',
|
|
473
|
+
config: startConfig
|
|
474
|
+
});
|
|
475
|
+
isWorkerMode = true;
|
|
476
|
+
|
|
477
|
+
// 设置超时检测:如果指定时间内没有收到 READY 消息,降级到主线程模式
|
|
478
|
+
workerReadyTimeout = setTimeout(function () {
|
|
479
|
+
if (!workerReady) {
|
|
480
|
+
logger.warn("Worker failed to send READY within ".concat(WORKER_READY_TIMEOUT, "ms, falling back to main thread"));
|
|
481
|
+
// 销毁并触发降级回调
|
|
482
|
+
destroyWorker(true);
|
|
483
|
+
}
|
|
484
|
+
}, WORKER_READY_TIMEOUT);
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* 销毁 Worker
|
|
490
|
+
* @param triggerFallback 是否触发降级回调
|
|
491
|
+
*/
|
|
492
|
+
export function destroyWorker() {
|
|
493
|
+
var triggerFallback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
494
|
+
// 清理超时检测
|
|
495
|
+
if (workerReadyTimeout) {
|
|
496
|
+
clearTimeout(workerReadyTimeout);
|
|
497
|
+
workerReadyTimeout = null;
|
|
498
|
+
}
|
|
499
|
+
if (worker) {
|
|
500
|
+
try {
|
|
501
|
+
worker.postMessage({
|
|
502
|
+
type: 'STOP'
|
|
503
|
+
});
|
|
504
|
+
worker.terminate();
|
|
505
|
+
} catch (e) {
|
|
506
|
+
// 忽略终止错误
|
|
507
|
+
}
|
|
508
|
+
worker = null;
|
|
509
|
+
}
|
|
510
|
+
workerReady = false;
|
|
511
|
+
isWorkerMode = false;
|
|
512
|
+
isFetchingBatch = false;
|
|
513
|
+
errorModeUploading = false;
|
|
514
|
+
errorModeWindowStart = 0;
|
|
515
|
+
batchIdCounter = 0;
|
|
516
|
+
pendingBatches.clear();
|
|
517
|
+
|
|
518
|
+
// 触发降级回调(仅在需要时)
|
|
519
|
+
if (triggerFallback && onWorkerFallback) {
|
|
520
|
+
onWorkerFallback();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* 是否正在使用 Worker 模式
|
|
526
|
+
*/
|
|
527
|
+
export function isUsingWorkerMode() {
|
|
528
|
+
return isWorkerMode && worker !== null;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* 设置错误模式上传状态(Worker 模式)
|
|
533
|
+
*/
|
|
534
|
+
export function setWorkerErrorModeUploading(uploading) {
|
|
535
|
+
errorModeUploading = uploading;
|
|
536
|
+
|
|
537
|
+
// 同步通知 Worker
|
|
538
|
+
sendToWorker({
|
|
539
|
+
type: 'SET_ERROR_MODE_UPLOADING',
|
|
540
|
+
uploading: uploading
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* 设置错误模式上传时间窗口起点(Worker 模式)
|
|
546
|
+
*/
|
|
547
|
+
export function setWorkerErrorModeWindowStart(startTime) {
|
|
548
|
+
errorModeWindowStart = startTime;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* 更新 Worker 配置
|
|
553
|
+
*/
|
|
554
|
+
export function updateWorkerConfig(config) {
|
|
555
|
+
sendToWorker({
|
|
556
|
+
type: 'UPDATE_CONFIG',
|
|
557
|
+
config: config
|
|
558
|
+
});
|
|
559
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload Worker 脚本
|
|
3
|
+
*
|
|
4
|
+
* 在 Web Worker 中执行上传任务,将 JSON 序列化 + FormData 构建 + XHR 上传
|
|
5
|
+
* 从主线程移到 Worker 线程,避免阻塞 UI。
|
|
6
|
+
*
|
|
7
|
+
* 通信协议:
|
|
8
|
+
* 主线程 → Worker: WorkerCommand
|
|
9
|
+
* Worker → 主线程: WorkerMessage
|
|
10
|
+
*/
|
|
11
|
+
export interface WorkerConfig {
|
|
12
|
+
serv: string;
|
|
13
|
+
appId: string;
|
|
14
|
+
storageKey: string;
|
|
15
|
+
uploadInterval: number;
|
|
16
|
+
maxRetryCount: number;
|
|
17
|
+
}
|
|
18
|
+
export type WorkerCommand = {
|
|
19
|
+
type: 'START';
|
|
20
|
+
config: WorkerConfig;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'STOP';
|
|
23
|
+
} | {
|
|
24
|
+
type: 'UPLOAD_BATCH';
|
|
25
|
+
batchId: string;
|
|
26
|
+
events: any[];
|
|
27
|
+
queryParams: Record<string, string>;
|
|
28
|
+
customHeaders: Record<string, string>;
|
|
29
|
+
} | {
|
|
30
|
+
type: 'UPDATE_CONFIG';
|
|
31
|
+
config: Partial<WorkerConfig>;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'SET_ERROR_MODE_UPLOADING';
|
|
34
|
+
uploading: boolean;
|
|
35
|
+
};
|
|
36
|
+
export type WorkerMessage = {
|
|
37
|
+
type: 'READY';
|
|
38
|
+
} | {
|
|
39
|
+
type: 'REQUEST_BATCH';
|
|
40
|
+
} | {
|
|
41
|
+
type: 'UPLOAD_SUCCESS';
|
|
42
|
+
batchId: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'UPLOAD_FAILURE';
|
|
45
|
+
batchId: string;
|
|
46
|
+
error: string;
|
|
47
|
+
failureCount: number;
|
|
48
|
+
} | {
|
|
49
|
+
type: 'MAX_RETRY_REACHED';
|
|
50
|
+
batchId: string;
|
|
51
|
+
error: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* 返回 Worker 脚本的源代码字符串
|
|
55
|
+
* 用于通过 Blob URL 内联创建 Worker
|
|
56
|
+
*/
|
|
57
|
+
export declare function getUploadWorkerCode(): string;
|
|
58
|
+
//# sourceMappingURL=upload-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-worker.d.ts","sourceRoot":"","sources":["../../../src/lib/upload-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAGD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,GAAG,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACpI;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,0BAA0B,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAG7D,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAChF;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAIlE;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAmN5C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload Worker 脚本
|
|
3
|
+
*
|
|
4
|
+
* 在 Web Worker 中执行上传任务,将 JSON 序列化 + FormData 构建 + XHR 上传
|
|
5
|
+
* 从主线程移到 Worker 线程,避免阻塞 UI。
|
|
6
|
+
*
|
|
7
|
+
* 通信协议:
|
|
8
|
+
* 主线程 → Worker: WorkerCommand
|
|
9
|
+
* Worker → 主线程: WorkerMessage
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { REQUEST_TIMEOUT } from "./constants";
|
|
13
|
+
|
|
14
|
+
// ==================== 类型定义 ====================
|
|
15
|
+
|
|
16
|
+
// 主线程 → Worker
|
|
17
|
+
|
|
18
|
+
// Worker → 主线程
|
|
19
|
+
|
|
20
|
+
// ==================== Worker 代码(字符串形式) ====================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 返回 Worker 脚本的源代码字符串
|
|
24
|
+
* 用于通过 Blob URL 内联创建 Worker
|
|
25
|
+
*/
|
|
26
|
+
export function getUploadWorkerCode() {
|
|
27
|
+
return "\n'use strict';\n\n// ===== Worker \u5185\u90E8\u72B6\u6001 =====\nvar config = null;\nvar pollTimer = null;\nvar isRunning = false;\nvar failureCountMap = {};\n\n// ===== UUID \u751F\u6210 =====\nfunction generateUUID() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = (Math.random() * 16) | 0;\n var v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n// ===== \u6784\u5EFA\u4E0A\u4F20 URL =====\nfunction buildUploadUrl(serv, queryParams) {\n var params = [];\n for (var key in queryParams) {\n if (queryParams.hasOwnProperty(key) && queryParams[key] != null && queryParams[key] !== '') {\n params.push(encodeURIComponent(key) + '=' + encodeURIComponent(queryParams[key]));\n }\n }\n var queryString = params.join('&');\n return serv + (queryString ? '?' + queryString : '');\n}\n\n// ===== \u6267\u884C\u4E0A\u4F20 =====\nfunction performUpload(msg) {\n var batchId = msg.batchId;\n var events = msg.events;\n var queryParams = msg.queryParams;\n var customHeaders = msg.customHeaders;\n\n try {\n // JSON \u5E8F\u5217\u5316\u5728 Worker \u4E2D\u6267\u884C\uFF0C\u4E0D\u963B\u585E\u4E3B\u7EBF\u7A0B\n var jsonStr = JSON.stringify(events);\n var blob = new Blob([jsonStr], { type: 'application/json' });\n var formData = new FormData();\n formData.append('file', blob, generateUUID() + '.json');\n\n var url = buildUploadUrl(config ? config.serv : '', queryParams);\n\n var xhr = new XMLHttpRequest();\n xhr.open('POST', url, true);\n xhr.timeout = ".concat(REQUEST_TIMEOUT, ";\n\n // \u8BBE\u7F6E\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n if (customHeaders) {\n for (var key in customHeaders) {\n if (customHeaders.hasOwnProperty(key)) {\n xhr.setRequestHeader(key, customHeaders[key]);\n }\n }\n }\n\n xhr.onreadystatechange = function() {\n if (xhr.readyState === 4) {\n if (xhr.status >= 200 && xhr.status < 300) {\n // \u68C0\u67E5\u4E1A\u52A1\u5C42 success \u5B57\u6BB5\n var uploadSuccess = true;\n var errorMessage = '';\n try {\n var responseData = JSON.parse(xhr.responseText);\n if (responseData && typeof responseData === 'object' && responseData.success === false) {\n uploadSuccess = false;\n errorMessage = responseData.errorMessage || 'Upload failed with success=false';\n }\n } catch(e) {\n // \u65E0\u6CD5\u89E3\u6790 JSON\uFF0C\u89C6\u4E3A\u6210\u529F\n }\n\n if (uploadSuccess) {\n // \u4E0A\u4F20\u6210\u529F\uFF0C\u91CD\u7F6E\u5931\u8D25\u8BA1\u6570\n var batchKey = config ? (config.appId + '_' + config.storageKey) : batchId;\n delete failureCountMap[batchKey];\n self.postMessage({ type: 'UPLOAD_SUCCESS', batchId: batchId });\n } else {\n handleFailure(batchId, errorMessage);\n }\n } else {\n handleFailure(batchId, 'HTTP ' + xhr.status + ': ' + xhr.statusText);\n }\n }\n };\n\n xhr.onerror = function() {\n handleFailure(batchId, 'Network error');\n };\n\n xhr.ontimeout = function() {\n handleFailure(batchId, 'Request timeout');\n };\n\n xhr.send(formData);\n } catch (e) {\n handleFailure(batchId, 'Upload exception: ' + (e.message || String(e)));\n }\n}\n\n// ===== \u5904\u7406\u4E0A\u4F20\u5931\u8D25 =====\nfunction handleFailure(batchId, errorMessage) {\n var batchKey = config ? (config.appId + '_' + config.storageKey) : batchId;\n var currentCount = failureCountMap[batchKey] || 0;\n var newCount = currentCount + 1;\n failureCountMap[batchKey] = newCount;\n\n var maxRetry = (config && config.maxRetryCount) ? config.maxRetryCount : 3;\n\n if (newCount >= maxRetry) {\n // \u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570\n delete failureCountMap[batchKey];\n self.postMessage({\n type: 'MAX_RETRY_REACHED',\n batchId: batchId,\n error: errorMessage\n });\n } else {\n self.postMessage({\n type: 'UPLOAD_FAILURE',\n batchId: batchId,\n error: errorMessage,\n failureCount: newCount\n });\n }\n}\n\n// ===== \u8F6E\u8BE2\u8C03\u5EA6 =====\nfunction startPolling() {\n if (pollTimer) {\n clearInterval(pollTimer);\n }\n\n var interval = (config && config.uploadInterval) ? config.uploadInterval : 2000;\n\n pollTimer = setInterval(function() {\n if (!isRunning) return;\n\n // \u5411\u4E3B\u7EBF\u7A0B\u8BF7\u6C42\u6570\u636E\u6279\u6B21\n // \u4E3B\u7EBF\u7A0B\u4F1A\u6839\u636E\u5F53\u524D\u6A21\u5F0F\uFF08full/error\uFF09\u548C\u72B6\u6001\u51B3\u5B9A\u662F\u5426\u63D0\u4F9B\u6570\u636E\n self.postMessage({\n type: 'REQUEST_BATCH'\n });\n }, interval);\n}\n\nfunction stopPolling() {\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n}\n\n// ===== \u6E05\u7406 =====\nfunction cleanup() {\n isRunning = false;\n stopPolling();\n failureCountMap = {};\n config = null;\n}\n\n// ===== \u6D88\u606F\u5904\u7406 =====\nself.onmessage = function(e) {\n var msg = e.data;\n if (!msg || !msg.type) return;\n\n switch (msg.type) {\n case 'START':\n config = msg.config;\n isRunning = true;\n failureCountMap = {};\n startPolling();\n break;\n\n case 'STOP':\n cleanup();\n break;\n\n case 'UPLOAD_BATCH':\n if (isRunning) {\n performUpload(msg);\n }\n break;\n\n case 'UPDATE_CONFIG':\n if (msg.config) {\n for (var key in msg.config) {\n if (msg.config.hasOwnProperty(key) && config) {\n config[key] = msg.config[key];\n }\n }\n // \u5982\u679C uploadInterval \u53D8\u4E86\uFF0C\u91CD\u542F\u8F6E\u8BE2\n if (msg.config.uploadInterval && isRunning) {\n startPolling();\n }\n }\n break;\n\n case 'SET_ERROR_MODE_UPLOADING':\n // \u72B6\u6001\u7531\u4E3B\u7EBF\u7A0B\u7BA1\u7406\uFF0CWorker \u4EC5\u63A5\u6536\u6307\u4EE4\u4FDD\u6301\u534F\u8BAE\u517C\u5BB9\n break;\n }\n};\n\n// Worker \u5C31\u7EEA\u901A\u77E5\nself.postMessage({ type: 'READY' });\n");
|
|
28
|
+
}
|