@spatius/avatarkit 1.3.8 → 1.3.9
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/CHANGELOG.md +35 -0
- package/dist/assets/{OpusDecoderWorker.worker-DVOREuTG.js → OpusDecoderWorker.worker-DWI3dYQC.js} +2640 -2585
- package/dist/assets/{OpusEncoderWorker.worker-fkLsXEK1.js → OpusEncoderWorker.worker-BlkaRHli.js} +2640 -2585
- package/dist/core/AvatarController.d.ts +13 -0
- package/dist/core/AvatarSDK.d.ts +12 -2
- package/dist/core/AvatarView.d.ts +30 -31
- package/dist/core/avatarViewRegistry.d.ts +2 -0
- package/dist/index.js +699 -282
- package/dist/internal-telemetry.d.ts +12 -23
- package/dist/internal-telemetry.js +16 -29
- package/dist/{otel-trace-CZJAGjGg.js → otel-trace-DqXSWBaw.js} +1101 -1006
- package/dist/types/character.d.ts +0 -13
- package/dist/types/index.d.ts +9 -6
- package/package.json +1 -1
- package/dist/rolldown-runtime-FDOR9p9I.js +0 -24
|
@@ -1,4 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
2
24
|
//#region core/sdk-ref.ts
|
|
3
25
|
var ref = null;
|
|
4
26
|
/** @internal 仅供 AvatarSDK 在自身模块求值时回填。 */
|
|
@@ -1292,8 +1314,6 @@ var AvatarState = /* @__PURE__ */ function(AvatarState) {
|
|
|
1292
1314
|
return AvatarState;
|
|
1293
1315
|
}({});
|
|
1294
1316
|
var ErrorCode = /* @__PURE__ */ function(ErrorCode) {
|
|
1295
|
-
/** AppID not recognized (reserved, future appID validation logic) */
|
|
1296
|
-
ErrorCode["appIDUnrecognized"] = "appIDUnrecognized";
|
|
1297
1317
|
/** Session Token invalid (WebSocket close code 4010) */
|
|
1298
1318
|
ErrorCode["sessionTokenInvalid"] = "sessionTokenInvalid";
|
|
1299
1319
|
/** Session Token expired (WebSocket close code 4010) */
|
|
@@ -1312,10 +1332,17 @@ var ErrorCode = /* @__PURE__ */ function(ErrorCode) {
|
|
|
1312
1332
|
ErrorCode["failedToDownloadAvatarAssets"] = "failedToDownloadAvatarAssets";
|
|
1313
1333
|
/** Avatar asset compatibility_flags not supported by this SDK version (upgrade SDK) */
|
|
1314
1334
|
ErrorCode["unsupportedAvatarAsset"] = "unsupportedAvatarAsset";
|
|
1335
|
+
/**
|
|
1336
|
+
* Neither WebGL2 nor WebGPU is available, so the avatar cannot be drawn at all.
|
|
1337
|
+
* Typically hardware acceleration is disabled in the browser, the GPU driver is
|
|
1338
|
+
* blocklisted, or the page runs in a restricted iframe. Deterministic — no point
|
|
1339
|
+
* retrying; tell the user to enable graphics acceleration.
|
|
1340
|
+
*/
|
|
1341
|
+
ErrorCode["renderNotSupported"] = "renderNotSupported";
|
|
1342
|
+
/** Render system failed to initialize for a reason other than missing WebGL2/WebGPU */
|
|
1343
|
+
ErrorCode["renderInitFailed"] = "renderInitFailed";
|
|
1315
1344
|
/** WebSocket connection error (handshake failure, network error) */
|
|
1316
1345
|
ErrorCode["websocketError"] = "websocketError";
|
|
1317
|
-
/** WebSocket connection closed abnormally (close code 1006) */
|
|
1318
|
-
ErrorCode["websocketClosedAbnormally"] = "websocketClosedAbnormally";
|
|
1319
1346
|
/** WebSocket closed with unexpected close code */
|
|
1320
1347
|
ErrorCode["websocketClosedUnexpected"] = "websocketClosedUnexpected";
|
|
1321
1348
|
/** Session timeout (WebSocket close code 4002) */
|
|
@@ -1328,8 +1355,6 @@ var ErrorCode = /* @__PURE__ */ function(ErrorCode) {
|
|
|
1328
1355
|
ErrorCode["playbackStartFailed"] = "playbackStartFailed";
|
|
1329
1356
|
/** Playback initialization failed */
|
|
1330
1357
|
ErrorCode["playbackInitFailed"] = "playbackInitFailed";
|
|
1331
|
-
/** Audio-only playback initialization failed */
|
|
1332
|
-
ErrorCode["audioOnlyInitFailed"] = "audioOnlyInitFailed";
|
|
1333
1358
|
/** No audio data to play */
|
|
1334
1359
|
ErrorCode["noAudio"] = "noAudio";
|
|
1335
1360
|
/**
|
|
@@ -1355,93 +1380,671 @@ var AvatarError = class extends Error {
|
|
|
1355
1380
|
this.name = "AvatarError";
|
|
1356
1381
|
}
|
|
1357
1382
|
};
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
return !(!t || !1 === t.navigator.onLine);
|
|
1372
|
-
}
|
|
1373
|
-
var d = "undefined" != typeof globalThis ? globalThis : t;
|
|
1374
|
-
d && "undefined" == typeof self && (d.self = d), d && "undefined" == typeof File && (d.File = function() {});
|
|
1375
|
-
var v = null != t ? t : {};
|
|
1376
|
-
var c = {
|
|
1377
|
-
DEBUG: !1,
|
|
1378
|
-
LIB_VERSION: "0.7.0",
|
|
1379
|
-
LIB_NAME: "browser-common"
|
|
1380
|
-
};
|
|
1381
|
-
function f(t, i, e, r, s, n, o) {
|
|
1382
|
-
try {
|
|
1383
|
-
var a = t[n](o), l = a.value;
|
|
1384
|
-
} catch (t) {
|
|
1385
|
-
e(t);
|
|
1386
|
-
return;
|
|
1383
|
+
var logSink = class LogSink {
|
|
1384
|
+
static instance = null;
|
|
1385
|
+
db = null;
|
|
1386
|
+
dbName = "avatarkit_logs";
|
|
1387
|
+
storeName = "logs";
|
|
1388
|
+
version = 1;
|
|
1389
|
+
trimThreshold = 5e3;
|
|
1390
|
+
trimBufferSize = 1e3;
|
|
1391
|
+
isFlushing = false;
|
|
1392
|
+
constructor() {}
|
|
1393
|
+
static getInstance() {
|
|
1394
|
+
if (!LogSink.instance) LogSink.instance = new LogSink();
|
|
1395
|
+
return LogSink.instance;
|
|
1387
1396
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
return new Promise((
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1397
|
+
/**
|
|
1398
|
+
* Initialize IndexedDB
|
|
1399
|
+
*/
|
|
1400
|
+
async initialize() {
|
|
1401
|
+
if (this.db) return;
|
|
1402
|
+
return new Promise((resolve, reject) => {
|
|
1403
|
+
const request = indexedDB.open(this.dbName, this.version);
|
|
1404
|
+
request.onerror = () => {
|
|
1405
|
+
reject(/* @__PURE__ */ new Error("Failed to open IndexedDB"));
|
|
1406
|
+
};
|
|
1407
|
+
request.onsuccess = () => {
|
|
1408
|
+
this.db = request.result;
|
|
1409
|
+
resolve();
|
|
1410
|
+
};
|
|
1411
|
+
request.onupgradeneeded = (event) => {
|
|
1412
|
+
const db = event.target.result;
|
|
1413
|
+
if (!db.objectStoreNames.contains(this.storeName)) db.createObjectStore(this.storeName, {
|
|
1414
|
+
keyPath: "id",
|
|
1415
|
+
autoIncrement: true
|
|
1416
|
+
}).createIndex("ts", "ts", { unique: false });
|
|
1417
|
+
};
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Append log entry to IndexedDB
|
|
1422
|
+
*/
|
|
1423
|
+
async append(entry) {
|
|
1424
|
+
if (!this.db) try {
|
|
1425
|
+
await this.initialize();
|
|
1426
|
+
} catch {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (!this.db) return;
|
|
1430
|
+
try {
|
|
1431
|
+
const store = this.db.transaction([this.storeName], "readwrite").objectStore(this.storeName);
|
|
1432
|
+
await new Promise((resolve, reject) => {
|
|
1433
|
+
const request = store.add(entry);
|
|
1434
|
+
request.onsuccess = () => resolve();
|
|
1435
|
+
request.onerror = () => reject(/* @__PURE__ */ new Error("Failed to add log"));
|
|
1436
|
+
});
|
|
1437
|
+
this.getCount().then((count) => {
|
|
1438
|
+
if (count > this.trimThreshold) this.trim().catch(() => {});
|
|
1439
|
+
}).catch(() => {});
|
|
1440
|
+
} catch (error) {}
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Get total log count
|
|
1444
|
+
*/
|
|
1445
|
+
async getCount() {
|
|
1446
|
+
if (!this.db) return 0;
|
|
1447
|
+
return new Promise((resolve, reject) => {
|
|
1448
|
+
const request = this.db.transaction([this.storeName], "readonly").objectStore(this.storeName).count();
|
|
1449
|
+
request.onsuccess = () => {
|
|
1450
|
+
resolve(request.result);
|
|
1451
|
+
};
|
|
1452
|
+
request.onerror = () => {
|
|
1453
|
+
reject(/* @__PURE__ */ new Error("Failed to count logs"));
|
|
1454
|
+
};
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Trim logs to keep only recent entries
|
|
1459
|
+
*/
|
|
1460
|
+
async trim() {
|
|
1461
|
+
if (!this.db || this.isFlushing) return;
|
|
1462
|
+
try {
|
|
1463
|
+
const store = this.db.transaction([this.storeName], "readwrite").objectStore(this.storeName);
|
|
1464
|
+
const getAllRequest = store.getAll();
|
|
1465
|
+
const entries = await new Promise((resolve, reject) => {
|
|
1466
|
+
getAllRequest.onsuccess = () => {
|
|
1467
|
+
resolve(getAllRequest.result);
|
|
1468
|
+
};
|
|
1469
|
+
getAllRequest.onerror = () => {
|
|
1470
|
+
reject(/* @__PURE__ */ new Error("Failed to get logs"));
|
|
1471
|
+
};
|
|
1472
|
+
});
|
|
1473
|
+
const keepCount = this.trimThreshold - this.trimBufferSize;
|
|
1474
|
+
if (entries.length <= keepCount) return;
|
|
1475
|
+
entries.sort((a, b) => a.ts - b.ts);
|
|
1476
|
+
const toDelete = entries.slice(0, entries.length - keepCount);
|
|
1477
|
+
for (const entry of toDelete) if (entry.id !== void 0) await new Promise((resolve, reject) => {
|
|
1478
|
+
const deleteRequest = store.delete(entry.id);
|
|
1479
|
+
deleteRequest.onsuccess = () => resolve();
|
|
1480
|
+
deleteRequest.onerror = () => reject(/* @__PURE__ */ new Error("Failed to delete log"));
|
|
1481
|
+
});
|
|
1482
|
+
} catch (error) {}
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Flush logs to server
|
|
1486
|
+
*/
|
|
1487
|
+
async flush() {
|
|
1488
|
+
if (!this.db || this.isFlushing) return;
|
|
1489
|
+
this.isFlushing = true;
|
|
1490
|
+
try {
|
|
1491
|
+
const getAllRequest = this.db.transaction([this.storeName], "readonly").objectStore(this.storeName).getAll();
|
|
1492
|
+
const entries = await new Promise((resolve, reject) => {
|
|
1493
|
+
getAllRequest.onsuccess = () => {
|
|
1494
|
+
resolve(getAllRequest.result);
|
|
1495
|
+
};
|
|
1496
|
+
getAllRequest.onerror = () => {
|
|
1497
|
+
reject(/* @__PURE__ */ new Error("Failed to get logs"));
|
|
1498
|
+
};
|
|
1499
|
+
});
|
|
1500
|
+
if (entries.length === 0) {
|
|
1501
|
+
this.isFlushing = false;
|
|
1502
|
+
return;
|
|
1400
1503
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1504
|
+
const requestBody = { logs: entries.map((entry) => ({
|
|
1505
|
+
...entry,
|
|
1506
|
+
extra: {
|
|
1507
|
+
...entry.extra,
|
|
1508
|
+
platform: "Web"
|
|
1509
|
+
}
|
|
1510
|
+
})) };
|
|
1511
|
+
const jsonData = JSON.stringify(requestBody);
|
|
1512
|
+
const gzippedData = await this.gzip(jsonData);
|
|
1513
|
+
const response = await fetch("https://hogtool.spatialwalk.ai/batch-upload-log", {
|
|
1514
|
+
method: "POST",
|
|
1515
|
+
headers: {
|
|
1516
|
+
"Content-Type": "application/json",
|
|
1517
|
+
"Content-Encoding": "gzip"
|
|
1518
|
+
},
|
|
1519
|
+
body: gzippedData,
|
|
1520
|
+
signal: AbortSignal.timeout(15e3)
|
|
1521
|
+
});
|
|
1522
|
+
if (!response.ok) throw new Error(`Failed to upload logs: ${response.status} ${response.statusText}`);
|
|
1523
|
+
await this.clearFirst(entries.length);
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
console.warn("[LogSink] Failed to flush logs:", error);
|
|
1526
|
+
throw error;
|
|
1527
|
+
} finally {
|
|
1528
|
+
this.isFlushing = false;
|
|
1410
1529
|
}
|
|
1411
|
-
return t;
|
|
1412
|
-
}, _.apply(null, arguments);
|
|
1413
|
-
}
|
|
1414
|
-
function g(t, i) {
|
|
1415
|
-
if (null == t) return {};
|
|
1416
|
-
var e = {};
|
|
1417
|
-
for (var r in t) if ({}.hasOwnProperty.call(t, r)) {
|
|
1418
|
-
if (-1 !== i.indexOf(r)) continue;
|
|
1419
|
-
e[r] = t[r];
|
|
1420
1530
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1531
|
+
/**
|
|
1532
|
+
* Clear first N log entries
|
|
1533
|
+
*/
|
|
1534
|
+
async clearFirst(count) {
|
|
1535
|
+
if (!this.db || count <= 0) return;
|
|
1536
|
+
try {
|
|
1537
|
+
const store = this.db.transaction([this.storeName], "readwrite").objectStore(this.storeName);
|
|
1538
|
+
const getAllRequest = store.getAll();
|
|
1539
|
+
const entries = await new Promise((resolve, reject) => {
|
|
1540
|
+
getAllRequest.onsuccess = () => {
|
|
1541
|
+
resolve(getAllRequest.result);
|
|
1542
|
+
};
|
|
1543
|
+
getAllRequest.onerror = () => {
|
|
1544
|
+
reject(/* @__PURE__ */ new Error("Failed to get logs"));
|
|
1545
|
+
};
|
|
1546
|
+
});
|
|
1547
|
+
entries.sort((a, b) => a.ts - b.ts);
|
|
1548
|
+
const toDelete = entries.slice(0, count);
|
|
1549
|
+
for (const entry of toDelete) if (entry.id !== void 0) await new Promise((resolve, reject) => {
|
|
1550
|
+
const deleteRequest = store.delete(entry.id);
|
|
1551
|
+
deleteRequest.onsuccess = () => resolve();
|
|
1552
|
+
deleteRequest.onerror = () => reject(/* @__PURE__ */ new Error("Failed to delete log"));
|
|
1553
|
+
});
|
|
1554
|
+
} catch (error) {}
|
|
1429
1555
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1556
|
+
/**
|
|
1557
|
+
* Gzip string data
|
|
1558
|
+
*/
|
|
1559
|
+
async gzip(data) {
|
|
1560
|
+
try {
|
|
1561
|
+
const encoded = new TextEncoder().encode(data);
|
|
1562
|
+
const compressedStream = new ReadableStream({ start(controller) {
|
|
1563
|
+
controller.enqueue(encoded);
|
|
1564
|
+
controller.close();
|
|
1565
|
+
} }).pipeThrough(new CompressionStream("gzip"));
|
|
1566
|
+
return await new Response(compressedStream).blob();
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
console.error("[LogSink] gzip() error:", error);
|
|
1569
|
+
throw error;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
}.getInstance();
|
|
1573
|
+
//#endregion
|
|
1574
|
+
//#region node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/url-alphabet/index.js
|
|
1575
|
+
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
1576
|
+
//#endregion
|
|
1577
|
+
//#region node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/index.browser.js
|
|
1578
|
+
var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
|
|
1579
|
+
var customRandom = (alphabet, defaultSize, getRandom) => {
|
|
1580
|
+
let mask = (2 << Math.log2(alphabet.length - 1)) - 1;
|
|
1581
|
+
let step = -~(1.6 * mask * defaultSize / alphabet.length);
|
|
1582
|
+
return (size = defaultSize) => {
|
|
1583
|
+
let id = "";
|
|
1584
|
+
while (true) {
|
|
1585
|
+
let bytes = getRandom(step);
|
|
1586
|
+
let j = step | 0;
|
|
1587
|
+
while (j--) {
|
|
1588
|
+
id += alphabet[bytes[j] & mask] || "";
|
|
1589
|
+
if (id.length >= size) return id;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
};
|
|
1594
|
+
var customAlphabet = (alphabet, size = 21) => customRandom(alphabet, size | 0, random);
|
|
1595
|
+
var nanoid$2 = (size = 21) => {
|
|
1596
|
+
let id = "";
|
|
1597
|
+
let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
|
|
1598
|
+
while (size--) id += urlAlphabet[bytes[size] & 63];
|
|
1599
|
+
return id;
|
|
1600
|
+
};
|
|
1601
|
+
//#endregion
|
|
1602
|
+
//#region utils/client-id.ts
|
|
1603
|
+
/**
|
|
1604
|
+
* Client ID management utility
|
|
1605
|
+
* Used to generate and persist unique device identifier
|
|
1606
|
+
* @internal
|
|
1607
|
+
*/
|
|
1608
|
+
var CLIENT_ID_STORAGE_KEY = "spavatar_client_id";
|
|
1609
|
+
/**
|
|
1610
|
+
* Get or create client_id
|
|
1611
|
+
* - If exists in localStorage, return existing client_id
|
|
1612
|
+
* - If not exists, generate new client_id and store to localStorage
|
|
1613
|
+
* @returns client_id string
|
|
1614
|
+
* @internal
|
|
1615
|
+
*/
|
|
1616
|
+
function getOrCreateClientId() {
|
|
1617
|
+
try {
|
|
1618
|
+
const stored = localStorage.getItem(CLIENT_ID_STORAGE_KEY);
|
|
1619
|
+
if (stored) return stored;
|
|
1620
|
+
const clientId = nanoid$2(21);
|
|
1621
|
+
localStorage.setItem(CLIENT_ID_STORAGE_KEY, clientId);
|
|
1622
|
+
return clientId;
|
|
1623
|
+
} catch (error) {
|
|
1624
|
+
return `temp_${nanoid$2(21)}_${Date.now()}`;
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
//#endregion
|
|
1628
|
+
//#region utils/conversationId.ts
|
|
1629
|
+
/**
|
|
1630
|
+
* Conversation ID generation utility
|
|
1631
|
+
*
|
|
1632
|
+
* Unified format: timestamp (UTC time, second-level, format: YYYYMMDDHHmmss) + underscore + 12-digit NanoID
|
|
1633
|
+
* Example: 20251027143034_aB3dEf9hIjKl
|
|
1634
|
+
*
|
|
1635
|
+
* Time part uses UTC time for easier cross-platform log troubleshooting with unified timezone conversion
|
|
1636
|
+
* @internal
|
|
1637
|
+
*/
|
|
1638
|
+
/**
|
|
1639
|
+
* Custom character set: only uppercase, lowercase letters and numbers (URL safe, no hyphens)
|
|
1640
|
+
* Character set: A-Z, a-z, 0-9 (62 characters)
|
|
1641
|
+
*/
|
|
1642
|
+
var nanoid$1 = customAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 12);
|
|
1643
|
+
/**
|
|
1644
|
+
* Generate unified conversation ID
|
|
1645
|
+
*
|
|
1646
|
+
* Format: timestamp (UTC time, second-level, format: YYYYMMDDHHmmss) + underscore + 12-digit NanoID
|
|
1647
|
+
* Time part uses UTC time for easier cross-platform log troubleshooting with unified timezone conversion
|
|
1648
|
+
*
|
|
1649
|
+
* @returns Conversation ID in format YYYYMMDDHHmmss_nanoid
|
|
1650
|
+
* @example
|
|
1651
|
+
* generateConversationId() // "20251027143034_aB3dEf9hIjKl"
|
|
1652
|
+
* @internal
|
|
1653
|
+
*/
|
|
1654
|
+
var generateTraceId = () => generateConversationId();
|
|
1655
|
+
function generateConversationId() {
|
|
1656
|
+
const now = /* @__PURE__ */ new Date();
|
|
1657
|
+
return `${`${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}${String(now.getUTCDate()).padStart(2, "0")}${String(now.getUTCHours()).padStart(2, "0")}${String(now.getUTCMinutes()).padStart(2, "0")}${String(now.getUTCSeconds()).padStart(2, "0")}`}_${nanoid$1()}`;
|
|
1658
|
+
}
|
|
1659
|
+
//#endregion
|
|
1660
|
+
//#region utils/id-manager.ts
|
|
1661
|
+
/**
|
|
1662
|
+
* ID Manager
|
|
1663
|
+
* Unified management of all types of IDs in SDK
|
|
1664
|
+
* @internal
|
|
1665
|
+
*/
|
|
1666
|
+
/** 与 conversation_id 同字符集:URL 安全、无连字符。 */
|
|
1667
|
+
var nanoid = customAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 12);
|
|
1668
|
+
var IdManager = class {
|
|
1669
|
+
ids = {
|
|
1670
|
+
clientId: "",
|
|
1671
|
+
sessionId: "",
|
|
1672
|
+
userId: null,
|
|
1673
|
+
appId: null,
|
|
1674
|
+
sessionToken: null,
|
|
1675
|
+
connectionId: null,
|
|
1676
|
+
conversationId: null
|
|
1677
|
+
};
|
|
1678
|
+
constructor() {
|
|
1679
|
+
this.ids.clientId = getOrCreateClientId();
|
|
1680
|
+
}
|
|
1681
|
+
getClientId() {
|
|
1682
|
+
return this.ids.clientId;
|
|
1683
|
+
}
|
|
1684
|
+
setUserId(userId) {
|
|
1685
|
+
this.ids.userId = userId;
|
|
1686
|
+
}
|
|
1687
|
+
getUserId() {
|
|
1688
|
+
return this.ids.userId;
|
|
1689
|
+
}
|
|
1690
|
+
setAppId(appId) {
|
|
1691
|
+
this.ids.appId = appId;
|
|
1692
|
+
}
|
|
1693
|
+
getAppId() {
|
|
1694
|
+
return this.ids.appId;
|
|
1695
|
+
}
|
|
1696
|
+
setSessionToken(token) {
|
|
1697
|
+
this.ids.sessionToken = token;
|
|
1698
|
+
}
|
|
1699
|
+
getSessionToken() {
|
|
1700
|
+
return this.ids.sessionToken;
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Generate new connectionId (for WebSocket connection)
|
|
1704
|
+
* @internal
|
|
1705
|
+
*/
|
|
1706
|
+
generateConnectionId() {
|
|
1707
|
+
this.ids.connectionId = generateConversationId();
|
|
1708
|
+
return this.ids.connectionId;
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Set connectionId (for connectionId received from server)
|
|
1712
|
+
* @internal
|
|
1713
|
+
*/
|
|
1714
|
+
setConnectionId(connectionId) {
|
|
1715
|
+
this.ids.connectionId = connectionId;
|
|
1716
|
+
}
|
|
1717
|
+
getConnectionId() {
|
|
1718
|
+
return this.ids.connectionId;
|
|
1719
|
+
}
|
|
1720
|
+
clearConnectionId() {
|
|
1721
|
+
this.ids.connectionId = null;
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Generate new conversationId (for each conversation)
|
|
1725
|
+
* @internal
|
|
1726
|
+
*/
|
|
1727
|
+
generateNewConversationId() {
|
|
1728
|
+
this.ids.conversationId = generateConversationId();
|
|
1729
|
+
return this.ids.conversationId;
|
|
1730
|
+
}
|
|
1731
|
+
getConversationId() {
|
|
1732
|
+
return this.ids.conversationId;
|
|
1733
|
+
}
|
|
1734
|
+
setConversationId(conversationId) {
|
|
1735
|
+
this.ids.conversationId = conversationId;
|
|
1736
|
+
}
|
|
1737
|
+
clearConversationId() {
|
|
1738
|
+
this.ids.conversationId = null;
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Get all IDs (for log reporting)
|
|
1742
|
+
* @internal
|
|
1743
|
+
*/
|
|
1744
|
+
getAllIds() {
|
|
1745
|
+
return { ...this.ids };
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Get public log parameters (contains all necessary IDs)
|
|
1749
|
+
* @internal
|
|
1750
|
+
*/
|
|
1751
|
+
getLogContext() {
|
|
1752
|
+
return {
|
|
1753
|
+
client_id: this.ids.clientId,
|
|
1754
|
+
session_id: this.ids.sessionId,
|
|
1755
|
+
user_id: this.ids.userId || "",
|
|
1756
|
+
app_id: this.ids.appId || ""
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
/** 本次启动的 session id;首次调用时生成。 @internal */
|
|
1760
|
+
getSessionId() {
|
|
1761
|
+
if (!this.ids.sessionId) this.ids.sessionId = nanoid();
|
|
1762
|
+
return this.ids.sessionId;
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Clear all IDs (for testing or reset)
|
|
1766
|
+
* @internal
|
|
1767
|
+
*/
|
|
1768
|
+
clear() {
|
|
1769
|
+
this.ids.userId = null;
|
|
1770
|
+
this.ids.appId = null;
|
|
1771
|
+
this.ids.sessionToken = null;
|
|
1772
|
+
this.ids.connectionId = null;
|
|
1773
|
+
this.ids.conversationId = null;
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
var idManager = new IdManager();
|
|
1777
|
+
//#endregion
|
|
1778
|
+
//#region utils/log-sanitizer.ts
|
|
1779
|
+
var ALGORITHM_REG = new RegExp(`\\b(${[
|
|
1780
|
+
"flame",
|
|
1781
|
+
"keyframe",
|
|
1782
|
+
"keyframes",
|
|
1783
|
+
"splat",
|
|
1784
|
+
"splats",
|
|
1785
|
+
"covariance",
|
|
1786
|
+
"gaussian",
|
|
1787
|
+
"cov6",
|
|
1788
|
+
"packeddata",
|
|
1789
|
+
"sortorder",
|
|
1790
|
+
"sortindex",
|
|
1791
|
+
"avatar_core",
|
|
1792
|
+
"emscripten",
|
|
1793
|
+
"wasm",
|
|
1794
|
+
"blendshape",
|
|
1795
|
+
"expressionparam",
|
|
1796
|
+
"rig"
|
|
1797
|
+
].join("|")})\\b`, "gi");
|
|
1798
|
+
/** Chinese characters (CJK Unified Ideographs) */
|
|
1799
|
+
var CHINESE_REG = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/;
|
|
1800
|
+
/** Patterns to redact: capture group 1 = key, 2 = value; replace value with *** */
|
|
1801
|
+
var SENSITIVE_PATTERNS = [
|
|
1802
|
+
{
|
|
1803
|
+
reg: /\b(token|sessiontoken|accesstoken|apikey|apisecret|secret|password|auth)=([^\s,}\]'"]+)/gi,
|
|
1804
|
+
replacement: "$1=***"
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
reg: /\b(conversationid|connectionid|clientid|sessionid)=([^\s,}\]'"]+)/gi,
|
|
1808
|
+
replacement: "$1=***"
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
reg: /\b(appid|app_id)=([^\s,}\]'"]+)/gi,
|
|
1812
|
+
replacement: "$1=***"
|
|
1813
|
+
},
|
|
1814
|
+
{
|
|
1815
|
+
reg: /\b(userid|user_id)=([^\s,}\]'"]+)/gi,
|
|
1816
|
+
replacement: "$1=***"
|
|
1817
|
+
},
|
|
1818
|
+
{
|
|
1819
|
+
reg: /(https?:\/\/)[^\s,}\]'"]+/gi,
|
|
1820
|
+
replacement: "$1***"
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
reg: /(wss?:\/\/)[^\s,}\]'"]+/gi,
|
|
1824
|
+
replacement: "$1***"
|
|
1825
|
+
}
|
|
1826
|
+
];
|
|
1827
|
+
/**
|
|
1828
|
+
* Check if message contains Chinese.
|
|
1829
|
+
*/
|
|
1830
|
+
function hasChinese(msg) {
|
|
1831
|
+
return CHINESE_REG.test(msg);
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Check if message contains algorithm/internal terms.
|
|
1835
|
+
*/
|
|
1836
|
+
function hasAlgorithmTerms(msg) {
|
|
1837
|
+
return ALGORITHM_REG.test(msg);
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Redact sensitive substrings in message.
|
|
1841
|
+
*/
|
|
1842
|
+
function redactSensitive(msg) {
|
|
1843
|
+
let out = msg;
|
|
1844
|
+
for (const { reg, replacement } of SENSITIVE_PATTERNS) out = out.replace(reg, replacement);
|
|
1845
|
+
return out;
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Sanitize a log message for local persistence.
|
|
1849
|
+
* - Do not persist if message contains Chinese or algorithm terms.
|
|
1850
|
+
* - Otherwise persist, but redact sensitive content first.
|
|
1851
|
+
*/
|
|
1852
|
+
function sanitizeForLocalLog(message) {
|
|
1853
|
+
if (hasChinese(message) || hasAlgorithmTerms(message)) return {
|
|
1854
|
+
shouldPersist: false,
|
|
1855
|
+
sanitized: message
|
|
1856
|
+
};
|
|
1857
|
+
return {
|
|
1858
|
+
shouldPersist: true,
|
|
1859
|
+
sanitized: redactSensitive(message)
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
//#endregion
|
|
1863
|
+
//#region utils/logger.ts
|
|
1864
|
+
/**
|
|
1865
|
+
* Logger Utility
|
|
1866
|
+
* Environment-aware logger wrapper with local log persistence
|
|
1867
|
+
* - Always writes to local log file (IndexedDB) regardless of logLevel
|
|
1868
|
+
* - Console output is controlled by logLevel
|
|
1869
|
+
* - Errors and warnings are always reported to PostHog (when enabled)
|
|
1870
|
+
*
|
|
1871
|
+
* Usage: Replace `console.log()` with `logger.log()`
|
|
1872
|
+
*/
|
|
1873
|
+
setGlobalLogLevel(LogLevel$1.Warning);
|
|
1874
|
+
var baseLogger = useLogger("Web").useGlobalConfig();
|
|
1875
|
+
logSink.initialize().catch(() => {});
|
|
1876
|
+
var currentLogLevel = LogLevel.off;
|
|
1877
|
+
/**
|
|
1878
|
+
* Write log to local storage.
|
|
1879
|
+
* Skips persistence if message contains Chinese, algorithm terms (e.g. Flame, splat), or sensitive content.
|
|
1880
|
+
* Otherwise redacts sensitive substrings then persists.
|
|
1881
|
+
*/
|
|
1882
|
+
async function writeToLocalLog(message, level, extra = {}) {
|
|
1883
|
+
try {
|
|
1884
|
+
const { shouldPersist, sanitized } = sanitizeForLocalLog(message);
|
|
1885
|
+
if (!shouldPersist) return;
|
|
1886
|
+
const version = requireSDK().version;
|
|
1887
|
+
const appId = idManager.getAppId();
|
|
1888
|
+
const userId = idManager.getUserId();
|
|
1889
|
+
await logSink.append({
|
|
1890
|
+
level,
|
|
1891
|
+
ts: Date.now(),
|
|
1892
|
+
msg: sanitized,
|
|
1893
|
+
extra: {
|
|
1894
|
+
...extra,
|
|
1895
|
+
_category: "Web",
|
|
1896
|
+
sdk_version: version || "",
|
|
1897
|
+
app_id: appId || "",
|
|
1898
|
+
user_id: userId || "",
|
|
1899
|
+
platform: "Web",
|
|
1900
|
+
framework: "",
|
|
1901
|
+
framework_version: ""
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
} catch {}
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Format console message with [AvatarKit] prefix
|
|
1908
|
+
*/
|
|
1909
|
+
function formatConsoleMessage(message) {
|
|
1910
|
+
return `[AvatarKit] ${message}`;
|
|
1911
|
+
}
|
|
1912
|
+
var logger = {
|
|
1913
|
+
debug: (message, ...args) => {
|
|
1914
|
+
writeToLocalLog(message, "info", {}).catch(() => {});
|
|
1915
|
+
if (currentLogLevel === LogLevel.all) baseLogger.debug(formatConsoleMessage(message), ...args);
|
|
1916
|
+
},
|
|
1917
|
+
verbose: (message, ...args) => {
|
|
1918
|
+
writeToLocalLog(message, "info", {}).catch(() => {});
|
|
1919
|
+
if (currentLogLevel === LogLevel.all) baseLogger.verbose(formatConsoleMessage(message), ...args);
|
|
1920
|
+
},
|
|
1921
|
+
log: (message, ...args) => {
|
|
1922
|
+
writeToLocalLog(message, "info", {}).catch(() => {});
|
|
1923
|
+
if (currentLogLevel === LogLevel.all) baseLogger.log(formatConsoleMessage(message), ...args);
|
|
1924
|
+
},
|
|
1925
|
+
warn: (message, ...args) => {
|
|
1926
|
+
writeToLocalLog(message, "warn", {}).catch(() => {});
|
|
1927
|
+
if (currentLogLevel === LogLevel.warning || currentLogLevel === LogLevel.all) baseLogger.warn(formatConsoleMessage(message), ...args);
|
|
1928
|
+
},
|
|
1929
|
+
error: (message, ...args) => {
|
|
1930
|
+
const fullMessage = message + (args.length > 0 ? " " + args.map((a) => a instanceof Error ? a.message : typeof a === "string" ? a : JSON.stringify(a)).join(" ") : "");
|
|
1931
|
+
writeToLocalLog(fullMessage, "error", {}).catch(() => {});
|
|
1932
|
+
if (currentLogLevel !== LogLevel.off) baseLogger.error(formatConsoleMessage(fullMessage));
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
/**
|
|
1936
|
+
* Set SDK log level
|
|
1937
|
+
* Note: Local log writing is always enabled regardless of logLevel
|
|
1938
|
+
* @param level SDK's LogLevel enum value
|
|
1939
|
+
* @internal
|
|
1940
|
+
*/
|
|
1941
|
+
function setLogLevel(level) {
|
|
1942
|
+
currentLogLevel = level;
|
|
1943
|
+
switch (level) {
|
|
1944
|
+
case LogLevel.off:
|
|
1945
|
+
setGlobalLogLevel(LogLevel$1.Error);
|
|
1946
|
+
break;
|
|
1947
|
+
case LogLevel.error:
|
|
1948
|
+
setGlobalLogLevel(LogLevel$1.Error);
|
|
1949
|
+
break;
|
|
1950
|
+
case LogLevel.warning:
|
|
1951
|
+
setGlobalLogLevel(LogLevel$1.Warning);
|
|
1952
|
+
break;
|
|
1953
|
+
case LogLevel.all:
|
|
1954
|
+
setGlobalLogLevel(LogLevel$1.Debug);
|
|
1955
|
+
break;
|
|
1956
|
+
default:
|
|
1957
|
+
setGlobalLogLevel(LogLevel$1.Warning);
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
//#endregion
|
|
1962
|
+
//#region node_modules/.pnpm/posthog-js@1.422.5_react@19.2.4/node_modules/posthog-js/dist/module.js
|
|
1963
|
+
var t = "undefined" != typeof window ? window : void 0;
|
|
1964
|
+
var i = "undefined" != typeof globalThis ? globalThis : t;
|
|
1965
|
+
var e = null == i ? void 0 : i.navigator;
|
|
1966
|
+
var r = null == i ? void 0 : i.document;
|
|
1967
|
+
var s = null == i ? void 0 : i.location;
|
|
1968
|
+
var n = null == i ? void 0 : i.fetch;
|
|
1969
|
+
var o = null != i && i.XMLHttpRequest && "withCredentials" in new i.XMLHttpRequest() ? i.XMLHttpRequest : void 0;
|
|
1970
|
+
var a = null == i ? void 0 : i.AbortController;
|
|
1971
|
+
var l = null == i ? void 0 : i.CompressionStream;
|
|
1972
|
+
var h = null == e ? void 0 : e.userAgent;
|
|
1973
|
+
function u() {
|
|
1974
|
+
return !(!t || !1 === t.navigator.onLine);
|
|
1975
|
+
}
|
|
1976
|
+
var d = "undefined" != typeof globalThis ? globalThis : t;
|
|
1977
|
+
d && "undefined" == typeof self && (d.self = d), d && "undefined" == typeof File && (d.File = function() {});
|
|
1978
|
+
var v = null != t ? t : {};
|
|
1979
|
+
var c = {
|
|
1980
|
+
DEBUG: !1,
|
|
1981
|
+
LIB_VERSION: "0.7.0",
|
|
1982
|
+
LIB_NAME: "browser-common"
|
|
1983
|
+
};
|
|
1984
|
+
function f(t, i, e, r, s, n, o) {
|
|
1985
|
+
try {
|
|
1986
|
+
var a = t[n](o), l = a.value;
|
|
1987
|
+
} catch (t) {
|
|
1988
|
+
e(t);
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
a.done ? i(l) : Promise.resolve(l).then(r, s);
|
|
1992
|
+
}
|
|
1993
|
+
function p(t) {
|
|
1994
|
+
return function() {
|
|
1995
|
+
var i = this, e = arguments;
|
|
1996
|
+
return new Promise((function(r, s) {
|
|
1997
|
+
var n = t.apply(i, e);
|
|
1998
|
+
function o(t) {
|
|
1999
|
+
f(n, r, s, o, a, "next", t);
|
|
2000
|
+
}
|
|
2001
|
+
function a(t) {
|
|
2002
|
+
f(n, r, s, o, a, "throw", t);
|
|
2003
|
+
}
|
|
2004
|
+
o(void 0);
|
|
2005
|
+
}));
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
function _() {
|
|
2009
|
+
return _ = Object.assign ? Object.assign.bind() : function(t) {
|
|
2010
|
+
for (var i = 1; arguments.length > i; i++) {
|
|
2011
|
+
var e = arguments[i];
|
|
2012
|
+
for (var r in e) ({}).hasOwnProperty.call(e, r) && (t[r] = e[r]);
|
|
2013
|
+
}
|
|
2014
|
+
return t;
|
|
2015
|
+
}, _.apply(null, arguments);
|
|
2016
|
+
}
|
|
2017
|
+
function g(t, i) {
|
|
2018
|
+
if (null == t) return {};
|
|
2019
|
+
var e = {};
|
|
2020
|
+
for (var r in t) if ({}.hasOwnProperty.call(t, r)) {
|
|
2021
|
+
if (-1 !== i.indexOf(r)) continue;
|
|
2022
|
+
e[r] = t[r];
|
|
2023
|
+
}
|
|
2024
|
+
return e;
|
|
2025
|
+
}
|
|
2026
|
+
var m = (t) => {
|
|
2027
|
+
if ("string" != typeof t) return t;
|
|
2028
|
+
try {
|
|
2029
|
+
return JSON.parse(t);
|
|
2030
|
+
} catch (i) {
|
|
2031
|
+
return t;
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
function y(t) {
|
|
2035
|
+
return "string" == typeof t || t;
|
|
2036
|
+
}
|
|
2037
|
+
function b(t) {
|
|
2038
|
+
return "string" == typeof t ? t : void 0;
|
|
2039
|
+
}
|
|
2040
|
+
var w = [
|
|
2041
|
+
"$feature_flag",
|
|
2042
|
+
"$feature_flag_response",
|
|
2043
|
+
"$feature_flag_has_experiment",
|
|
2044
|
+
"$feature_flag_id",
|
|
2045
|
+
"$feature_flag_version",
|
|
2046
|
+
"$feature_flag_reason",
|
|
2047
|
+
"$feature_flag_request_id",
|
|
1445
2048
|
"$feature_flag_evaluated_at",
|
|
1446
2049
|
"$feature_flag_error",
|
|
1447
2050
|
"locally_evaluated",
|
|
@@ -11104,232 +11707,81 @@ var iu = _({}, zh, qh, Hh, Vh, Wh, Gh, Jh, Kh, Yh, Qh, { conversations: class {
|
|
|
11104
11707
|
kind: "retry-later",
|
|
11105
11708
|
error: null !== (e = t.error) && void 0 !== e ? e : /* @__PURE__ */ new Error("metrics request failed with status " + i)
|
|
11106
11709
|
});
|
|
11107
|
-
}
|
|
11108
|
-
}
|
|
11109
|
-
}));
|
|
11110
|
-
}));
|
|
11111
|
-
}
|
|
11112
|
-
Gc() {
|
|
11113
|
-
return this._instance.requestRouter.endpointFor("api", "/i/v1/metrics") + "?token=" + encodeURIComponent(this._instance.config.token);
|
|
11114
|
-
}
|
|
11115
|
-
} });
|
|
11116
|
-
Rl.__defaultExtensionClasses = _({}, iu);
|
|
11117
|
-
var eu = function() {
|
|
11118
|
-
c.SDK_DIST_CHANNEL = "npm";
|
|
11119
|
-
var i = fl[xl] = new Rl();
|
|
11120
|
-
return function() {
|
|
11121
|
-
function i() {
|
|
11122
|
-
i.done || (i.done = !0, El = !1, es(fl, (function(t) {
|
|
11123
|
-
t._dom_loaded();
|
|
11124
|
-
})));
|
|
11125
|
-
}
|
|
11126
|
-
null != r && r.addEventListener ? "complete" === r.readyState ? i() : us(r, "DOMContentLoaded", i, { capture: !1 }) : t && Ie.error("Browser doesn't support `document.addEventListener` so PostHog couldn't be initialized");
|
|
11127
|
-
}(), i;
|
|
11128
|
-
}();
|
|
11129
|
-
//#endregion
|
|
11130
|
-
//#region node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/url-alphabet/index.js
|
|
11131
|
-
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
11132
|
-
//#endregion
|
|
11133
|
-
//#region node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/index.browser.js
|
|
11134
|
-
var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
|
|
11135
|
-
var customRandom = (alphabet, defaultSize, getRandom) => {
|
|
11136
|
-
let mask = (2 << Math.log2(alphabet.length - 1)) - 1;
|
|
11137
|
-
let step = -~(1.6 * mask * defaultSize / alphabet.length);
|
|
11138
|
-
return (size = defaultSize) => {
|
|
11139
|
-
let id = "";
|
|
11140
|
-
while (true) {
|
|
11141
|
-
let bytes = getRandom(step);
|
|
11142
|
-
let j = step | 0;
|
|
11143
|
-
while (j--) {
|
|
11144
|
-
id += alphabet[bytes[j] & mask] || "";
|
|
11145
|
-
if (id.length >= size) return id;
|
|
11146
|
-
}
|
|
11147
|
-
}
|
|
11148
|
-
};
|
|
11149
|
-
};
|
|
11150
|
-
var customAlphabet = (alphabet, size = 21) => customRandom(alphabet, size | 0, random);
|
|
11151
|
-
var nanoid$2 = (size = 21) => {
|
|
11152
|
-
let id = "";
|
|
11153
|
-
let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
|
|
11154
|
-
while (size--) id += urlAlphabet[bytes[size] & 63];
|
|
11155
|
-
return id;
|
|
11156
|
-
};
|
|
11157
|
-
//#endregion
|
|
11158
|
-
//#region utils/client-id.ts
|
|
11159
|
-
/**
|
|
11160
|
-
* Client ID management utility
|
|
11161
|
-
* Used to generate and persist unique device identifier
|
|
11162
|
-
* @internal
|
|
11163
|
-
*/
|
|
11164
|
-
var CLIENT_ID_STORAGE_KEY = "spavatar_client_id";
|
|
11165
|
-
/**
|
|
11166
|
-
* Get or create client_id
|
|
11167
|
-
* - If exists in localStorage, return existing client_id
|
|
11168
|
-
* - If not exists, generate new client_id and store to localStorage
|
|
11169
|
-
* @returns client_id string
|
|
11170
|
-
* @internal
|
|
11171
|
-
*/
|
|
11172
|
-
function getOrCreateClientId() {
|
|
11173
|
-
try {
|
|
11174
|
-
const stored = localStorage.getItem(CLIENT_ID_STORAGE_KEY);
|
|
11175
|
-
if (stored) return stored;
|
|
11176
|
-
const clientId = nanoid$2(21);
|
|
11177
|
-
localStorage.setItem(CLIENT_ID_STORAGE_KEY, clientId);
|
|
11178
|
-
return clientId;
|
|
11179
|
-
} catch (error) {
|
|
11180
|
-
return `temp_${nanoid$2(21)}_${Date.now()}`;
|
|
11181
|
-
}
|
|
11182
|
-
}
|
|
11183
|
-
//#endregion
|
|
11184
|
-
//#region utils/conversationId.ts
|
|
11185
|
-
/**
|
|
11186
|
-
* Conversation ID generation utility
|
|
11187
|
-
*
|
|
11188
|
-
* Unified format: timestamp (UTC time, second-level, format: YYYYMMDDHHmmss) + underscore + 12-digit NanoID
|
|
11189
|
-
* Example: 20251027143034_aB3dEf9hIjKl
|
|
11190
|
-
*
|
|
11191
|
-
* Time part uses UTC time for easier cross-platform log troubleshooting with unified timezone conversion
|
|
11192
|
-
* @internal
|
|
11193
|
-
*/
|
|
11194
|
-
/**
|
|
11195
|
-
* Custom character set: only uppercase, lowercase letters and numbers (URL safe, no hyphens)
|
|
11196
|
-
* Character set: A-Z, a-z, 0-9 (62 characters)
|
|
11197
|
-
*/
|
|
11198
|
-
var nanoid$1 = customAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 12);
|
|
11199
|
-
/**
|
|
11200
|
-
* Generate unified conversation ID
|
|
11201
|
-
*
|
|
11202
|
-
* Format: timestamp (UTC time, second-level, format: YYYYMMDDHHmmss) + underscore + 12-digit NanoID
|
|
11203
|
-
* Time part uses UTC time for easier cross-platform log troubleshooting with unified timezone conversion
|
|
11204
|
-
*
|
|
11205
|
-
* @returns Conversation ID in format YYYYMMDDHHmmss_nanoid
|
|
11206
|
-
* @example
|
|
11207
|
-
* generateConversationId() // "20251027143034_aB3dEf9hIjKl"
|
|
11208
|
-
* @internal
|
|
11209
|
-
*/
|
|
11210
|
-
var generateTraceId = () => generateConversationId();
|
|
11211
|
-
function generateConversationId() {
|
|
11212
|
-
const now = /* @__PURE__ */ new Date();
|
|
11213
|
-
return `${`${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}${String(now.getUTCDate()).padStart(2, "0")}${String(now.getUTCHours()).padStart(2, "0")}${String(now.getUTCMinutes()).padStart(2, "0")}${String(now.getUTCSeconds()).padStart(2, "0")}`}_${nanoid$1()}`;
|
|
11214
|
-
}
|
|
11215
|
-
//#endregion
|
|
11216
|
-
//#region utils/id-manager.ts
|
|
11217
|
-
/**
|
|
11218
|
-
* ID Manager
|
|
11219
|
-
* Unified management of all types of IDs in SDK
|
|
11220
|
-
* @internal
|
|
11221
|
-
*/
|
|
11222
|
-
/** 与 conversation_id 同字符集:URL 安全、无连字符。 */
|
|
11223
|
-
var nanoid = customAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 12);
|
|
11224
|
-
var IdManager = class {
|
|
11225
|
-
ids = {
|
|
11226
|
-
clientId: "",
|
|
11227
|
-
sessionId: "",
|
|
11228
|
-
userId: null,
|
|
11229
|
-
appId: null,
|
|
11230
|
-
sessionToken: null,
|
|
11231
|
-
connectionId: null,
|
|
11232
|
-
conversationId: null
|
|
11233
|
-
};
|
|
11234
|
-
constructor() {
|
|
11235
|
-
this.ids.clientId = getOrCreateClientId();
|
|
11236
|
-
}
|
|
11237
|
-
getClientId() {
|
|
11238
|
-
return this.ids.clientId;
|
|
11239
|
-
}
|
|
11240
|
-
setUserId(userId) {
|
|
11241
|
-
this.ids.userId = userId;
|
|
11242
|
-
}
|
|
11243
|
-
getUserId() {
|
|
11244
|
-
return this.ids.userId;
|
|
11245
|
-
}
|
|
11246
|
-
setAppId(appId) {
|
|
11247
|
-
this.ids.appId = appId;
|
|
11248
|
-
}
|
|
11249
|
-
getAppId() {
|
|
11250
|
-
return this.ids.appId;
|
|
11251
|
-
}
|
|
11252
|
-
setSessionToken(token) {
|
|
11253
|
-
this.ids.sessionToken = token;
|
|
11254
|
-
}
|
|
11255
|
-
getSessionToken() {
|
|
11256
|
-
return this.ids.sessionToken;
|
|
11257
|
-
}
|
|
11258
|
-
/**
|
|
11259
|
-
* Generate new connectionId (for WebSocket connection)
|
|
11260
|
-
* @internal
|
|
11261
|
-
*/
|
|
11262
|
-
generateConnectionId() {
|
|
11263
|
-
this.ids.connectionId = generateConversationId();
|
|
11264
|
-
return this.ids.connectionId;
|
|
11265
|
-
}
|
|
11266
|
-
/**
|
|
11267
|
-
* Set connectionId (for connectionId received from server)
|
|
11268
|
-
* @internal
|
|
11269
|
-
*/
|
|
11270
|
-
setConnectionId(connectionId) {
|
|
11271
|
-
this.ids.connectionId = connectionId;
|
|
11272
|
-
}
|
|
11273
|
-
getConnectionId() {
|
|
11274
|
-
return this.ids.connectionId;
|
|
11275
|
-
}
|
|
11276
|
-
clearConnectionId() {
|
|
11277
|
-
this.ids.connectionId = null;
|
|
11278
|
-
}
|
|
11279
|
-
/**
|
|
11280
|
-
* Generate new conversationId (for each conversation)
|
|
11281
|
-
* @internal
|
|
11282
|
-
*/
|
|
11283
|
-
generateNewConversationId() {
|
|
11284
|
-
this.ids.conversationId = generateConversationId();
|
|
11285
|
-
return this.ids.conversationId;
|
|
11286
|
-
}
|
|
11287
|
-
getConversationId() {
|
|
11288
|
-
return this.ids.conversationId;
|
|
11289
|
-
}
|
|
11290
|
-
setConversationId(conversationId) {
|
|
11291
|
-
this.ids.conversationId = conversationId;
|
|
11292
|
-
}
|
|
11293
|
-
clearConversationId() {
|
|
11294
|
-
this.ids.conversationId = null;
|
|
11295
|
-
}
|
|
11296
|
-
/**
|
|
11297
|
-
* Get all IDs (for log reporting)
|
|
11298
|
-
* @internal
|
|
11299
|
-
*/
|
|
11300
|
-
getAllIds() {
|
|
11301
|
-
return { ...this.ids };
|
|
11302
|
-
}
|
|
11303
|
-
/**
|
|
11304
|
-
* Get public log parameters (contains all necessary IDs)
|
|
11305
|
-
* @internal
|
|
11306
|
-
*/
|
|
11307
|
-
getLogContext() {
|
|
11308
|
-
return {
|
|
11309
|
-
client_id: this.ids.clientId,
|
|
11310
|
-
session_id: this.ids.sessionId,
|
|
11311
|
-
user_id: this.ids.userId || "",
|
|
11312
|
-
app_id: this.ids.appId || ""
|
|
11313
|
-
};
|
|
11710
|
+
}
|
|
11711
|
+
}
|
|
11712
|
+
}));
|
|
11713
|
+
}));
|
|
11314
11714
|
}
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
if (!this.ids.sessionId) this.ids.sessionId = nanoid();
|
|
11318
|
-
return this.ids.sessionId;
|
|
11715
|
+
Gc() {
|
|
11716
|
+
return this._instance.requestRouter.endpointFor("api", "/i/v1/metrics") + "?token=" + encodeURIComponent(this._instance.config.token);
|
|
11319
11717
|
}
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11718
|
+
} });
|
|
11719
|
+
Rl.__defaultExtensionClasses = _({}, iu);
|
|
11720
|
+
var eu = function() {
|
|
11721
|
+
c.SDK_DIST_CHANNEL = "npm";
|
|
11722
|
+
var i = fl[xl] = new Rl();
|
|
11723
|
+
return function() {
|
|
11724
|
+
function i() {
|
|
11725
|
+
i.done || (i.done = !0, El = !1, es(fl, (function(t) {
|
|
11726
|
+
t._dom_loaded();
|
|
11727
|
+
})));
|
|
11728
|
+
}
|
|
11729
|
+
null != r && r.addEventListener ? "complete" === r.readyState ? i() : us(r, "DOMContentLoaded", i, { capture: !1 }) : t && Ie.error("Browser doesn't support `document.addEventListener` so PostHog couldn't be initialized");
|
|
11730
|
+
}(), i;
|
|
11731
|
+
}();
|
|
11732
|
+
//#endregion
|
|
11733
|
+
//#region utils/telemetry-rate-limit.ts
|
|
11734
|
+
/**
|
|
11735
|
+
* 事件限频(三端同一规则,见 knowledge/error-telemetry-gap-audit.md「限频策略」)。
|
|
11736
|
+
*
|
|
11737
|
+
* 只给**循环路径**上的错误事件用(每帧 / 每包 / 每 tick):一台机器卡在某个坏状态时,
|
|
11738
|
+
* 同一条错误一秒能刷几十次,既刷爆上报量也淹没别的事件。
|
|
11739
|
+
*
|
|
11740
|
+
* - key = 事件名 + `stage` / `op` / `path` / `direction` / `backend` 之一(低基数判别字段),
|
|
11741
|
+
* **不含** reason 自由文本——否则每条 reason 都是新 key,限频等于没有
|
|
11742
|
+
* - 同一 key 每 session(页面生命周期)首次必发
|
|
11743
|
+
* - 之后 60s 内只发一次;被压掉的次数累计进下一条的 `suppressed_count`
|
|
11744
|
+
*/
|
|
11745
|
+
var WINDOW_MS = 6e4;
|
|
11746
|
+
var slots = /* @__PURE__ */ new Map();
|
|
11747
|
+
var DISCRIMINATORS = [
|
|
11748
|
+
"stage",
|
|
11749
|
+
"op",
|
|
11750
|
+
"path",
|
|
11751
|
+
"direction",
|
|
11752
|
+
"backend",
|
|
11753
|
+
"code"
|
|
11754
|
+
];
|
|
11755
|
+
/** @internal */
|
|
11756
|
+
function rateLimitKey(event, contents) {
|
|
11757
|
+
for (const k of DISCRIMINATORS) {
|
|
11758
|
+
const v = contents[k];
|
|
11759
|
+
if (v !== void 0 && v !== null && v !== "") return `${event}|${k}=${String(v)}`;
|
|
11330
11760
|
}
|
|
11331
|
-
|
|
11332
|
-
|
|
11761
|
+
return event;
|
|
11762
|
+
}
|
|
11763
|
+
/**
|
|
11764
|
+
* 判定这次能不能发。返回 `null` = 压掉;返回数字 = 放行,数字是自上次放行以来被压掉的次数。
|
|
11765
|
+
* @internal
|
|
11766
|
+
*/
|
|
11767
|
+
function admitRateLimited(key, now = Date.now()) {
|
|
11768
|
+
const slot = slots.get(key);
|
|
11769
|
+
if (!slot) {
|
|
11770
|
+
slots.set(key, {
|
|
11771
|
+
lastSent: now,
|
|
11772
|
+
suppressed: 0
|
|
11773
|
+
});
|
|
11774
|
+
return 0;
|
|
11775
|
+
}
|
|
11776
|
+
if (now - slot.lastSent < WINDOW_MS) {
|
|
11777
|
+
slot.suppressed++;
|
|
11778
|
+
return null;
|
|
11779
|
+
}
|
|
11780
|
+
const suppressed = slot.suppressed;
|
|
11781
|
+
slot.lastSent = now;
|
|
11782
|
+
slot.suppressed = 0;
|
|
11783
|
+
return suppressed;
|
|
11784
|
+
}
|
|
11333
11785
|
//#endregion
|
|
11334
11786
|
//#region config/constants.ts
|
|
11335
11787
|
var POSTHOG_HOST = "https://i.spatialwalk.ai";
|
|
@@ -19863,755 +20315,398 @@ async function replayPersistedEvents() {
|
|
|
19863
20315
|
}
|
|
19864
20316
|
if (loggerProvider) loggerProvider.forceFlush().catch(() => {});
|
|
19865
20317
|
} catch (error) {
|
|
19866
|
-
logger.warn("[OTel] Failed to load persisted events:", error instanceof Error ? error.message : String(error));
|
|
19867
|
-
}
|
|
19868
|
-
}
|
|
19869
|
-
/**
|
|
19870
|
-
* Cleanup OTel (flush remaining events and shutdown).
|
|
19871
|
-
* @internal
|
|
19872
|
-
*/
|
|
19873
|
-
function cleanupOtel() {
|
|
19874
|
-
if (!isInitialized$2 || !loggerProvider) return;
|
|
19875
|
-
try {
|
|
19876
|
-
loggerProvider.shutdown().catch((error) => {
|
|
19877
|
-
logger.warn("[OTel] Shutdown error:", error instanceof Error ? error.message : String(error));
|
|
19878
|
-
});
|
|
19879
|
-
isInitialized$2 = false;
|
|
19880
|
-
loggerProvider = null;
|
|
19881
|
-
logger.log("[OTel] Cleaned up");
|
|
19882
|
-
} catch (error) {
|
|
19883
|
-
logger.warn("[OTel] Failed to cleanup:", error instanceof Error ? error.message : String(error));
|
|
19884
|
-
isInitialized$2 = false;
|
|
19885
|
-
loggerProvider = null;
|
|
19886
|
-
}
|
|
19887
|
-
}
|
|
19888
|
-
//#endregion
|
|
19889
|
-
//#region utils/posthog-tracker.ts
|
|
19890
|
-
/**
|
|
19891
|
-
* PostHog Telemetry Tool (Browser Version)
|
|
19892
|
-
*
|
|
19893
|
-
* Unified PostHog telemetry reporting, supports custom reporting
|
|
19894
|
-
* Uses PostHog JS SDK for event tracking, suitable for browser environments
|
|
19895
|
-
* @internal
|
|
19896
|
-
*/
|
|
19897
|
-
/**
|
|
19898
|
-
* 构建时间戳字段:本地时间戳(校准基准+单调流逝)+ 后台时间戳(拿到才带)。
|
|
19899
|
-
* @internal
|
|
19900
|
-
*/
|
|
19901
|
-
function timestampFields(enqueueMono) {
|
|
19902
|
-
const local = enqueueMono === void 0 ? clockSync.localNow() : clockSync.localAt(enqueueMono);
|
|
19903
|
-
const server = enqueueMono === void 0 ? clockSync.serverNow() : clockSync.serverAt(enqueueMono);
|
|
19904
|
-
const fields = { timestamp: local };
|
|
19905
|
-
if (server !== null) fields.server_timestamp = server;
|
|
19906
|
-
return fields;
|
|
19907
|
-
}
|
|
19908
|
-
/**
|
|
19909
|
-
* 客户端环境字段(仅 OTel log 通道补——PostHog 通道由其 SDK 自动附加同类 `$` 属性)。
|
|
19910
|
-
* 全部从浏览器 API 现取(与 PostHog 客户端取法一致),不解析 UA/不造设备信息,
|
|
19911
|
-
* 浏览器/OS/设备类型交给后端从 user_agent 解析。GeoIP(国家/城市/IP)由后端从 IP 推断,此处不涉及。
|
|
19912
|
-
* 每条 log 实时取:url/pathname 随 SPA 路由变,取当前值才准。全程 try/catch 兜底,缺失即省略该字段。
|
|
19913
|
-
* @internal
|
|
19914
|
-
*/
|
|
19915
|
-
function clientContextFields() {
|
|
19916
|
-
const fields = {};
|
|
19917
|
-
try {
|
|
19918
|
-
if (typeof window !== "undefined" && window.location) {
|
|
19919
|
-
const loc = window.location;
|
|
19920
|
-
if (loc.host) fields.host = loc.host;
|
|
19921
|
-
if (loc.hostname) fields.domain = loc.hostname;
|
|
19922
|
-
if (loc.href) fields.url = loc.href;
|
|
19923
|
-
if (loc.pathname) fields.pathname = loc.pathname;
|
|
19924
|
-
}
|
|
19925
|
-
if (typeof document !== "undefined" && document.referrer) fields.referrer = document.referrer;
|
|
19926
|
-
if (typeof navigator !== "undefined") {
|
|
19927
|
-
if (navigator.userAgent) fields.user_agent = navigator.userAgent;
|
|
19928
|
-
if (navigator.language) fields.locale = navigator.language;
|
|
19929
|
-
}
|
|
19930
|
-
if (typeof screen !== "undefined") {
|
|
19931
|
-
if (screen.width) fields.screen_width = screen.width;
|
|
19932
|
-
if (screen.height) fields.screen_height = screen.height;
|
|
19933
|
-
}
|
|
19934
|
-
if (typeof window !== "undefined") {
|
|
19935
|
-
if (window.innerWidth) fields.viewport_width = window.innerWidth;
|
|
19936
|
-
if (window.innerHeight) fields.viewport_height = window.innerHeight;
|
|
19937
|
-
}
|
|
19938
|
-
const tz = Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone;
|
|
19939
|
-
if (tz) fields.timezone = tz;
|
|
19940
|
-
} catch {}
|
|
19941
|
-
return fields;
|
|
19942
|
-
}
|
|
19943
|
-
var sdkVersion$1 = "1.0.0";
|
|
19944
|
-
var isInitialized$1 = false;
|
|
19945
|
-
var commonRegion = "";
|
|
19946
|
-
var commonDsm = "";
|
|
19947
|
-
function isPostHogEnabled() {
|
|
19948
|
-
return !commonRegion.startsWith("cn-");
|
|
19949
|
-
}
|
|
19950
|
-
var SDK_POSTHOG_INSTANCE_NAME = "spatius-posthog";
|
|
19951
|
-
var sdkPosthogInstance = null;
|
|
19952
|
-
var eventQueue = [];
|
|
19953
|
-
/**
|
|
19954
|
-
* Get common fields for PostHog (excluding level)
|
|
19955
|
-
* userProperties = 公共字段除了 level 外的所有字段
|
|
19956
|
-
* @internal
|
|
19957
|
-
*/
|
|
19958
|
-
function getCommonFields() {
|
|
19959
|
-
const logContext = idManager.getLogContext();
|
|
19960
|
-
return {
|
|
19961
|
-
platform: "Web",
|
|
19962
|
-
sdk_version: sdkVersion$1,
|
|
19963
|
-
app_id: logContext.app_id || "",
|
|
19964
|
-
user_id: logContext.user_id || "",
|
|
19965
|
-
client_id: logContext.client_id,
|
|
19966
|
-
region: commonRegion,
|
|
19967
|
-
dsm: commonDsm,
|
|
19968
|
-
framework: "",
|
|
19969
|
-
framework_version: ""
|
|
19970
|
-
};
|
|
19971
|
-
}
|
|
19972
|
-
/**
|
|
19973
|
-
* Initialize PostHog with the SDK version.
|
|
19974
|
-
* @internal
|
|
19975
|
-
*/
|
|
19976
|
-
function initializePostHog(version, commonFields) {
|
|
19977
|
-
commonRegion = commonFields.region;
|
|
19978
|
-
commonDsm = commonFields.dsm;
|
|
19979
|
-
if (!isPostHogEnabled()) {
|
|
19980
|
-
logger.log(`[PostHog] Tracking disabled for region=${commonRegion} (data residency)`);
|
|
19981
|
-
return;
|
|
19982
|
-
}
|
|
19983
|
-
const { host, apiKey, disableCompression } = getPostHogConfig();
|
|
19984
|
-
if (!apiKey) {
|
|
19985
|
-
logger.warn("[PostHog] API Key not configured, tracking disabled");
|
|
19986
|
-
return;
|
|
19987
|
-
}
|
|
19988
|
-
if (isInitialized$1) {
|
|
19989
|
-
logger.log("[PostHog] Already initialized, skipping");
|
|
19990
|
-
return;
|
|
19991
|
-
}
|
|
19992
|
-
sdkVersion$1 = version;
|
|
19993
|
-
try {
|
|
19994
|
-
const logContext = idManager.getLogContext();
|
|
19995
|
-
eu.init(apiKey, {
|
|
19996
|
-
api_host: host,
|
|
19997
|
-
person_profiles: "identified_only",
|
|
19998
|
-
capture_pageview: false,
|
|
19999
|
-
capture_pageleave: false,
|
|
20000
|
-
disable_compression: disableCompression,
|
|
20001
|
-
disable_session_recording: true,
|
|
20002
|
-
autocapture: false,
|
|
20003
|
-
capture_performance: false,
|
|
20004
|
-
loaded: (posthogInstance) => {
|
|
20005
|
-
logger.log(`[PostHog] Initialized successfully - host: ${host}, instance: ${SDK_POSTHOG_INSTANCE_NAME}`);
|
|
20006
|
-
isInitialized$1 = true;
|
|
20007
|
-
sdkPosthogInstance = posthogInstance;
|
|
20008
|
-
const userProperties = getCommonFields();
|
|
20009
|
-
if (logContext.user_id) posthogInstance.identify(logContext.user_id, userProperties);
|
|
20010
|
-
else posthogInstance.register(userProperties);
|
|
20011
|
-
posthogInstance.setPersonPropertiesForFlags(userProperties);
|
|
20012
|
-
flushEventQueue();
|
|
20013
|
-
clockSync.onReady(() => flushEventQueue());
|
|
20014
|
-
}
|
|
20015
|
-
}, SDK_POSTHOG_INSTANCE_NAME);
|
|
20016
|
-
} catch (error) {
|
|
20017
|
-
logger.warn("[PostHog] Failed to initialize:", error instanceof Error ? error.message : String(error));
|
|
20018
|
-
}
|
|
20019
|
-
}
|
|
20020
|
-
/**
|
|
20021
|
-
* Get SDK's PostHog instance (if not initialized, try to get it)
|
|
20022
|
-
* @internal
|
|
20023
|
-
*/
|
|
20024
|
-
function getSdkPosthogInstance() {
|
|
20025
|
-
if (sdkPosthogInstance) return sdkPosthogInstance;
|
|
20026
|
-
try {
|
|
20027
|
-
sdkPosthogInstance = eu.getInstance?.(SDK_POSTHOG_INSTANCE_NAME);
|
|
20028
|
-
return sdkPosthogInstance;
|
|
20029
|
-
} catch (error) {
|
|
20030
|
-
return null;
|
|
20031
|
-
}
|
|
20032
|
-
}
|
|
20033
|
-
/**
|
|
20034
|
-
* Get logs feature flag (default: false)
|
|
20035
|
-
* Reloads feature flags from server first, then checks if logs_feature_flag is enabled
|
|
20036
|
-
* @returns Promise<boolean> - true if feature flag is enabled, false otherwise
|
|
20037
|
-
* @internal
|
|
20038
|
-
*/
|
|
20039
|
-
async function getLogsFeatureFlag() {
|
|
20040
|
-
let instance = getSdkPosthogInstance();
|
|
20041
|
-
if (!instance) {
|
|
20042
|
-
const maxWaitTime = 5e3;
|
|
20043
|
-
const checkInterval = 100;
|
|
20044
|
-
const startTime = Date.now();
|
|
20045
|
-
while (!instance && Date.now() - startTime < maxWaitTime) {
|
|
20046
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
20047
|
-
instance = getSdkPosthogInstance();
|
|
20048
|
-
}
|
|
20049
|
-
}
|
|
20050
|
-
if (!instance) {
|
|
20051
|
-
logger.log("[PostHog] PostHog not initialized after waiting, logs feature flag disabled (default: false)");
|
|
20052
|
-
return false;
|
|
20053
|
-
}
|
|
20054
|
-
try {
|
|
20055
|
-
const commonFields = getCommonFields();
|
|
20056
|
-
instance.setPersonPropertiesForFlags(commonFields);
|
|
20057
|
-
instance.reloadFeatureFlags();
|
|
20058
|
-
await new Promise((resolve) => {
|
|
20059
|
-
instance.onFeatureFlags((_flags, _variants, { errorsLoading } = {}) => {
|
|
20060
|
-
if (errorsLoading) logger.warn("[PostHog] Error loading feature flags");
|
|
20061
|
-
resolve();
|
|
20062
|
-
});
|
|
20063
|
-
});
|
|
20064
|
-
const isEnabled = instance.isFeatureEnabled("logs_feature_flag") === true;
|
|
20065
|
-
logger.log(`[PostHog] Logs feature flag: ${isEnabled ? "enabled" : "disabled"}`);
|
|
20066
|
-
return isEnabled;
|
|
20067
|
-
} catch (error) {
|
|
20068
|
-
logger.warn("[PostHog] Failed to get logs feature flag:", error instanceof Error ? error.message : String(error));
|
|
20069
|
-
return false;
|
|
20318
|
+
logger.warn("[OTel] Failed to load persisted events:", error instanceof Error ? error.message : String(error));
|
|
20070
20319
|
}
|
|
20071
20320
|
}
|
|
20072
20321
|
/**
|
|
20073
|
-
*
|
|
20322
|
+
* Cleanup OTel (flush remaining events and shutdown).
|
|
20074
20323
|
* @internal
|
|
20075
20324
|
*/
|
|
20076
|
-
function
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20325
|
+
function cleanupOtel() {
|
|
20326
|
+
if (!isInitialized$2 || !loggerProvider) return;
|
|
20327
|
+
try {
|
|
20328
|
+
loggerProvider.shutdown().catch((error) => {
|
|
20329
|
+
logger.warn("[OTel] Shutdown error:", error instanceof Error ? error.message : String(error));
|
|
20330
|
+
});
|
|
20331
|
+
isInitialized$2 = false;
|
|
20332
|
+
loggerProvider = null;
|
|
20333
|
+
logger.log("[OTel] Cleaned up");
|
|
20334
|
+
} catch (error) {
|
|
20335
|
+
logger.warn("[OTel] Failed to cleanup:", error instanceof Error ? error.message : String(error));
|
|
20336
|
+
isInitialized$2 = false;
|
|
20337
|
+
loggerProvider = null;
|
|
20338
|
+
}
|
|
20081
20339
|
}
|
|
20340
|
+
//#endregion
|
|
20341
|
+
//#region utils/posthog-tracker.ts
|
|
20082
20342
|
/**
|
|
20083
|
-
*
|
|
20343
|
+
* PostHog Telemetry Tool (Browser Version)
|
|
20344
|
+
*
|
|
20345
|
+
* Unified PostHog telemetry reporting, supports custom reporting
|
|
20346
|
+
* Uses PostHog JS SDK for event tracking, suitable for browser environments
|
|
20084
20347
|
* @internal
|
|
20085
20348
|
*/
|
|
20086
|
-
function flushEventQueue() {
|
|
20087
|
-
if (!sdkPosthogInstance || !clockSync.isReady() || eventQueue.length === 0) return;
|
|
20088
|
-
logger.log(`[PostHog] Flushing ${eventQueue.length} queued events`);
|
|
20089
|
-
setTimeout(() => {
|
|
20090
|
-
for (const { event, level, contents, enqueueMono } of eventQueue) try {
|
|
20091
|
-
const properties = {
|
|
20092
|
-
...getCommonFields(),
|
|
20093
|
-
level,
|
|
20094
|
-
service_module: "sdk",
|
|
20095
|
-
...timestampFields(enqueueMono),
|
|
20096
|
-
...resolveMarks(contents)
|
|
20097
|
-
};
|
|
20098
|
-
sdkPosthogInstance.capture(event, properties);
|
|
20099
|
-
} catch (error) {
|
|
20100
|
-
logger.warn(`[PostHog] Failed to flush queued event ${event}:`, error instanceof Error ? error.message : String(error));
|
|
20101
|
-
}
|
|
20102
|
-
eventQueue.length = 0;
|
|
20103
|
-
}, 0);
|
|
20104
|
-
}
|
|
20105
20349
|
/**
|
|
20106
|
-
*
|
|
20107
|
-
* @param event Event name
|
|
20108
|
-
* @param level Log level
|
|
20109
|
-
* @param contents Event contents
|
|
20350
|
+
* 构建时间戳字段:本地时间戳(校准基准+单调流逝)+ 后台时间戳(拿到才带)。
|
|
20110
20351
|
* @internal
|
|
20111
20352
|
*/
|
|
20112
|
-
function
|
|
20113
|
-
|
|
20114
|
-
const
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
level,
|
|
20119
|
-
contents,
|
|
20120
|
-
enqueueMono: performance.now()
|
|
20121
|
-
});
|
|
20122
|
-
return;
|
|
20123
|
-
}
|
|
20124
|
-
sdkPosthogInstance = instance;
|
|
20125
|
-
try {
|
|
20126
|
-
const properties = {
|
|
20127
|
-
...getCommonFields(),
|
|
20128
|
-
level,
|
|
20129
|
-
service_module: "sdk",
|
|
20130
|
-
...timestampFields(),
|
|
20131
|
-
...resolveMarks(contents)
|
|
20132
|
-
};
|
|
20133
|
-
sdkPosthogInstance.capture(event, properties);
|
|
20134
|
-
} catch (error) {
|
|
20135
|
-
logger.warn("[PostHog] Failed to track event:", error instanceof Error ? error.message : String(error));
|
|
20136
|
-
}
|
|
20353
|
+
function timestampFields(enqueueMono) {
|
|
20354
|
+
const local = enqueueMono === void 0 ? clockSync.localNow() : clockSync.localAt(enqueueMono);
|
|
20355
|
+
const server = enqueueMono === void 0 ? clockSync.serverNow() : clockSync.serverAt(enqueueMono);
|
|
20356
|
+
const fields = { timestamp: local };
|
|
20357
|
+
if (server !== null) fields.server_timestamp = server;
|
|
20358
|
+
return fields;
|
|
20137
20359
|
}
|
|
20138
20360
|
/**
|
|
20139
|
-
*
|
|
20361
|
+
* 客户端环境字段(仅 OTel log 通道补——PostHog 通道由其 SDK 自动附加同类 `$` 属性)。
|
|
20362
|
+
* 全部从浏览器 API 现取(与 PostHog 客户端取法一致),不解析 UA/不造设备信息,
|
|
20363
|
+
* 浏览器/OS/设备类型交给后端从 user_agent 解析。GeoIP(国家/城市/IP)由后端从 IP 推断,此处不涉及。
|
|
20364
|
+
* 每条 log 实时取:url/pathname 随 SPA 路由变,取当前值才准。全程 try/catch 兜底,缺失即省略该字段。
|
|
20140
20365
|
* @internal
|
|
20141
20366
|
*/
|
|
20142
|
-
function
|
|
20143
|
-
|
|
20367
|
+
function clientContextFields() {
|
|
20368
|
+
const fields = {};
|
|
20144
20369
|
try {
|
|
20145
|
-
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
20370
|
+
if (typeof window !== "undefined" && window.location) {
|
|
20371
|
+
const loc = window.location;
|
|
20372
|
+
if (loc.host) fields.host = loc.host;
|
|
20373
|
+
if (loc.hostname) fields.domain = loc.hostname;
|
|
20374
|
+
if (loc.href) fields.url = loc.href;
|
|
20375
|
+
if (loc.pathname) fields.pathname = loc.pathname;
|
|
20149
20376
|
}
|
|
20150
|
-
|
|
20151
|
-
if (typeof
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
};
|
|
20168
|
-
/** 组装上报 context(附加 session_token 后缀)并写一条本地 console。两条上报通道共用。 */
|
|
20169
|
-
function buildTelemetryContext(event, level, contents) {
|
|
20170
|
-
const sessionToken = idManager.getSessionToken() ?? "";
|
|
20171
|
-
const context = {
|
|
20172
|
-
session_token_suffix: sessionToken ? sessionToken.slice(-8) : "",
|
|
20173
|
-
connection_id: idManager.getConnectionId() ?? "",
|
|
20174
|
-
...clientContextFields(),
|
|
20175
|
-
...contents
|
|
20176
|
-
};
|
|
20177
|
-
const logMethod = TELEMETRY_LOG_METHOD[level] ?? "log";
|
|
20178
|
-
const propsDescription = Object.entries(contents).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
20179
|
-
logger[logMethod](`[Telemetry] ${event} [${propsDescription}]`);
|
|
20180
|
-
return context;
|
|
20181
|
-
}
|
|
20182
|
-
/** 通过 OTel 上报(独立通道,失败隔离,不影响 PostHog)。 */
|
|
20183
|
-
function emitToOtel(event, level, context) {
|
|
20184
|
-
try {
|
|
20185
|
-
trackEventOtel(event, level, context);
|
|
20186
|
-
} catch (error) {
|
|
20187
|
-
logger.warn("[OTel] Dual-send failed:", error instanceof Error ? error.message : String(error));
|
|
20188
|
-
}
|
|
20377
|
+
if (typeof document !== "undefined" && document.referrer) fields.referrer = document.referrer;
|
|
20378
|
+
if (typeof navigator !== "undefined") {
|
|
20379
|
+
if (navigator.userAgent) fields.user_agent = navigator.userAgent;
|
|
20380
|
+
if (navigator.language) fields.locale = navigator.language;
|
|
20381
|
+
}
|
|
20382
|
+
if (typeof screen !== "undefined") {
|
|
20383
|
+
if (screen.width) fields.screen_width = screen.width;
|
|
20384
|
+
if (screen.height) fields.screen_height = screen.height;
|
|
20385
|
+
}
|
|
20386
|
+
if (typeof window !== "undefined") {
|
|
20387
|
+
if (window.innerWidth) fields.viewport_width = window.innerWidth;
|
|
20388
|
+
if (window.innerHeight) fields.viewport_height = window.innerHeight;
|
|
20389
|
+
}
|
|
20390
|
+
const tz = Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone;
|
|
20391
|
+
if (tz) fields.timezone = tz;
|
|
20392
|
+
} catch {}
|
|
20393
|
+
return fields;
|
|
20189
20394
|
}
|
|
20190
|
-
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
|
|
20195
|
-
|
|
20196
|
-
* @param contents Log contents
|
|
20197
|
-
* @internal
|
|
20198
|
-
*/
|
|
20199
|
-
function logEvent(event, level = "info", contents = {}) {
|
|
20200
|
-
const context = buildTelemetryContext(event, level, contents);
|
|
20201
|
-
trackEvent(event, level, context);
|
|
20202
|
-
emitToOtel(event, level, context);
|
|
20395
|
+
var sdkVersion$1 = "1.0.0";
|
|
20396
|
+
var isInitialized$1 = false;
|
|
20397
|
+
var commonRegion = "";
|
|
20398
|
+
var commonDsm = "";
|
|
20399
|
+
function isPostHogEnabled() {
|
|
20400
|
+
return !commonRegion.startsWith("cn-");
|
|
20203
20401
|
}
|
|
20402
|
+
var SDK_POSTHOG_INSTANCE_NAME = "spatius-posthog";
|
|
20403
|
+
var sdkPosthogInstance = null;
|
|
20404
|
+
var eventQueue = [];
|
|
20204
20405
|
/**
|
|
20205
|
-
*
|
|
20206
|
-
*
|
|
20207
|
-
*
|
|
20208
|
-
* metric 的天然形态是「一个数值 + 一组低基数标签」,故签名为 (name, value, labels, logExtra):
|
|
20209
|
-
* - `value` 被记录到 Histogram(延迟 ms / 帧率 / 时长 / 次数 / 比率等)
|
|
20210
|
-
* - `labels` 低基数标签(dsm / resolution / is_fallback / renderer_backend 等),
|
|
20211
|
-
* 同时进 metric 的 attributes 和 log。**不要**放高基数字段(会撑爆 metric 基数)。
|
|
20212
|
-
* - `logExtra` 只进 log 的明细(avatar_id / conversation_id 等高基数字段)。
|
|
20213
|
-
*
|
|
20214
|
-
* @param name Metric 名(如 `fetch_avatar_latency`)
|
|
20215
|
-
* @param value 被聚合的数值
|
|
20216
|
-
* @param labels 低基数标签
|
|
20217
|
-
* @param logExtra 仅 log 携带的高基数明细
|
|
20406
|
+
* Get common fields for PostHog (excluding level)
|
|
20407
|
+
* userProperties = 公共字段除了 level 外的所有字段
|
|
20218
20408
|
* @internal
|
|
20219
20409
|
*/
|
|
20220
|
-
function
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
*/
|
|
20245
|
-
async initialize() {
|
|
20246
|
-
if (this.db) return;
|
|
20247
|
-
return new Promise((resolve, reject) => {
|
|
20248
|
-
const request = indexedDB.open(this.dbName, this.version);
|
|
20249
|
-
request.onerror = () => {
|
|
20250
|
-
reject(/* @__PURE__ */ new Error("Failed to open IndexedDB"));
|
|
20251
|
-
};
|
|
20252
|
-
request.onsuccess = () => {
|
|
20253
|
-
this.db = request.result;
|
|
20254
|
-
resolve();
|
|
20255
|
-
};
|
|
20256
|
-
request.onupgradeneeded = (event) => {
|
|
20257
|
-
const db = event.target.result;
|
|
20258
|
-
if (!db.objectStoreNames.contains(this.storeName)) db.createObjectStore(this.storeName, {
|
|
20259
|
-
keyPath: "id",
|
|
20260
|
-
autoIncrement: true
|
|
20261
|
-
}).createIndex("ts", "ts", { unique: false });
|
|
20262
|
-
};
|
|
20263
|
-
});
|
|
20264
|
-
}
|
|
20265
|
-
/**
|
|
20266
|
-
* Append log entry to IndexedDB
|
|
20267
|
-
*/
|
|
20268
|
-
async append(entry) {
|
|
20269
|
-
if (!this.db) try {
|
|
20270
|
-
await this.initialize();
|
|
20271
|
-
} catch {
|
|
20272
|
-
return;
|
|
20273
|
-
}
|
|
20274
|
-
if (!this.db) return;
|
|
20275
|
-
try {
|
|
20276
|
-
const store = this.db.transaction([this.storeName], "readwrite").objectStore(this.storeName);
|
|
20277
|
-
await new Promise((resolve, reject) => {
|
|
20278
|
-
const request = store.add(entry);
|
|
20279
|
-
request.onsuccess = () => resolve();
|
|
20280
|
-
request.onerror = () => reject(/* @__PURE__ */ new Error("Failed to add log"));
|
|
20281
|
-
});
|
|
20282
|
-
this.getCount().then((count) => {
|
|
20283
|
-
if (count > this.trimThreshold) this.trim().catch(() => {});
|
|
20284
|
-
}).catch(() => {});
|
|
20285
|
-
} catch (error) {}
|
|
20410
|
+
function getCommonFields() {
|
|
20411
|
+
const logContext = idManager.getLogContext();
|
|
20412
|
+
return {
|
|
20413
|
+
platform: "Web",
|
|
20414
|
+
sdk_version: sdkVersion$1,
|
|
20415
|
+
app_id: logContext.app_id || "",
|
|
20416
|
+
user_id: logContext.user_id || "",
|
|
20417
|
+
client_id: logContext.client_id,
|
|
20418
|
+
region: commonRegion,
|
|
20419
|
+
dsm: commonDsm,
|
|
20420
|
+
framework: "",
|
|
20421
|
+
framework_version: ""
|
|
20422
|
+
};
|
|
20423
|
+
}
|
|
20424
|
+
/**
|
|
20425
|
+
* Initialize PostHog with the SDK version.
|
|
20426
|
+
* @internal
|
|
20427
|
+
*/
|
|
20428
|
+
function initializePostHog(version, commonFields) {
|
|
20429
|
+
commonRegion = commonFields.region;
|
|
20430
|
+
commonDsm = commonFields.dsm;
|
|
20431
|
+
if (!isPostHogEnabled()) {
|
|
20432
|
+
logger.log(`[PostHog] Tracking disabled for region=${commonRegion} (data residency)`);
|
|
20433
|
+
return;
|
|
20286
20434
|
}
|
|
20287
|
-
|
|
20288
|
-
|
|
20289
|
-
|
|
20290
|
-
|
|
20291
|
-
if (!this.db) return 0;
|
|
20292
|
-
return new Promise((resolve, reject) => {
|
|
20293
|
-
const request = this.db.transaction([this.storeName], "readonly").objectStore(this.storeName).count();
|
|
20294
|
-
request.onsuccess = () => {
|
|
20295
|
-
resolve(request.result);
|
|
20296
|
-
};
|
|
20297
|
-
request.onerror = () => {
|
|
20298
|
-
reject(/* @__PURE__ */ new Error("Failed to count logs"));
|
|
20299
|
-
};
|
|
20300
|
-
});
|
|
20435
|
+
const { host, apiKey, disableCompression } = getPostHogConfig();
|
|
20436
|
+
if (!apiKey) {
|
|
20437
|
+
logger.warn("[PostHog] API Key not configured, tracking disabled");
|
|
20438
|
+
return;
|
|
20301
20439
|
}
|
|
20302
|
-
|
|
20303
|
-
|
|
20304
|
-
|
|
20305
|
-
async trim() {
|
|
20306
|
-
if (!this.db || this.isFlushing) return;
|
|
20307
|
-
try {
|
|
20308
|
-
const store = this.db.transaction([this.storeName], "readwrite").objectStore(this.storeName);
|
|
20309
|
-
const getAllRequest = store.getAll();
|
|
20310
|
-
const entries = await new Promise((resolve, reject) => {
|
|
20311
|
-
getAllRequest.onsuccess = () => {
|
|
20312
|
-
resolve(getAllRequest.result);
|
|
20313
|
-
};
|
|
20314
|
-
getAllRequest.onerror = () => {
|
|
20315
|
-
reject(/* @__PURE__ */ new Error("Failed to get logs"));
|
|
20316
|
-
};
|
|
20317
|
-
});
|
|
20318
|
-
const keepCount = this.trimThreshold - this.trimBufferSize;
|
|
20319
|
-
if (entries.length <= keepCount) return;
|
|
20320
|
-
entries.sort((a, b) => a.ts - b.ts);
|
|
20321
|
-
const toDelete = entries.slice(0, entries.length - keepCount);
|
|
20322
|
-
for (const entry of toDelete) if (entry.id !== void 0) await new Promise((resolve, reject) => {
|
|
20323
|
-
const deleteRequest = store.delete(entry.id);
|
|
20324
|
-
deleteRequest.onsuccess = () => resolve();
|
|
20325
|
-
deleteRequest.onerror = () => reject(/* @__PURE__ */ new Error("Failed to delete log"));
|
|
20326
|
-
});
|
|
20327
|
-
} catch (error) {}
|
|
20440
|
+
if (isInitialized$1) {
|
|
20441
|
+
logger.log("[PostHog] Already initialized, skipping");
|
|
20442
|
+
return;
|
|
20328
20443
|
}
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
|
|
20345
|
-
|
|
20346
|
-
|
|
20347
|
-
|
|
20444
|
+
sdkVersion$1 = version;
|
|
20445
|
+
try {
|
|
20446
|
+
const logContext = idManager.getLogContext();
|
|
20447
|
+
eu.init(apiKey, {
|
|
20448
|
+
api_host: host,
|
|
20449
|
+
person_profiles: "identified_only",
|
|
20450
|
+
capture_pageview: false,
|
|
20451
|
+
capture_pageleave: false,
|
|
20452
|
+
disable_compression: disableCompression,
|
|
20453
|
+
disable_session_recording: true,
|
|
20454
|
+
autocapture: false,
|
|
20455
|
+
capture_performance: false,
|
|
20456
|
+
loaded: (posthogInstance) => {
|
|
20457
|
+
logger.log(`[PostHog] Initialized successfully - host: ${host}, instance: ${SDK_POSTHOG_INSTANCE_NAME}`);
|
|
20458
|
+
isInitialized$1 = true;
|
|
20459
|
+
sdkPosthogInstance = posthogInstance;
|
|
20460
|
+
const userProperties = getCommonFields();
|
|
20461
|
+
if (logContext.user_id) posthogInstance.identify(logContext.user_id, userProperties);
|
|
20462
|
+
else posthogInstance.register(userProperties);
|
|
20463
|
+
posthogInstance.setPersonPropertiesForFlags(userProperties);
|
|
20464
|
+
flushEventQueue();
|
|
20465
|
+
clockSync.onReady(() => flushEventQueue());
|
|
20348
20466
|
}
|
|
20349
|
-
|
|
20350
|
-
|
|
20351
|
-
|
|
20352
|
-
...entry.extra,
|
|
20353
|
-
platform: "Web"
|
|
20354
|
-
}
|
|
20355
|
-
})) };
|
|
20356
|
-
const jsonData = JSON.stringify(requestBody);
|
|
20357
|
-
const gzippedData = await this.gzip(jsonData);
|
|
20358
|
-
const response = await fetch("https://hogtool.spatialwalk.ai/batch-upload-log", {
|
|
20359
|
-
method: "POST",
|
|
20360
|
-
headers: {
|
|
20361
|
-
"Content-Type": "application/json",
|
|
20362
|
-
"Content-Encoding": "gzip"
|
|
20363
|
-
},
|
|
20364
|
-
body: gzippedData,
|
|
20365
|
-
signal: AbortSignal.timeout(15e3)
|
|
20366
|
-
});
|
|
20367
|
-
if (!response.ok) throw new Error(`Failed to upload logs: ${response.status} ${response.statusText}`);
|
|
20368
|
-
await this.clearFirst(entries.length);
|
|
20369
|
-
} catch (error) {
|
|
20370
|
-
console.warn("[LogSink] Failed to flush logs:", error);
|
|
20371
|
-
throw error;
|
|
20372
|
-
} finally {
|
|
20373
|
-
this.isFlushing = false;
|
|
20374
|
-
}
|
|
20467
|
+
}, SDK_POSTHOG_INSTANCE_NAME);
|
|
20468
|
+
} catch (error) {
|
|
20469
|
+
logger.warn("[PostHog] Failed to initialize:", error instanceof Error ? error.message : String(error));
|
|
20375
20470
|
}
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20379
|
-
|
|
20380
|
-
|
|
20381
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
20384
|
-
|
|
20385
|
-
|
|
20386
|
-
|
|
20387
|
-
|
|
20388
|
-
getAllRequest.onerror = () => {
|
|
20389
|
-
reject(/* @__PURE__ */ new Error("Failed to get logs"));
|
|
20390
|
-
};
|
|
20391
|
-
});
|
|
20392
|
-
entries.sort((a, b) => a.ts - b.ts);
|
|
20393
|
-
const toDelete = entries.slice(0, count);
|
|
20394
|
-
for (const entry of toDelete) if (entry.id !== void 0) await new Promise((resolve, reject) => {
|
|
20395
|
-
const deleteRequest = store.delete(entry.id);
|
|
20396
|
-
deleteRequest.onsuccess = () => resolve();
|
|
20397
|
-
deleteRequest.onerror = () => reject(/* @__PURE__ */ new Error("Failed to delete log"));
|
|
20398
|
-
});
|
|
20399
|
-
} catch (error) {}
|
|
20471
|
+
}
|
|
20472
|
+
/**
|
|
20473
|
+
* Get SDK's PostHog instance (if not initialized, try to get it)
|
|
20474
|
+
* @internal
|
|
20475
|
+
*/
|
|
20476
|
+
function getSdkPosthogInstance() {
|
|
20477
|
+
if (sdkPosthogInstance) return sdkPosthogInstance;
|
|
20478
|
+
try {
|
|
20479
|
+
sdkPosthogInstance = eu.getInstance?.(SDK_POSTHOG_INSTANCE_NAME);
|
|
20480
|
+
return sdkPosthogInstance;
|
|
20481
|
+
} catch (error) {
|
|
20482
|
+
return null;
|
|
20400
20483
|
}
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
|
|
20410
|
-
|
|
20411
|
-
|
|
20412
|
-
|
|
20413
|
-
|
|
20414
|
-
|
|
20484
|
+
}
|
|
20485
|
+
/**
|
|
20486
|
+
* Get logs feature flag (default: false)
|
|
20487
|
+
* Reloads feature flags from server first, then checks if logs_feature_flag is enabled
|
|
20488
|
+
* @returns Promise<boolean> - true if feature flag is enabled, false otherwise
|
|
20489
|
+
* @internal
|
|
20490
|
+
*/
|
|
20491
|
+
async function getLogsFeatureFlag() {
|
|
20492
|
+
let instance = getSdkPosthogInstance();
|
|
20493
|
+
if (!instance) {
|
|
20494
|
+
const maxWaitTime = 5e3;
|
|
20495
|
+
const checkInterval = 100;
|
|
20496
|
+
const startTime = Date.now();
|
|
20497
|
+
while (!instance && Date.now() - startTime < maxWaitTime) {
|
|
20498
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
20499
|
+
instance = getSdkPosthogInstance();
|
|
20415
20500
|
}
|
|
20416
20501
|
}
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
|
|
20420
|
-
|
|
20421
|
-
|
|
20422
|
-
|
|
20423
|
-
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
|
|
20428
|
-
|
|
20429
|
-
|
|
20430
|
-
|
|
20431
|
-
|
|
20432
|
-
|
|
20433
|
-
|
|
20434
|
-
|
|
20435
|
-
|
|
20436
|
-
|
|
20437
|
-
"rig"
|
|
20438
|
-
].join("|")})\\b`, "gi");
|
|
20439
|
-
/** Chinese characters (CJK Unified Ideographs) */
|
|
20440
|
-
var CHINESE_REG = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/;
|
|
20441
|
-
/** Patterns to redact: capture group 1 = key, 2 = value; replace value with *** */
|
|
20442
|
-
var SENSITIVE_PATTERNS = [
|
|
20443
|
-
{
|
|
20444
|
-
reg: /\b(token|sessiontoken|accesstoken|apikey|apisecret|secret|password|auth)=([^\s,}\]'"]+)/gi,
|
|
20445
|
-
replacement: "$1=***"
|
|
20446
|
-
},
|
|
20447
|
-
{
|
|
20448
|
-
reg: /\b(conversationid|connectionid|clientid|sessionid)=([^\s,}\]'"]+)/gi,
|
|
20449
|
-
replacement: "$1=***"
|
|
20450
|
-
},
|
|
20451
|
-
{
|
|
20452
|
-
reg: /\b(appid|app_id)=([^\s,}\]'"]+)/gi,
|
|
20453
|
-
replacement: "$1=***"
|
|
20454
|
-
},
|
|
20455
|
-
{
|
|
20456
|
-
reg: /\b(userid|user_id)=([^\s,}\]'"]+)/gi,
|
|
20457
|
-
replacement: "$1=***"
|
|
20458
|
-
},
|
|
20459
|
-
{
|
|
20460
|
-
reg: /(https?:\/\/)[^\s,}\]'"]+/gi,
|
|
20461
|
-
replacement: "$1***"
|
|
20462
|
-
},
|
|
20463
|
-
{
|
|
20464
|
-
reg: /(wss?:\/\/)[^\s,}\]'"]+/gi,
|
|
20465
|
-
replacement: "$1***"
|
|
20502
|
+
if (!instance) {
|
|
20503
|
+
logger.log("[PostHog] PostHog not initialized after waiting, logs feature flag disabled (default: false)");
|
|
20504
|
+
return false;
|
|
20505
|
+
}
|
|
20506
|
+
try {
|
|
20507
|
+
const commonFields = getCommonFields();
|
|
20508
|
+
instance.setPersonPropertiesForFlags(commonFields);
|
|
20509
|
+
instance.reloadFeatureFlags();
|
|
20510
|
+
await new Promise((resolve) => {
|
|
20511
|
+
instance.onFeatureFlags((_flags, _variants, { errorsLoading } = {}) => {
|
|
20512
|
+
if (errorsLoading) logger.warn("[PostHog] Error loading feature flags");
|
|
20513
|
+
resolve();
|
|
20514
|
+
});
|
|
20515
|
+
});
|
|
20516
|
+
const isEnabled = instance.isFeatureEnabled("logs_feature_flag") === true;
|
|
20517
|
+
logger.log(`[PostHog] Logs feature flag: ${isEnabled ? "enabled" : "disabled"}`);
|
|
20518
|
+
return isEnabled;
|
|
20519
|
+
} catch (error) {
|
|
20520
|
+
logger.warn("[PostHog] Failed to get logs feature flag:", error instanceof Error ? error.message : String(error));
|
|
20521
|
+
return false;
|
|
20466
20522
|
}
|
|
20467
|
-
];
|
|
20468
|
-
/**
|
|
20469
|
-
* Check if message contains Chinese.
|
|
20470
|
-
*/
|
|
20471
|
-
function hasChinese(msg) {
|
|
20472
|
-
return CHINESE_REG.test(msg);
|
|
20473
|
-
}
|
|
20474
|
-
/**
|
|
20475
|
-
* Check if message contains algorithm/internal terms.
|
|
20476
|
-
*/
|
|
20477
|
-
function hasAlgorithmTerms(msg) {
|
|
20478
|
-
return ALGORITHM_REG.test(msg);
|
|
20479
20523
|
}
|
|
20480
20524
|
/**
|
|
20481
|
-
*
|
|
20525
|
+
* Update PostHog person properties for feature flags evaluation
|
|
20526
|
+
* @internal
|
|
20482
20527
|
*/
|
|
20483
|
-
function
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20528
|
+
function updatePostHogPersonPropertiesForFlags() {
|
|
20529
|
+
const instance = getSdkPosthogInstance();
|
|
20530
|
+
if (!instance) return;
|
|
20531
|
+
const commonFields = getCommonFields();
|
|
20532
|
+
instance.setPersonPropertiesForFlags(commonFields);
|
|
20487
20533
|
}
|
|
20488
20534
|
/**
|
|
20489
|
-
*
|
|
20490
|
-
*
|
|
20491
|
-
* - Otherwise persist, but redact sensitive content first.
|
|
20535
|
+
* Send queued events (called after PostHog initialization completes)
|
|
20536
|
+
* @internal
|
|
20492
20537
|
*/
|
|
20493
|
-
function
|
|
20494
|
-
if (
|
|
20495
|
-
|
|
20496
|
-
|
|
20497
|
-
|
|
20498
|
-
|
|
20499
|
-
|
|
20500
|
-
|
|
20501
|
-
|
|
20538
|
+
function flushEventQueue() {
|
|
20539
|
+
if (!sdkPosthogInstance || !clockSync.isReady() || eventQueue.length === 0) return;
|
|
20540
|
+
logger.log(`[PostHog] Flushing ${eventQueue.length} queued events`);
|
|
20541
|
+
setTimeout(() => {
|
|
20542
|
+
for (const { event, level, contents, enqueueMono } of eventQueue) try {
|
|
20543
|
+
const properties = {
|
|
20544
|
+
...getCommonFields(),
|
|
20545
|
+
level,
|
|
20546
|
+
service_module: "sdk",
|
|
20547
|
+
...timestampFields(enqueueMono),
|
|
20548
|
+
...resolveMarks(contents)
|
|
20549
|
+
};
|
|
20550
|
+
sdkPosthogInstance.capture(event, properties);
|
|
20551
|
+
} catch (error) {
|
|
20552
|
+
logger.warn(`[PostHog] Failed to flush queued event ${event}:`, error instanceof Error ? error.message : String(error));
|
|
20553
|
+
}
|
|
20554
|
+
eventQueue.length = 0;
|
|
20555
|
+
}, 0);
|
|
20502
20556
|
}
|
|
20503
|
-
//#endregion
|
|
20504
|
-
//#region utils/logger.ts
|
|
20505
20557
|
/**
|
|
20506
|
-
*
|
|
20507
|
-
* Environment-aware logger wrapper with local log persistence
|
|
20508
|
-
* - Always writes to local log file (IndexedDB) regardless of logLevel
|
|
20509
|
-
* - Console output is controlled by logLevel
|
|
20510
|
-
* - Errors and warnings are always reported to PostHog (when enabled)
|
|
20558
|
+
* **只发 PostHog**,不进 OTel——也就是不进 OpenObserve,看板和告警都查不到。
|
|
20511
20559
|
*
|
|
20512
|
-
*
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
|
|
20517
|
-
|
|
20518
|
-
|
|
20519
|
-
|
|
20520
|
-
var currentLogLevel = LogLevel.off;
|
|
20521
|
-
/**
|
|
20522
|
-
* Write log to local storage.
|
|
20523
|
-
* Skips persistence if message contains Chinese, algorithm terms (e.g. Flame, splat), or sensitive content.
|
|
20524
|
-
* Otherwise redacts sensitive substrings then persists.
|
|
20560
|
+
* 业务事件禁止直接调这个,用 {@link logEvent}(PostHog + OTel 双发)。这个名字
|
|
20561
|
+
* 故意起得难看:它以前叫 `trackEvent`,和 `internal-telemetry.ts` 里给 RTC 暴露的
|
|
20562
|
+
* OTel 单发 `trackEvent` 同名反向,`render_blank_canvas` 与 `render_init_failed`
|
|
20563
|
+
* 先后因此只进了 PostHog、后台零记录。现在唯一的调用点是 `logEvent` 本身,不再导出。
|
|
20564
|
+
* @param event Event name
|
|
20565
|
+
* @param level Log level
|
|
20566
|
+
* @param contents Event contents
|
|
20567
|
+
* @internal
|
|
20525
20568
|
*/
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
const userId = idManager.getUserId();
|
|
20533
|
-
await logSink.append({
|
|
20569
|
+
function trackPostHogOnly(event, level = "info", contents = {}) {
|
|
20570
|
+
if (!isPostHogEnabled()) return;
|
|
20571
|
+
const instance = getSdkPosthogInstance();
|
|
20572
|
+
if (!instance || !clockSync.isReady()) {
|
|
20573
|
+
eventQueue.push({
|
|
20574
|
+
event,
|
|
20534
20575
|
level,
|
|
20535
|
-
|
|
20536
|
-
|
|
20537
|
-
extra: {
|
|
20538
|
-
...extra,
|
|
20539
|
-
_category: "Web",
|
|
20540
|
-
sdk_version: version || "",
|
|
20541
|
-
app_id: appId || "",
|
|
20542
|
-
user_id: userId || "",
|
|
20543
|
-
platform: "Web",
|
|
20544
|
-
framework: "",
|
|
20545
|
-
framework_version: ""
|
|
20546
|
-
}
|
|
20576
|
+
contents,
|
|
20577
|
+
enqueueMono: performance.now()
|
|
20547
20578
|
});
|
|
20548
|
-
|
|
20579
|
+
return;
|
|
20580
|
+
}
|
|
20581
|
+
sdkPosthogInstance = instance;
|
|
20582
|
+
try {
|
|
20583
|
+
const properties = {
|
|
20584
|
+
...getCommonFields(),
|
|
20585
|
+
level,
|
|
20586
|
+
service_module: "sdk",
|
|
20587
|
+
...timestampFields(),
|
|
20588
|
+
...resolveMarks(contents)
|
|
20589
|
+
};
|
|
20590
|
+
sdkPosthogInstance.capture(event, properties);
|
|
20591
|
+
} catch (error) {
|
|
20592
|
+
logger.warn("[PostHog] Failed to track event:", error instanceof Error ? error.message : String(error));
|
|
20593
|
+
}
|
|
20549
20594
|
}
|
|
20550
20595
|
/**
|
|
20551
|
-
*
|
|
20596
|
+
* Cleanup PostHog (flush remaining events)
|
|
20597
|
+
* @internal
|
|
20552
20598
|
*/
|
|
20553
|
-
function
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
if (
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
if (currentLogLevel === LogLevel.warning || currentLogLevel === LogLevel.all) baseLogger.warn(formatConsoleMessage(message), ...args);
|
|
20572
|
-
},
|
|
20573
|
-
error: (message, ...args) => {
|
|
20574
|
-
const fullMessage = message + (args.length > 0 ? " " + args.map((a) => a instanceof Error ? a.message : typeof a === "string" ? a : JSON.stringify(a)).join(" ") : "");
|
|
20575
|
-
writeToLocalLog(fullMessage, "error", {}).catch(() => {});
|
|
20576
|
-
if (currentLogLevel !== LogLevel.off) baseLogger.error(formatConsoleMessage(fullMessage));
|
|
20599
|
+
function cleanupPostHog() {
|
|
20600
|
+
if (!isInitialized$1) return;
|
|
20601
|
+
try {
|
|
20602
|
+
const instance = getSdkPosthogInstance();
|
|
20603
|
+
if (!instance) {
|
|
20604
|
+
isInitialized$1 = false;
|
|
20605
|
+
return;
|
|
20606
|
+
}
|
|
20607
|
+
sdkPosthogInstance = instance;
|
|
20608
|
+
if (typeof instance.shutdown === "function") instance.shutdown();
|
|
20609
|
+
else if (typeof instance.flush === "function") instance.flush();
|
|
20610
|
+
isInitialized$1 = false;
|
|
20611
|
+
sdkPosthogInstance = null;
|
|
20612
|
+
logger.log("[PostHog] Cleaned up");
|
|
20613
|
+
} catch (error) {
|
|
20614
|
+
logger.warn("[PostHog] Failed to cleanup:", error instanceof Error ? error.message : String(error));
|
|
20615
|
+
isInitialized$1 = false;
|
|
20616
|
+
sdkPosthogInstance = null;
|
|
20577
20617
|
}
|
|
20618
|
+
}
|
|
20619
|
+
var TELEMETRY_LOG_METHOD = {
|
|
20620
|
+
debug: "log",
|
|
20621
|
+
info: "log",
|
|
20622
|
+
warning: "warn",
|
|
20623
|
+
error: "error"
|
|
20578
20624
|
};
|
|
20625
|
+
/** 组装上报 context(附加 session_token 后缀)并写一条本地 console。两条上报通道共用。 */
|
|
20626
|
+
function buildTelemetryContext(event, level, contents) {
|
|
20627
|
+
const sessionToken = idManager.getSessionToken() ?? "";
|
|
20628
|
+
const context = {
|
|
20629
|
+
session_token_suffix: sessionToken ? sessionToken.slice(-8) : "",
|
|
20630
|
+
connection_id: idManager.getConnectionId() ?? "",
|
|
20631
|
+
...clientContextFields(),
|
|
20632
|
+
...contents
|
|
20633
|
+
};
|
|
20634
|
+
const logMethod = TELEMETRY_LOG_METHOD[level] ?? "log";
|
|
20635
|
+
const propsDescription = Object.entries(contents).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
20636
|
+
logger[logMethod](`[Telemetry] ${event} [${propsDescription}]`);
|
|
20637
|
+
return context;
|
|
20638
|
+
}
|
|
20639
|
+
/** 通过 OTel 上报(独立通道,失败隔离,不影响 PostHog)。 */
|
|
20640
|
+
function emitToOtel(event, level, context) {
|
|
20641
|
+
try {
|
|
20642
|
+
trackEventOtel(event, level, context);
|
|
20643
|
+
} catch (error) {
|
|
20644
|
+
logger.warn("[OTel] Dual-send failed:", error instanceof Error ? error.message : String(error));
|
|
20645
|
+
}
|
|
20646
|
+
}
|
|
20579
20647
|
/**
|
|
20580
|
-
*
|
|
20581
|
-
*
|
|
20582
|
-
* @
|
|
20648
|
+
* 日志上报接口:同时发往 PostHog 与 OTel。
|
|
20649
|
+
* 用于 SDK 内部**日志类**事件(失败、状态变更、生命周期等)。
|
|
20650
|
+
* 指标类(延迟/帧率/时长/次数等度量)请用 {@link logMetric}——只发 OTel。
|
|
20651
|
+
* @param event Event name
|
|
20652
|
+
* @param level Log level
|
|
20653
|
+
* @param contents Log contents
|
|
20583
20654
|
* @internal
|
|
20584
20655
|
*/
|
|
20585
|
-
function
|
|
20586
|
-
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
break;
|
|
20594
|
-
case LogLevel.warning:
|
|
20595
|
-
setGlobalLogLevel(LogLevel$1.Warning);
|
|
20596
|
-
break;
|
|
20597
|
-
case LogLevel.all:
|
|
20598
|
-
setGlobalLogLevel(LogLevel$1.Debug);
|
|
20599
|
-
break;
|
|
20600
|
-
default:
|
|
20601
|
-
setGlobalLogLevel(LogLevel$1.Warning);
|
|
20602
|
-
break;
|
|
20656
|
+
function logEvent(event, level = "info", contents = {}, opts = {}) {
|
|
20657
|
+
if (opts.rateLimit) {
|
|
20658
|
+
const suppressed = admitRateLimited(rateLimitKey(event, contents));
|
|
20659
|
+
if (suppressed === null) return;
|
|
20660
|
+
if (suppressed > 0) contents = {
|
|
20661
|
+
...contents,
|
|
20662
|
+
suppressed_count: suppressed
|
|
20663
|
+
};
|
|
20603
20664
|
}
|
|
20665
|
+
const context = buildTelemetryContext(event, level, contents);
|
|
20666
|
+
trackPostHogOnly(event, level, context);
|
|
20667
|
+
emitToOtel(event, level, context);
|
|
20668
|
+
}
|
|
20669
|
+
/**
|
|
20670
|
+
* 指标上报:**只发** OTel Metric 信号(Histogram,供聚合 P95/均值/SLO)。
|
|
20671
|
+
* 不发 PostHog,也不再附带一条 log。
|
|
20672
|
+
*
|
|
20673
|
+
* 曾经每条 metric 都双发一条同名 log。那条 log 里 95% 的字节是每条都重复的
|
|
20674
|
+
* resource 字段(一条约 1KB,有效载荷只有事件名 + value 约 50 字节),而数值本身
|
|
20675
|
+
* 已经在 metric 信号里,聚合查询走的也是 metric——log 那一路纯属冗余。一轮播放
|
|
20676
|
+
* 12 条 metric 就多出 12KB 重复数据。
|
|
20677
|
+
*
|
|
20678
|
+
* **需要高基数明细(avatar_id / conversation_id / trace_id 等)时,在调用点自己
|
|
20679
|
+
* 再发一条 {@link logMetricDetail}**——显式一行,比让每条 metric 都默认背一条 log 便宜得多。
|
|
20680
|
+
*
|
|
20681
|
+
* - `value` 被记录到 Histogram(延迟 ms / 帧率 / 时长 / 次数 / 比率等)
|
|
20682
|
+
* - `labels` 低基数标签(dsm / resolution / is_fallback / renderer_backend 等)。
|
|
20683
|
+
* **不要**放高基数字段,会撑爆 metric 基数。
|
|
20684
|
+
*
|
|
20685
|
+
* @param name Metric 名(如 `fetch_avatar_latency`)
|
|
20686
|
+
* @param value 被聚合的数值
|
|
20687
|
+
* @param labels 低基数标签
|
|
20688
|
+
* @internal
|
|
20689
|
+
*/
|
|
20690
|
+
function logMetric(name, value, labels = {}) {
|
|
20691
|
+
recordMetric(name, value, labels);
|
|
20604
20692
|
}
|
|
20605
20693
|
/**
|
|
20606
|
-
*
|
|
20694
|
+
* 一条 metric 的高基数明细,作为独立 log 发出(**只走 OTel,不发 PostHog**)。
|
|
20695
|
+
*
|
|
20696
|
+
* 事件名与对应 metric 同名——看板按名查;`props` 里放同一份低基数 labels +
|
|
20697
|
+
* 高基数明细,与 {@link logMetric} 曾经自带的那条记录一致。
|
|
20698
|
+
*
|
|
20699
|
+
* 只在真的有 metric 装不下的字段(avatar_id / conversation_id / trace_id /
|
|
20700
|
+
* description 等)时才调;没有明细就别发,那种 log 是 metric 的逐字重复。
|
|
20701
|
+
*
|
|
20702
|
+
* 与 android `SPLogger.logMetricDetail` / iOS `logMetricDetail` 同形。
|
|
20703
|
+
* @internal
|
|
20607
20704
|
*/
|
|
20608
|
-
function
|
|
20609
|
-
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
|
|
20613
|
-
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : ""
|
|
20614
|
-
});
|
|
20705
|
+
function logMetricDetail(name, value, props = {}) {
|
|
20706
|
+
emitToOtel(name, "info", buildTelemetryContext(name, "info", {
|
|
20707
|
+
value,
|
|
20708
|
+
...props
|
|
20709
|
+
}));
|
|
20615
20710
|
}
|
|
20616
20711
|
//#endregion
|
|
20617
20712
|
//#region node_modules/.pnpm/@bufbuild+protobuf@2.10.1/node_modules/@bufbuild/protobuf/dist/esm/wire/varint.js
|
|
@@ -24593,4 +24688,4 @@ function cleanupOtelTrace() {
|
|
|
24593
24688
|
}
|
|
24594
24689
|
}
|
|
24595
24690
|
//#endregion
|
|
24596
|
-
export { DEFAULT_REGION as $,
|
|
24691
|
+
export { DEFAULT_REGION as $, fetchBootstrap as A, OTEL_STREAM_NAME as B, cleanupOtel as C, monoTimestamp as D, monoMarkFrom as E, recordMetric as F, generateTraceId as G, logger as H, getSdkPackage as I, AvatarError as J, logSink as K, resolveSdkVersion as L, cleanupOtelMetrics as M, initializeOtelMetrics as N, resolveMarks as O, recordHttpClientDuration as P, DEFAULT_OPUS_BITRATE as Q, setSdkIdentity as R, updatePostHogPersonPropertiesForFlags as S, trackEventOtel as T, setLogLevel as U, isDebugMode as V, idManager as W, ConnectionState as X, AvatarState as Y, ConversationState as Z, getLogsFeatureFlag as _, startLoadTrace as a, LogLevel as at, logMetric as b, AudioFormat as c, TransitionType as ct, MessageType as d, __commonJSMin as dt, DEFAULT_REGION_REQUEST as et, TransportCompression as f, __toESM as ft, clientContextFields as g, cleanupPostHog as h, startInitTrace as i, LoadProgress as it, hostOf as j, clockSync as k, EgressType as l, requireSDK as lt, BinaryWriter as m, initializeOtelTrace as n, ErrorCode as nt, startPlaybackTrace as o, RENDER_QUALITY_PARAMS as ot, BinaryReader as p, AnimationType as q, startConnectTrace as r, FrameStarvationMode as rt, buildTraceparent as s, RenderQuality as st, cleanupOtelTrace as t, DrivingServiceMode as tt, Message as u, setSDKRef as ut, initializePostHog as v, initializeOtel as w, logMetricDetail as x, logEvent as y, OTEL_LOGS_ENDPOINT as z };
|