@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.
- package/LICENSE +21 -0
- package/README.md +343 -0
- package/bin/event-tracker-suggest.mjs +141 -0
- package/bin/event-tracker.mjs +157 -0
- package/dist/event-tracker.cjs +3449 -0
- package/dist/event-tracker.js +3455 -0
- package/dist/event-tracker.min.js +1 -0
- package/dist/event-tracker.mjs +3402 -0
- package/dist/plugin/auto-track.cjs +201 -0
- package/dist/plugin/auto-track.mjs +199 -0
- package/dist/plugin/debug-sender.cjs +214 -0
- package/dist/plugin/debug-sender.mjs +211 -0
- package/dist/plugin/encrypt.cjs +284 -0
- package/dist/plugin/encrypt.mjs +275 -0
- package/dist/plugin/exposure.cjs +121 -0
- package/dist/plugin/exposure.mjs +119 -0
- package/dist/plugin/indexeddb-sender.cjs +94 -0
- package/dist/plugin/indexeddb-sender.mjs +92 -0
- package/dist/plugin/pageleave.cjs +74 -0
- package/dist/plugin/pageleave.mjs +72 -0
- package/dist/plugin/pageload.cjs +198 -0
- package/dist/plugin/pageload.mjs +196 -0
- package/dist/plugin/session-event.cjs +142 -0
- package/dist/plugin/session-event.mjs +140 -0
- package/dist/plugin/webview-bridge.cjs +111 -0
- package/dist/plugin/webview-bridge.mjs +109 -0
- package/dist/types/core/env.d.ts +10 -0
- package/dist/types/core/errors.d.ts +76 -0
- package/dist/types/core/failed-queue.d.ts +24 -0
- package/dist/types/core/identity-api.d.ts +6 -0
- package/dist/types/core/identity.d.ts +39 -0
- package/dist/types/core/instance-channel.d.ts +34 -0
- package/dist/types/core/lifecycle.d.ts +26 -0
- package/dist/types/core/logger.d.ts +13 -0
- package/dist/types/core/plugin.d.ts +60 -0
- package/dist/types/core/profile-api.d.ts +6 -0
- package/dist/types/core/profile-store.d.ts +23 -0
- package/dist/types/core/register-api.d.ts +22 -0
- package/dist/types/core/retry-policy.d.ts +33 -0
- package/dist/types/core/send-pipeline.d.ts +55 -0
- package/dist/types/core/signed-identity.d.ts +29 -0
- package/dist/types/core/stage.d.ts +48 -0
- package/dist/types/core/track-api.d.ts +6 -0
- package/dist/types/core/tracker.d.ts +130 -0
- package/dist/types/core/uuid.d.ts +4 -0
- package/dist/types/index.d.ts +40 -0
- package/dist/types/plugins/auto-track/index.d.ts +21 -0
- package/dist/types/plugins/debug-sender/index.d.ts +24 -0
- package/dist/types/plugins/encrypt/index.d.ts +56 -0
- package/dist/types/plugins/exposure/index.d.ts +18 -0
- package/dist/types/plugins/indexeddb-sender/index.d.ts +15 -0
- package/dist/types/plugins/pageleave/index.d.ts +12 -0
- package/dist/types/plugins/pageload/index.d.ts +14 -0
- package/dist/types/plugins/session-event/index.d.ts +14 -0
- package/dist/types/plugins/webview-bridge/index.d.ts +33 -0
- package/dist/types/presets/preset-properties.d.ts +25 -0
- package/dist/types/send/ajax-sender.d.ts +11 -0
- package/dist/types/send/batch-sender.d.ts +17 -0
- package/dist/types/send/beacon-sender.d.ts +10 -0
- package/dist/types/send/encode.d.ts +8 -0
- package/dist/types/send/image-sender.d.ts +10 -0
- package/dist/types/send/sender.d.ts +20 -0
- package/dist/types/storage/indexeddb-store.d.ts +14 -0
- package/dist/types/storage/store.d.ts +56 -0
- package/dist/types/types/common.d.ts +26 -0
- package/dist/types/types/config.d.ts +117 -0
- package/dist/types/types/constants.d.ts +110 -0
- package/dist/types/types/index.d.ts +25 -0
- package/dist/types/types.test-d.d.ts +5 -0
- package/dist/types.test-d.ts +103 -0
- package/package.json +218 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 序列化 & 编码
|
|
3
|
+
*/
|
|
4
|
+
function encodeEvent(event, encryptor) {
|
|
5
|
+
var json = JSON.stringify(event);
|
|
6
|
+
return encryptor ? encryptor(json) : json;
|
|
7
|
+
}
|
|
8
|
+
function buildUrl(serverUrl, payload, contentType) {
|
|
9
|
+
var base = Array.isArray(serverUrl) ? serverUrl[0] || '' : serverUrl;
|
|
10
|
+
var has = base.indexOf('?') >= 0;
|
|
11
|
+
{
|
|
12
|
+
return "".concat(base).concat(has ? '&' : '?', "data=").concat(encodeURIComponent(payload));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 浏览器端辅助方法
|
|
18
|
+
*/
|
|
19
|
+
function isBrowser() {
|
|
20
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
21
|
+
}
|
|
22
|
+
function hasImage() {
|
|
23
|
+
return isBrowser() && typeof Image === 'function';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* SDK 预置事件名与预置属性 key(v1.1:完整对齐神策)
|
|
28
|
+
* 命名风格对齐神策,方便跨平台对接
|
|
29
|
+
*/
|
|
30
|
+
/** 预置事件名 */
|
|
31
|
+
/** 预置属性 key */
|
|
32
|
+
var PresetProperty = {
|
|
33
|
+
// 设备 / 环境
|
|
34
|
+
AppId: '$app_id',
|
|
35
|
+
AppVersion: '$app_version',
|
|
36
|
+
Lib: '$lib',
|
|
37
|
+
LibMethod: '$lib_method',
|
|
38
|
+
LibDetail: '$lib_detail',
|
|
39
|
+
LibVersion: '$lib_version',
|
|
40
|
+
ScreenWidth: '$screen_width',
|
|
41
|
+
ScreenHeight: '$screen_height',
|
|
42
|
+
ViewportWidth: '$viewport_width',
|
|
43
|
+
ViewportHeight: '$viewport_height',
|
|
44
|
+
TimezoneOffset: '$timezone_offset',
|
|
45
|
+
UserAgent: '$user_agent',
|
|
46
|
+
Language: '$language',
|
|
47
|
+
FirstBrowserCharset: '$first_browser_charset',
|
|
48
|
+
FirstBrowserLanguage: '$first_browser_language',
|
|
49
|
+
// 位置 / 流量来源
|
|
50
|
+
URL: '$url',
|
|
51
|
+
URLPath: '$url_path',
|
|
52
|
+
Title: '$title',
|
|
53
|
+
Referrer: '$referrer',
|
|
54
|
+
ReferrerHost: '$referrer_host',
|
|
55
|
+
LatestReferrer: '$latest_referrer',
|
|
56
|
+
LatestReferrerHost: '$latest_referrer_host',
|
|
57
|
+
LatestLandingPage: '$latest_landing_page',
|
|
58
|
+
LatestSearchKeyword: '$latest_search_keyword',
|
|
59
|
+
LatestTrafficSourceType: '$latest_traffic_source_type',
|
|
60
|
+
FirstReferrer: '$first_referrer',
|
|
61
|
+
FirstReferrerHost: '$first_referrer_host',
|
|
62
|
+
FirstSearchKeyword: '$first_search_keyword',
|
|
63
|
+
FirstTrafficSourceType: '$first_traffic_source_type',
|
|
64
|
+
FirstVisitTime: '$first_visit_time',
|
|
65
|
+
SearchKeywordId: '$search_keyword_id',
|
|
66
|
+
SearchKeywordIdHash: '$search_keyword_id_hash',
|
|
67
|
+
SearchKeywordIdType: '$search_keyword_id_type',
|
|
68
|
+
IsFirstDay: '$is_first_day',
|
|
69
|
+
IsFirstTime: '$is_first_time',
|
|
70
|
+
// UTM
|
|
71
|
+
LatestUtmSource: '$latest_utm_source',
|
|
72
|
+
LatestUtmMedium: '$latest_utm_medium',
|
|
73
|
+
LatestUtmCampaign: '$latest_utm_campaign',
|
|
74
|
+
LatestUtmContent: '$latest_utm_content',
|
|
75
|
+
LatestUtmTerm: '$latest_utm_term',
|
|
76
|
+
Utms: '$utms',
|
|
77
|
+
// 身份
|
|
78
|
+
DistinctId: '$distinct_id',
|
|
79
|
+
AnonymousId: '$anonymous_id',
|
|
80
|
+
FirstId: '$first_id',
|
|
81
|
+
OriginalId: '$original_id',
|
|
82
|
+
LoginId: '$login_id',
|
|
83
|
+
IsLoginId: '$is_login_id',
|
|
84
|
+
IdentityLoginId: '$identity_login_id',
|
|
85
|
+
IdentityAnonymousId: '$identity_anonymous_id',
|
|
86
|
+
IdentityCookieId: '$identity_cookie_id',
|
|
87
|
+
IdentityEmail: '$identity_email',
|
|
88
|
+
IdentityMobile: '$identity_mobile',
|
|
89
|
+
// 全埋点元素属性
|
|
90
|
+
ElementSelector: '$element_selector',
|
|
91
|
+
ElementPath: '$element_path',
|
|
92
|
+
ElementType: '$element_type',
|
|
93
|
+
ElementId: '$element_id',
|
|
94
|
+
ElementClassName: '$element_class_name',
|
|
95
|
+
ElementName: '$element_name',
|
|
96
|
+
ElementContent: '$element_content',
|
|
97
|
+
ElementTargetUrl: '$element_target_url',
|
|
98
|
+
ElementPosition: '$element_position',
|
|
99
|
+
PageX: '$page_x',
|
|
100
|
+
PageY: '$page_y',
|
|
101
|
+
// 性能
|
|
102
|
+
PageHeight: '$page_height',
|
|
103
|
+
PageResourceSize: '$page_resource_size',
|
|
104
|
+
EventDuration: '$event_duration',
|
|
105
|
+
ViewportPosition: '$viewport_position',
|
|
106
|
+
// 渠道
|
|
107
|
+
LatestWxAdClickId: '$latest_wx_ad_click_id',
|
|
108
|
+
LatestWxAdCallbacks: '$latest_wx_ad_callbacks',
|
|
109
|
+
LatestWxAdHashKey: '$latest_wx_ad_hash_key',
|
|
110
|
+
IsChannelCallbackEvent: '$is_channel_callback_event',
|
|
111
|
+
// 会话
|
|
112
|
+
EventSessionId: '$event_session_id',
|
|
113
|
+
// 元信息
|
|
114
|
+
Time: '$time',
|
|
115
|
+
Event: '$event',
|
|
116
|
+
Option: '$option',
|
|
117
|
+
Project: '$project',
|
|
118
|
+
Token: '$token',
|
|
119
|
+
// 特殊
|
|
120
|
+
SignupMethod: '$signup_method',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Debug 发送器(独立 plugin)
|
|
125
|
+
* - 启用后走 GET 请求到 debug_mode_url(默认从 server_url 推导 ?debug)
|
|
126
|
+
* - 成功后 alert("debug数据发送成功" + data)
|
|
127
|
+
* - 失败时 alert("debug失败" + JSON.stringify(err))
|
|
128
|
+
* - 优先级最高(先于其它 senders 执行)
|
|
129
|
+
*/
|
|
130
|
+
function deriveDebugUrl(serverUrl) {
|
|
131
|
+
var base = Array.isArray(serverUrl) ? serverUrl[0] : serverUrl;
|
|
132
|
+
return base.includes('?') ? base + '&debug' : base + '?debug';
|
|
133
|
+
}
|
|
134
|
+
function debugSender(opts) {
|
|
135
|
+
if (opts === void 0) { opts = {}; }
|
|
136
|
+
return {
|
|
137
|
+
name: 'debug-sender',
|
|
138
|
+
install: function (_a) {
|
|
139
|
+
var _b, _c, _d, _e, _f;
|
|
140
|
+
var config = _a.config, stages = _a.stages, registry = _a.registry, sdk = _a.sdk;
|
|
141
|
+
var enabled = (_b = opts.enable) !== null && _b !== void 0 ? _b : !!config.debug_mode;
|
|
142
|
+
if (!enabled)
|
|
143
|
+
return;
|
|
144
|
+
var url = (_d = (_c = opts.url) !== null && _c !== void 0 ? _c : config.debug_mode_url) !== null && _d !== void 0 ? _d : deriveDebugUrl(config.server_url);
|
|
145
|
+
var upload = (_f = (_e = opts.upload) !== null && _e !== void 0 ? _e : config.debug_mode_upload) !== null && _f !== void 0 ? _f : 'true';
|
|
146
|
+
stages.sendDataStage.register({
|
|
147
|
+
name: 'debug-sender',
|
|
148
|
+
priority: 30, // 最高优先级
|
|
149
|
+
entry: function (ctx) {
|
|
150
|
+
var event = ctx.data;
|
|
151
|
+
try {
|
|
152
|
+
var enc = encodeEvent(event, config.encryptor);
|
|
153
|
+
Promise.resolve(enc).then(function (payload) {
|
|
154
|
+
var fullUrl = buildUrl(url, payload, 'form');
|
|
155
|
+
if (hasImage()) {
|
|
156
|
+
var img = new Image(1, 1);
|
|
157
|
+
img.onload = function () {
|
|
158
|
+
var _a;
|
|
159
|
+
try {
|
|
160
|
+
(_a = opts.onSuccess) === null || _a === void 0 ? void 0 : _a.call(opts, event);
|
|
161
|
+
}
|
|
162
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
163
|
+
try {
|
|
164
|
+
if (typeof alert !== 'undefined')
|
|
165
|
+
alert('debug数据发送成功' + (event.event || ''));
|
|
166
|
+
}
|
|
167
|
+
catch ( /* ignore */_c) { /* ignore */ }
|
|
168
|
+
};
|
|
169
|
+
img.onerror = function () {
|
|
170
|
+
var _a;
|
|
171
|
+
var err = new Error('Image load failed');
|
|
172
|
+
try {
|
|
173
|
+
(_a = opts.onError) === null || _a === void 0 ? void 0 : _a.call(opts, err, event);
|
|
174
|
+
}
|
|
175
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
176
|
+
try {
|
|
177
|
+
if (typeof alert !== 'undefined')
|
|
178
|
+
alert('debug失败 错误原因' + JSON.stringify({ message: err.message }));
|
|
179
|
+
}
|
|
180
|
+
catch ( /* ignore */_c) { /* ignore */ }
|
|
181
|
+
};
|
|
182
|
+
img.src = fullUrl;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
ctx.cancellationToken.stop();
|
|
186
|
+
}
|
|
187
|
+
}).catch(function (e) {
|
|
188
|
+
sdk.getLogger().error('debug-sender error:', e);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
sdk.getLogger().error('debug-sender error:', e);
|
|
193
|
+
}
|
|
194
|
+
return event;
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
// 标记 Dry-Run 风格
|
|
198
|
+
registry.on('sdkAfterInitAPI', function () {
|
|
199
|
+
Object.defineProperty(globalThis, '__et_debug_upload__', { value: upload, writable: true });
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
uninstall: function () {
|
|
203
|
+
try {
|
|
204
|
+
delete globalThis.__et_debug_upload__;
|
|
205
|
+
}
|
|
206
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export { PresetProperty, debugSender };
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/******************************************************************************
|
|
4
|
+
Copyright (c) Microsoft Corporation.
|
|
5
|
+
|
|
6
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
7
|
+
purpose with or without fee is hereby granted.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
***************************************************************************** */
|
|
17
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
21
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
22
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
23
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
24
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
25
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
26
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function __generator(thisArg, body) {
|
|
31
|
+
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);
|
|
32
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
33
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
34
|
+
function step(op) {
|
|
35
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
36
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
37
|
+
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;
|
|
38
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
39
|
+
switch (op[0]) {
|
|
40
|
+
case 0: case 1: t = op; break;
|
|
41
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
42
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
43
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
44
|
+
default:
|
|
45
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
46
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
47
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
48
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
49
|
+
if (t[2]) _.ops.pop();
|
|
50
|
+
_.trys.pop(); continue;
|
|
51
|
+
}
|
|
52
|
+
op = body.call(thisArg, _);
|
|
53
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
54
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
59
|
+
var e = new Error(message);
|
|
60
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* encrypt 插件(v1.1 增强版)
|
|
65
|
+
*
|
|
66
|
+
* 三个能力:
|
|
67
|
+
* 1. 加密 + 解密配对(神策风格)
|
|
68
|
+
* - config.encryptor / config.decoder
|
|
69
|
+
* - 加密函数接收 JSON 字符串返回字符串;解密函数反向
|
|
70
|
+
*
|
|
71
|
+
* 2. Web Crypto AES-GCM
|
|
72
|
+
* - aesGcmEncrypt / aesGcmDecrypt
|
|
73
|
+
* - 基于 Web Crypto API,256-bit key,输出 base64(iv||ciphertext)
|
|
74
|
+
*
|
|
75
|
+
* 3. 钩子:encryptEvent / decryptEvent
|
|
76
|
+
* - 用于在事件被发送前/接收后做二次处理(脱敏、补字段等)
|
|
77
|
+
*
|
|
78
|
+
* 用法示例:
|
|
79
|
+
* import { encryptor, aesGcmEncrypt, aesGcmDecrypt } from '@macroui/event-tracker/plugin/encrypt';
|
|
80
|
+
* const key = 'my-32-byte-secret-key-1234567890ab';
|
|
81
|
+
* sdk.use(encryptor({
|
|
82
|
+
* encrypt: aesGcmEncrypt(key),
|
|
83
|
+
* decrypt: aesGcmDecrypt(key),
|
|
84
|
+
* encryptEvent: (e) => scrubPII(e),
|
|
85
|
+
* decryptEvent: (e) => e,
|
|
86
|
+
* }));
|
|
87
|
+
*/
|
|
88
|
+
// ============================================================
|
|
89
|
+
// 1. XOR + Base64(兼容 v1.0,作为默认简易实现)
|
|
90
|
+
// ============================================================
|
|
91
|
+
function xorBase64(input, key) {
|
|
92
|
+
var k = key || 'default';
|
|
93
|
+
var out = '';
|
|
94
|
+
for (var i = 0; i < input.length; i++) {
|
|
95
|
+
out += String.fromCharCode(input.charCodeAt(i) ^ k.charCodeAt(i % k.length));
|
|
96
|
+
}
|
|
97
|
+
if (typeof btoa !== 'undefined')
|
|
98
|
+
return btoa(out);
|
|
99
|
+
return Buffer.from(out, 'binary').toString('base64');
|
|
100
|
+
}
|
|
101
|
+
function defaultEncryptor(key) {
|
|
102
|
+
return function (data) { return xorBase64(data, key); };
|
|
103
|
+
}
|
|
104
|
+
function defaultDecryptor(key) {
|
|
105
|
+
return function (data) {
|
|
106
|
+
var bin = typeof atob !== 'undefined' ? atob(data) : Buffer.from(data, 'base64').toString('binary');
|
|
107
|
+
var out = '';
|
|
108
|
+
var k = key || 'default';
|
|
109
|
+
for (var i = 0; i < bin.length; i++) {
|
|
110
|
+
out += String.fromCharCode(bin.charCodeAt(i) ^ k.charCodeAt(i % k.length));
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// ============================================================
|
|
116
|
+
// 2. AES-GCM(Web Crypto API;需要 https / localhost)
|
|
117
|
+
// ============================================================
|
|
118
|
+
var ENC = new TextEncoder();
|
|
119
|
+
var DEC = new TextDecoder();
|
|
120
|
+
function bytesToB64(bytes) {
|
|
121
|
+
if (typeof btoa !== 'undefined') {
|
|
122
|
+
var s = '';
|
|
123
|
+
for (var i = 0; i < bytes.length; i++)
|
|
124
|
+
s += String.fromCharCode(bytes[i]);
|
|
125
|
+
return btoa(s);
|
|
126
|
+
}
|
|
127
|
+
return Buffer.from(bytes).toString('base64');
|
|
128
|
+
}
|
|
129
|
+
function b64ToBytes(b64) {
|
|
130
|
+
if (typeof atob !== 'undefined') {
|
|
131
|
+
var bin = atob(b64);
|
|
132
|
+
var out = new Uint8Array(bin.length);
|
|
133
|
+
for (var i = 0; i < bin.length; i++)
|
|
134
|
+
out[i] = bin.charCodeAt(i);
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
return new Uint8Array(Buffer.from(b64, 'base64'));
|
|
138
|
+
}
|
|
139
|
+
function importAesKey(rawKey) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
141
|
+
var subtle, hash;
|
|
142
|
+
return __generator(this, function (_a) {
|
|
143
|
+
switch (_a.label) {
|
|
144
|
+
case 0:
|
|
145
|
+
subtle = getSubtle();
|
|
146
|
+
return [4 /*yield*/, subtle.digest('SHA-256', ENC.encode(rawKey))];
|
|
147
|
+
case 1:
|
|
148
|
+
hash = _a.sent();
|
|
149
|
+
return [2 /*return*/, subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt'])];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function getSubtle() {
|
|
155
|
+
var _a;
|
|
156
|
+
if (typeof globalThis !== 'undefined' && ((_a = globalThis.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) {
|
|
157
|
+
return globalThis.crypto.subtle;
|
|
158
|
+
}
|
|
159
|
+
// node 18+ 的 webcrypto
|
|
160
|
+
try {
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
162
|
+
var webcrypto = require('crypto').webcrypto;
|
|
163
|
+
globalThis.crypto = webcrypto;
|
|
164
|
+
return webcrypto.subtle;
|
|
165
|
+
}
|
|
166
|
+
catch (_b) {
|
|
167
|
+
throw new Error('[encrypt] Web Crypto API not available (need https/localhost or Node 18+)');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/** AES-GCM 加密(同步 API 抛错;改用 Async 版本)
|
|
171
|
+
* @deprecated Web Crypto API 是异步的,同步签名不现实;
|
|
172
|
+
* 改用 aesGcmEncryptAsync + asyncSend helper,或预计算密文。
|
|
173
|
+
*/
|
|
174
|
+
function aesGcmEncrypt(_rawKey) {
|
|
175
|
+
return function () {
|
|
176
|
+
throw new Error('[encrypt] Web Crypto subtle.encrypt is async-only; use aesGcmEncryptAsync and send via asyncSend()');
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/** AES-GCM 加密(异步版):返回 Promise<string> */
|
|
180
|
+
function aesGcmEncryptAsync(rawKey) {
|
|
181
|
+
var _this = this;
|
|
182
|
+
var keyPromise = null;
|
|
183
|
+
return function (data) { return __awaiter(_this, void 0, void 0, function () {
|
|
184
|
+
var key, subtle, iv, cipher, _a, out;
|
|
185
|
+
return __generator(this, function (_b) {
|
|
186
|
+
switch (_b.label) {
|
|
187
|
+
case 0:
|
|
188
|
+
if (!keyPromise)
|
|
189
|
+
keyPromise = importAesKey(rawKey);
|
|
190
|
+
return [4 /*yield*/, keyPromise];
|
|
191
|
+
case 1:
|
|
192
|
+
key = _b.sent();
|
|
193
|
+
subtle = getSubtle();
|
|
194
|
+
iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
195
|
+
_a = Uint8Array.bind;
|
|
196
|
+
return [4 /*yield*/, subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, ENC.encode(data))];
|
|
197
|
+
case 2:
|
|
198
|
+
cipher = new (_a.apply(Uint8Array, [void 0, _b.sent()]))();
|
|
199
|
+
out = new Uint8Array(iv.length + cipher.length);
|
|
200
|
+
out.set(iv, 0);
|
|
201
|
+
out.set(cipher, iv.length);
|
|
202
|
+
return [2 /*return*/, bytesToB64(out)];
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}); };
|
|
206
|
+
}
|
|
207
|
+
/** AES-GCM 解密(异步版) */
|
|
208
|
+
function aesGcmDecryptAsync(rawKey) {
|
|
209
|
+
var _this = this;
|
|
210
|
+
var keyPromise = null;
|
|
211
|
+
return function (data) { return __awaiter(_this, void 0, void 0, function () {
|
|
212
|
+
var key, subtle, raw, iv, cipher, plain;
|
|
213
|
+
return __generator(this, function (_a) {
|
|
214
|
+
switch (_a.label) {
|
|
215
|
+
case 0:
|
|
216
|
+
if (!keyPromise)
|
|
217
|
+
keyPromise = importAesKey(rawKey);
|
|
218
|
+
return [4 /*yield*/, keyPromise];
|
|
219
|
+
case 1:
|
|
220
|
+
key = _a.sent();
|
|
221
|
+
subtle = getSubtle();
|
|
222
|
+
raw = b64ToBytes(data);
|
|
223
|
+
iv = raw.slice(0, 12);
|
|
224
|
+
cipher = raw.slice(12);
|
|
225
|
+
return [4 /*yield*/, subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, cipher)];
|
|
226
|
+
case 2:
|
|
227
|
+
plain = _a.sent();
|
|
228
|
+
return [2 /*return*/, DEC.decode(plain)];
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}); };
|
|
232
|
+
}
|
|
233
|
+
// ============================================================
|
|
234
|
+
// 3. 插件
|
|
235
|
+
// ============================================================
|
|
236
|
+
function encryptor(opts) {
|
|
237
|
+
return {
|
|
238
|
+
name: 'encryptor',
|
|
239
|
+
install: function (_a) {
|
|
240
|
+
var sdk = _a.sdk, config = _a.config;
|
|
241
|
+
// 写入 config;tracker.sendEvent 会自动调用
|
|
242
|
+
config.encryptor = opts.encrypt;
|
|
243
|
+
config.decoder = opts.decrypt;
|
|
244
|
+
// config.encryptEvent/decryptEvent 在 TrackerConfig 里是宽松的 Record<string,unknown> 函数
|
|
245
|
+
var cfg = config;
|
|
246
|
+
cfg.encryptEvent = opts.encryptEvent;
|
|
247
|
+
cfg.decryptEvent = opts.decryptEvent;
|
|
248
|
+
// 保留到 sdk 上,供调试 / 二次使用
|
|
249
|
+
var ref = sdk;
|
|
250
|
+
ref.__encryptor = opts.encrypt;
|
|
251
|
+
ref.__decoder = opts.decrypt;
|
|
252
|
+
ref.__encryptEvent = opts.encryptEvent;
|
|
253
|
+
ref.__decryptEvent = opts.decryptEvent;
|
|
254
|
+
},
|
|
255
|
+
uninstall: function (ctx) {
|
|
256
|
+
if (!ctx)
|
|
257
|
+
return;
|
|
258
|
+
var config = ctx.config, sdk = ctx.sdk;
|
|
259
|
+
delete config.encryptor;
|
|
260
|
+
delete config.decoder;
|
|
261
|
+
var cfg = config;
|
|
262
|
+
delete cfg.encryptEvent;
|
|
263
|
+
delete cfg.decryptEvent;
|
|
264
|
+
var ref = sdk;
|
|
265
|
+
delete ref.__encryptor;
|
|
266
|
+
delete ref.__decoder;
|
|
267
|
+
delete ref.__encryptEvent;
|
|
268
|
+
delete ref.__decryptEvent;
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/** 便捷工厂:XOR 配对 */
|
|
273
|
+
function xorEncryptor(key) {
|
|
274
|
+
return encryptor({ encrypt: defaultEncryptor(key), decrypt: defaultDecryptor(key) });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
exports.aesGcmDecryptAsync = aesGcmDecryptAsync;
|
|
278
|
+
exports.aesGcmEncrypt = aesGcmEncrypt;
|
|
279
|
+
exports.aesGcmEncryptAsync = aesGcmEncryptAsync;
|
|
280
|
+
exports.defaultDecryptor = defaultDecryptor;
|
|
281
|
+
exports.defaultEncryptor = defaultEncryptor;
|
|
282
|
+
exports.encryptor = encryptor;
|
|
283
|
+
exports.xorBase64 = xorBase64;
|
|
284
|
+
exports.xorEncryptor = xorEncryptor;
|