@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
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.destroyWorker = destroyWorker;
|
|
7
|
+
exports.initWorkerUpload = initWorkerUpload;
|
|
8
|
+
exports.isUsingWorkerMode = isUsingWorkerMode;
|
|
9
|
+
exports.setWorkerErrorModeUploading = setWorkerErrorModeUploading;
|
|
10
|
+
exports.setWorkerErrorModeWindowStart = setWorkerErrorModeWindowStart;
|
|
11
|
+
exports.updateWorkerConfig = updateWorkerConfig;
|
|
12
|
+
var _uploadWorker = require("./upload-worker");
|
|
13
|
+
var _storageManager = require("./storage-manager");
|
|
14
|
+
var _utils = require("./utils");
|
|
15
|
+
var _config = require("./config");
|
|
16
|
+
var _api = require("./api");
|
|
17
|
+
var _net = require("./net");
|
|
18
|
+
var _rrweb = require("rrweb");
|
|
19
|
+
var _init = require("./init");
|
|
20
|
+
var _logger = _interopRequireDefault(require("./logger"));
|
|
21
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
22
|
+
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); }
|
|
23
|
+
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; }
|
|
24
|
+
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); } }
|
|
25
|
+
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); }); }; } /**
|
|
26
|
+
* Upload Worker 管理器
|
|
27
|
+
*
|
|
28
|
+
* 职责:
|
|
29
|
+
* - Worker 生命周期管理(创建、销毁、降级)
|
|
30
|
+
* - 将 Worker 脚本以 inline Blob 方式加载
|
|
31
|
+
* - 封装 postMessage/onmessage 通信
|
|
32
|
+
* - 处理 Worker 回传的消息,调用对应的主线程逻辑
|
|
33
|
+
* - 提供与现有 uploader.ts 相同的对外 API
|
|
34
|
+
*/
|
|
35
|
+
// Worker 实例
|
|
36
|
+
var worker = null;
|
|
37
|
+
|
|
38
|
+
// Worker 是否就绪
|
|
39
|
+
var workerReady = false;
|
|
40
|
+
|
|
41
|
+
// 是否正在使用 Worker 模式
|
|
42
|
+
var isWorkerMode = false;
|
|
43
|
+
|
|
44
|
+
// 当前配置缓存
|
|
45
|
+
var currentServ = '';
|
|
46
|
+
var currentAppId = '';
|
|
47
|
+
|
|
48
|
+
// 错误模式状态
|
|
49
|
+
var errorModeUploading = false;
|
|
50
|
+
var errorModeWindowStart = 0;
|
|
51
|
+
|
|
52
|
+
// 上传锁(Worker 模式下用于防止并发数据读取)
|
|
53
|
+
var isFetchingBatch = false;
|
|
54
|
+
|
|
55
|
+
// batchId 计数器
|
|
56
|
+
var batchIdCounter = 0;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 生成唯一的 batchId
|
|
60
|
+
*/
|
|
61
|
+
function generateBatchId() {
|
|
62
|
+
return "batch_".concat(Date.now(), "_").concat(++batchIdCounter);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 创建内联 Worker
|
|
67
|
+
* 使用 Blob URL 方式,避免额外的文件加载问题
|
|
68
|
+
*/
|
|
69
|
+
function createInlineWorker() {
|
|
70
|
+
try {
|
|
71
|
+
// 检查 Worker 是否可用
|
|
72
|
+
if (typeof Worker === 'undefined') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
var workerCode = (0, _uploadWorker.getUploadWorkerCode)();
|
|
76
|
+
var blob = new Blob([workerCode], {
|
|
77
|
+
type: 'application/javascript'
|
|
78
|
+
});
|
|
79
|
+
var url = URL.createObjectURL(blob);
|
|
80
|
+
var w = new Worker(url);
|
|
81
|
+
// 创建后立即释放 Blob URL,Worker 已经加载了代码
|
|
82
|
+
URL.revokeObjectURL(url);
|
|
83
|
+
return w;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
_logger.default.warn('Failed to create inline Worker, will fallback to main thread:', e);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 处理 Worker 发来的消息
|
|
92
|
+
*/
|
|
93
|
+
function handleWorkerMessage(e) {
|
|
94
|
+
var msg = e.data;
|
|
95
|
+
if (!msg || !msg.type) return;
|
|
96
|
+
switch (msg.type) {
|
|
97
|
+
case 'READY':
|
|
98
|
+
workerReady = true;
|
|
99
|
+
_logger.default.log('Upload Worker is ready');
|
|
100
|
+
break;
|
|
101
|
+
case 'REQUEST_BATCH':
|
|
102
|
+
handleRequestBatch(msg.mode);
|
|
103
|
+
break;
|
|
104
|
+
case 'UPLOAD_SUCCESS':
|
|
105
|
+
handleUploadSuccess(msg.batchId);
|
|
106
|
+
break;
|
|
107
|
+
case 'UPLOAD_FAILURE':
|
|
108
|
+
handleUploadFailure(msg.batchId, msg.error, msg.failureCount);
|
|
109
|
+
break;
|
|
110
|
+
case 'MAX_RETRY_REACHED':
|
|
111
|
+
handleMaxRetryReached(msg.batchId, msg.error);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 处理 Worker 请求数据批次
|
|
118
|
+
* Worker 内部定时器触发,主线程从 storage 读取数据后发给 Worker
|
|
119
|
+
*/
|
|
120
|
+
function handleRequestBatch(_x) {
|
|
121
|
+
return _handleRequestBatch.apply(this, arguments);
|
|
122
|
+
} // 待处理的批次元数据
|
|
123
|
+
function _handleRequestBatch() {
|
|
124
|
+
_handleRequestBatch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(mode) {
|
|
125
|
+
var currentConfig, isErrorMode, events, windowStart, config, consumeOnly, queryParams, customHeaders, batchId;
|
|
126
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
127
|
+
while (1) switch (_context.prev = _context.next) {
|
|
128
|
+
case 0:
|
|
129
|
+
if (!isFetchingBatch) {
|
|
130
|
+
_context.next = 2;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
return _context.abrupt("return");
|
|
134
|
+
case 2:
|
|
135
|
+
// 检查采集总开关
|
|
136
|
+
currentConfig = (0, _config.getConfig)();
|
|
137
|
+
if (currentConfig.enable) {
|
|
138
|
+
_context.next = 6;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
_logger.default.log('SDK is disabled by config, skipping batch request');
|
|
142
|
+
return _context.abrupt("return");
|
|
143
|
+
case 6:
|
|
144
|
+
isErrorMode = currentConfig.captureMode === 'error'; // 错误模式下,如果未处于 uploading 状态,跳过
|
|
145
|
+
if (!(isErrorMode && !errorModeUploading)) {
|
|
146
|
+
_context.next = 9;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
return _context.abrupt("return");
|
|
150
|
+
case 9:
|
|
151
|
+
_context.prev = 9;
|
|
152
|
+
isFetchingBatch = true;
|
|
153
|
+
windowStart = 0;
|
|
154
|
+
if (!isErrorMode) {
|
|
155
|
+
_context.next = 20;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
// 错误模式:按时间范围提取事件
|
|
159
|
+
windowStart = errorModeWindowStart > 0 ? errorModeWindowStart : Date.now();
|
|
160
|
+
_context.next = 16;
|
|
161
|
+
return _storageManager.storage.pullByTimeRange((0, _utils.getDeviceId)(), windowStart, Date.now());
|
|
162
|
+
case 16:
|
|
163
|
+
events = _context.sent;
|
|
164
|
+
errorModeWindowStart = 0;
|
|
165
|
+
_context.next = 23;
|
|
166
|
+
break;
|
|
167
|
+
case 20:
|
|
168
|
+
_context.next = 22;
|
|
169
|
+
return _storageManager.storage.pull((0, _utils.getDeviceId)(), 100);
|
|
170
|
+
case 22:
|
|
171
|
+
events = _context.sent;
|
|
172
|
+
case 23:
|
|
173
|
+
if (!(!events || events.length === 0)) {
|
|
174
|
+
_context.next = 26;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
isFetchingBatch = false;
|
|
178
|
+
return _context.abrupt("return");
|
|
179
|
+
case 26:
|
|
180
|
+
// 检查是否只消费不上报
|
|
181
|
+
config = (0, _config.getConfig)();
|
|
182
|
+
consumeOnly = sessionStorage.getItem('RLOG_CONSUME_ONLY') === 'YES' || config.consumeOnly;
|
|
183
|
+
if (!consumeOnly) {
|
|
184
|
+
_context.next = 32;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
_logger.default.log('Consume only mode, skip upload');
|
|
188
|
+
isFetchingBatch = false;
|
|
189
|
+
return _context.abrupt("return");
|
|
190
|
+
case 32:
|
|
191
|
+
// 构建查询参数
|
|
192
|
+
queryParams = (0, _api.buildQueryParams)({
|
|
193
|
+
appId: currentAppId,
|
|
194
|
+
deviceId: (0, _utils.getDeviceId)(),
|
|
195
|
+
data: events
|
|
196
|
+
}); // 获取自定义请求头
|
|
197
|
+
customHeaders = (0, _net.getCustomHeaders)(); // 生成 batchId 并记录元数据(用于成功/失败回调)
|
|
198
|
+
batchId = generateBatchId(); // 将批次元数据存储起来,供回调使用
|
|
199
|
+
pendingBatches.set(batchId, {
|
|
200
|
+
events: events,
|
|
201
|
+
isErrorMode: isErrorMode,
|
|
202
|
+
windowStart: windowStart
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// 发送数据给 Worker 执行上传
|
|
206
|
+
sendToWorker({
|
|
207
|
+
type: 'UPLOAD_BATCH',
|
|
208
|
+
batchId: batchId,
|
|
209
|
+
events: events,
|
|
210
|
+
queryParams: queryParams,
|
|
211
|
+
customHeaders: customHeaders
|
|
212
|
+
});
|
|
213
|
+
isFetchingBatch = false;
|
|
214
|
+
_context.next = 44;
|
|
215
|
+
break;
|
|
216
|
+
case 40:
|
|
217
|
+
_context.prev = 40;
|
|
218
|
+
_context.t0 = _context["catch"](9);
|
|
219
|
+
isFetchingBatch = false;
|
|
220
|
+
_logger.default.error('Error fetching batch for Worker:', _context.t0);
|
|
221
|
+
case 44:
|
|
222
|
+
case "end":
|
|
223
|
+
return _context.stop();
|
|
224
|
+
}
|
|
225
|
+
}, _callee, null, [[9, 40]]);
|
|
226
|
+
}));
|
|
227
|
+
return _handleRequestBatch.apply(this, arguments);
|
|
228
|
+
}
|
|
229
|
+
var pendingBatches = new Map();
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 处理上传成功
|
|
233
|
+
*/
|
|
234
|
+
function handleUploadSuccess(_x2) {
|
|
235
|
+
return _handleUploadSuccess.apply(this, arguments);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 处理上传失败(未达到最大重试次数)
|
|
239
|
+
*/
|
|
240
|
+
function _handleUploadSuccess() {
|
|
241
|
+
_handleUploadSuccess = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(batchId) {
|
|
242
|
+
var batch, lastEvent;
|
|
243
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
244
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
245
|
+
case 0:
|
|
246
|
+
batch = pendingBatches.get(batchId);
|
|
247
|
+
if (batch) {
|
|
248
|
+
_context2.next = 3;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
return _context2.abrupt("return");
|
|
252
|
+
case 3:
|
|
253
|
+
pendingBatches.delete(batchId);
|
|
254
|
+
|
|
255
|
+
// 错误模式:上传成功后移除已上传的事件
|
|
256
|
+
if (!(batch.isErrorMode && batch.windowStart > 0 && batch.events.length > 0)) {
|
|
257
|
+
_context2.next = 9;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
_context2.next = 7;
|
|
261
|
+
return _storageManager.storage.removeByTimeRange((0, _utils.getDeviceId)(), batch.windowStart);
|
|
262
|
+
case 7:
|
|
263
|
+
// 更新窗口起点为最后一个事件的时间戳
|
|
264
|
+
lastEvent = batch.events[batch.events.length - 1];
|
|
265
|
+
if (lastEvent && lastEvent.timestamp) {
|
|
266
|
+
errorModeWindowStart = lastEvent.timestamp;
|
|
267
|
+
}
|
|
268
|
+
case 9:
|
|
269
|
+
case "end":
|
|
270
|
+
return _context2.stop();
|
|
271
|
+
}
|
|
272
|
+
}, _callee2);
|
|
273
|
+
}));
|
|
274
|
+
return _handleUploadSuccess.apply(this, arguments);
|
|
275
|
+
}
|
|
276
|
+
function handleUploadFailure(_x3, _x4, _x5) {
|
|
277
|
+
return _handleUploadFailure.apply(this, arguments);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* 处理达到最大重试次数
|
|
281
|
+
*/
|
|
282
|
+
function _handleUploadFailure() {
|
|
283
|
+
_handleUploadFailure = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(batchId, error, failureCount) {
|
|
284
|
+
var batch;
|
|
285
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
286
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
287
|
+
case 0:
|
|
288
|
+
batch = pendingBatches.get(batchId);
|
|
289
|
+
if (batch) {
|
|
290
|
+
_context3.next = 3;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
return _context3.abrupt("return");
|
|
294
|
+
case 3:
|
|
295
|
+
pendingBatches.delete(batchId);
|
|
296
|
+
_logger.default.warn("Upload failed ".concat(failureCount, " time(s), will retry in next cycle:"), error);
|
|
297
|
+
|
|
298
|
+
// 将事件放回存储头部,保持时间戳顺序
|
|
299
|
+
_context3.next = 7;
|
|
300
|
+
return _storageManager.storage.unshiftBatch((0, _utils.getDeviceId)(), batch.events);
|
|
301
|
+
case 7:
|
|
302
|
+
case "end":
|
|
303
|
+
return _context3.stop();
|
|
304
|
+
}
|
|
305
|
+
}, _callee3);
|
|
306
|
+
}));
|
|
307
|
+
return _handleUploadFailure.apply(this, arguments);
|
|
308
|
+
}
|
|
309
|
+
function handleMaxRetryReached(_x6, _x7) {
|
|
310
|
+
return _handleMaxRetryReached.apply(this, arguments);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* 向 Worker 发送消息
|
|
314
|
+
*/
|
|
315
|
+
function _handleMaxRetryReached() {
|
|
316
|
+
_handleMaxRetryReached = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(batchId, error) {
|
|
317
|
+
var batch, config, maxRetryCount, customEvent;
|
|
318
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
319
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
320
|
+
case 0:
|
|
321
|
+
batch = pendingBatches.get(batchId);
|
|
322
|
+
if (batch) {
|
|
323
|
+
_context4.next = 3;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
return _context4.abrupt("return");
|
|
327
|
+
case 3:
|
|
328
|
+
pendingBatches.delete(batchId);
|
|
329
|
+
config = (0, _config.getConfig)();
|
|
330
|
+
maxRetryCount = config.maxRetryCount || 3;
|
|
331
|
+
_logger.default.warn("Upload failed ".concat(maxRetryCount, " times consecutively, stopping upload"));
|
|
332
|
+
|
|
333
|
+
// 将事件放回存储
|
|
334
|
+
_context4.next = 9;
|
|
335
|
+
return _storageManager.storage.unshiftBatch((0, _utils.getDeviceId)(), batch.events);
|
|
336
|
+
case 9:
|
|
337
|
+
// 创建并派发自定义事件
|
|
338
|
+
customEvent = new CustomEvent('rlog-upload-failure', {
|
|
339
|
+
detail: {
|
|
340
|
+
error: error,
|
|
341
|
+
retryCount: maxRetryCount,
|
|
342
|
+
appId: currentAppId,
|
|
343
|
+
deviceId: (0, _utils.getDeviceId)()
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
if (typeof window !== 'undefined') {
|
|
347
|
+
window.dispatchEvent(customEvent);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 使用 addCustomEvent 记录上传失败事件
|
|
351
|
+
try {
|
|
352
|
+
_rrweb.record.addCustomEvent('rlog-upload-failure', {
|
|
353
|
+
error: error,
|
|
354
|
+
retryCount: maxRetryCount,
|
|
355
|
+
appId: currentAppId,
|
|
356
|
+
deviceId: (0, _utils.getDeviceId)(),
|
|
357
|
+
timestamp: Date.now()
|
|
358
|
+
});
|
|
359
|
+
} catch (e) {
|
|
360
|
+
_logger.default.warn('Failed to record upload failure event via addCustomEvent:', e);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 停止 Worker
|
|
364
|
+
destroyWorker();
|
|
365
|
+
|
|
366
|
+
// 停止 rrweb 录制
|
|
367
|
+
setTimeout(function () {
|
|
368
|
+
(0, _init.cancelRlog)();
|
|
369
|
+
}, 0);
|
|
370
|
+
_logger.default.error('Upload and recording stopped due to repeated failures');
|
|
371
|
+
case 15:
|
|
372
|
+
case "end":
|
|
373
|
+
return _context4.stop();
|
|
374
|
+
}
|
|
375
|
+
}, _callee4);
|
|
376
|
+
}));
|
|
377
|
+
return _handleMaxRetryReached.apply(this, arguments);
|
|
378
|
+
}
|
|
379
|
+
function sendToWorker(command) {
|
|
380
|
+
if (worker && workerReady) {
|
|
381
|
+
worker.postMessage(command);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* 处理 Worker 错误
|
|
387
|
+
*/
|
|
388
|
+
function handleWorkerError(e) {
|
|
389
|
+
_logger.default.error('Upload Worker error:', e.message);
|
|
390
|
+
// Worker 出错时销毁,后续由 uploader.ts 降级到主线程
|
|
391
|
+
destroyWorker();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ==================== 对外 API ====================
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* 尝试初始化 Worker 模式
|
|
398
|
+
* @returns 是否成功启用 Worker 模式
|
|
399
|
+
*/
|
|
400
|
+
function initWorkerUpload(serv, appId) {
|
|
401
|
+
currentServ = serv;
|
|
402
|
+
currentAppId = appId;
|
|
403
|
+
|
|
404
|
+
// 尝试创建 Worker
|
|
405
|
+
var w = createInlineWorker();
|
|
406
|
+
if (!w) {
|
|
407
|
+
isWorkerMode = false;
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
worker = w;
|
|
411
|
+
worker.onmessage = handleWorkerMessage;
|
|
412
|
+
worker.onerror = handleWorkerError;
|
|
413
|
+
|
|
414
|
+
// 发送启动指令
|
|
415
|
+
var config = (0, _config.getConfig)();
|
|
416
|
+
var startConfig = {
|
|
417
|
+
serv: serv,
|
|
418
|
+
appId: appId,
|
|
419
|
+
deviceId: (0, _utils.getDeviceId)(),
|
|
420
|
+
uploadInterval: config.uploadInterval,
|
|
421
|
+
maxRetryCount: config.maxRetryCount || 3
|
|
422
|
+
};
|
|
423
|
+
worker.postMessage({
|
|
424
|
+
type: 'START',
|
|
425
|
+
config: startConfig
|
|
426
|
+
});
|
|
427
|
+
isWorkerMode = true;
|
|
428
|
+
_logger.default.log('Upload Worker mode initialized');
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* 销毁 Worker
|
|
434
|
+
*/
|
|
435
|
+
function destroyWorker() {
|
|
436
|
+
if (worker) {
|
|
437
|
+
try {
|
|
438
|
+
worker.postMessage({
|
|
439
|
+
type: 'STOP'
|
|
440
|
+
});
|
|
441
|
+
worker.terminate();
|
|
442
|
+
} catch (e) {
|
|
443
|
+
// 忽略终止错误
|
|
444
|
+
}
|
|
445
|
+
worker = null;
|
|
446
|
+
}
|
|
447
|
+
workerReady = false;
|
|
448
|
+
isWorkerMode = false;
|
|
449
|
+
isFetchingBatch = false;
|
|
450
|
+
errorModeUploading = false;
|
|
451
|
+
errorModeWindowStart = 0;
|
|
452
|
+
batchIdCounter = 0;
|
|
453
|
+
pendingBatches.clear();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* 是否正在使用 Worker 模式
|
|
458
|
+
*/
|
|
459
|
+
function isUsingWorkerMode() {
|
|
460
|
+
return isWorkerMode && worker !== null;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* 设置错误模式上传状态(Worker 模式)
|
|
465
|
+
*/
|
|
466
|
+
function setWorkerErrorModeUploading(uploading) {
|
|
467
|
+
errorModeUploading = uploading;
|
|
468
|
+
|
|
469
|
+
// 同步通知 Worker
|
|
470
|
+
sendToWorker({
|
|
471
|
+
type: 'SET_ERROR_MODE_UPLOADING',
|
|
472
|
+
uploading: uploading
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* 设置错误模式上传时间窗口起点(Worker 模式)
|
|
478
|
+
*/
|
|
479
|
+
function setWorkerErrorModeWindowStart(startTime) {
|
|
480
|
+
errorModeWindowStart = startTime;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* 更新 Worker 配置
|
|
485
|
+
*/
|
|
486
|
+
function updateWorkerConfig(config) {
|
|
487
|
+
sendToWorker({
|
|
488
|
+
type: 'UPDATE_CONFIG',
|
|
489
|
+
config: config
|
|
490
|
+
});
|
|
491
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
deviceId: 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
|
+
mode: 'full' | 'error';
|
|
41
|
+
} | {
|
|
42
|
+
type: 'UPLOAD_SUCCESS';
|
|
43
|
+
batchId: string;
|
|
44
|
+
} | {
|
|
45
|
+
type: 'UPLOAD_FAILURE';
|
|
46
|
+
batchId: string;
|
|
47
|
+
error: string;
|
|
48
|
+
failureCount: number;
|
|
49
|
+
} | {
|
|
50
|
+
type: 'MAX_RETRY_REACHED';
|
|
51
|
+
batchId: string;
|
|
52
|
+
error: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* 返回 Worker 脚本的源代码字符串
|
|
56
|
+
* 用于通过 Blob URL 内联创建 Worker
|
|
57
|
+
*/
|
|
58
|
+
export declare function getUploadWorkerCode(): string;
|
|
59
|
+
//# 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;AAIH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,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,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GACjD;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,CAsN5C"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getUploadWorkerCode = getUploadWorkerCode;
|
|
7
|
+
/**
|
|
8
|
+
* Upload Worker 脚本
|
|
9
|
+
*
|
|
10
|
+
* 在 Web Worker 中执行上传任务,将 JSON 序列化 + FormData 构建 + XHR 上传
|
|
11
|
+
* 从主线程移到 Worker 线程,避免阻塞 UI。
|
|
12
|
+
*
|
|
13
|
+
* 通信协议:
|
|
14
|
+
* 主线程 → Worker: WorkerCommand
|
|
15
|
+
* Worker → 主线程: WorkerMessage
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ==================== 类型定义 ====================
|
|
19
|
+
|
|
20
|
+
// 主线程 → Worker
|
|
21
|
+
|
|
22
|
+
// Worker → 主线程
|
|
23
|
+
|
|
24
|
+
// ==================== Worker 代码(字符串形式) ====================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 返回 Worker 脚本的源代码字符串
|
|
28
|
+
* 用于通过 Blob URL 内联创建 Worker
|
|
29
|
+
*/
|
|
30
|
+
function getUploadWorkerCode() {
|
|
31
|
+
return "\n'use strict';\n\n// ===== Worker \u5185\u90E8\u72B6\u6001 =====\nvar config = null;\nvar pollTimer = null;\nvar isRunning = false;\nvar errorModeUploading = 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 = 10000;\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.deviceId) : 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.deviceId) : 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 mode: errorModeUploading ? 'error' : 'full'\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 errorModeUploading = false;\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 errorModeUploading = msg.uploading;\n break;\n }\n};\n\n// Worker \u5C31\u7EEA\u901A\u77E5\nself.postMessage({ type: 'READY' });\n";
|
|
32
|
+
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 设置错误模式上传状态
|
|
3
3
|
* 由 ErrorTrigger 调用:错误触发时设置为 true,cooldown 结束时设置为 false
|
|
4
|
+
* 自动分发到 Worker 模式或主线程模式
|
|
4
5
|
*/
|
|
5
6
|
export declare function setErrorModeUploading(uploading: boolean): void;
|
|
6
7
|
/**
|
|
7
8
|
* 设置错误模式上传时间窗口起点
|
|
8
9
|
* 由 ErrorTrigger 调用,记录错误触发前的时间窗口起点
|
|
10
|
+
* 自动分发到 Worker 模式或主线程模式
|
|
9
11
|
*/
|
|
10
12
|
export declare function setErrorModeWindowStart(startTime: number): void;
|
|
11
13
|
/**
|
|
12
14
|
* 启动数据上传循环
|
|
15
|
+
* 优先尝试 Worker 模式,失败则降级到主线程模式
|
|
16
|
+
*
|
|
13
17
|
* 全量模式:持续轮询上传
|
|
14
18
|
* 错误模式:idle 时跳过上传,uploading 时轮询上传(由 ErrorTrigger 控制状态)
|
|
15
19
|
* @param serv 服务器地址
|
|
@@ -18,6 +22,7 @@ export declare function setErrorModeWindowStart(startTime: number): void;
|
|
|
18
22
|
export declare function startUploadLoop(serv: string, appId: string): void;
|
|
19
23
|
/**
|
|
20
24
|
* 取消上传循环
|
|
25
|
+
* 同时处理 Worker 模式和主线程模式
|
|
21
26
|
*/
|
|
22
27
|
export declare function cancelUploadLoop(): void;
|
|
23
28
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../../../src/lib/uploader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../../../src/lib/uploader.ts"],"names":[],"mappings":"AAgCA;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAM/D;AAqPD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAYjE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAYtC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
|