@macroui/event-tracker 1.3.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/bin/event-tracker-suggest.mjs +141 -0
  4. package/bin/event-tracker.mjs +157 -0
  5. package/dist/event-tracker.cjs +3449 -0
  6. package/dist/event-tracker.js +3455 -0
  7. package/dist/event-tracker.min.js +1 -0
  8. package/dist/event-tracker.mjs +3402 -0
  9. package/dist/plugin/auto-track.cjs +201 -0
  10. package/dist/plugin/auto-track.mjs +199 -0
  11. package/dist/plugin/debug-sender.cjs +214 -0
  12. package/dist/plugin/debug-sender.mjs +211 -0
  13. package/dist/plugin/encrypt.cjs +284 -0
  14. package/dist/plugin/encrypt.mjs +275 -0
  15. package/dist/plugin/exposure.cjs +121 -0
  16. package/dist/plugin/exposure.mjs +119 -0
  17. package/dist/plugin/indexeddb-sender.cjs +94 -0
  18. package/dist/plugin/indexeddb-sender.mjs +92 -0
  19. package/dist/plugin/pageleave.cjs +74 -0
  20. package/dist/plugin/pageleave.mjs +72 -0
  21. package/dist/plugin/pageload.cjs +198 -0
  22. package/dist/plugin/pageload.mjs +196 -0
  23. package/dist/plugin/session-event.cjs +142 -0
  24. package/dist/plugin/session-event.mjs +140 -0
  25. package/dist/plugin/webview-bridge.cjs +111 -0
  26. package/dist/plugin/webview-bridge.mjs +109 -0
  27. package/dist/types/core/env.d.ts +10 -0
  28. package/dist/types/core/errors.d.ts +76 -0
  29. package/dist/types/core/failed-queue.d.ts +24 -0
  30. package/dist/types/core/identity-api.d.ts +6 -0
  31. package/dist/types/core/identity.d.ts +39 -0
  32. package/dist/types/core/instance-channel.d.ts +34 -0
  33. package/dist/types/core/lifecycle.d.ts +26 -0
  34. package/dist/types/core/logger.d.ts +13 -0
  35. package/dist/types/core/plugin.d.ts +60 -0
  36. package/dist/types/core/profile-api.d.ts +6 -0
  37. package/dist/types/core/profile-store.d.ts +23 -0
  38. package/dist/types/core/register-api.d.ts +22 -0
  39. package/dist/types/core/retry-policy.d.ts +33 -0
  40. package/dist/types/core/send-pipeline.d.ts +55 -0
  41. package/dist/types/core/signed-identity.d.ts +29 -0
  42. package/dist/types/core/stage.d.ts +48 -0
  43. package/dist/types/core/track-api.d.ts +6 -0
  44. package/dist/types/core/tracker.d.ts +130 -0
  45. package/dist/types/core/uuid.d.ts +4 -0
  46. package/dist/types/index.d.ts +40 -0
  47. package/dist/types/plugins/auto-track/index.d.ts +21 -0
  48. package/dist/types/plugins/debug-sender/index.d.ts +24 -0
  49. package/dist/types/plugins/encrypt/index.d.ts +56 -0
  50. package/dist/types/plugins/exposure/index.d.ts +18 -0
  51. package/dist/types/plugins/indexeddb-sender/index.d.ts +15 -0
  52. package/dist/types/plugins/pageleave/index.d.ts +12 -0
  53. package/dist/types/plugins/pageload/index.d.ts +14 -0
  54. package/dist/types/plugins/session-event/index.d.ts +14 -0
  55. package/dist/types/plugins/webview-bridge/index.d.ts +33 -0
  56. package/dist/types/presets/preset-properties.d.ts +25 -0
  57. package/dist/types/send/ajax-sender.d.ts +11 -0
  58. package/dist/types/send/batch-sender.d.ts +17 -0
  59. package/dist/types/send/beacon-sender.d.ts +10 -0
  60. package/dist/types/send/encode.d.ts +8 -0
  61. package/dist/types/send/image-sender.d.ts +10 -0
  62. package/dist/types/send/sender.d.ts +20 -0
  63. package/dist/types/storage/indexeddb-store.d.ts +14 -0
  64. package/dist/types/storage/store.d.ts +56 -0
  65. package/dist/types/types/common.d.ts +26 -0
  66. package/dist/types/types/config.d.ts +117 -0
  67. package/dist/types/types/constants.d.ts +110 -0
  68. package/dist/types/types/index.d.ts +25 -0
  69. package/dist/types/types.test-d.d.ts +5 -0
  70. package/dist/types.test-d.ts +103 -0
  71. package/package.json +218 -0
@@ -0,0 +1,3449 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /******************************************************************************
6
+ Copyright (c) Microsoft Corporation.
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ ***************************************************************************** */
19
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
20
+
21
+ var extendStatics = function(d, b) {
22
+ extendStatics = Object.setPrototypeOf ||
23
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
24
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
25
+ return extendStatics(d, b);
26
+ };
27
+
28
+ function __extends(d, b) {
29
+ if (typeof b !== "function" && b !== null)
30
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
31
+ extendStatics(d, b);
32
+ function __() { this.constructor = d; }
33
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
34
+ }
35
+
36
+ var __assign = function() {
37
+ __assign = Object.assign || function __assign(t) {
38
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
39
+ s = arguments[i];
40
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
41
+ }
42
+ return t;
43
+ };
44
+ return __assign.apply(this, arguments);
45
+ };
46
+
47
+ function __awaiter(thisArg, _arguments, P, generator) {
48
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
49
+ return new (P || (P = Promise))(function (resolve, reject) {
50
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
51
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
52
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
53
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
54
+ });
55
+ }
56
+
57
+ function __generator(thisArg, body) {
58
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
59
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
60
+ function verb(n) { return function (v) { return step([n, v]); }; }
61
+ function step(op) {
62
+ if (f) throw new TypeError("Generator is already executing.");
63
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
64
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
65
+ if (y = 0, t) op = [op[0] & 2, t.value];
66
+ switch (op[0]) {
67
+ case 0: case 1: t = op; break;
68
+ case 4: _.label++; return { value: op[1], done: false };
69
+ case 5: _.label++; y = op[1]; op = [0]; continue;
70
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
71
+ default:
72
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
73
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
74
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
75
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
76
+ if (t[2]) _.ops.pop();
77
+ _.trys.pop(); continue;
78
+ }
79
+ op = body.call(thisArg, _);
80
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
81
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
82
+ }
83
+ }
84
+
85
+ function __spreadArray(to, from, pack) {
86
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
87
+ if (ar || !(i in from)) {
88
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
89
+ ar[i] = from[i];
90
+ }
91
+ }
92
+ return to.concat(ar || Array.prototype.slice.call(from));
93
+ }
94
+
95
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
96
+ var e = new Error(message);
97
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
98
+ };
99
+
100
+ /**
101
+ * 浏览器端辅助方法
102
+ */
103
+ function isBrowser() {
104
+ return typeof window !== 'undefined' && typeof window.document !== 'undefined';
105
+ }
106
+ function hasLocalStorage() {
107
+ try {
108
+ if (!isBrowser() || !window.localStorage)
109
+ return false;
110
+ var k = '__et_test__';
111
+ window.localStorage.setItem(k, '1');
112
+ window.localStorage.removeItem(k);
113
+ return true;
114
+ }
115
+ catch (_a) {
116
+ return false;
117
+ }
118
+ }
119
+ function hasCookie() {
120
+ if (!isBrowser() || !document.cookie)
121
+ return false;
122
+ document.cookie = '__et_test__=1';
123
+ var ok = document.cookie.indexOf('__et_test__') !== -1;
124
+ document.cookie = '__et_test__=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
125
+ return ok;
126
+ }
127
+ function hasBeacon() {
128
+ return isBrowser() && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function';
129
+ }
130
+ function hasFetch() {
131
+ return isBrowser() && typeof fetch === 'function';
132
+ }
133
+ function hasImage() {
134
+ return isBrowser() && typeof Image === 'function';
135
+ }
136
+
137
+ /**
138
+ * UUID v4 生成(无依赖,使用 crypto.getRandomValues)
139
+ */
140
+ function uuidv4() {
141
+ if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
142
+ var arr = new Uint8Array(16);
143
+ crypto.getRandomValues(arr);
144
+ // RFC 4122 v4
145
+ arr[6] = (arr[6] & 0x0f) | 0x40;
146
+ arr[8] = (arr[8] & 0x3f) | 0x80;
147
+ var hex = Array.from(arr, function (b) { return b.toString(16).padStart(2, '0'); });
148
+ return (hex.slice(0, 4).join('') +
149
+ '-' +
150
+ hex.slice(4, 6).join('') +
151
+ '-' +
152
+ hex.slice(6, 8).join('') +
153
+ '-' +
154
+ hex.slice(8, 10).join('') +
155
+ '-' +
156
+ hex.slice(10, 16).join(''));
157
+ }
158
+ // 退化实现
159
+ var d = Date.now();
160
+ var d2 = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
161
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
162
+ var r = Math.random() * 16;
163
+ if (d > 0) {
164
+ r = (d + r) % 16 | 0;
165
+ d = Math.floor(d / 16);
166
+ }
167
+ else {
168
+ r = (d2 + r) % 16 | 0;
169
+ d2 = Math.floor(d2 / 16);
170
+ }
171
+ return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
172
+ });
173
+ }
174
+
175
+ /**
176
+ * 简易 logger
177
+ */
178
+ var PREFIX = '[EventTracker]';
179
+ var Logger = /** @class */ (function () {
180
+ function Logger(config) {
181
+ this.config = config;
182
+ }
183
+ Logger.prototype.log = function () {
184
+ var args = [];
185
+ for (var _i = 0; _i < arguments.length; _i++) {
186
+ args[_i] = arguments[_i];
187
+ }
188
+ if (this.shouldLog('log', args))
189
+ this.out('log', args);
190
+ };
191
+ Logger.prototype.warn = function () {
192
+ var args = [];
193
+ for (var _i = 0; _i < arguments.length; _i++) {
194
+ args[_i] = arguments[_i];
195
+ }
196
+ if (this.shouldLog('warn', args))
197
+ this.out('warn', args);
198
+ };
199
+ Logger.prototype.error = function () {
200
+ var args = [];
201
+ for (var _i = 0; _i < arguments.length; _i++) {
202
+ args[_i] = arguments[_i];
203
+ }
204
+ if (this.shouldLog('error', args))
205
+ this.out('error', args);
206
+ };
207
+ Logger.prototype.shouldLog = function (level, args) {
208
+ if (this.config.debug === 2) {
209
+ if (level === 'error')
210
+ throw args[0] instanceof Error ? args[0] : new Error(String(args[0]));
211
+ return true;
212
+ }
213
+ if (this.config.debug === 1) {
214
+ if (level === 'warn' || level === 'error')
215
+ return true;
216
+ }
217
+ if (this.config.show_log && level === 'log')
218
+ return true;
219
+ return false;
220
+ };
221
+ Logger.prototype.out = function (level, args) {
222
+ var fn = typeof console !== 'undefined' && console[level] ? console[level] : console.log;
223
+ try {
224
+ fn.call.apply(fn, __spreadArray([console, PREFIX], args, false));
225
+ }
226
+ catch (_a) {
227
+ /* ignore */
228
+ }
229
+ };
230
+ return Logger;
231
+ }());
232
+
233
+ /**
234
+ * 用户身份管理
235
+ * - distinct_id 业务用户 ID
236
+ * - anonymous_id 匿名 ID(首次访问生成,永不重置)
237
+ * - first_id 首次访问 ID
238
+ */
239
+ var KEY_DISTINCT = 'et_distinct_id';
240
+ var KEY_ANONYMOUS = 'et_anonymous_id';
241
+ var KEY_FIRST = 'et_first_id';
242
+ var KEY_LOGIN = 'et_login_id';
243
+ var KEY_ORIGINAL = 'et_original_id';
244
+ var IdentityManager = /** @class */ (function () {
245
+ function IdentityManager(store, opts) {
246
+ if (opts === void 0) { opts = {}; }
247
+ var _this = this;
248
+ this.store = store;
249
+ this.opts = opts;
250
+ this.loginId = '';
251
+ this.originalId = '';
252
+ this.anonymousId = this.readOrCreate(KEY_ANONYMOUS, function () { return uuidv4(); }, this.opts.getAnonymousId, this.opts.setAnonymousId);
253
+ this.firstId = this.readOrCreate(KEY_FIRST, function () { return uuidv4(); }, this.opts.getFirstId, this.opts.setFirstId);
254
+ this.distinctId = this.readOrCreate(KEY_DISTINCT, function () { return _this.anonymousId; }, this.opts.getDistinctId, this.opts.setDistinctId);
255
+ }
256
+ IdentityManager.prototype.readOrCreate = function (key, factory, reader, writer) {
257
+ if (reader) {
258
+ try {
259
+ var v = reader();
260
+ if (v)
261
+ return v;
262
+ }
263
+ catch (_a) {
264
+ /* fall through */
265
+ }
266
+ }
267
+ var stored = this.store.get(key);
268
+ if (stored)
269
+ return stored;
270
+ var fresh = factory();
271
+ this.persist(key, fresh, writer);
272
+ return fresh;
273
+ };
274
+ IdentityManager.prototype.persist = function (key, value, writer) {
275
+ if (writer) {
276
+ try {
277
+ writer(value);
278
+ return;
279
+ }
280
+ catch (_a) {
281
+ /* fall through */
282
+ }
283
+ }
284
+ this.store.set(key, value);
285
+ };
286
+ IdentityManager.prototype.getDistinctId = function () {
287
+ return this.distinctId;
288
+ };
289
+ IdentityManager.prototype.getAnonymousId = function () {
290
+ return this.anonymousId;
291
+ };
292
+ IdentityManager.prototype.getFirstId = function () {
293
+ return this.firstId;
294
+ };
295
+ IdentityManager.prototype.getLoginId = function () {
296
+ return this.loginId;
297
+ };
298
+ IdentityManager.prototype.getOriginalId = function () {
299
+ return this.originalId;
300
+ };
301
+ /** 设置业务用户 ID(保留 anonymous/first 关系) */
302
+ IdentityManager.prototype.identify = function (distinctId, isSave) {
303
+ if (isSave === void 0) { isSave = true; }
304
+ if (distinctId === this.loginId)
305
+ return;
306
+ var prevDistinct = this.distinctId;
307
+ this.distinctId = distinctId;
308
+ this.loginId = distinctId;
309
+ this.originalId = prevDistinct;
310
+ this.persist(KEY_DISTINCT, distinctId, this.opts.setDistinctId);
311
+ if (isSave) {
312
+ this.persist(KEY_LOGIN, distinctId);
313
+ }
314
+ else {
315
+ this.store.remove(KEY_LOGIN);
316
+ }
317
+ this.persist(KEY_ORIGINAL, prevDistinct);
318
+ };
319
+ /** 重置:业务用户登出,回到匿名 */
320
+ IdentityManager.prototype.resetAnonymousIdentity = function (newAnonymousId) {
321
+ this.distinctId = newAnonymousId || this.anonymousId;
322
+ this.loginId = '';
323
+ this.originalId = '';
324
+ this.persist(KEY_DISTINCT, this.distinctId, this.opts.setDistinctId);
325
+ this.store.remove(KEY_LOGIN);
326
+ this.store.remove(KEY_ORIGINAL);
327
+ if (newAnonymousId) {
328
+ this.anonymousId = newAnonymousId;
329
+ this.persist(KEY_ANONYMOUS, newAnonymousId, this.opts.setAnonymousId);
330
+ }
331
+ };
332
+ /** 登入:等价 identify */
333
+ IdentityManager.prototype.login = function (loginId) {
334
+ this.identify(loginId, true);
335
+ };
336
+ /** 登出:保留 anonymous,重置 distinct */
337
+ IdentityManager.prototype.logout = function () {
338
+ this.resetAnonymousIdentity();
339
+ };
340
+ return IdentityManager;
341
+ }());
342
+
343
+ /**
344
+ * SDK 预置事件名与预置属性 key(v1.1:完整对齐神策)
345
+ * 命名风格对齐神策,方便跨平台对接
346
+ */
347
+ /** 预置事件名 */
348
+ var PresetEvent = {
349
+ PageView: '$pageview',
350
+ WebPageLoad: '$WebPageLoad',
351
+ WebPageLeave: '$WebPageLeave',
352
+ WebClick: '$WebClick',
353
+ WebStay: '$WebStay',
354
+ SignUp: '$SignUp',
355
+ BindID: '$BindID',
356
+ UnbindID: '$UnbindID',
357
+ ABTestTrigger: '$ABTestTrigger',
358
+ PlanPopupClick: '$PlanPopupClick',
359
+ PlanPopupDisplay: '$PlanPopupDisplay',
360
+ ChannelLinkReaching: '$ChannelLinkReaching',
361
+ ElementExposure: '$element_exposure',
362
+ ScrollDepth: '$scrolldepth',
363
+ ExitClick: '$exit_click',
364
+ PageLeave: '$page_leave',
365
+ HeatmapEvent: '$heatmap_event',
366
+ };
367
+ /** 预置属性 key */
368
+ var PresetProperty = {
369
+ // 设备 / 环境
370
+ AppId: '$app_id',
371
+ AppVersion: '$app_version',
372
+ Lib: '$lib',
373
+ LibMethod: '$lib_method',
374
+ LibDetail: '$lib_detail',
375
+ LibVersion: '$lib_version',
376
+ ScreenWidth: '$screen_width',
377
+ ScreenHeight: '$screen_height',
378
+ ViewportWidth: '$viewport_width',
379
+ ViewportHeight: '$viewport_height',
380
+ TimezoneOffset: '$timezone_offset',
381
+ UserAgent: '$user_agent',
382
+ Language: '$language',
383
+ FirstBrowserCharset: '$first_browser_charset',
384
+ FirstBrowserLanguage: '$first_browser_language',
385
+ // 位置 / 流量来源
386
+ URL: '$url',
387
+ URLPath: '$url_path',
388
+ Title: '$title',
389
+ Referrer: '$referrer',
390
+ ReferrerHost: '$referrer_host',
391
+ LatestReferrer: '$latest_referrer',
392
+ LatestReferrerHost: '$latest_referrer_host',
393
+ LatestLandingPage: '$latest_landing_page',
394
+ LatestSearchKeyword: '$latest_search_keyword',
395
+ LatestTrafficSourceType: '$latest_traffic_source_type',
396
+ FirstReferrer: '$first_referrer',
397
+ FirstReferrerHost: '$first_referrer_host',
398
+ FirstSearchKeyword: '$first_search_keyword',
399
+ FirstTrafficSourceType: '$first_traffic_source_type',
400
+ FirstVisitTime: '$first_visit_time',
401
+ SearchKeywordId: '$search_keyword_id',
402
+ SearchKeywordIdHash: '$search_keyword_id_hash',
403
+ SearchKeywordIdType: '$search_keyword_id_type',
404
+ IsFirstDay: '$is_first_day',
405
+ IsFirstTime: '$is_first_time',
406
+ // UTM
407
+ LatestUtmSource: '$latest_utm_source',
408
+ LatestUtmMedium: '$latest_utm_medium',
409
+ LatestUtmCampaign: '$latest_utm_campaign',
410
+ LatestUtmContent: '$latest_utm_content',
411
+ LatestUtmTerm: '$latest_utm_term',
412
+ Utms: '$utms',
413
+ // 身份
414
+ DistinctId: '$distinct_id',
415
+ AnonymousId: '$anonymous_id',
416
+ FirstId: '$first_id',
417
+ OriginalId: '$original_id',
418
+ LoginId: '$login_id',
419
+ IsLoginId: '$is_login_id',
420
+ IdentityLoginId: '$identity_login_id',
421
+ IdentityAnonymousId: '$identity_anonymous_id',
422
+ IdentityCookieId: '$identity_cookie_id',
423
+ IdentityEmail: '$identity_email',
424
+ IdentityMobile: '$identity_mobile',
425
+ // 全埋点元素属性
426
+ ElementSelector: '$element_selector',
427
+ ElementPath: '$element_path',
428
+ ElementType: '$element_type',
429
+ ElementId: '$element_id',
430
+ ElementClassName: '$element_class_name',
431
+ ElementName: '$element_name',
432
+ ElementContent: '$element_content',
433
+ ElementTargetUrl: '$element_target_url',
434
+ ElementPosition: '$element_position',
435
+ PageX: '$page_x',
436
+ PageY: '$page_y',
437
+ // 性能
438
+ PageHeight: '$page_height',
439
+ PageResourceSize: '$page_resource_size',
440
+ EventDuration: '$event_duration',
441
+ ViewportPosition: '$viewport_position',
442
+ // 渠道
443
+ LatestWxAdClickId: '$latest_wx_ad_click_id',
444
+ LatestWxAdCallbacks: '$latest_wx_ad_callbacks',
445
+ LatestWxAdHashKey: '$latest_wx_ad_hash_key',
446
+ IsChannelCallbackEvent: '$is_channel_callback_event',
447
+ // 会话
448
+ EventSessionId: '$event_session_id',
449
+ // 元信息
450
+ Time: '$time',
451
+ Event: '$event',
452
+ Option: '$option',
453
+ Project: '$project',
454
+ Token: '$token',
455
+ // 特殊
456
+ SignupMethod: '$signup_method',
457
+ };
458
+ /** 库自身信息 */
459
+ var LIB_NAME = 'event-tracker';
460
+ var LIB_VERSION = '1.1.0';
461
+
462
+ /**
463
+ * 公共属性 / 预置事件属性
464
+ * - 每次事件都会附加的属性
465
+ * - 包括 $lib / $lib_version / $app_id / $screen_width / $viewport_width / $url / $referrer / $latest_utm_* / ...
466
+ *
467
+ * 泛型 TRegisterProps:让用户能约束 register() / preset 的字段类型
468
+ */
469
+ var PresetPropertyBuilder = /** @class */ (function () {
470
+ function PresetPropertyBuilder() {
471
+ /** 当前会话级用户预设属性(来自 config.preset_properties) */
472
+ this.preset = {};
473
+ }
474
+ PresetPropertyBuilder.prototype.setPreset = function (props) {
475
+ this.preset = __assign({}, props);
476
+ };
477
+ /** 一次性写入:每条事件都带 */
478
+ PresetPropertyBuilder.prototype.register = function (props) {
479
+ Object.assign(this.preset, props);
480
+ };
481
+ /** 一次性写入:只在同会话内首次写入生效 */
482
+ PresetPropertyBuilder.prototype.registerOnce = function (props) {
483
+ for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) {
484
+ var k = _a[_i];
485
+ if (!(k in this.preset)) {
486
+ this.preset[k] = props[k];
487
+ }
488
+ }
489
+ };
490
+ PresetPropertyBuilder.prototype.clearRegister = function () {
491
+ this.preset = {};
492
+ };
493
+ PresetPropertyBuilder.prototype.unsetRegister = function (key) {
494
+ if (Object.prototype.hasOwnProperty.call(this.preset, key)) {
495
+ delete this.preset[key];
496
+ }
497
+ };
498
+ PresetPropertyBuilder.prototype.getPreset = function () {
499
+ return __assign({}, this.preset);
500
+ };
501
+ /** 计算一次最终合并的属性(含 lib 自身信息、UA、URL 等) */
502
+ PresetPropertyBuilder.prototype.build = function (config, baseProps) {
503
+ if (baseProps === void 0) { baseProps = {}; }
504
+ var out = __assign(__assign({}, this.preset), baseProps);
505
+ out[PresetProperty.Lib] = config.lib_name || 'event-tracker';
506
+ out[PresetProperty.LibVersion] = config.lib_version || '0.1.0';
507
+ if (config.app_id)
508
+ out[PresetProperty.AppId] = config.app_id;
509
+ if (config.app_version)
510
+ out[PresetProperty.AppVersion] = config.app_version;
511
+ if (isBrowser()) {
512
+ try {
513
+ out[PresetProperty.URL] = location.href;
514
+ out[PresetProperty.URLPath] = location.pathname;
515
+ out[PresetProperty.Title] = document.title;
516
+ if (document.referrer) {
517
+ out[PresetProperty.Referrer] = document.referrer;
518
+ }
519
+ var sw = (window.screen && window.screen.width) || 0;
520
+ var sh = (window.screen && window.screen.height) || 0;
521
+ if (sw)
522
+ out[PresetProperty.ScreenWidth] = sw;
523
+ if (sh)
524
+ out[PresetProperty.ScreenHeight] = sh;
525
+ var vw = window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || 0;
526
+ var vh = window.innerHeight || (document.documentElement && document.documentElement.clientHeight) || 0;
527
+ if (vw)
528
+ out[PresetProperty.ViewportWidth] = vw;
529
+ if (vh)
530
+ out[PresetProperty.ViewportHeight] = vh;
531
+ if (navigator) {
532
+ out[PresetProperty.Language] = navigator.language || '';
533
+ out[PresetProperty.UserAgent] = config.user_agent || navigator.userAgent || '';
534
+ out[PresetProperty.TimezoneOffset] = new Date().getTimezoneOffset();
535
+ }
536
+ }
537
+ catch (_a) {
538
+ /* ignore in non-browser */
539
+ }
540
+ }
541
+ // utm 参数
542
+ if (isBrowser() && config.is_collect_utm !== false) {
543
+ var utm = parseUtm(location.search);
544
+ for (var _i = 0, _b = Object.entries(utm); _i < _b.length; _i++) {
545
+ var _c = _b[_i], k = _c[0], v = _c[1];
546
+ var propKey = PresetProperty.LatestUtmSource.replace('Source', capitalize(k.replace('utm_', '')));
547
+ out[propKey] = v;
548
+ }
549
+ out[PresetProperty.LatestTrafficSourceType] = out[PresetProperty.LatestUtmSource] ? '付费广告流量' : '直接流量';
550
+ }
551
+ return out;
552
+ };
553
+ return PresetPropertyBuilder;
554
+ }());
555
+ function capitalize(s) {
556
+ return s.charAt(0).toUpperCase() + s.slice(1);
557
+ }
558
+ function parseUtm(query) {
559
+ var out = {};
560
+ if (!query)
561
+ return out;
562
+ var s = query.startsWith('?') ? query.substring(1) : query;
563
+ var parts = s.split('&');
564
+ for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {
565
+ var p = parts_1[_i];
566
+ var eq = p.indexOf('=');
567
+ if (eq === -1)
568
+ continue;
569
+ var k = decodeURIComponent(p.substring(0, eq));
570
+ var v = decodeURIComponent(p.substring(eq + 1));
571
+ if (k.startsWith('utm_'))
572
+ out[k] = v;
573
+ }
574
+ return out;
575
+ }
576
+
577
+ /**
578
+ * 统一 KV 存储抽象
579
+ * - CookieStore
580
+ * - LocalStorageStore
581
+ * - SessionStorageStore
582
+ * - MemoryStore
583
+ */
584
+ var MemoryStore = /** @class */ (function () {
585
+ function MemoryStore() {
586
+ this.backing = new Map();
587
+ }
588
+ MemoryStore.prototype.get = function (key) {
589
+ return this.backing.has(key) ? this.backing.get(key) : null;
590
+ };
591
+ MemoryStore.prototype.set = function (key, value) {
592
+ this.backing.set(key, value);
593
+ };
594
+ MemoryStore.prototype.remove = function (key) {
595
+ this.backing.delete(key);
596
+ };
597
+ MemoryStore.prototype.keys = function (prefix) {
598
+ if (prefix === void 0) { prefix = ''; }
599
+ return Array.from(this.backing.keys()).filter(function (k) { return k.startsWith(prefix); });
600
+ };
601
+ return MemoryStore;
602
+ }());
603
+ var CookieStore = /** @class */ (function () {
604
+ function CookieStore(domain, crossSubdomain, secure) {
605
+ if (crossSubdomain === void 0) { crossSubdomain = true; }
606
+ if (secure === void 0) { secure = false; }
607
+ this.domain = domain;
608
+ this.crossSubdomain = crossSubdomain;
609
+ this.secure = secure;
610
+ }
611
+ CookieStore.prototype.get = function (key) {
612
+ var name = encodeURIComponent(key) + '=';
613
+ var ca = document.cookie.split(';');
614
+ for (var i = 0; i < ca.length; i++) {
615
+ var c = ca[i];
616
+ while (c.charAt(0) === ' ')
617
+ c = c.substring(1);
618
+ if (c.indexOf(name) === 0) {
619
+ return decodeURIComponent(c.substring(name.length, c.length));
620
+ }
621
+ }
622
+ return null;
623
+ };
624
+ CookieStore.prototype.set = function (key, value, expiresDays) {
625
+ if (expiresDays === void 0) { expiresDays = 365; }
626
+ var expires = '';
627
+ if (expiresDays) {
628
+ var date = new Date();
629
+ date.setTime(date.getTime() + expiresDays * 24 * 60 * 60 * 1000);
630
+ expires = '; expires=' + date.toUTCString();
631
+ }
632
+ var cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) + expires + '; path=/';
633
+ if (this.domain && this.crossSubdomain) {
634
+ cookie += '; domain=' + this.domain;
635
+ }
636
+ if (this.secure)
637
+ cookie += '; Secure';
638
+ document.cookie = cookie;
639
+ };
640
+ CookieStore.prototype.remove = function (key) {
641
+ this.set(key, '', -1);
642
+ };
643
+ CookieStore.prototype.keys = function (prefix) {
644
+ if (prefix === void 0) { prefix = ''; }
645
+ return document.cookie.split(';').map(function (c) {
646
+ var eq = c.indexOf('=');
647
+ return decodeURIComponent((eq > -1 ? c.substring(0, eq) : c).trim());
648
+ }).filter(function (k) { return k.startsWith(prefix); });
649
+ };
650
+ return CookieStore;
651
+ }());
652
+ var LocalStorageStore = /** @class */ (function () {
653
+ function LocalStorageStore() {
654
+ }
655
+ LocalStorageStore.prototype.get = function (key) {
656
+ try {
657
+ return window.localStorage.getItem(key);
658
+ }
659
+ catch (_a) {
660
+ return null;
661
+ }
662
+ };
663
+ LocalStorageStore.prototype.set = function (key, value) {
664
+ try {
665
+ window.localStorage.setItem(key, value);
666
+ }
667
+ catch (_a) {
668
+ /* quota exceeded, ignore */
669
+ }
670
+ };
671
+ LocalStorageStore.prototype.remove = function (key) {
672
+ try {
673
+ window.localStorage.removeItem(key);
674
+ }
675
+ catch (_a) {
676
+ /* ignore */
677
+ }
678
+ };
679
+ LocalStorageStore.prototype.keys = function (prefix) {
680
+ if (prefix === void 0) { prefix = ''; }
681
+ var out = [];
682
+ try {
683
+ for (var i = 0; i < window.localStorage.length; i++) {
684
+ var k = window.localStorage.key(i);
685
+ if (k && k.startsWith(prefix))
686
+ out.push(k);
687
+ }
688
+ }
689
+ catch (_a) {
690
+ /* ignore */
691
+ }
692
+ return out;
693
+ };
694
+ return LocalStorageStore;
695
+ }());
696
+ /** 自动选最合适的存储(cookie 不可用 → localStorage → memory) */
697
+ function autoStore(opts) {
698
+ if (hasCookie())
699
+ return new CookieStore(opts === null || opts === void 0 ? void 0 : opts.cookieDomain, opts === null || opts === void 0 ? void 0 : opts.cookieCrossSubdomain, opts === null || opts === void 0 ? void 0 : opts.secureCookie);
700
+ if (hasLocalStorage())
701
+ return new LocalStorageStore();
702
+ return new MemoryStore();
703
+ }
704
+
705
+ /**
706
+ * 序列化 & 编码
707
+ */
708
+ function encodeEvent(event, encryptor) {
709
+ var json = JSON.stringify(event);
710
+ return encryptor ? encryptor(json) : json;
711
+ }
712
+ function encodeBatch(events, encryptor) {
713
+ var json = JSON.stringify(events);
714
+ return encryptor ? encryptor(json) : json;
715
+ }
716
+ function buildUrl(serverUrl, payload, contentType) {
717
+ var base = Array.isArray(serverUrl) ? serverUrl[0] || '' : serverUrl;
718
+ var has = base.indexOf('?') >= 0;
719
+ {
720
+ return "".concat(base).concat(has ? '&' : '?', "data=").concat(encodeURIComponent(payload));
721
+ }
722
+ }
723
+
724
+ /**
725
+ * Image 发送器(1x1 gif 兜底,兼容性最好)
726
+ */
727
+ /**
728
+ * 间接构造 Image(避免编译期绑定到 module-local Image)
729
+ * 测试可以通过覆盖 globalThis.Image 来 spy
730
+ */
731
+ function makeImage() {
732
+ // 用 eval 避免编译器静态解析 Image 标识符
733
+ // eslint-disable-next-line no-eval
734
+ var g = (0, eval)('this') || globalThis;
735
+ var Ctor = g.Image;
736
+ if (typeof Ctor === 'function')
737
+ return new Ctor();
738
+ return { src: '', onload: undefined, onerror: undefined, onabort: undefined };
739
+ }
740
+ var ImageSender = /** @class */ (function () {
741
+ function ImageSender() {
742
+ this.name = 'image';
743
+ }
744
+ ImageSender.prototype.canUse = function () {
745
+ return hasImage();
746
+ };
747
+ ImageSender.prototype.send = function (event, ctx) {
748
+ return __awaiter(this, void 0, void 0, function () {
749
+ var payload, url_1, e_1;
750
+ return __generator(this, function (_a) {
751
+ switch (_a.label) {
752
+ case 0:
753
+ _a.trys.push([0, 3, , 4]);
754
+ return [4 /*yield*/, Promise.resolve(encodeEvent(event, ctx.config.encryptor))];
755
+ case 1:
756
+ payload = _a.sent();
757
+ url_1 = buildUrl(ctx.config.server_url || '', payload);
758
+ return [4 /*yield*/, new Promise(function (resolve) {
759
+ var img = makeImage();
760
+ var done = function () { return resolve(); };
761
+ img.onload = done;
762
+ img.onerror = done;
763
+ img.onabort = done;
764
+ img.src = url_1;
765
+ setTimeout(done, 3000);
766
+ })];
767
+ case 2:
768
+ _a.sent();
769
+ return [3 /*break*/, 4];
770
+ case 3:
771
+ e_1 = _a.sent();
772
+ ctx.logger.error('ImageSender failed:', e_1);
773
+ return [3 /*break*/, 4];
774
+ case 4: return [2 /*return*/];
775
+ }
776
+ });
777
+ });
778
+ };
779
+ return ImageSender;
780
+ }());
781
+
782
+ /**
783
+ * Beacon 发送器(navigator.sendBeacon)
784
+ */
785
+ var BeaconSender = /** @class */ (function () {
786
+ function BeaconSender() {
787
+ this.name = 'beacon';
788
+ }
789
+ BeaconSender.prototype.canUse = function () {
790
+ return hasBeacon();
791
+ };
792
+ BeaconSender.prototype.send = function (event, ctx) {
793
+ return __awaiter(this, void 0, void 0, function () {
794
+ var payload, url, blob, ok, e_1;
795
+ return __generator(this, function (_a) {
796
+ switch (_a.label) {
797
+ case 0:
798
+ _a.trys.push([0, 2, , 3]);
799
+ return [4 /*yield*/, Promise.resolve(encodeEvent(event, ctx.config.encryptor))];
800
+ case 1:
801
+ payload = _a.sent();
802
+ url = buildUrl(ctx.config.server_url || '', payload);
803
+ blob = new Blob([payload], { type: 'application/x-www-form-urlencoded' });
804
+ ok = navigator.sendBeacon(url, blob);
805
+ if (!ok) {
806
+ ctx.logger.warn('BeaconSender returned false, URL:', url.slice(0, 80));
807
+ }
808
+ return [3 /*break*/, 3];
809
+ case 2:
810
+ e_1 = _a.sent();
811
+ ctx.logger.error('BeaconSender failed:', e_1);
812
+ return [3 /*break*/, 3];
813
+ case 3: return [2 /*return*/];
814
+ }
815
+ });
816
+ });
817
+ };
818
+ return BeaconSender;
819
+ }());
820
+
821
+ /**
822
+ * Ajax 发送器(fetch + keepalive)
823
+ */
824
+ var AjaxSender = /** @class */ (function () {
825
+ function AjaxSender() {
826
+ this.name = 'ajax';
827
+ }
828
+ AjaxSender.prototype.canUse = function () {
829
+ return hasFetch();
830
+ };
831
+ AjaxSender.prototype.send = function (event, ctx) {
832
+ return __awaiter(this, void 0, void 0, function () {
833
+ var payload, url, body, res, e_1;
834
+ var _a, _b, _c, _d, _e, _f;
835
+ return __generator(this, function (_g) {
836
+ switch (_g.label) {
837
+ case 0:
838
+ _g.trys.push([0, 3, , 4]);
839
+ return [4 /*yield*/, Promise.resolve(encodeEvent(event, ctx.config.encryptor))];
840
+ case 1:
841
+ payload = _g.sent();
842
+ url = Array.isArray(ctx.config.server_url) ? ctx.config.server_url[0] : ctx.config.server_url;
843
+ body = "data=".concat(encodeURIComponent(payload));
844
+ return [4 /*yield*/, fetch(url, {
845
+ method: 'POST',
846
+ mode: 'cors',
847
+ credentials: 'omit',
848
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
849
+ body: body,
850
+ keepalive: true,
851
+ })];
852
+ case 2:
853
+ res = _g.sent();
854
+ if (!res.ok) {
855
+ ctx.logger.warn('AjaxSender non-2xx:', res.status);
856
+ (_b = (_a = ctx.config).on_error) === null || _b === void 0 ? void 0 : _b.call(_a, new Error("HTTP ".concat(res.status)), event);
857
+ }
858
+ else {
859
+ (_d = (_c = ctx.config).callback) === null || _d === void 0 ? void 0 : _d.call(_c, event);
860
+ }
861
+ return [3 /*break*/, 4];
862
+ case 3:
863
+ e_1 = _g.sent();
864
+ ctx.logger.error('AjaxSender failed:', e_1);
865
+ (_f = (_e = ctx.config).on_error) === null || _f === void 0 ? void 0 : _f.call(_e, e_1, event);
866
+ return [3 /*break*/, 4];
867
+ case 4: return [2 /*return*/];
868
+ }
869
+ });
870
+ });
871
+ };
872
+ AjaxSender.prototype.sendBatch = function (events, ctx) {
873
+ return __awaiter(this, void 0, void 0, function () {
874
+ var payload, url, body, e_2;
875
+ return __generator(this, function (_a) {
876
+ switch (_a.label) {
877
+ case 0:
878
+ _a.trys.push([0, 3, , 4]);
879
+ return [4 /*yield*/, Promise.resolve(encodeBatch(events, ctx.config.encryptor))];
880
+ case 1:
881
+ payload = _a.sent();
882
+ url = Array.isArray(ctx.config.server_url) ? ctx.config.server_url[0] : ctx.config.server_url;
883
+ body = "data=".concat(encodeURIComponent(payload), "&batch=1");
884
+ return [4 /*yield*/, fetch(url, {
885
+ method: 'POST',
886
+ mode: 'cors',
887
+ credentials: 'omit',
888
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
889
+ body: body,
890
+ keepalive: true,
891
+ })];
892
+ case 2:
893
+ _a.sent();
894
+ return [3 /*break*/, 4];
895
+ case 3:
896
+ e_2 = _a.sent();
897
+ ctx.logger.error('AjaxSender.sendBatch failed:', e_2);
898
+ return [3 /*break*/, 4];
899
+ case 4: return [2 /*return*/];
900
+ }
901
+ });
902
+ });
903
+ };
904
+ return AjaxSender;
905
+ }());
906
+
907
+ var ajaxSender = /*#__PURE__*/Object.freeze({
908
+ __proto__: null,
909
+ AjaxSender: AjaxSender
910
+ });
911
+
912
+ /**
913
+ * Batch 发送器:事件先入队到 localStorage,定时或达上限时一次性 POST
914
+ */
915
+ var QUEUE_KEY$1 = 'et_batch_queue';
916
+ var DEFAULT_INTERVAL = 6000;
917
+ var BatchSender = /** @class */ (function () {
918
+ function BatchSender() {
919
+ this.name = 'batch';
920
+ this.timer = null;
921
+ }
922
+ BatchSender.prototype.canUse = function () {
923
+ return typeof window !== 'undefined' && !!window.localStorage;
924
+ };
925
+ BatchSender.prototype.start = function (ctx) {
926
+ var _this = this;
927
+ if (!this.canUse())
928
+ return;
929
+ var bs = ctx.config.batch_send;
930
+ var interval = (typeof bs === 'object' && (bs === null || bs === void 0 ? void 0 : bs.send_interval)) || ctx.config.datasend_timeout || DEFAULT_INTERVAL;
931
+ this.timer = setInterval(function () { return _this.flush(ctx); }, interval);
932
+ };
933
+ BatchSender.prototype.stop = function () {
934
+ if (this.timer)
935
+ clearInterval(this.timer);
936
+ this.timer = null;
937
+ };
938
+ BatchSender.prototype.send = function (event, ctx) {
939
+ return __awaiter(this, void 0, void 0, function () {
940
+ var max, queue;
941
+ var _a;
942
+ return __generator(this, function (_b) {
943
+ switch (_b.label) {
944
+ case 0:
945
+ this.enqueue(event, ctx.config);
946
+ max = (typeof ctx.config.batch_send === 'object' && ((_a = ctx.config.batch_send) === null || _a === void 0 ? void 0 : _a.max_length)) || 50;
947
+ queue = this.read(ctx.config);
948
+ if (!(queue.length >= max)) return [3 /*break*/, 2];
949
+ return [4 /*yield*/, this.flush(ctx)];
950
+ case 1:
951
+ _b.sent();
952
+ _b.label = 2;
953
+ case 2: return [2 /*return*/];
954
+ }
955
+ });
956
+ });
957
+ };
958
+ BatchSender.prototype.enqueue = function (event, config) {
959
+ var q = this.read(config);
960
+ q.push(event);
961
+ this.write(q, config);
962
+ };
963
+ BatchSender.prototype.read = function (config) {
964
+ try {
965
+ var raw = window.localStorage.getItem(QUEUE_KEY$1);
966
+ if (!raw)
967
+ return [];
968
+ var arr = JSON.parse(raw);
969
+ return Array.isArray(arr) ? arr : [];
970
+ }
971
+ catch (_a) {
972
+ return [];
973
+ }
974
+ };
975
+ BatchSender.prototype.write = function (queue, config) {
976
+ try {
977
+ var payload = encodeBatch(queue, config.encryptor);
978
+ // 异步时(Promise<string>)暂存 JSON 原文
979
+ var str = typeof payload === 'string' ? payload : JSON.stringify(queue);
980
+ window.localStorage.setItem(QUEUE_KEY$1, str);
981
+ }
982
+ catch (_a) {
983
+ // quota exceed, drop oldest half
984
+ var half = queue.slice(Math.floor(queue.length / 2));
985
+ try {
986
+ var payload2 = encodeBatch(half, config.encryptor);
987
+ var str2 = typeof payload2 === 'string' ? payload2 : JSON.stringify(half);
988
+ window.localStorage.setItem(QUEUE_KEY$1, str2);
989
+ }
990
+ catch (_b) {
991
+ /* ignore */
992
+ }
993
+ }
994
+ };
995
+ BatchSender.prototype.flush = function (ctx) {
996
+ return __awaiter(this, void 0, void 0, function () {
997
+ var queue, ajax, beacon, _i, queue_1, ev;
998
+ return __generator(this, function (_a) {
999
+ switch (_a.label) {
1000
+ case 0:
1001
+ queue = this.read(ctx.config);
1002
+ if (!queue.length)
1003
+ return [2 /*return*/];
1004
+ // 清空队列
1005
+ try {
1006
+ window.localStorage.removeItem(QUEUE_KEY$1);
1007
+ }
1008
+ catch (_b) {
1009
+ /* ignore */
1010
+ }
1011
+ return [4 /*yield*/, Promise.resolve().then(function () { return ajaxSender; })];
1012
+ case 1:
1013
+ ajax = new (_a.sent()).AjaxSender();
1014
+ if (!ajax.canUse()) return [3 /*break*/, 3];
1015
+ return [4 /*yield*/, ajax.sendBatch(queue, ctx)];
1016
+ case 2:
1017
+ _a.sent();
1018
+ return [3 /*break*/, 7];
1019
+ case 3:
1020
+ beacon = new BeaconSender();
1021
+ _i = 0, queue_1 = queue;
1022
+ _a.label = 4;
1023
+ case 4:
1024
+ if (!(_i < queue_1.length)) return [3 /*break*/, 7];
1025
+ ev = queue_1[_i];
1026
+ return [4 /*yield*/, beacon.send(ev, ctx)];
1027
+ case 5:
1028
+ _a.sent();
1029
+ _a.label = 6;
1030
+ case 6:
1031
+ _i++;
1032
+ return [3 /*break*/, 4];
1033
+ case 7: return [2 /*return*/];
1034
+ }
1035
+ });
1036
+ });
1037
+ };
1038
+ return BatchSender;
1039
+ }());
1040
+
1041
+ /**
1042
+ * 事件数据拦截 Stage
1043
+ * - 3 个 Stage: buildDataStage / businessStage / sendDataStage
1044
+ * - 按 priority 升序执行
1045
+ * - cancellationToken 可中断后续
1046
+ *
1047
+ * 灵感来自神策 sa-sdk-javascript 内部实现
1048
+ */
1049
+ var StageChain = /** @class */ (function () {
1050
+ function StageChain(stage) {
1051
+ this.stage = stage;
1052
+ this.list = [];
1053
+ this.stopped = false;
1054
+ this.stopFlag = false;
1055
+ }
1056
+ /** 注册一个拦截器(同名覆盖) */
1057
+ StageChain.prototype.register = function (spec) {
1058
+ var _a;
1059
+ this.unregister(spec.name);
1060
+ this.list.push({ priority: (_a = spec.priority) !== null && _a !== void 0 ? _a : 100, spec: spec });
1061
+ this.list.sort(function (a, b) { return a.priority - b.priority; });
1062
+ };
1063
+ /** 移除一个拦截器 */
1064
+ StageChain.prototype.unregister = function (name) {
1065
+ this.list = this.list.filter(function (item) { return item.spec.name !== name; });
1066
+ };
1067
+ /** 获取所有拦截器名(调试用) */
1068
+ StageChain.prototype.list2 = function () {
1069
+ return this.list.map(function (i) { return "".concat(i.spec.name, "@").concat(i.priority); });
1070
+ };
1071
+ /** 重置 stage(含清除停止标志) */
1072
+ StageChain.prototype.reset = function () {
1073
+ this.stopFlag = false;
1074
+ };
1075
+ /** 主入口:顺序执行 */
1076
+ StageChain.prototype.run = function (input, owner) {
1077
+ var _this = this;
1078
+ this.stopFlag = false;
1079
+ var ctx = {
1080
+ data: input,
1081
+ meta: { stage: this.stage },
1082
+ cancellationToken: {
1083
+ stop: function () { _this.stopFlag = true; },
1084
+ },
1085
+ owner: owner,
1086
+ };
1087
+ for (var _i = 0, _a = this.list; _i < _a.length; _i++) {
1088
+ var spec = _a[_i].spec;
1089
+ if (this.stopFlag)
1090
+ break;
1091
+ var out = spec.entry(ctx);
1092
+ if (out !== undefined) {
1093
+ ctx.data = out;
1094
+ }
1095
+ }
1096
+ return ctx.data;
1097
+ };
1098
+ return StageChain;
1099
+ }());
1100
+
1101
+ /**
1102
+ * 7 个生命周期钩子
1103
+ * - sdkReady SDK 加载完成,init 之前
1104
+ * - sdkInitPara init 处理参数时(合并前)
1105
+ * - sdkAfterInitPara init 处理参数后
1106
+ * - sdkInitAPI API 挂到 window 之前
1107
+ * - sdkAfterInitAPI API 挂到 window 之后
1108
+ * - changeDistinctId distinct 变化时
1109
+ * - switch 配置切换(多实例)
1110
+ */
1111
+ var Lifecycle = /** @class */ (function () {
1112
+ function Lifecycle() {
1113
+ this.hooks = {
1114
+ sdkReady: new Set(),
1115
+ sdkInitPara: new Set(),
1116
+ sdkAfterInitPara: new Set(),
1117
+ sdkInitAPI: new Set(),
1118
+ sdkAfterInitAPI: new Set(),
1119
+ changeDistinctId: new Set(),
1120
+ switch: new Set(),
1121
+ };
1122
+ }
1123
+ /** 注册钩子,返回取消函数 */
1124
+ Lifecycle.prototype.on = function (name, fn) {
1125
+ var _this = this;
1126
+ if (!this.hooks[name]) {
1127
+ throw new Error("[Lifecycle] unknown hook: ".concat(name));
1128
+ }
1129
+ this.hooks[name].add(fn);
1130
+ return function () { return _this.off(name, fn); };
1131
+ };
1132
+ /** 取消钩子 */
1133
+ Lifecycle.prototype.off = function (name, fn) {
1134
+ var _a;
1135
+ (_a = this.hooks[name]) === null || _a === void 0 ? void 0 : _a.delete(fn);
1136
+ };
1137
+ /** 同步触发钩子(catch 内部错误,不向外抛) */
1138
+ Lifecycle.prototype.emit = function (name) {
1139
+ var args = [];
1140
+ for (var _i = 1; _i < arguments.length; _i++) {
1141
+ args[_i - 1] = arguments[_i];
1142
+ }
1143
+ var set = this.hooks[name];
1144
+ if (!set)
1145
+ return;
1146
+ // 拷贝一份以容忍 off() 出现在回调中
1147
+ for (var _a = 0, _b = Array.from(set); _a < _b.length; _a++) {
1148
+ var fn = _b[_a];
1149
+ try {
1150
+ fn.apply(void 0, args);
1151
+ }
1152
+ catch (e) {
1153
+ // 钩子内错误吞掉,避免影响主流程
1154
+ if (typeof console !== 'undefined' && console.error) {
1155
+ console.error("[EventTracker] hook ".concat(name, " error:"), e);
1156
+ }
1157
+ }
1158
+ }
1159
+ };
1160
+ /** 移除全部钩子 */
1161
+ Lifecycle.prototype.clear = function () {
1162
+ for (var _i = 0, _a = Object.keys(this.hooks); _i < _a.length; _i++) {
1163
+ var k = _a[_i];
1164
+ this.hooks[k].clear();
1165
+ }
1166
+ };
1167
+ /** 调试用 */
1168
+ Lifecycle.prototype.inspect = function () {
1169
+ var out = {};
1170
+ for (var _i = 0, _a = Object.keys(this.hooks); _i < _a.length; _i++) {
1171
+ var k = _a[_i];
1172
+ out[k] = this.hooks[k].size;
1173
+ }
1174
+ return out;
1175
+ };
1176
+ return Lifecycle;
1177
+ }());
1178
+
1179
+ var PluginRegistry = /** @class */ (function () {
1180
+ function PluginRegistry() {
1181
+ /** 7 个生命周期钩子 */
1182
+ this.lifecycle = new Lifecycle();
1183
+ /** 已安装的 plugin 列表 */
1184
+ this.plugins = [];
1185
+ /** commonWays:quick() 调用入口 */
1186
+ this.commonWays = {};
1187
+ this.stages = {
1188
+ buildDataStage: new StageChain('buildDataStage'),
1189
+ businessStage: new StageChain('businessStage'),
1190
+ sendDataStage: new StageChain('sendDataStage'),
1191
+ };
1192
+ }
1193
+ /** hook 注册快捷方法(向后兼容 + 简化插件作者) */
1194
+ PluginRegistry.prototype.on = function (name, fn) {
1195
+ return this.lifecycle.on(name, fn);
1196
+ };
1197
+ PluginRegistry.prototype.off = function (name, fn) {
1198
+ this.lifecycle.off(name, fn);
1199
+ };
1200
+ PluginRegistry.prototype.emit = function (name) {
1201
+ var _a;
1202
+ var args = [];
1203
+ for (var _i = 1; _i < arguments.length; _i++) {
1204
+ args[_i - 1] = arguments[_i];
1205
+ }
1206
+ (_a = this.lifecycle).emit.apply(_a, __spreadArray([name], args, false));
1207
+ };
1208
+ /** stage 注册拦截器 */
1209
+ PluginRegistry.prototype.intercept = function (stage, spec) {
1210
+ this.stages[stage].register(spec);
1211
+ };
1212
+ /** 兼容旧 API:invoke(name, ...args) = emit(name, ...args) */
1213
+ PluginRegistry.prototype.invoke = function (name) {
1214
+ var _a;
1215
+ var args = [];
1216
+ for (var _i = 1; _i < arguments.length; _i++) {
1217
+ args[_i - 1] = arguments[_i];
1218
+ }
1219
+ (_a = this.lifecycle).emit.apply(_a, __spreadArray([name], args, false));
1220
+ };
1221
+ /** 安装 plugin */
1222
+ PluginRegistry.prototype.install = function (plugin, ctx) {
1223
+ // 同名先卸载
1224
+ var idx = this.plugins.findIndex(function (p) { return p.name === plugin.name; });
1225
+ if (idx >= 0) {
1226
+ this.uninstall(this.plugins[idx]);
1227
+ this.plugins.splice(idx, 1);
1228
+ }
1229
+ this.plugins.push(plugin);
1230
+ if (plugin.install) {
1231
+ var full = __assign(__assign({}, ctx), { registry: this, stages: this.stages });
1232
+ try {
1233
+ plugin.install(full);
1234
+ }
1235
+ catch (e) {
1236
+ if (typeof console !== 'undefined' && console.error) {
1237
+ console.error("[EventTracker] plugin ".concat(plugin.name, " install failed:"), e);
1238
+ }
1239
+ }
1240
+ }
1241
+ };
1242
+ /** 卸载 plugin */
1243
+ PluginRegistry.prototype.uninstall = function (plugin, ctx) {
1244
+ var _a;
1245
+ try {
1246
+ (_a = plugin.uninstall) === null || _a === void 0 ? void 0 : _a.call(plugin, ctx);
1247
+ }
1248
+ catch (e) {
1249
+ if (typeof console !== 'undefined' && console.error) {
1250
+ console.error("[EventTracker] plugin ".concat(plugin.name, " uninstall failed:"), e);
1251
+ }
1252
+ }
1253
+ };
1254
+ /** 列出已安装 */
1255
+ PluginRegistry.prototype.list = function () {
1256
+ return this.plugins.map(function (p) { return p.name; });
1257
+ };
1258
+ return PluginRegistry;
1259
+ }());
1260
+
1261
+ /**
1262
+ * 跨实例 channel
1263
+ * - 用 BroadcastChannel 实现,浏览器跨 tab 通信
1264
+ * - 降级到 localStorage(无 BroadcastChannel 时)
1265
+ * - 主要用于:
1266
+ * - 共享 disabled 状态
1267
+ * - 共享 changeDistinctId
1268
+ * - 共享 session_id
1269
+ */
1270
+ var CHANNEL_NAME = 'event-tracker';
1271
+ var InstanceChannel = /** @class */ (function () {
1272
+ function InstanceChannel() {
1273
+ this.bc = null;
1274
+ this.listeners = new Set();
1275
+ this.sourceId = "".concat(Date.now(), "-").concat(Math.random().toString(36).slice(2, 8));
1276
+ this.localStorageKey = "et_channel_".concat(CHANNEL_NAME);
1277
+ this.pollTimer = null;
1278
+ this.initBroadcastChannel();
1279
+ if (!this.bc)
1280
+ this.initLocalStoragePolling();
1281
+ }
1282
+ InstanceChannel.prototype.initBroadcastChannel = function () {
1283
+ var _this = this;
1284
+ if (typeof BroadcastChannel === 'undefined')
1285
+ return;
1286
+ try {
1287
+ this.bc = new BroadcastChannel(CHANNEL_NAME);
1288
+ this.bc.onmessage = function (ev) {
1289
+ var _a;
1290
+ if (((_a = ev.data) === null || _a === void 0 ? void 0 : _a.source) === _this.sourceId)
1291
+ return;
1292
+ _this.listeners.forEach(function (fn) { return fn(ev.data); });
1293
+ };
1294
+ }
1295
+ catch (_a) {
1296
+ this.bc = null;
1297
+ }
1298
+ };
1299
+ InstanceChannel.prototype.initLocalStoragePolling = function () {
1300
+ var _this = this;
1301
+ // storage event 兼容性
1302
+ if (typeof window !== 'undefined') {
1303
+ window.addEventListener('storage', function (ev) {
1304
+ if (ev.key !== _this.localStorageKey || !ev.newValue)
1305
+ return;
1306
+ try {
1307
+ var msg_1 = JSON.parse(ev.newValue);
1308
+ if (msg_1.source === _this.sourceId)
1309
+ return;
1310
+ _this.listeners.forEach(function (fn) { return fn(msg_1); });
1311
+ }
1312
+ catch (_a) {
1313
+ /* ignore */
1314
+ }
1315
+ });
1316
+ }
1317
+ };
1318
+ /** 发送消息 */
1319
+ InstanceChannel.prototype.post = function (type, payload) {
1320
+ var msg = { type: type, source: this.sourceId, payload: payload };
1321
+ if (this.bc) {
1322
+ try {
1323
+ this.bc.postMessage(msg);
1324
+ }
1325
+ catch ( /* ignore */_a) { /* ignore */ }
1326
+ }
1327
+ else {
1328
+ // localStorage 降级
1329
+ try {
1330
+ localStorage.setItem(this.localStorageKey, JSON.stringify(msg));
1331
+ }
1332
+ catch ( /* ignore quota */_b) { /* ignore quota */ }
1333
+ }
1334
+ };
1335
+ /** 监听消息,返回取消函数 */
1336
+ InstanceChannel.prototype.on = function (fn) {
1337
+ var _this = this;
1338
+ this.listeners.add(fn);
1339
+ return function () { return _this.listeners.delete(fn); };
1340
+ };
1341
+ /** 关闭 */
1342
+ InstanceChannel.prototype.close = function () {
1343
+ if (this.bc) {
1344
+ try {
1345
+ this.bc.close();
1346
+ }
1347
+ catch ( /* ignore */_a) { /* ignore */ }
1348
+ this.bc = null;
1349
+ }
1350
+ this.listeners.clear();
1351
+ if (this.pollTimer) {
1352
+ clearInterval(this.pollTimer);
1353
+ this.pollTimer = null;
1354
+ }
1355
+ };
1356
+ /** 实例 id(用于调试 / 验证) */
1357
+ InstanceChannel.prototype.getSourceId = function () {
1358
+ return this.sourceId;
1359
+ };
1360
+ return InstanceChannel;
1361
+ }());
1362
+
1363
+ /**
1364
+ * 失败重试 + 退避
1365
+ * - 包装单个 send 调用
1366
+ * - 退避: 1s → 2s → 4s
1367
+ * - 超过 max_retry_duration 放弃
1368
+ * - 失败事件可写入失败队列(待用)
1369
+ */
1370
+ var RetryPolicy = /** @class */ (function () {
1371
+ function RetryPolicy(config, logger) {
1372
+ this.config = config;
1373
+ this.logger = logger;
1374
+ }
1375
+ /** 计算本次重试要 sleep 多久(指数退避) */
1376
+ RetryPolicy.prototype.backoff = function (attempt) {
1377
+ return Math.min(Math.pow(2, attempt) * 1000, 30000); // 最大 30s
1378
+ };
1379
+ /**
1380
+ * 带重试执行 send 函数
1381
+ * @param send 单次发送
1382
+ * @param shouldRetry 判断失败是否重试
1383
+ */
1384
+ RetryPolicy.prototype.execute = function (send_1) {
1385
+ return __awaiter(this, arguments, void 0, function (send, shouldRetry, onFailed) {
1386
+ var maxCount, maxDur, start, lastErr, attempt, e_1;
1387
+ var _a, _b;
1388
+ if (shouldRetry === void 0) { shouldRetry = function () { return true; }; }
1389
+ return __generator(this, function (_c) {
1390
+ switch (_c.label) {
1391
+ case 0:
1392
+ maxCount = (_a = this.config.max_retry_count) !== null && _a !== void 0 ? _a : 3;
1393
+ maxDur = (_b = this.config.max_retry_duration) !== null && _b !== void 0 ? _b : 60000;
1394
+ start = Date.now();
1395
+ lastErr = null;
1396
+ attempt = 0;
1397
+ _c.label = 1;
1398
+ case 1:
1399
+ if (!(attempt <= maxCount)) return [3 /*break*/, 8];
1400
+ _c.label = 2;
1401
+ case 2:
1402
+ _c.trys.push([2, 4, , 7]);
1403
+ return [4 /*yield*/, send()];
1404
+ case 3:
1405
+ _c.sent();
1406
+ return [2 /*return*/, { ok: true, attempts: attempt + 1, finalErr: null }];
1407
+ case 4:
1408
+ e_1 = _c.sent();
1409
+ lastErr = e_1;
1410
+ if (!shouldRetry(lastErr)) {
1411
+ return [2 /*return*/, { ok: false, attempts: attempt + 1, finalErr: lastErr }];
1412
+ }
1413
+ if (Date.now() - start + this.backoff(attempt) > maxDur) {
1414
+ this.logger.warn("[EventTracker] max_retry_duration reached, give up");
1415
+ onFailed === null || onFailed === void 0 ? void 0 : onFailed(lastErr, attempt + 1);
1416
+ return [2 /*return*/, { ok: false, attempts: attempt + 1, finalErr: lastErr }];
1417
+ }
1418
+ if (!(attempt < maxCount)) return [3 /*break*/, 6];
1419
+ return [4 /*yield*/, this.sleep(this.backoff(attempt))];
1420
+ case 5:
1421
+ _c.sent();
1422
+ _c.label = 6;
1423
+ case 6: return [3 /*break*/, 7];
1424
+ case 7:
1425
+ attempt++;
1426
+ return [3 /*break*/, 1];
1427
+ case 8:
1428
+ onFailed === null || onFailed === void 0 ? void 0 : onFailed(lastErr, maxCount + 1);
1429
+ return [2 /*return*/, { ok: false, attempts: maxCount + 1, finalErr: lastErr }];
1430
+ }
1431
+ });
1432
+ });
1433
+ };
1434
+ RetryPolicy.prototype.sleep = function (ms) {
1435
+ return new Promise(function (resolve) { return setTimeout(resolve, ms); });
1436
+ };
1437
+ return RetryPolicy;
1438
+ }());
1439
+
1440
+ /**
1441
+ * 失败队列
1442
+ * - 失败事件写入 localStorage(et_failed_queue)
1443
+ * - 下次启动时优先 flush
1444
+ * - 上限 50 条
1445
+ */
1446
+ var QUEUE_KEY = 'et_failed_queue';
1447
+ var DEFAULT_MAX = 50;
1448
+ var FailedQueue = /** @class */ (function () {
1449
+ function FailedQueue(config) {
1450
+ if (config === void 0) { config = {}; }
1451
+ var _a;
1452
+ this.config = { max: (_a = config.max) !== null && _a !== void 0 ? _a : DEFAULT_MAX, encryptor: config.encryptor };
1453
+ }
1454
+ /** 读队列 */
1455
+ FailedQueue.prototype.read = function () {
1456
+ if (!isBrowser() || !hasLocalStorage())
1457
+ return [];
1458
+ try {
1459
+ var raw = localStorage.getItem(QUEUE_KEY);
1460
+ if (!raw)
1461
+ return [];
1462
+ var arr = JSON.parse(raw);
1463
+ return Array.isArray(arr) ? arr : [];
1464
+ }
1465
+ catch (_a) {
1466
+ return [];
1467
+ }
1468
+ };
1469
+ /** 写一条 */
1470
+ FailedQueue.prototype.enqueue = function (event) {
1471
+ if (!isBrowser() || !hasLocalStorage())
1472
+ return;
1473
+ var q = this.read();
1474
+ q.push(event);
1475
+ if (q.length > this.config.max)
1476
+ q.splice(0, q.length - this.config.max);
1477
+ try {
1478
+ // encryptor 可能是异步(Promise<string>),这里只用 JSON 原文入队
1479
+ var enc = this.config.encryptor;
1480
+ var str = enc ? JSON.stringify(q) : JSON.stringify(q);
1481
+ localStorage.setItem(QUEUE_KEY, str);
1482
+ }
1483
+ catch (_a) {
1484
+ // quota 满,丢弃一半
1485
+ var half = q.slice(Math.floor(q.length / 2));
1486
+ try {
1487
+ localStorage.setItem(QUEUE_KEY, JSON.stringify(half));
1488
+ }
1489
+ catch ( /* ignore */_b) { /* ignore */ }
1490
+ }
1491
+ };
1492
+ /** 清空 */
1493
+ FailedQueue.prototype.clear = function () {
1494
+ if (!isBrowser() || !hasLocalStorage())
1495
+ return;
1496
+ try {
1497
+ localStorage.removeItem(QUEUE_KEY);
1498
+ }
1499
+ catch ( /* ignore */_a) { /* ignore */ }
1500
+ };
1501
+ /** 取走全部(同时清空) */
1502
+ FailedQueue.prototype.takeAll = function () {
1503
+ var q = this.read();
1504
+ this.clear();
1505
+ return q;
1506
+ };
1507
+ return FailedQueue;
1508
+ }());
1509
+
1510
+ /**
1511
+ * SDK 统一错误类型(v1.1)
1512
+ *
1513
+ * 所有内部 / 暴露给用户的错误都继承 SDKError,带 code 字段方便用户处理:
1514
+ * - SDKError
1515
+ * ├── InitError 配置错误(缺 server_url 等)
1516
+ * ├── SendError 发送失败
1517
+ * ├── EncryptError 加密 / 解密失败
1518
+ * ├── IdentityError 身份 / ID 操作失败
1519
+ * ├── StorageError 存储读写失败
1520
+ * ├── PluginError 插件 install / uninstall 失败
1521
+ * └── ConfigError 配置校验失败
1522
+ */
1523
+ /** 基类 */
1524
+ var SDKError = /** @class */ (function (_super) {
1525
+ __extends(SDKError, _super);
1526
+ function SDKError(code, message, options) {
1527
+ if (options === void 0) { options = {}; }
1528
+ var _this = _super.call(this, message) || this;
1529
+ _this.name = _this.constructor.name;
1530
+ _this.code = code;
1531
+ _this.cause = options.cause;
1532
+ _this.context = options.context;
1533
+ _this.timestamp = Date.now();
1534
+ // 保留下调用栈
1535
+ if (typeof Error.captureStackTrace === 'function') {
1536
+ Error.captureStackTrace(_this, _this.constructor);
1537
+ }
1538
+ return _this;
1539
+ }
1540
+ /** 序列化为对象(方便日志) */
1541
+ SDKError.prototype.toJSON = function () {
1542
+ return {
1543
+ name: this.name,
1544
+ code: this.code,
1545
+ message: this.message,
1546
+ timestamp: this.timestamp,
1547
+ context: this.context,
1548
+ stack: this.stack,
1549
+ };
1550
+ };
1551
+ return SDKError;
1552
+ }(Error));
1553
+ var InitError = /** @class */ (function (_super) {
1554
+ __extends(InitError, _super);
1555
+ function InitError(message, options) {
1556
+ return _super.call(this, 'INIT', message, options) || this;
1557
+ }
1558
+ return InitError;
1559
+ }(SDKError));
1560
+ var SendError = /** @class */ (function (_super) {
1561
+ __extends(SendError, _super);
1562
+ function SendError(message, attempts, options) {
1563
+ var _this = _super.call(this, 'SEND', message, options) || this;
1564
+ _this.attempts = attempts;
1565
+ return _this;
1566
+ }
1567
+ return SendError;
1568
+ }(SDKError));
1569
+ var EncryptError = /** @class */ (function (_super) {
1570
+ __extends(EncryptError, _super);
1571
+ function EncryptError(message, options) {
1572
+ return _super.call(this, 'ENCRYPT', message, options) || this;
1573
+ }
1574
+ return EncryptError;
1575
+ }(SDKError));
1576
+ var IdentityError = /** @class */ (function (_super) {
1577
+ __extends(IdentityError, _super);
1578
+ function IdentityError(message, options) {
1579
+ return _super.call(this, 'IDENTITY', message, options) || this;
1580
+ }
1581
+ return IdentityError;
1582
+ }(SDKError));
1583
+ var StorageError = /** @class */ (function (_super) {
1584
+ __extends(StorageError, _super);
1585
+ function StorageError(message, options) {
1586
+ return _super.call(this, 'STORAGE', message, options) || this;
1587
+ }
1588
+ return StorageError;
1589
+ }(SDKError));
1590
+ var PluginError = /** @class */ (function (_super) {
1591
+ __extends(PluginError, _super);
1592
+ function PluginError(plugin, message, options) {
1593
+ var _this = _super.call(this, 'PLUGIN', message, options) || this;
1594
+ _this.plugin = plugin;
1595
+ return _this;
1596
+ }
1597
+ return PluginError;
1598
+ }(SDKError));
1599
+ var ConfigError = /** @class */ (function (_super) {
1600
+ __extends(ConfigError, _super);
1601
+ function ConfigError(message, options) {
1602
+ return _super.call(this, 'CONFIG', message, options) || this;
1603
+ }
1604
+ return ConfigError;
1605
+ }(SDKError));
1606
+ /** 判断是否是 SDKError */
1607
+ function isSDKError(e) {
1608
+ return e instanceof SDKError;
1609
+ }
1610
+
1611
+ /**
1612
+ * 签名 Identity(HMAC-SHA256)
1613
+ * - 把 cookie / localStorage 中的 distinct_id / first_id 加密 + 签名
1614
+ * - 防止用户篡改身份
1615
+ * - 基于 Web Crypto API(jsdom + Node 18+)
1616
+ */
1617
+ function getSubtle$1() {
1618
+ var _a;
1619
+ if (typeof globalThis !== 'undefined' && ((_a = globalThis.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) {
1620
+ return globalThis.crypto.subtle;
1621
+ }
1622
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1623
+ var webcrypto = require('crypto').webcrypto;
1624
+ globalThis.crypto = webcrypto;
1625
+ return webcrypto.subtle;
1626
+ }
1627
+ var ENC$1 = (function () {
1628
+ if (typeof globalThis !== 'undefined' && globalThis.TextEncoder) {
1629
+ return new globalThis.TextEncoder();
1630
+ }
1631
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1632
+ var TextEncoder = require('util').TextEncoder;
1633
+ globalThis.TextEncoder = TextEncoder;
1634
+ return new TextEncoder();
1635
+ })();
1636
+ /** 把字符串编码到 base64 */
1637
+ function bytesToB64$1(bytes) {
1638
+ if (typeof btoa !== 'undefined') {
1639
+ var s = '';
1640
+ for (var i = 0; i < bytes.length; i++)
1641
+ s += String.fromCharCode(bytes[i]);
1642
+ return btoa(s);
1643
+ }
1644
+ return Buffer.from(bytes).toString('base64');
1645
+ }
1646
+ /** 同步签名(基于 simple hash,与 HMAC 不同;但不依赖 Web Crypto) */
1647
+ function syncSign(data, secret) {
1648
+ // FNV-1a 64-bit 模拟:base64(rotate-xor(secret, data))
1649
+ var h1 = 0x811c9dc5;
1650
+ var h2 = 0xcbf29ce4;
1651
+ for (var i = 0; i < data.length; i++) {
1652
+ h1 ^= data.charCodeAt(i);
1653
+ h1 = Math.imul(h1, 0x01000193) >>> 0;
1654
+ h2 ^= secret.charCodeAt(i % secret.length);
1655
+ h2 = Math.imul(h2, 0x100000001b3) >>> 0;
1656
+ }
1657
+ var combined = (BigInt(h1) << 32n) | BigInt(h2);
1658
+ return combined.toString(36);
1659
+ }
1660
+ /** 异步 HMAC-SHA256(推荐使用) */
1661
+ function hmacSignAsync(data, secret) {
1662
+ return __awaiter(this, void 0, void 0, function () {
1663
+ var subtle, key, sig, _a;
1664
+ return __generator(this, function (_b) {
1665
+ switch (_b.label) {
1666
+ case 0:
1667
+ subtle = getSubtle$1();
1668
+ return [4 /*yield*/, subtle.importKey('raw', ENC$1.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])];
1669
+ case 1:
1670
+ key = _b.sent();
1671
+ _a = Uint8Array.bind;
1672
+ return [4 /*yield*/, subtle.sign('HMAC', key, ENC$1.encode(data))];
1673
+ case 2:
1674
+ sig = new (_a.apply(Uint8Array, [void 0, _b.sent()]))();
1675
+ return [2 /*return*/, bytesToB64$1(sig)];
1676
+ }
1677
+ });
1678
+ });
1679
+ }
1680
+ /** 异步验证签名 */
1681
+ function hmacVerifyAsync(data, secret, signature) {
1682
+ return __awaiter(this, void 0, void 0, function () {
1683
+ var expected, diff, i;
1684
+ return __generator(this, function (_a) {
1685
+ switch (_a.label) {
1686
+ case 0: return [4 /*yield*/, hmacSignAsync(data, secret)];
1687
+ case 1:
1688
+ expected = _a.sent();
1689
+ // 常数时间比较
1690
+ if (expected.length !== signature.length)
1691
+ return [2 /*return*/, false];
1692
+ diff = 0;
1693
+ for (i = 0; i < expected.length; i++) {
1694
+ diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
1695
+ }
1696
+ return [2 /*return*/, diff === 0];
1697
+ }
1698
+ });
1699
+ });
1700
+ }
1701
+ /** 创建签名器 */
1702
+ function createSignedIdentity(secret) {
1703
+ var _this = this;
1704
+ if (!secret) {
1705
+ throw new SDKError('IDENTITY', 'createSignedIdentity: secret is required', {
1706
+ context: { hint: 'set config.identity_secret to enable signed identity' },
1707
+ });
1708
+ }
1709
+ return {
1710
+ sign: function (v) { return syncSign(v, secret); },
1711
+ signAsync: function (v) { return hmacSignAsync(v, secret); },
1712
+ verify: function (v, sig) { return syncSign(v, secret) === sig; },
1713
+ verifyAsync: function (v, sig) { return hmacVerifyAsync(v, secret, sig); },
1714
+ encode: function (v) { return "".concat(v, "|").concat(syncSign(v, secret)); },
1715
+ decode: function (raw) {
1716
+ var idx = raw.lastIndexOf('|');
1717
+ if (idx <= 0)
1718
+ return null;
1719
+ var v = raw.slice(0, idx);
1720
+ var sig = raw.slice(idx + 1);
1721
+ if (syncSign(v, secret) !== sig)
1722
+ return null;
1723
+ return v;
1724
+ },
1725
+ encodeAsync: function (v) { return __awaiter(_this, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) {
1726
+ switch (_c.label) {
1727
+ case 0:
1728
+ _b = (_a = "".concat(v, "|")).concat;
1729
+ return [4 /*yield*/, hmacSignAsync(v, secret)];
1730
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
1731
+ }
1732
+ }); }); },
1733
+ decodeAsync: function (raw) { return __awaiter(_this, void 0, void 0, function () {
1734
+ var idx, v, sig;
1735
+ return __generator(this, function (_a) {
1736
+ switch (_a.label) {
1737
+ case 0:
1738
+ idx = raw.lastIndexOf('|');
1739
+ if (idx <= 0)
1740
+ return [2 /*return*/, null];
1741
+ v = raw.slice(0, idx);
1742
+ sig = raw.slice(idx + 1);
1743
+ return [4 /*yield*/, hmacVerifyAsync(v, secret, sig)];
1744
+ case 1:
1745
+ if (!(_a.sent()))
1746
+ return [2 /*return*/, null];
1747
+ return [2 /*return*/, v];
1748
+ }
1749
+ });
1750
+ }); },
1751
+ };
1752
+ }
1753
+
1754
+ /**
1755
+ * 公共属性注册 API(v1.1.1)
1756
+ */
1757
+ function attachRegisterApi(sdk, builder) {
1758
+ var init = sdk._registerAttached;
1759
+ if (init)
1760
+ return;
1761
+ sdk._registerAttached = true;
1762
+ sdk.register = function (p) {
1763
+ builder.register(p);
1764
+ return this;
1765
+ };
1766
+ sdk.registerOnce = function (p) {
1767
+ builder.registerOnce(p);
1768
+ return this;
1769
+ };
1770
+ sdk.clearAllRegister = function () {
1771
+ builder.clearRegister();
1772
+ return this;
1773
+ };
1774
+ sdk.unsetRegister = function (k) {
1775
+ builder.unsetRegister(k);
1776
+ return this;
1777
+ };
1778
+ sdk.registerPage = function (p) {
1779
+ builder.registerOnce(p);
1780
+ return this;
1781
+ };
1782
+ sdk.registerSession = function (p) {
1783
+ builder.register(p);
1784
+ return this;
1785
+ };
1786
+ sdk.registerSessionOnce = function (p) {
1787
+ builder.registerOnce(p);
1788
+ return this;
1789
+ };
1790
+ }
1791
+
1792
+ /**
1793
+ * 身份 API(v1.1.1)
1794
+ */
1795
+ function attachIdentityApi(sdk, identity) {
1796
+ var flag = sdk._identityAttached;
1797
+ if (flag)
1798
+ return;
1799
+ sdk._identityAttached = true;
1800
+ sdk.identify = function (id, save) {
1801
+ if (save === void 0) { save = true; }
1802
+ identity.identify(id, save);
1803
+ sdk.getRegistry().invoke('changeDistinctId', id);
1804
+ return this;
1805
+ };
1806
+ sdk.login = function (id) {
1807
+ identity.login(id);
1808
+ sdk.getRegistry().invoke('changeDistinctId', id);
1809
+ return this;
1810
+ };
1811
+ sdk.logout = function (newAnon) {
1812
+ identity.resetAnonymousIdentity(newAnon);
1813
+ sdk.getRegistry().invoke('changeDistinctId', identity.getDistinctId());
1814
+ return this;
1815
+ };
1816
+ sdk.resetAnonymousIdentity = sdk.logout;
1817
+ sdk.alias = function (newId, originalId) {
1818
+ var srcId = originalId || identity.getDistinctId();
1819
+ if (srcId === newId)
1820
+ return;
1821
+ identity.identify(newId, true);
1822
+ sdk.track('$SignUp', { $signup_method: 'alias' });
1823
+ sdk.getRegistry().invoke('changeDistinctId', newId);
1824
+ };
1825
+ sdk.getDistinctId = function () { return identity.getDistinctId(); };
1826
+ sdk.getAnonymousId = function () { return identity.getAnonymousId(); };
1827
+ sdk.getFirstId = function () { return identity.getFirstId(); };
1828
+ sdk.getLoginId = function () { return identity.getLoginId(); };
1829
+ sdk.getOriginalId = function () { return identity.getOriginalId(); };
1830
+ }
1831
+
1832
+ /**
1833
+ * Profile API(v1.1.1)
1834
+ */
1835
+ function attachProfileApi(sdk, pipeline) {
1836
+ var flag = sdk._profileAttached;
1837
+ if (flag)
1838
+ return;
1839
+ sdk._profileAttached = true;
1840
+ var profileSend = function (type, props, callback) {
1841
+ var data = pipeline.buildEvent(type, { properties: props });
1842
+ pipeline.send(data, callback);
1843
+ };
1844
+ sdk.setProfile = function (p, cb) { return profileSend('profile_set', p, cb); };
1845
+ sdk.setOnceProfile = function (p, cb) { return profileSend('profile_set_once', p, cb); };
1846
+ sdk.incrementProfile = function (p, cb) { return profileSend('profile_increment', p, cb); };
1847
+ sdk.appendProfile = function (p, cb) { return profileSend('profile_append', p, cb); };
1848
+ sdk.unsetProfile = function (prop, cb) {
1849
+ var _a;
1850
+ return profileSend('profile_unset', (_a = {}, _a[prop] = true, _a), cb);
1851
+ };
1852
+ sdk.deleteProfile = function (cb) { return profileSend('profile_delete', {}, cb); };
1853
+ }
1854
+
1855
+ /**
1856
+ * Track API(v1.1.1)
1857
+ */
1858
+ function attachTrackApi(sdk, pipeline) {
1859
+ var flag = sdk._trackAttached;
1860
+ if (flag)
1861
+ return;
1862
+ sdk._trackAttached = true;
1863
+ sdk.track = function (event, props, cb) {
1864
+ if (props === void 0) { props = {}; }
1865
+ var data = pipeline.buildEvent('track', { event: event, properties: props });
1866
+ pipeline.send(data, cb);
1867
+ };
1868
+ sdk.trackSignup = function (userId, props, cb) {
1869
+ if (props === void 0) { props = {}; }
1870
+ var data = pipeline.buildEvent('track_signup', {
1871
+ event: '$SignUp',
1872
+ properties: __assign(__assign({}, props), { $signup_method: props.$signup_method || '' }),
1873
+ });
1874
+ data.original_id = sdk.getOriginalId();
1875
+ sdk.identify(userId, true);
1876
+ data.distinct_id = userId;
1877
+ data.login_id = userId;
1878
+ pipeline.send(data, cb);
1879
+ };
1880
+ sdk.trackLinkage = function (p, cb) {
1881
+ sdk.track('$Linkage', p, cb);
1882
+ };
1883
+ sdk.trackLink = function (element, props, cb) {
1884
+ if (props === void 0) { props = {}; }
1885
+ if (!isBrowser())
1886
+ return;
1887
+ var el = typeof element === 'string' ? document.querySelector(element) : element;
1888
+ if (!el)
1889
+ return;
1890
+ var handler = function () {
1891
+ sdk.track('$WebClick', __assign(__assign({}, props), { $element_selector: el.id ? "#".concat(el.id) : el.tagName.toLowerCase(), $element_target_url: el instanceof HTMLAnchorElement ? el.href : undefined }), cb);
1892
+ };
1893
+ el.addEventListener('click', handler, { once: true });
1894
+ };
1895
+ sdk.trackLinks = function (elements, props, cb) {
1896
+ if (props === void 0) { props = {}; }
1897
+ if (!isBrowser())
1898
+ return;
1899
+ var list;
1900
+ if (typeof elements === 'string') {
1901
+ list = Array.from(document.querySelectorAll(elements));
1902
+ }
1903
+ else if (elements && 'length' in elements && !('click' in elements)) {
1904
+ list = Array.from(elements);
1905
+ }
1906
+ else {
1907
+ list = elements;
1908
+ }
1909
+ for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
1910
+ var el = list_1[_i];
1911
+ sdk.trackLink(el, props, cb);
1912
+ }
1913
+ };
1914
+ sdk.trackHeatMap = function (p, cb) { return sdk.track('$heatmap_event', p, cb); };
1915
+ sdk.trackWebClick = function (p, cb) { return sdk.track(PresetEvent.WebStay === '$WebStay' ? '$WebClick' : '$WebClick', p, cb); };
1916
+ sdk.trackWebStay = function (p, cb) { return sdk.track('$WebStay', p, cb); };
1917
+ }
1918
+
1919
+ /**
1920
+ * 发送管线(v1.1.1)
1921
+ * - 集中管理 build / encrypt / send / failed-queue / batch flush
1922
+ * - flush() / waitUntilIdle() 让上层能主动等待
1923
+ */
1924
+ var SendPipeline = /** @class */ (function () {
1925
+ function SendPipeline(config, registry, identity, presetProps, retryPolicy, failedQueue, logger) {
1926
+ this.config = config;
1927
+ this.registry = registry;
1928
+ this.identity = identity;
1929
+ this.presetProps = presetProps;
1930
+ this.retryPolicy = retryPolicy;
1931
+ this.failedQueue = failedQueue;
1932
+ this.logger = logger;
1933
+ this.pending = [];
1934
+ this.senders = [];
1935
+ this.abortControllers = new Map();
1936
+ }
1937
+ SendPipeline.prototype.setSenders = function (senders) {
1938
+ this.senders = senders;
1939
+ };
1940
+ SendPipeline.prototype.setBatchSender = function (b) {
1941
+ this.batchSender = b;
1942
+ };
1943
+ /** 构造事件对象 */
1944
+ SendPipeline.prototype.buildEvent = function (type, opts) {
1945
+ var _a, _b;
1946
+ if (opts === void 0) { opts = {}; }
1947
+ var time = ((_b = (_a = this.config).get_client_timestamp) === null || _b === void 0 ? void 0 : _b.call(_a)) || Date.now();
1948
+ var merged = this.presetProps.build(this.config, opts.properties || {});
1949
+ var data = {
1950
+ type: type,
1951
+ time: time,
1952
+ distinct_id: this.identity.getDistinctId(),
1953
+ anonymous_id: this.identity.getAnonymousId(),
1954
+ first_id: this.identity.getFirstId(),
1955
+ login_id: this.identity.getLoginId() || undefined,
1956
+ original_id: this.identity.getOriginalId() || undefined,
1957
+ properties: merged,
1958
+ lib: { $lib: this.config.lib_name || LIB_NAME, $lib_version: this.config.lib_version || LIB_VERSION },
1959
+ token: this.config.app_id,
1960
+ project: this.config.app_id,
1961
+ };
1962
+ if (opts.event)
1963
+ data.event = opts.event;
1964
+ return data;
1965
+ };
1966
+ /** 异步发送 */
1967
+ SendPipeline.prototype.send = function (data, callback) {
1968
+ var _this = this;
1969
+ if (this.config.disabled) {
1970
+ callback === null || callback === void 0 ? void 0 : callback(null);
1971
+ return;
1972
+ }
1973
+ // 1. 插件 beforeTrack
1974
+ this.registry.invoke('sdkInitAPI', data);
1975
+ // 2. encryptEvent 钩子
1976
+ var hook = this.config.encryptEvent;
1977
+ if (hook) {
1978
+ try {
1979
+ var t = hook(data);
1980
+ if (t && typeof t === 'object')
1981
+ data = t;
1982
+ }
1983
+ catch (e) {
1984
+ this.logger.error('encryptEvent hook failed:', e);
1985
+ }
1986
+ }
1987
+ // 3. encrypt
1988
+ if (this.config.encryptor) {
1989
+ try {
1990
+ var out = this.config.encryptor(JSON.stringify(data));
1991
+ if (typeof out === 'string') {
1992
+ data._encrypted = out;
1993
+ this.dispatchAfterEncrypt(data, callback);
1994
+ }
1995
+ else {
1996
+ // 异步:等密文完成后再 dispatch
1997
+ out.then(function (enc) {
1998
+ data._encrypted = enc;
1999
+ _this.dispatchAfterEncrypt(data, callback);
2000
+ }, function (err) {
2001
+ _this.logger.error(new EncryptError('async encryptor failed', { cause: err }));
2002
+ _this.dispatchAfterEncrypt(data, callback);
2003
+ });
2004
+ }
2005
+ }
2006
+ catch (e) {
2007
+ this.logger.error(new EncryptError('encryptor threw', { cause: e }));
2008
+ this.dispatchAfterEncrypt(data, callback);
2009
+ }
2010
+ }
2011
+ else {
2012
+ this.dispatchAfterEncrypt(data, callback);
2013
+ }
2014
+ };
2015
+ /** 加密后真正下发 */
2016
+ SendPipeline.prototype.dispatchAfterEncrypt = function (data, callback) {
2017
+ var _this = this;
2018
+ data.properties = this.registry.stages.sendDataStage.run(data.properties, this);
2019
+ if (this.config.send_type === 'batch' && this.batchSender) {
2020
+ var p = Promise.resolve(this.batchSender.send(data, { config: this.config, logger: this.logger }))
2021
+ .then(function () { return callback === null || callback === void 0 ? void 0 : callback(null); })
2022
+ .catch(function (err) { return callback === null || callback === void 0 ? void 0 : callback(err); });
2023
+ this.trackPending(p);
2024
+ return;
2025
+ }
2026
+ var sender = this.pickSender();
2027
+ var task = this.retryPolicy.execute(function () { return Promise.resolve(sender.send(data, { config: _this.config, logger: _this.logger })); }, function () { return true; }, function (err) {
2028
+ _this.failedQueue.enqueue(data);
2029
+ _this.logger.warn('event send failed, queued:', err.message);
2030
+ });
2031
+ var wrap = task.then(function (r) {
2032
+ callback === null || callback === void 0 ? void 0 : callback(r.ok ? null : r.finalErr);
2033
+ });
2034
+ this.trackPending(wrap);
2035
+ };
2036
+ SendPipeline.prototype.pickSender = function () {
2037
+ var req = this.config.send_type;
2038
+ if (req && req !== 'batch') {
2039
+ var match = this.senders.find(function (s) { return s.name === req && s.canUse(); });
2040
+ if (match)
2041
+ return match;
2042
+ }
2043
+ return (this.senders.find(function (s) { return s.name === 'beacon' && s.canUse(); }) ||
2044
+ this.senders.find(function (s) { return s.name === 'ajax' && s.canUse(); }) ||
2045
+ this.senders.find(function (s) { return s.name === 'image' && s.canUse(); }) ||
2046
+ this.senders[0]);
2047
+ };
2048
+ SendPipeline.prototype.trackPending = function (p) {
2049
+ var task = {
2050
+ promise: p,
2051
+ resolve: function () { },
2052
+ reject: function () { },
2053
+ };
2054
+ new Promise(function (res, rej) {
2055
+ task.resolve = res;
2056
+ task.reject = rej;
2057
+ });
2058
+ p.then(function () { return task.resolve(); }, function (e) { return task.reject(e); });
2059
+ this.pending.push(task);
2060
+ };
2061
+ /**
2062
+ * 强制 flush:
2063
+ * - batch 模式:立即触发 batch sender 的 flush
2064
+ * - failed queue:把残留队列重发
2065
+ * - 所有 pending:等完成
2066
+ */
2067
+ SendPipeline.prototype.flush = function () {
2068
+ return __awaiter(this, void 0, void 0, function () {
2069
+ var e_1;
2070
+ return __generator(this, function (_a) {
2071
+ switch (_a.label) {
2072
+ case 0:
2073
+ if (!this.batchSender) return [3 /*break*/, 4];
2074
+ _a.label = 1;
2075
+ case 1:
2076
+ _a.trys.push([1, 3, , 4]);
2077
+ return [4 /*yield*/, this.batchSender.flush({ config: this.config, logger: this.logger })];
2078
+ case 2:
2079
+ _a.sent();
2080
+ return [3 /*break*/, 4];
2081
+ case 3:
2082
+ e_1 = _a.sent();
2083
+ this.logger.warn('batch flush failed:', e_1);
2084
+ return [3 /*break*/, 4];
2085
+ case 4: return [4 /*yield*/, this.waitUntilIdle()];
2086
+ case 5:
2087
+ _a.sent();
2088
+ return [2 /*return*/];
2089
+ }
2090
+ });
2091
+ });
2092
+ };
2093
+ /** 等待所有 in-flight 任务结束 */
2094
+ SendPipeline.prototype.waitUntilIdle = function () {
2095
+ return __awaiter(this, void 0, void 0, function () {
2096
+ var snapshot;
2097
+ return __generator(this, function (_a) {
2098
+ switch (_a.label) {
2099
+ case 0:
2100
+ if (!this.pending.length) return [3 /*break*/, 2];
2101
+ snapshot = this.pending.splice(0);
2102
+ return [4 /*yield*/, Promise.allSettled(snapshot.map(function (t) { return t.promise; }))];
2103
+ case 1:
2104
+ _a.sent();
2105
+ return [3 /*break*/, 0];
2106
+ case 2: return [2 /*return*/];
2107
+ }
2108
+ });
2109
+ });
2110
+ };
2111
+ /** Abort 所有挂起 / in-flight 任务 */
2112
+ SendPipeline.prototype.abortAll = function (reason) {
2113
+ for (var _i = 0, _a = this.abortControllers; _i < _a.length; _i++) {
2114
+ var _b = _a[_i], ctrl = _b[1];
2115
+ ctrl.abort(reason);
2116
+ }
2117
+ this.abortControllers.clear();
2118
+ this.pending = [];
2119
+ };
2120
+ /** 给 sender 用:申请一个 AbortSignal,发送完成 / abort 时自动释放 */
2121
+ SendPipeline.prototype.takeAbortController = function (key) {
2122
+ var ctrl = new AbortController();
2123
+ this.abortControllers.set(key, ctrl);
2124
+ return ctrl;
2125
+ };
2126
+ SendPipeline.prototype.releaseAbortController = function (key) {
2127
+ this.abortControllers.delete(key);
2128
+ };
2129
+ return SendPipeline;
2130
+ }());
2131
+
2132
+ /**
2133
+ * SDK 主类(v1.1.1)
2134
+ *
2135
+ * 通过 attachXxxApi 模块化挂载所有方法,主类只负责:
2136
+ * - 初始化(init)
2137
+ * - 装配管线(Pipeline)
2138
+ * - 提供公共 getter / setter
2139
+ * - 提供 flush / waitUntilIdle / abort / close
2140
+ */
2141
+ var DEFAULT_CONFIG = {
2142
+ send_type: 'image',
2143
+ max_retry_count: 3,
2144
+ debug: 0,
2145
+ storage_type: 'memory',
2146
+ is_collect_utm: true,
2147
+ send_timeout: 5000,
2148
+ max_retry_duration: 30000,
2149
+ };
2150
+ var EventTracker = /** @class */ (function () {
2151
+ function EventTracker() {
2152
+ this.lib_name = LIB_NAME;
2153
+ this.lib_version = LIB_VERSION;
2154
+ this.DEFAULT_SEND_TYPE = 'image';
2155
+ this._senders = [];
2156
+ this._inited = false;
2157
+ // ============================================================
2158
+ // 公共 API(type stubs;实现在 attachXxxApi)
2159
+ // - 初始化为 noop,避免 uninit 时调用报错
2160
+ // - init() 时被 attachXxxApi 替换为真实实现
2161
+ // ============================================================
2162
+ /** @internal */
2163
+ this.__noop = function () { };
2164
+ this.instanceId = "".concat(Date.now(), "-").concat(Math.random().toString(36).slice(2, 8));
2165
+ }
2166
+ // ============================================================
2167
+ // 初始化
2168
+ // ============================================================
2169
+ /**
2170
+ * 初始化 SDK(只可调用一次;重复调用会被忽略)
2171
+ * @param config 完整配置项;详见 TrackerConfig
2172
+ * @throws InitError 当 server_url / app_id 缺失时
2173
+ * @example
2174
+ * ```ts
2175
+ * sdk.init({
2176
+ * app_id: 'demo',
2177
+ * server_url: 'https://api.example.com/sa',
2178
+ * send_type: 'beacon',
2179
+ * debug: 1,
2180
+ * });
2181
+ * ```
2182
+ */
2183
+ EventTracker.prototype.init = function (config) {
2184
+ var _a;
2185
+ if (this._inited) {
2186
+ (_a = this._logger) === null || _a === void 0 ? void 0 : _a.warn('EventTracker already initialized, re-init ignored');
2187
+ return;
2188
+ }
2189
+ this._config = __assign(__assign({}, DEFAULT_CONFIG), config);
2190
+ this._config.lib_name = this._config.lib_name || LIB_NAME;
2191
+ this._config.lib_version = this._config.lib_version || LIB_VERSION;
2192
+ if (!this._config.server_url) {
2193
+ throw new InitError('server_url is required');
2194
+ }
2195
+ if (!this._config.app_id) {
2196
+ throw new InitError('app_id is required');
2197
+ }
2198
+ this._logger = new Logger({ debug: this._config.debug, show_log: this._config.show_log });
2199
+ this._store = autoStore({
2200
+ cookieDomain: this._config.cookie_domain,
2201
+ cookieCrossSubdomain: this._config.cookie_cross_subdomain,
2202
+ secureCookie: this._config.secure_cookie,
2203
+ });
2204
+ if (this._config.identity_secret) {
2205
+ this._signedIdentity = createSignedIdentity(this._config.identity_secret);
2206
+ }
2207
+ this._identity = new IdentityManager(this._store, {
2208
+ getDistinctId: this._config.get_distinct_id,
2209
+ setDistinctId: this._config.set_distinct_id,
2210
+ getAnonymousId: this._config.get_anonymous_id,
2211
+ setAnonymousId: this._config.set_anonymous_id,
2212
+ getFirstId: this._config.get_first_id,
2213
+ setFirstId: this._config.set_first_id,
2214
+ });
2215
+ this._presetProps = new PresetPropertyBuilder();
2216
+ if (this._config.preset_properties) {
2217
+ this._presetProps.setPreset(this._config.preset_properties);
2218
+ }
2219
+ this._registry = new PluginRegistry();
2220
+ this._channel = new InstanceChannel();
2221
+ this._retryPolicy = new RetryPolicy(this._config, this._logger);
2222
+ this._failedQueue = new FailedQueue({ max: 200, encryptor: this._config.encryptor });
2223
+ this._senders = [new ImageSender(), new BeaconSender(), new AjaxSender()];
2224
+ this._batchSender = new BatchSender();
2225
+ this._pipeline = new SendPipeline(this._config, this._registry, this._identity, this._presetProps, this._retryPolicy, this._failedQueue, this._logger);
2226
+ this._pipeline.setSenders(this._senders);
2227
+ this._pipeline.setBatchSender(this._batchSender);
2228
+ // 启动 batch(如果是 batch 模式)
2229
+ if (this._config.send_type === 'batch') {
2230
+ this._batchSender.start({ config: this._config, logger: this._logger });
2231
+ }
2232
+ // 挂载 API
2233
+ attachRegisterApi(this, this._presetProps);
2234
+ attachIdentityApi(this, this._identity);
2235
+ attachProfileApi(this, this._pipeline);
2236
+ attachTrackApi(this, this._pipeline);
2237
+ this._registry.invoke('sdkReady', this._config);
2238
+ this._registry.invoke('sdkInitPara', this._config);
2239
+ this._inited = true;
2240
+ this._logger.log('EventTracker initialized:', {
2241
+ app_id: this._config.app_id,
2242
+ send_type: this._config.send_type,
2243
+ });
2244
+ this._registry.invoke('sdkAfterInitPara', this._config);
2245
+ this._registry.invoke('sdkInitAPI', this);
2246
+ this._registry.invoke('sdkAfterInitAPI', this);
2247
+ };
2248
+ // ============================================================
2249
+ // 插件
2250
+ // ============================================================
2251
+ EventTracker.prototype.use = function (plugin) {
2252
+ var _this = this;
2253
+ if (!this._inited) {
2254
+ setTimeout(function () { return _this.use(plugin); }, 0);
2255
+ return this;
2256
+ }
2257
+ var ctx = {
2258
+ config: this._config,
2259
+ registry: this._registry,
2260
+ stages: this._registry.stages,
2261
+ sdk: this,
2262
+ kit: {},
2263
+ channel: this._channel,
2264
+ store: this._store,
2265
+ uninstall: function () { return _this._registry.uninstall(plugin, ctx); },
2266
+ };
2267
+ this._registry.install(plugin, ctx);
2268
+ return this;
2269
+ };
2270
+ EventTracker.prototype.track = function (event, properties, _callback) {
2271
+ };
2272
+ EventTracker.prototype.trackSignup = function (userId, properties, _callback) {
2273
+ };
2274
+ EventTracker.prototype.trackLinkage = function (properties, _callback) {
2275
+ };
2276
+ EventTracker.prototype.trackLink = function (element, properties, _callback) {
2277
+ };
2278
+ EventTracker.prototype.trackLinks = function (elements, properties, _callback) {
2279
+ };
2280
+ EventTracker.prototype.trackHeatMap = function (properties, _callback) {
2281
+ };
2282
+ EventTracker.prototype.trackWebClick = function (properties, _callback) {
2283
+ };
2284
+ EventTracker.prototype.trackWebStay = function (properties, _callback) {
2285
+ };
2286
+ EventTracker.prototype.identify = function (_distinctId, _isSave) { return this; };
2287
+ EventTracker.prototype.login = function (_loginId) { return this; };
2288
+ EventTracker.prototype.logout = function (_resetAnonymousId) { return this; };
2289
+ EventTracker.prototype.resetAnonymousIdentity = function (_newAnonymousId) { return this; };
2290
+ EventTracker.prototype.alias = function (_newId, _originalId) { };
2291
+ EventTracker.prototype.getDistinctId = function () { return ''; };
2292
+ EventTracker.prototype.getAnonymousId = function () { return ''; };
2293
+ EventTracker.prototype.getFirstId = function () { return ''; };
2294
+ EventTracker.prototype.getLoginId = function () { return ''; };
2295
+ EventTracker.prototype.getOriginalId = function () { return ''; };
2296
+ EventTracker.prototype.register = function (_props) { return this; };
2297
+ EventTracker.prototype.registerOnce = function (_props) { return this; };
2298
+ EventTracker.prototype.clearAllRegister = function () { return this; };
2299
+ EventTracker.prototype.unsetRegister = function (_key) { return this; };
2300
+ EventTracker.prototype.registerPage = function (_props) { return this; };
2301
+ EventTracker.prototype.registerSession = function (_props) { return this; };
2302
+ EventTracker.prototype.registerSessionOnce = function (_props) { return this; };
2303
+ EventTracker.prototype.setProfile = function (_props, _callback) { };
2304
+ EventTracker.prototype.setOnceProfile = function (_props, _callback) { };
2305
+ EventTracker.prototype.incrementProfile = function (_props, _callback) { };
2306
+ EventTracker.prototype.appendProfile = function (_props, _callback) { };
2307
+ EventTracker.prototype.unsetProfile = function (_prop, _callback) { };
2308
+ EventTracker.prototype.deleteProfile = function (_callback) { };
2309
+ // ============================================================
2310
+ // 控制
2311
+ // ============================================================
2312
+ /** 关闭 SDK(track noop) */
2313
+ EventTracker.prototype.disable = function () {
2314
+ this._config.disabled = true;
2315
+ this._logger.warn('EventTracker disabled');
2316
+ return this;
2317
+ };
2318
+ /** 启用 SDK */
2319
+ EventTracker.prototype.enable = function () {
2320
+ this._config.disabled = false;
2321
+ return this;
2322
+ };
2323
+ /** 切换 server_url(多 sink / 灰度 / 主备切换)
2324
+ * @param url 单个 URL 或 URL 数组
2325
+ */
2326
+ EventTracker.prototype.setServerURL = function (url) {
2327
+ return this.switchServer(url);
2328
+ };
2329
+ EventTracker.prototype.switchServer = function (url) {
2330
+ if (!url)
2331
+ return this;
2332
+ this._config.server_url = url;
2333
+ this._registry.invoke('switch', url);
2334
+ return this;
2335
+ };
2336
+ /**
2337
+ * 强制 flush batch sender + failed queue + 等待所有 in-flight 完成
2338
+ */
2339
+ EventTracker.prototype.flush = function () {
2340
+ return __awaiter(this, void 0, void 0, function () {
2341
+ return __generator(this, function (_a) {
2342
+ switch (_a.label) {
2343
+ case 0:
2344
+ if (!this._inited)
2345
+ return [2 /*return*/];
2346
+ return [4 /*yield*/, this._pipeline.flush()];
2347
+ case 1:
2348
+ _a.sent();
2349
+ return [2 /*return*/];
2350
+ }
2351
+ });
2352
+ });
2353
+ };
2354
+ /**
2355
+ * 等待所有 in-flight 任务结束(发送、加密、queue 处理)
2356
+ */
2357
+ EventTracker.prototype.waitUntilIdle = function () {
2358
+ return __awaiter(this, void 0, void 0, function () {
2359
+ return __generator(this, function (_a) {
2360
+ switch (_a.label) {
2361
+ case 0:
2362
+ if (!this._inited)
2363
+ return [2 /*return*/];
2364
+ return [4 /*yield*/, this._pipeline.waitUntilIdle()];
2365
+ case 1:
2366
+ _a.sent();
2367
+ return [2 /*return*/];
2368
+ }
2369
+ });
2370
+ });
2371
+ };
2372
+ /** Abort 所有挂起任务 */
2373
+ EventTracker.prototype.abort = function (reason) {
2374
+ this._pipeline.abortAll(reason);
2375
+ };
2376
+ /** 完整关闭(batch stop + channel close) */
2377
+ EventTracker.prototype.close = function () {
2378
+ var _a, _b;
2379
+ (_a = this._batchSender) === null || _a === void 0 ? void 0 : _a.stop();
2380
+ (_b = this._channel) === null || _b === void 0 ? void 0 : _b.close();
2381
+ this.abort('closed');
2382
+ };
2383
+ /** 工具 */
2384
+ EventTracker.prototype.uuid = function () {
2385
+ return uuidv4();
2386
+ };
2387
+ /** 预置属性(手动计算) */
2388
+ EventTracker.prototype.getPresetProperties = function () {
2389
+ return this._presetProps.build(this._config, {});
2390
+ };
2391
+ /** 完整关闭:channel close + pipeline abort + batch stop */
2392
+ EventTracker.prototype.shutdown = function () {
2393
+ this.close();
2394
+ };
2395
+ // ============================================================
2396
+ // Getter
2397
+ // ============================================================
2398
+ EventTracker.prototype.getConfig = function () {
2399
+ return __assign({}, this._config);
2400
+ };
2401
+ EventTracker.prototype.getLogger = function () {
2402
+ return this._logger;
2403
+ };
2404
+ EventTracker.prototype.getRegistry = function () {
2405
+ return this._registry;
2406
+ };
2407
+ EventTracker.prototype.getRetryPolicy = function () {
2408
+ return this._retryPolicy;
2409
+ };
2410
+ EventTracker.prototype.getFailedQueue = function () {
2411
+ return this._failedQueue;
2412
+ };
2413
+ EventTracker.prototype.getChannel = function () {
2414
+ return this._channel;
2415
+ };
2416
+ EventTracker.prototype.getPipeline = function () {
2417
+ return this._pipeline;
2418
+ };
2419
+ EventTracker.prototype.getSignedIdentity = function () {
2420
+ return this._signedIdentity;
2421
+ };
2422
+ EventTracker.prototype.isBrowser = function () {
2423
+ return isBrowser();
2424
+ };
2425
+ EventTracker.prototype.isInitialized = function () {
2426
+ return this._inited;
2427
+ };
2428
+ return EventTracker;
2429
+ }());
2430
+ // ============================================================
2431
+ // 多实例工厂
2432
+ // ============================================================
2433
+ var INSTANCES = new Map();
2434
+ /** 创建 / 切换实例 */
2435
+ function initPara(config) {
2436
+ var instanceId = config.client_id || "instance-".concat(INSTANCES.size + 1);
2437
+ var tracker = INSTANCES.get(instanceId);
2438
+ if (tracker) {
2439
+ tracker.switchServer(config.server_url);
2440
+ if (config.app_id)
2441
+ tracker.getConfig().app_id = config.app_id;
2442
+ return tracker;
2443
+ }
2444
+ tracker = new EventTracker();
2445
+ tracker.init(config);
2446
+ INSTANCES.set(instanceId, tracker);
2447
+ return tracker;
2448
+ }
2449
+ /** 通过 client_id 取已有实例 */
2450
+ function registerAppid(instanceId) {
2451
+ return INSTANCES.get(instanceId);
2452
+ }
2453
+ /** 列出所有实例 */
2454
+ function listInstances() {
2455
+ return Array.from(INSTANCES.values());
2456
+ }
2457
+ // 兼容旧 SDK 全局
2458
+ if (typeof window !== 'undefined') {
2459
+ window.eventTracker = new EventTracker();
2460
+ }
2461
+
2462
+ /**
2463
+ * auto-track 插件
2464
+ * - $pageview: 路由变化时自动 track
2465
+ * - $WebClick: 监听 click,自动 track
2466
+ * - $WebStay: 页面离开时上报停留时长
2467
+ * - $scrolldepth: 25/50/75/100% 触发
2468
+ */
2469
+ function autoTrack(opts) {
2470
+ var _a;
2471
+ if (opts === void 0) { opts = {}; }
2472
+ var sdkRef = null;
2473
+ var enterTime = Date.now();
2474
+ var reachedThresholds = new Set();
2475
+ var thresholds = (_a = opts.scrollThresholds) !== null && _a !== void 0 ? _a : [25, 50, 75, 100];
2476
+ function fire(event, properties) {
2477
+ if (properties === void 0) { properties = {}; }
2478
+ if (!sdkRef)
2479
+ return;
2480
+ sdkRef.track(event, properties);
2481
+ }
2482
+ function path() {
2483
+ try {
2484
+ return location.pathname + location.search + location.hash;
2485
+ }
2486
+ catch (_a) {
2487
+ return '';
2488
+ }
2489
+ }
2490
+ function setupPageview() {
2491
+ var send = function () {
2492
+ var _a;
2493
+ fire(PresetEvent.PageView, (_a = {},
2494
+ _a[PresetProperty.URLPath] = path(),
2495
+ _a[PresetProperty.Title] = document.title,
2496
+ _a[PresetProperty.Referrer] = document.referrer,
2497
+ _a));
2498
+ };
2499
+ // 首次加载
2500
+ setTimeout(send, 0);
2501
+ // history.pushState/replaceState
2502
+ var wrap = function (key) {
2503
+ var orig = history[key];
2504
+ history[key] = function () {
2505
+ var args = [];
2506
+ for (var _i = 0; _i < arguments.length; _i++) {
2507
+ args[_i] = arguments[_i];
2508
+ }
2509
+ var r = orig.apply(this, args);
2510
+ setTimeout(send, 0);
2511
+ return r;
2512
+ };
2513
+ };
2514
+ wrap('pushState');
2515
+ wrap('replaceState');
2516
+ // popstate
2517
+ window.addEventListener('popstate', function () { return setTimeout(send, 0); });
2518
+ // hashchange
2519
+ window.addEventListener('hashchange', function () { return setTimeout(send, 0); });
2520
+ }
2521
+ function setupWebClick(selector, attrs) {
2522
+ var onClick = function (e) {
2523
+ var _a;
2524
+ var target = e.target;
2525
+ if (!target)
2526
+ return;
2527
+ var el = target.closest(selector || 'a, button, [data-track-click]');
2528
+ if (!el)
2529
+ return;
2530
+ var props = (_a = {},
2531
+ _a[PresetProperty.ElementSelector] = cssPath(el),
2532
+ _a[PresetProperty.ElementType] = el.tagName.toLowerCase(),
2533
+ _a[PresetProperty.ElementContent] = (el.textContent || '').trim().slice(0, 100),
2534
+ _a[PresetProperty.ElementId] = el.id || undefined,
2535
+ _a[PresetProperty.ElementClassName] = el.className || undefined,
2536
+ _a);
2537
+ if (el instanceof HTMLAnchorElement) {
2538
+ props['$element_href'] = el.href;
2539
+ }
2540
+ for (var _i = 0, attrs_1 = attrs; _i < attrs_1.length; _i++) {
2541
+ var a = attrs_1[_i];
2542
+ var v = el.getAttribute(a);
2543
+ if (v != null)
2544
+ props[a] = v;
2545
+ }
2546
+ fire(PresetEvent.WebClick, props);
2547
+ };
2548
+ document.addEventListener('click', onClick, true);
2549
+ }
2550
+ function cssPath(el) {
2551
+ if (el.id)
2552
+ return "#".concat(el.id);
2553
+ var parts = [];
2554
+ var cur = el;
2555
+ var _loop_1 = function () {
2556
+ var self_1 = cur;
2557
+ var part = self_1.tagName.toLowerCase();
2558
+ if (self_1.classList && self_1.classList.length) {
2559
+ part += '.' + Array.from(self_1.classList).slice(0, 2).join('.');
2560
+ }
2561
+ var parent_1 = self_1.parentElement;
2562
+ if (parent_1) {
2563
+ var siblings = Array.from(parent_1.children).filter(function (c) { return c instanceof Element && c.tagName === self_1.tagName; });
2564
+ if (siblings.length > 1) {
2565
+ part += ":nth-of-type(".concat(siblings.indexOf(self_1) + 1, ")");
2566
+ }
2567
+ }
2568
+ parts.unshift(part);
2569
+ cur = parent_1;
2570
+ };
2571
+ while (cur && cur !== document.body && parts.length < 5) {
2572
+ _loop_1();
2573
+ }
2574
+ return parts.join(' > ');
2575
+ }
2576
+ function setupWebStay() {
2577
+ var onEnter = function () { enterTime = Date.now(); };
2578
+ var onLeave = function () {
2579
+ var _a;
2580
+ var dur = Date.now() - enterTime;
2581
+ fire(PresetEvent.WebStay, (_a = {},
2582
+ _a[PresetProperty.EventDuration] = dur,
2583
+ _a[PresetProperty.URLPath] = path(),
2584
+ _a));
2585
+ };
2586
+ window.addEventListener('beforeunload', onLeave);
2587
+ document.addEventListener('visibilitychange', function () {
2588
+ if (document.visibilityState === 'hidden')
2589
+ onLeave();
2590
+ else
2591
+ onEnter();
2592
+ });
2593
+ }
2594
+ function setupScrollDepth() {
2595
+ reachedThresholds = new Set();
2596
+ var onScroll = function () {
2597
+ var _a;
2598
+ var docH = document.documentElement.scrollHeight - window.innerHeight;
2599
+ if (docH <= 0)
2600
+ return;
2601
+ var pct = Math.min(100, Math.round((window.scrollY / docH) * 100));
2602
+ for (var _i = 0, thresholds_1 = thresholds; _i < thresholds_1.length; _i++) {
2603
+ var t = thresholds_1[_i];
2604
+ if (pct >= t && !reachedThresholds.has(t)) {
2605
+ reachedThresholds.add(t);
2606
+ fire('$scroll_depth', (_a = { percent: t }, _a[PresetProperty.URLPath] = path(), _a));
2607
+ }
2608
+ }
2609
+ };
2610
+ window.addEventListener('scroll', onScroll, { passive: true });
2611
+ }
2612
+ return {
2613
+ name: 'auto-track',
2614
+ install: function (_a) {
2615
+ var sdk = _a.sdk;
2616
+ if (!isBrowser())
2617
+ return;
2618
+ sdkRef = sdk;
2619
+ if (opts.pageview !== false)
2620
+ setupPageview();
2621
+ if (opts.webclick !== false)
2622
+ setupWebClick(opts.webclickSelector, opts.webclickAttrs || ['data-track-id', 'data-sensors-click']);
2623
+ if (opts.webstay !== false)
2624
+ setupWebStay();
2625
+ if (opts.scrolldepth)
2626
+ setupScrollDepth();
2627
+ },
2628
+ };
2629
+ }
2630
+
2631
+ /**
2632
+ * pageload 插件:performance API 采集首屏加载性能
2633
+ * - $WebPageLoad 事件,包含:
2634
+ * $load_time / $dom_ready_time / $dom_interactive_time / $response_time
2635
+ * $page_resource_size / $page_height / $viewport_position
2636
+ */
2637
+ function pageLoad(opts) {
2638
+ if (opts === void 0) { opts = {}; }
2639
+ var sdkRef = null;
2640
+ function fire() {
2641
+ var _a;
2642
+ if (!sdkRef)
2643
+ return;
2644
+ var props = (_a = {},
2645
+ _a[PresetProperty.URLPath] = location.pathname + location.search + location.hash,
2646
+ _a[PresetProperty.Title] = document.title,
2647
+ _a);
2648
+ // 尝试 performance API
2649
+ try {
2650
+ var perf = (typeof performance !== 'undefined' ? performance : null);
2651
+ if (perf) {
2652
+ var nav = perf.getEntriesByType('navigation')[0];
2653
+ if (nav) {
2654
+ if (typeof nav.domContentLoadedEventEnd === 'number') {
2655
+ props['$dom_ready_time'] = Math.round(nav.domContentLoadedEventEnd - nav.startTime);
2656
+ }
2657
+ if (typeof nav.domInteractive === 'number') {
2658
+ props['$dom_interactive_time'] = Math.round(nav.domInteractive - nav.startTime);
2659
+ }
2660
+ if (typeof nav.loadEventEnd === 'number' && typeof nav.startTime === 'number') {
2661
+ props['$load_time'] = Math.round(nav.loadEventEnd - nav.startTime);
2662
+ }
2663
+ if (typeof nav.responseEnd === 'number' && typeof nav.requestStart === 'number') {
2664
+ props['$response_time'] = Math.round(nav.responseEnd - nav.requestStart);
2665
+ }
2666
+ if (typeof nav.transferSize === 'number') {
2667
+ props[PresetProperty.PageResourceSize] = nav.transferSize;
2668
+ }
2669
+ }
2670
+ // paint
2671
+ var fp = perf.getEntriesByName('first-paint')[0];
2672
+ var fcp = perf.getEntriesByName('first-contentful-paint')[0];
2673
+ if (fp)
2674
+ props['$first_paint'] = Math.round(fp.startTime);
2675
+ if (fcp)
2676
+ props['$first_contentful_paint'] = Math.round(fcp.startTime);
2677
+ }
2678
+ }
2679
+ catch (_b) {
2680
+ /* ignore */
2681
+ }
2682
+ // 页面尺寸
2683
+ try {
2684
+ if (document.documentElement) {
2685
+ props[PresetProperty.PageHeight] = document.documentElement.scrollHeight;
2686
+ props[PresetProperty.ViewportPosition] = window.scrollY || 0;
2687
+ }
2688
+ }
2689
+ catch (_c) {
2690
+ /* ignore */
2691
+ }
2692
+ sdkRef.track(PresetEvent.WebPageLoad, props);
2693
+ }
2694
+ return {
2695
+ name: 'pageload',
2696
+ install: function (_a) {
2697
+ var sdk = _a.sdk;
2698
+ if (!isBrowser())
2699
+ return;
2700
+ sdkRef = sdk;
2701
+ var fireNow = opts.immediate !== false;
2702
+ if (document.readyState === 'complete') {
2703
+ if (fireNow)
2704
+ fire();
2705
+ else
2706
+ window.addEventListener('load', fire, { once: true });
2707
+ }
2708
+ else {
2709
+ window.addEventListener('load', function () {
2710
+ if (fireNow)
2711
+ fire();
2712
+ else
2713
+ setTimeout(fire, 0);
2714
+ }, { once: true });
2715
+ }
2716
+ },
2717
+ };
2718
+ }
2719
+
2720
+ /**
2721
+ * pageleave 插件:用户离开页面(关闭、跳转、切到后台)时上报
2722
+ * - $WebPageLeave 事件
2723
+ * - 包含 $event_duration / $url_path / $referrer / $viewport_position
2724
+ * - 包含 page_visible 标记(visibilitychange API)
2725
+ */
2726
+ function pageLeave(_opts) {
2727
+ var sdkRef = null;
2728
+ var enterTime = Date.now();
2729
+ function fire(reason) {
2730
+ var _a;
2731
+ if (!sdkRef)
2732
+ return;
2733
+ var dur = Date.now() - enterTime;
2734
+ sdkRef.track(PresetEvent.WebPageLeave, (_a = {},
2735
+ _a[PresetProperty.EventDuration] = dur,
2736
+ _a[PresetProperty.URLPath] = location.pathname + location.search + location.hash,
2737
+ _a[PresetProperty.Title] = document.title,
2738
+ _a[PresetProperty.ViewportPosition] = window.scrollY || 0,
2739
+ _a.$page_leave_reason = reason,
2740
+ _a));
2741
+ }
2742
+ return {
2743
+ name: 'pageleave',
2744
+ install: function (_a) {
2745
+ var sdk = _a.sdk;
2746
+ if (!isBrowser())
2747
+ return;
2748
+ sdkRef = sdk;
2749
+ enterTime = Date.now();
2750
+ // 重新进入页面时刷新 enterTime
2751
+ var onVisible = function () {
2752
+ if (document.visibilityState === 'visible')
2753
+ enterTime = Date.now();
2754
+ };
2755
+ document.addEventListener('visibilitychange', onVisible);
2756
+ // 关闭/离开
2757
+ window.addEventListener('pagehide', function () { return fire('pagehide'); });
2758
+ window.addEventListener('beforeunload', function () { return fire('unload'); });
2759
+ document.addEventListener('visibilitychange', function () {
2760
+ if (document.visibilityState === 'hidden')
2761
+ fire('hidden');
2762
+ });
2763
+ },
2764
+ uninstall: function () {
2765
+ sdkRef = null;
2766
+ },
2767
+ };
2768
+ }
2769
+
2770
+ /**
2771
+ * exposure 插件:元素曝光(IntersectionObserver)
2772
+ * - $element_exposure 事件
2773
+ * - 元素进入视口超过阈值持续 X ms 才上报(默认 500ms)
2774
+ * - 同元素同阈值只上报一次
2775
+ */
2776
+ function exposure(opts) {
2777
+ if (opts === void 0) { opts = {}; }
2778
+ var sdkRef = null;
2779
+ var observer = null;
2780
+ var reported = new WeakSet();
2781
+ function cssPath(el) {
2782
+ if (el.id)
2783
+ return "#".concat(el.id);
2784
+ var parts = [];
2785
+ var cur = el;
2786
+ while (cur && cur !== document.body && parts.length < 5) {
2787
+ var part = cur.tagName.toLowerCase();
2788
+ if (cur.classList && cur.classList.length) {
2789
+ part += '.' + Array.from(cur.classList).slice(0, 2).join('.');
2790
+ }
2791
+ parts.unshift(part);
2792
+ cur = cur.parentElement;
2793
+ }
2794
+ return parts.join(' > ');
2795
+ }
2796
+ function setupObserver() {
2797
+ var _a, _b;
2798
+ if (!('IntersectionObserver' in window))
2799
+ return;
2800
+ var threshold = (_a = opts.threshold) !== null && _a !== void 0 ? _a : 0.5;
2801
+ var duration = (_b = opts.duration) !== null && _b !== void 0 ? _b : 500;
2802
+ observer = new IntersectionObserver(function (entries) {
2803
+ var _loop_1 = function (entry) {
2804
+ if (entry.intersectionRatio >= threshold) {
2805
+ var el_1 = entry.target;
2806
+ var timer_1 = setTimeout(function () {
2807
+ var _a;
2808
+ if (reported.has(el_1))
2809
+ return;
2810
+ reported.add(el_1);
2811
+ if (!sdkRef)
2812
+ return;
2813
+ var props = (_a = {},
2814
+ _a[PresetProperty.ElementSelector] = cssPath(el_1),
2815
+ _a[PresetProperty.ElementType] = el_1.tagName.toLowerCase(),
2816
+ _a.$intersection_ratio = entry.intersectionRatio,
2817
+ _a.$exposure_duration = duration,
2818
+ _a);
2819
+ if (el_1 instanceof HTMLElement && el_1.dataset) {
2820
+ var prefix = opts.attrPrefix || 'data-exp-';
2821
+ for (var _i = 0, _b = Object.keys(el_1.dataset); _i < _b.length; _i++) {
2822
+ var k = _b[_i];
2823
+ if (k.startsWith(prefix)) {
2824
+ props[k] = el_1.dataset[k];
2825
+ }
2826
+ }
2827
+ }
2828
+ sdkRef.track(PresetEvent.ElementExposure, props);
2829
+ }, duration);
2830
+ // 离开时取消未上报
2831
+ var off_1 = function () {
2832
+ clearTimeout(timer_1);
2833
+ el_1.removeEventListener('mouseleave', off_1);
2834
+ };
2835
+ el_1.addEventListener('mouseleave', off_1);
2836
+ }
2837
+ };
2838
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
2839
+ var entry = entries_1[_i];
2840
+ _loop_1(entry);
2841
+ }
2842
+ }, { threshold: [threshold] });
2843
+ if (opts.selector) {
2844
+ var targets = document.querySelectorAll(opts.selector);
2845
+ targets.forEach(function (t) { return observer.observe(t); });
2846
+ }
2847
+ }
2848
+ return {
2849
+ name: 'exposure',
2850
+ install: function (_a) {
2851
+ var sdk = _a.sdk;
2852
+ if (!isBrowser())
2853
+ return;
2854
+ sdkRef = sdk;
2855
+ if (document.readyState === 'complete')
2856
+ setupObserver();
2857
+ else
2858
+ window.addEventListener('load', setupObserver, { once: true });
2859
+ },
2860
+ uninstall: function () {
2861
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
2862
+ observer = null;
2863
+ sdkRef = null;
2864
+ },
2865
+ };
2866
+ }
2867
+
2868
+ /**
2869
+ * session-event 插件:会话管理
2870
+ * - 每条事件附 $event_session_id(首次访问生成,存 storage)
2871
+ * - $session_start / $session_end 事件
2872
+ * - 超时(默认 30 分钟)则重开会话
2873
+ */
2874
+ var SESSION_KEY = 'et_session_id';
2875
+ var SESSION_LAST_KEY = 'et_session_last_active';
2876
+ var DEFAULT_TIMEOUT = 30 * 60 * 1000; // 30 分钟
2877
+ function sessionEvent(opts) {
2878
+ var _a;
2879
+ if (opts === void 0) { opts = {}; }
2880
+ var timeout = (_a = opts.timeout) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT;
2881
+ var sdkRef = null;
2882
+ var storeRef = null;
2883
+ var sessionId = '';
2884
+ var lastActive = 0;
2885
+ function newSession(reason) {
2886
+ sessionId = uuidv4();
2887
+ lastActive = Date.now();
2888
+ if (storeRef) {
2889
+ storeRef.set(SESSION_KEY, sessionId);
2890
+ storeRef.set(SESSION_LAST_KEY, String(lastActive));
2891
+ }
2892
+ if (sdkRef && reason === 'start') {
2893
+ sdkRef.track('$session_start', { $event_session_id: sessionId });
2894
+ }
2895
+ }
2896
+ function check() {
2897
+ var now = Date.now();
2898
+ if (!sessionId || (lastActive && now - lastActive > timeout)) {
2899
+ newSession(now - lastActive > timeout ? 'timeout' : 'start');
2900
+ }
2901
+ lastActive = now;
2902
+ if (storeRef)
2903
+ storeRef.set(SESSION_LAST_KEY, String(lastActive));
2904
+ }
2905
+ var interceptor = {
2906
+ name: 'session-event',
2907
+ priority: 10, // 早于业务拦截器
2908
+ entry: function (ctx) {
2909
+ if (ctx.meta.stage !== 'buildDataStage')
2910
+ return;
2911
+ check();
2912
+ var ev = ctx.data;
2913
+ if (ev && ev.properties) {
2914
+ ev.properties[PresetProperty.EventSessionId] = sessionId;
2915
+ }
2916
+ },
2917
+ };
2918
+ return {
2919
+ name: 'session-event',
2920
+ install: function (_a) {
2921
+ var sdk = _a.sdk, stages = _a.stages, store = _a.store;
2922
+ if (!isBrowser())
2923
+ return;
2924
+ sdkRef = sdk;
2925
+ storeRef = store;
2926
+ // 读已有
2927
+ var existing = store.get(SESSION_KEY);
2928
+ var last = Number(store.get(SESSION_LAST_KEY) || '0');
2929
+ if (existing && last && Date.now() - last < timeout) {
2930
+ sessionId = existing;
2931
+ lastActive = last;
2932
+ }
2933
+ else {
2934
+ newSession('start');
2935
+ }
2936
+ stages.buildDataStage.register(interceptor);
2937
+ // 跨 tab 同步:通过 storage 事件检测其它 tab 关闭
2938
+ window.addEventListener('storage', function (ev) {
2939
+ if (ev.key === SESSION_KEY && !ev.newValue) {
2940
+ // 其它 tab 关闭了会话,本地也清掉
2941
+ sessionId = '';
2942
+ }
2943
+ });
2944
+ },
2945
+ uninstall: function () {
2946
+ sdkRef = null;
2947
+ storeRef = null;
2948
+ },
2949
+ };
2950
+ }
2951
+
2952
+ /**
2953
+ * Debug 发送器(独立 plugin)
2954
+ * - 启用后走 GET 请求到 debug_mode_url(默认从 server_url 推导 ?debug)
2955
+ * - 成功后 alert("debug数据发送成功" + data)
2956
+ * - 失败时 alert("debug失败" + JSON.stringify(err))
2957
+ * - 优先级最高(先于其它 senders 执行)
2958
+ */
2959
+ function deriveDebugUrl(serverUrl) {
2960
+ var base = Array.isArray(serverUrl) ? serverUrl[0] : serverUrl;
2961
+ return base.includes('?') ? base + '&debug' : base + '?debug';
2962
+ }
2963
+ function debugSender(opts) {
2964
+ if (opts === void 0) { opts = {}; }
2965
+ return {
2966
+ name: 'debug-sender',
2967
+ install: function (_a) {
2968
+ var _b, _c, _d, _e, _f;
2969
+ var config = _a.config, stages = _a.stages, registry = _a.registry, sdk = _a.sdk;
2970
+ var enabled = (_b = opts.enable) !== null && _b !== void 0 ? _b : !!config.debug_mode;
2971
+ if (!enabled)
2972
+ return;
2973
+ var url = (_d = (_c = opts.url) !== null && _c !== void 0 ? _c : config.debug_mode_url) !== null && _d !== void 0 ? _d : deriveDebugUrl(config.server_url);
2974
+ var upload = (_f = (_e = opts.upload) !== null && _e !== void 0 ? _e : config.debug_mode_upload) !== null && _f !== void 0 ? _f : 'true';
2975
+ stages.sendDataStage.register({
2976
+ name: 'debug-sender',
2977
+ priority: 30, // 最高优先级
2978
+ entry: function (ctx) {
2979
+ var event = ctx.data;
2980
+ try {
2981
+ var enc = encodeEvent(event, config.encryptor);
2982
+ Promise.resolve(enc).then(function (payload) {
2983
+ var fullUrl = buildUrl(url, payload, 'form');
2984
+ if (hasImage()) {
2985
+ var img = new Image(1, 1);
2986
+ img.onload = function () {
2987
+ var _a;
2988
+ try {
2989
+ (_a = opts.onSuccess) === null || _a === void 0 ? void 0 : _a.call(opts, event);
2990
+ }
2991
+ catch ( /* ignore */_b) { /* ignore */ }
2992
+ try {
2993
+ if (typeof alert !== 'undefined')
2994
+ alert('debug数据发送成功' + (event.event || ''));
2995
+ }
2996
+ catch ( /* ignore */_c) { /* ignore */ }
2997
+ };
2998
+ img.onerror = function () {
2999
+ var _a;
3000
+ var err = new Error('Image load failed');
3001
+ try {
3002
+ (_a = opts.onError) === null || _a === void 0 ? void 0 : _a.call(opts, err, event);
3003
+ }
3004
+ catch ( /* ignore */_b) { /* ignore */ }
3005
+ try {
3006
+ if (typeof alert !== 'undefined')
3007
+ alert('debug失败 错误原因' + JSON.stringify({ message: err.message }));
3008
+ }
3009
+ catch ( /* ignore */_c) { /* ignore */ }
3010
+ };
3011
+ img.src = fullUrl;
3012
+ }
3013
+ else {
3014
+ ctx.cancellationToken.stop();
3015
+ }
3016
+ }).catch(function (e) {
3017
+ sdk.getLogger().error('debug-sender error:', e);
3018
+ });
3019
+ }
3020
+ catch (e) {
3021
+ sdk.getLogger().error('debug-sender error:', e);
3022
+ }
3023
+ return event;
3024
+ },
3025
+ });
3026
+ // 标记 Dry-Run 风格
3027
+ registry.on('sdkAfterInitAPI', function () {
3028
+ Object.defineProperty(globalThis, '__et_debug_upload__', { value: upload, writable: true });
3029
+ });
3030
+ },
3031
+ uninstall: function () {
3032
+ try {
3033
+ delete globalThis.__et_debug_upload__;
3034
+ }
3035
+ catch ( /* ignore */_a) { /* ignore */ }
3036
+ },
3037
+ };
3038
+ }
3039
+
3040
+ /**
3041
+ * indexeddb-sender 插件:把事件写到 IndexedDB(用于离线缓存 / 批量消费)
3042
+ * - 不实际发送,只把序列化事件追加到指定 IDB store
3043
+ * - 适合后续被 webview bridge / native / service worker 消费
3044
+ */
3045
+ function indexedDBSender(opts) {
3046
+ if (opts === void 0) { opts = {}; }
3047
+ var dbName = opts.dbName || 'event-tracker-outbox';
3048
+ var storeName = opts.storeName || 'outbox';
3049
+ var max = opts.max || 500;
3050
+ var dbPromise = null;
3051
+ function openDB() {
3052
+ if (!isBrowser() || typeof indexedDB === 'undefined')
3053
+ return Promise.resolve(null);
3054
+ return new Promise(function (resolve) {
3055
+ try {
3056
+ var req_1 = indexedDB.open(dbName, 1);
3057
+ req_1.onupgradeneeded = function () {
3058
+ var db = req_1.result;
3059
+ if (!db.objectStoreNames.contains(storeName)) {
3060
+ db.createObjectStore(storeName, { autoIncrement: true });
3061
+ }
3062
+ };
3063
+ req_1.onsuccess = function () { return resolve(req_1.result); };
3064
+ req_1.onerror = function () { return resolve(null); };
3065
+ }
3066
+ catch (_a) {
3067
+ resolve(null);
3068
+ }
3069
+ });
3070
+ }
3071
+ var interceptor = {
3072
+ name: 'indexeddb-sender',
3073
+ priority: 60,
3074
+ entry: function (ctx) {
3075
+ if (ctx.meta.stage !== 'sendDataStage')
3076
+ return;
3077
+ var ev = ctx.data;
3078
+ dbPromise !== null && dbPromise !== void 0 ? dbPromise : (dbPromise = openDB());
3079
+ dbPromise.then(function (db) {
3080
+ if (!db)
3081
+ return;
3082
+ try {
3083
+ var tx = db.transaction(storeName, 'readwrite');
3084
+ var store_1 = tx.objectStore(storeName);
3085
+ var payload_1 = encodeEvent(ev);
3086
+ // 限制大小:> max 则删头
3087
+ var cntReq_1 = store_1.count();
3088
+ cntReq_1.onsuccess = function () {
3089
+ if (cntReq_1.result >= max) {
3090
+ store_1.delete(IDBKeyRange.lowerBound(0, true));
3091
+ }
3092
+ store_1.add({ time: Date.now(), payload: payload_1 });
3093
+ };
3094
+ }
3095
+ catch (_a) {
3096
+ /* ignore */
3097
+ }
3098
+ }).catch(function () { });
3099
+ return ev;
3100
+ },
3101
+ };
3102
+ return {
3103
+ name: 'indexeddb-sender',
3104
+ install: function (_a) {
3105
+ var stages = _a.stages;
3106
+ if (!isBrowser())
3107
+ return;
3108
+ stages.sendDataStage.register(interceptor);
3109
+ },
3110
+ uninstall: function () {
3111
+ dbPromise = null;
3112
+ },
3113
+ };
3114
+ }
3115
+
3116
+ /**
3117
+ * WebView 桥(vapph5 协议)
3118
+ * - 客户端(iOS/Android)注入 window.vapph5define 对象
3119
+ * - H5 通过 vapph5define.call(name, args) 调用客户端方法
3120
+ * - 客户端通过 vapph5define.handle(name, fn) 监听 H5 调用
3121
+ *
3122
+ * 本插件负责:
3123
+ * 1. 探测 WebView 标识
3124
+ * 2. 在 SDK 启动时调用 vapph5define.call('getAppInfo'),把 app_version/app_id/...
3125
+ * 注入到 config 与公共属性
3126
+ * 3. 暴露 helper:bridge.call(name, args) 与 bridge.on(name, fn)
3127
+ */
3128
+ function webviewBridge(opts) {
3129
+ if (opts === void 0) { opts = {}; }
3130
+ return {
3131
+ name: 'webview-bridge',
3132
+ install: function (_a) {
3133
+ var sdk = _a.sdk, config = _a.config;
3134
+ if (!isBrowser())
3135
+ return;
3136
+ var win = window;
3137
+ var defineName = opts.defineName || 'vapph5define';
3138
+ var define = win[defineName];
3139
+ if (!define || typeof define.call !== 'function') {
3140
+ return; // 不在 WebView 中
3141
+ }
3142
+ // 暴露 SDK 内部调用的便捷方法
3143
+ var bridge = {
3144
+ call: function (name) {
3145
+ var args = [];
3146
+ for (var _i = 1; _i < arguments.length; _i++) {
3147
+ args[_i - 1] = arguments[_i];
3148
+ }
3149
+ return define.call.apply(define, __spreadArray([name], args, false));
3150
+ },
3151
+ on: function (name, fn) { return define.handle(name, fn); },
3152
+ isInApp: function () { return define.isInApp === true || define.call('isInApp') === true; },
3153
+ };
3154
+ sdk.bridge = bridge;
3155
+ config.is_webview = true;
3156
+ if (opts.fetchOnInit !== false) {
3157
+ try {
3158
+ var protocolName = opts.protocolName || 'getAppInfo';
3159
+ var result = define.call(protocolName);
3160
+ if (result && typeof result === 'object') {
3161
+ var fm = opts.fieldMap || {};
3162
+ for (var _i = 0, _b = Object.entries(fm); _i < _b.length; _i++) {
3163
+ var _c = _b[_i], sdkKey = _c[0], resultKey = _c[1];
3164
+ var v = result[resultKey];
3165
+ if (v !== undefined && v !== null) {
3166
+ config[sdkKey] = v;
3167
+ }
3168
+ }
3169
+ sdk.register({
3170
+ $app_version: result.app_version || result.appVersion,
3171
+ $app_id: result.app_id || result.appId,
3172
+ $channel: result.channel,
3173
+ $lib: result.lib || result.lib_name || result.libName,
3174
+ });
3175
+ }
3176
+ }
3177
+ catch (_d) {
3178
+ /* ignore */
3179
+ }
3180
+ }
3181
+ },
3182
+ };
3183
+ }
3184
+
3185
+ /**
3186
+ * encrypt 插件(v1.1 增强版)
3187
+ *
3188
+ * 三个能力:
3189
+ * 1. 加密 + 解密配对(神策风格)
3190
+ * - config.encryptor / config.decoder
3191
+ * - 加密函数接收 JSON 字符串返回字符串;解密函数反向
3192
+ *
3193
+ * 2. Web Crypto AES-GCM
3194
+ * - aesGcmEncrypt / aesGcmDecrypt
3195
+ * - 基于 Web Crypto API,256-bit key,输出 base64(iv||ciphertext)
3196
+ *
3197
+ * 3. 钩子:encryptEvent / decryptEvent
3198
+ * - 用于在事件被发送前/接收后做二次处理(脱敏、补字段等)
3199
+ *
3200
+ * 用法示例:
3201
+ * import { encryptor, aesGcmEncrypt, aesGcmDecrypt } from '@macroui/event-tracker/plugin/encrypt';
3202
+ * const key = 'my-32-byte-secret-key-1234567890ab';
3203
+ * sdk.use(encryptor({
3204
+ * encrypt: aesGcmEncrypt(key),
3205
+ * decrypt: aesGcmDecrypt(key),
3206
+ * encryptEvent: (e) => scrubPII(e),
3207
+ * decryptEvent: (e) => e,
3208
+ * }));
3209
+ */
3210
+ // ============================================================
3211
+ // 1. XOR + Base64(兼容 v1.0,作为默认简易实现)
3212
+ // ============================================================
3213
+ function xorBase64(input, key) {
3214
+ var k = key || 'default';
3215
+ var out = '';
3216
+ for (var i = 0; i < input.length; i++) {
3217
+ out += String.fromCharCode(input.charCodeAt(i) ^ k.charCodeAt(i % k.length));
3218
+ }
3219
+ if (typeof btoa !== 'undefined')
3220
+ return btoa(out);
3221
+ return Buffer.from(out, 'binary').toString('base64');
3222
+ }
3223
+ function defaultEncryptor(key) {
3224
+ return function (data) { return xorBase64(data, key); };
3225
+ }
3226
+ function defaultDecryptor(key) {
3227
+ return function (data) {
3228
+ var bin = typeof atob !== 'undefined' ? atob(data) : Buffer.from(data, 'base64').toString('binary');
3229
+ var out = '';
3230
+ var k = key || 'default';
3231
+ for (var i = 0; i < bin.length; i++) {
3232
+ out += String.fromCharCode(bin.charCodeAt(i) ^ k.charCodeAt(i % k.length));
3233
+ }
3234
+ return out;
3235
+ };
3236
+ }
3237
+ // ============================================================
3238
+ // 2. AES-GCM(Web Crypto API;需要 https / localhost)
3239
+ // ============================================================
3240
+ var ENC = new TextEncoder();
3241
+ var DEC = new TextDecoder();
3242
+ function bytesToB64(bytes) {
3243
+ if (typeof btoa !== 'undefined') {
3244
+ var s = '';
3245
+ for (var i = 0; i < bytes.length; i++)
3246
+ s += String.fromCharCode(bytes[i]);
3247
+ return btoa(s);
3248
+ }
3249
+ return Buffer.from(bytes).toString('base64');
3250
+ }
3251
+ function b64ToBytes(b64) {
3252
+ if (typeof atob !== 'undefined') {
3253
+ var bin = atob(b64);
3254
+ var out = new Uint8Array(bin.length);
3255
+ for (var i = 0; i < bin.length; i++)
3256
+ out[i] = bin.charCodeAt(i);
3257
+ return out;
3258
+ }
3259
+ return new Uint8Array(Buffer.from(b64, 'base64'));
3260
+ }
3261
+ function importAesKey(rawKey) {
3262
+ return __awaiter(this, void 0, void 0, function () {
3263
+ var subtle, hash;
3264
+ return __generator(this, function (_a) {
3265
+ switch (_a.label) {
3266
+ case 0:
3267
+ subtle = getSubtle();
3268
+ return [4 /*yield*/, subtle.digest('SHA-256', ENC.encode(rawKey))];
3269
+ case 1:
3270
+ hash = _a.sent();
3271
+ return [2 /*return*/, subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt'])];
3272
+ }
3273
+ });
3274
+ });
3275
+ }
3276
+ function getSubtle() {
3277
+ var _a;
3278
+ if (typeof globalThis !== 'undefined' && ((_a = globalThis.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) {
3279
+ return globalThis.crypto.subtle;
3280
+ }
3281
+ // node 18+ 的 webcrypto
3282
+ try {
3283
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
3284
+ var webcrypto = require('crypto').webcrypto;
3285
+ globalThis.crypto = webcrypto;
3286
+ return webcrypto.subtle;
3287
+ }
3288
+ catch (_b) {
3289
+ throw new Error('[encrypt] Web Crypto API not available (need https/localhost or Node 18+)');
3290
+ }
3291
+ }
3292
+ /** AES-GCM 加密(异步版):返回 Promise<string> */
3293
+ function aesGcmEncryptAsync(rawKey) {
3294
+ var _this = this;
3295
+ var keyPromise = null;
3296
+ return function (data) { return __awaiter(_this, void 0, void 0, function () {
3297
+ var key, subtle, iv, cipher, _a, out;
3298
+ return __generator(this, function (_b) {
3299
+ switch (_b.label) {
3300
+ case 0:
3301
+ if (!keyPromise)
3302
+ keyPromise = importAesKey(rawKey);
3303
+ return [4 /*yield*/, keyPromise];
3304
+ case 1:
3305
+ key = _b.sent();
3306
+ subtle = getSubtle();
3307
+ iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
3308
+ _a = Uint8Array.bind;
3309
+ return [4 /*yield*/, subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, ENC.encode(data))];
3310
+ case 2:
3311
+ cipher = new (_a.apply(Uint8Array, [void 0, _b.sent()]))();
3312
+ out = new Uint8Array(iv.length + cipher.length);
3313
+ out.set(iv, 0);
3314
+ out.set(cipher, iv.length);
3315
+ return [2 /*return*/, bytesToB64(out)];
3316
+ }
3317
+ });
3318
+ }); };
3319
+ }
3320
+ /** AES-GCM 解密(异步版) */
3321
+ function aesGcmDecryptAsync(rawKey) {
3322
+ var _this = this;
3323
+ var keyPromise = null;
3324
+ return function (data) { return __awaiter(_this, void 0, void 0, function () {
3325
+ var key, subtle, raw, iv, cipher, plain;
3326
+ return __generator(this, function (_a) {
3327
+ switch (_a.label) {
3328
+ case 0:
3329
+ if (!keyPromise)
3330
+ keyPromise = importAesKey(rawKey);
3331
+ return [4 /*yield*/, keyPromise];
3332
+ case 1:
3333
+ key = _a.sent();
3334
+ subtle = getSubtle();
3335
+ raw = b64ToBytes(data);
3336
+ iv = raw.slice(0, 12);
3337
+ cipher = raw.slice(12);
3338
+ return [4 /*yield*/, subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, cipher)];
3339
+ case 2:
3340
+ plain = _a.sent();
3341
+ return [2 /*return*/, DEC.decode(plain)];
3342
+ }
3343
+ });
3344
+ }); };
3345
+ }
3346
+ // ============================================================
3347
+ // 3. 插件
3348
+ // ============================================================
3349
+ function encryptor(opts) {
3350
+ return {
3351
+ name: 'encryptor',
3352
+ install: function (_a) {
3353
+ var sdk = _a.sdk, config = _a.config;
3354
+ // 写入 config;tracker.sendEvent 会自动调用
3355
+ config.encryptor = opts.encrypt;
3356
+ config.decoder = opts.decrypt;
3357
+ // config.encryptEvent/decryptEvent 在 TrackerConfig 里是宽松的 Record<string,unknown> 函数
3358
+ var cfg = config;
3359
+ cfg.encryptEvent = opts.encryptEvent;
3360
+ cfg.decryptEvent = opts.decryptEvent;
3361
+ // 保留到 sdk 上,供调试 / 二次使用
3362
+ var ref = sdk;
3363
+ ref.__encryptor = opts.encrypt;
3364
+ ref.__decoder = opts.decrypt;
3365
+ ref.__encryptEvent = opts.encryptEvent;
3366
+ ref.__decryptEvent = opts.decryptEvent;
3367
+ },
3368
+ uninstall: function (ctx) {
3369
+ if (!ctx)
3370
+ return;
3371
+ var config = ctx.config, sdk = ctx.sdk;
3372
+ delete config.encryptor;
3373
+ delete config.decoder;
3374
+ var cfg = config;
3375
+ delete cfg.encryptEvent;
3376
+ delete cfg.decryptEvent;
3377
+ var ref = sdk;
3378
+ delete ref.__encryptor;
3379
+ delete ref.__decoder;
3380
+ delete ref.__encryptEvent;
3381
+ delete ref.__decryptEvent;
3382
+ },
3383
+ };
3384
+ }
3385
+ /** 便捷工厂:XOR 配对 */
3386
+ function xorEncryptor(key) {
3387
+ return encryptor({ encrypt: defaultEncryptor(key), decrypt: defaultDecryptor(key) });
3388
+ }
3389
+
3390
+ /**
3391
+ * 主入口:构造一个默认 SDK 实例(兼容神策风格的全局对象)
3392
+ *
3393
+ * 暴露:
3394
+ * - default export: 已经 attach 了所有 API 的全局单例
3395
+ * - EventTracker: 类,可以多实例
3396
+ * - named exports: 所有插件 / 工具
3397
+ */
3398
+ // 默认实例(神策风格的 window.eventTracker)
3399
+ // - 所有方法初始化为 noop(见 EventTracker 上的 default impl)
3400
+ // - init() 后被 attachXxxApi 替换
3401
+ var tracker = new EventTracker();
3402
+ if (typeof window !== 'undefined') {
3403
+ window.eventTracker = tracker;
3404
+ }
3405
+
3406
+ exports.ConfigError = ConfigError;
3407
+ exports.EncryptError = EncryptError;
3408
+ exports.EventTracker = EventTracker;
3409
+ exports.FailedQueue = FailedQueue;
3410
+ exports.IdentityError = IdentityError;
3411
+ exports.IdentityManager = IdentityManager;
3412
+ exports.InitError = InitError;
3413
+ exports.InstanceChannel = InstanceChannel;
3414
+ exports.LIB_NAME = LIB_NAME;
3415
+ exports.LIB_VERSION = LIB_VERSION;
3416
+ exports.Logger = Logger;
3417
+ exports.PluginError = PluginError;
3418
+ exports.PluginRegistry = PluginRegistry;
3419
+ exports.PresetEvent = PresetEvent;
3420
+ exports.PresetProperty = PresetProperty;
3421
+ exports.PresetPropertyBuilder = PresetPropertyBuilder;
3422
+ exports.RetryPolicy = RetryPolicy;
3423
+ exports.SDKError = SDKError;
3424
+ exports.SendError = SendError;
3425
+ exports.SendPipeline = SendPipeline;
3426
+ exports.StorageError = StorageError;
3427
+ exports.aesGcmDecryptAsync = aesGcmDecryptAsync;
3428
+ exports.aesGcmEncryptAsync = aesGcmEncryptAsync;
3429
+ exports.autoTrack = autoTrack;
3430
+ exports.createSignedIdentity = createSignedIdentity;
3431
+ exports.debugSender = debugSender;
3432
+ exports.default = tracker;
3433
+ exports.defaultDecryptor = defaultDecryptor;
3434
+ exports.defaultEncryptor = defaultEncryptor;
3435
+ exports.encryptor = encryptor;
3436
+ exports.exposure = exposure;
3437
+ exports.hmacSignAsync = hmacSignAsync;
3438
+ exports.hmacVerifyAsync = hmacVerifyAsync;
3439
+ exports.indexedDBSender = indexedDBSender;
3440
+ exports.initPara = initPara;
3441
+ exports.isSDKError = isSDKError;
3442
+ exports.listInstances = listInstances;
3443
+ exports.pageLeave = pageLeave;
3444
+ exports.pageLoad = pageLoad;
3445
+ exports.registerAppid = registerAppid;
3446
+ exports.sessionEvent = sessionEvent;
3447
+ exports.tracker = tracker;
3448
+ exports.webviewBridge = webviewBridge;
3449
+ exports.xorEncryptor = xorEncryptor;