@yunzhimao/telemetry 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/cjs/browser.js +3 -2
- package/cjs/index.d.ts +1 -1
- package/cjs/index.js +3 -2
- package/cjs/uuid.d.ts +2 -0
- package/cjs/uuid.js +17 -0
- package/dist/browser.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/uuid.d.ts +2 -0
- package/dist/uuid.js +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @yunzhimao/telemetry
|
|
2
2
|
|
|
3
|
-
通用行为采集 SDK
|
|
3
|
+
通用行为采集 SDK,源码统一维护在 CRM 仓库的 `packages/telemetry-sdk`,宿主安装 npm 精确版本。只采集显式行为,不自动捕获 DOM、URL、请求内容或异常。
|
|
4
4
|
|
|
5
5
|
## 浏览器
|
|
6
6
|
|
|
@@ -47,13 +47,13 @@ void tracker.flush();
|
|
|
47
47
|
- Token 只在内存中,不进入离线队列;事件重试保持 eventId 和原始时间。安装标识不是设备指纹,行为不是可信业务结算依据。
|
|
48
48
|
- `track()` 不等待网络且不向业务抛采集错误;存储失败退回内存。`onDiagnostic(code, count)` 不含原始事件或 Token。
|
|
49
49
|
- `destroy()` 卸载监听/定时器并保留队列;退出登录必须先 `reset()` 清队列。关页发送仅尽力而为,关闭标签页的未发送快照不承诺由其他标签页补发,过期后清理。
|
|
50
|
-
-
|
|
50
|
+
- 默认入口依赖现代运行时的 fetch、TextEncoder 和安全随机能力;`0.2.1` 起优先使用 crypto.randomUUID,HTTP 环境缺少该方法时使用 crypto.getRandomValues 生成标准 UUID v4,不使用 Math.random。两者都不可用时初始化报 secure_random_unavailable;浏览器子路径要求 DOM,IndexedDB 不可用时降级内存,插件 storage 由调用方注入;无 Vue/Pinia/CRM 商业依赖。此兼容不替代生产环境的 HTTPS 传输保护。
|
|
51
51
|
|
|
52
52
|
## 验证与发布
|
|
53
53
|
|
|
54
54
|
在仓库根目录运行 `pnpm --filter @yunzhimao/telemetry test`。`pnpm --filter @yunzhimao/telemetry pack --pack-destination .tmp` 只生成本地包,不发布。包内容限定为 ESM dist、CommonJS cjs、类型声明和本说明。
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
发布时同时更新 package.json 与 src/index.ts 的版本号,测试通过后进入 `packages/telemetry-sdk` 执行 `npm publish --access public --registry=https://registry.npmjs.org`。宿主使用新版本前需更新依赖、重新构建和部署;仅发布 npm 不会更新已部署页面。不要删除源码目录,也不要把私钥、环境配置、内部服务端包或其他仓库打入包。
|
|
57
57
|
|
|
58
58
|
## ERP 后端 / Node
|
|
59
59
|
|
package/cjs/browser.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.browserDevice = browserDevice;
|
|
|
4
4
|
exports.indexedDbStorage = indexedDbStorage;
|
|
5
5
|
exports.createBrowserTracker = createBrowserTracker;
|
|
6
6
|
const index_js_1 = require("./index.js");
|
|
7
|
+
const uuid_js_1 = require("./uuid.js");
|
|
7
8
|
/** 只采集基础设备属性;不读取 URL、DOM 内容、Token 或指纹。 */
|
|
8
9
|
function browserDevice() {
|
|
9
10
|
if (typeof navigator === 'undefined')
|
|
@@ -22,7 +23,7 @@ function browserDevice() {
|
|
|
22
23
|
}
|
|
23
24
|
function indexedDbStorage(namespace = 'yzm-telemetry') {
|
|
24
25
|
// 每个标签页独立队列,避免多个页面读改写同一个快照导致相互覆盖。
|
|
25
|
-
let tab =
|
|
26
|
+
let tab = (0, uuid_js_1.randomUuid)();
|
|
26
27
|
try {
|
|
27
28
|
const tabKey = `${namespace}:tab`;
|
|
28
29
|
tab = sessionStorage.getItem(tabKey) ?? tab;
|
|
@@ -78,7 +79,7 @@ function indexedDbStorage(namespace = 'yzm-telemetry') {
|
|
|
78
79
|
function createBrowserTracker(options) {
|
|
79
80
|
let anonymousId = options.anonymousId;
|
|
80
81
|
try {
|
|
81
|
-
anonymousId ??= localStorage.getItem('yzm-telemetry:installation') ??
|
|
82
|
+
anonymousId ??= localStorage.getItem('yzm-telemetry:installation') ?? (0, uuid_js_1.randomUuid)();
|
|
82
83
|
localStorage.setItem('yzm-telemetry:installation', anonymousId);
|
|
83
84
|
}
|
|
84
85
|
catch { /* 不支持持久化时使用本次会话随机标识。 */ }
|
package/cjs/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EventName, TelemetryIdentity, TrackInput, TrackerOptions } from './types.js';
|
|
2
2
|
export type { BehaviorEvent, DeviceInfo, EventName, QueueStorage, SourceApp, StoredQueue, TelemetryIdentity, TokenSession, TrackInput, TrackerOptions } from './types.js';
|
|
3
|
-
export declare const SDK_VERSION = "0.2.
|
|
3
|
+
export declare const SDK_VERSION = "0.2.1";
|
|
4
4
|
export declare const identityKey: (identity: TelemetryIdentity) => string;
|
|
5
5
|
/** 与宿主登录解耦;先绑定身份,异步请求只负责采集授权,不能改变队列归属。 */
|
|
6
6
|
export declare function createTracker(options: TrackerOptions): {
|
package/cjs/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.identityKey = exports.SDK_VERSION = void 0;
|
|
4
4
|
exports.createTracker = createTracker;
|
|
5
|
-
|
|
5
|
+
const uuid_js_1 = require("./uuid.js");
|
|
6
|
+
exports.SDK_VERSION = '0.2.1';
|
|
6
7
|
/** 至少“领域.行为.动作”三段;活跃为协议保留名,不再接受旧通用事件名。 */
|
|
7
8
|
const validEventName = (name) => typeof name === 'string' &&
|
|
8
9
|
name.length <= 100 && (name === 'app.active' || /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*){2,}$/.test(name));
|
|
@@ -13,7 +14,7 @@ exports.identityKey = identityKey;
|
|
|
13
14
|
/** 与宿主登录解耦;先绑定身份,异步请求只负责采集授权,不能改变队列归属。 */
|
|
14
15
|
function createTracker(options) {
|
|
15
16
|
const now = options.now ?? Date.now;
|
|
16
|
-
const randomId = options.randomId ??
|
|
17
|
+
const randomId = options.randomId ?? uuid_js_1.randomUuid;
|
|
17
18
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
18
19
|
const anonymousId = options.anonymousId ?? randomId();
|
|
19
20
|
const sessionId = randomId();
|
package/cjs/uuid.d.ts
ADDED
package/cjs/uuid.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.randomUuid = randomUuid;
|
|
4
|
+
/** HTTPS 优先使用原生 UUID;HTTP 环境用安全随机字节兼容,不降级为 Math.random。 */
|
|
5
|
+
function randomUuid() {
|
|
6
|
+
const crypto = globalThis.crypto;
|
|
7
|
+
if (typeof crypto?.randomUUID === 'function')
|
|
8
|
+
return crypto.randomUUID();
|
|
9
|
+
if (typeof crypto?.getRandomValues !== 'function')
|
|
10
|
+
throw new Error('secure_random_unavailable');
|
|
11
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
12
|
+
// UUID v4 的版本位固定为 0100,变体位固定为 10,其他位保留随机性。
|
|
13
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
14
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
15
|
+
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
16
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
17
|
+
}
|
package/dist/browser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createTracker } from './index.js';
|
|
2
|
+
import { randomUuid } from './uuid.js';
|
|
2
3
|
/** 只采集基础设备属性;不读取 URL、DOM 内容、Token 或指纹。 */
|
|
3
4
|
export function browserDevice() {
|
|
4
5
|
if (typeof navigator === 'undefined')
|
|
@@ -17,7 +18,7 @@ export function browserDevice() {
|
|
|
17
18
|
}
|
|
18
19
|
export function indexedDbStorage(namespace = 'yzm-telemetry') {
|
|
19
20
|
// 每个标签页独立队列,避免多个页面读改写同一个快照导致相互覆盖。
|
|
20
|
-
let tab =
|
|
21
|
+
let tab = randomUuid();
|
|
21
22
|
try {
|
|
22
23
|
const tabKey = `${namespace}:tab`;
|
|
23
24
|
tab = sessionStorage.getItem(tabKey) ?? tab;
|
|
@@ -73,7 +74,7 @@ export function indexedDbStorage(namespace = 'yzm-telemetry') {
|
|
|
73
74
|
export function createBrowserTracker(options) {
|
|
74
75
|
let anonymousId = options.anonymousId;
|
|
75
76
|
try {
|
|
76
|
-
anonymousId ??= localStorage.getItem('yzm-telemetry:installation') ??
|
|
77
|
+
anonymousId ??= localStorage.getItem('yzm-telemetry:installation') ?? randomUuid();
|
|
77
78
|
localStorage.setItem('yzm-telemetry:installation', anonymousId);
|
|
78
79
|
}
|
|
79
80
|
catch { /* 不支持持久化时使用本次会话随机标识。 */ }
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EventName, TelemetryIdentity, TrackInput, TrackerOptions } from './types.js';
|
|
2
2
|
export type { BehaviorEvent, DeviceInfo, EventName, QueueStorage, SourceApp, StoredQueue, TelemetryIdentity, TokenSession, TrackInput, TrackerOptions } from './types.js';
|
|
3
|
-
export declare const SDK_VERSION = "0.2.
|
|
3
|
+
export declare const SDK_VERSION = "0.2.1";
|
|
4
4
|
export declare const identityKey: (identity: TelemetryIdentity) => string;
|
|
5
5
|
/** 与宿主登录解耦;先绑定身份,异步请求只负责采集授权,不能改变队列归属。 */
|
|
6
6
|
export declare function createTracker(options: TrackerOptions): {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { randomUuid } from './uuid.js';
|
|
2
|
+
export const SDK_VERSION = '0.2.1';
|
|
2
3
|
/** 至少“领域.行为.动作”三段;活跃为协议保留名,不再接受旧通用事件名。 */
|
|
3
4
|
const validEventName = (name) => typeof name === 'string' &&
|
|
4
5
|
name.length <= 100 && (name === 'app.active' || /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*){2,}$/.test(name));
|
|
@@ -8,7 +9,7 @@ export const identityKey = (identity) => JSON.stringify([
|
|
|
8
9
|
/** 与宿主登录解耦;先绑定身份,异步请求只负责采集授权,不能改变队列归属。 */
|
|
9
10
|
export function createTracker(options) {
|
|
10
11
|
const now = options.now ?? Date.now;
|
|
11
|
-
const randomId = options.randomId ??
|
|
12
|
+
const randomId = options.randomId ?? randomUuid;
|
|
12
13
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
13
14
|
const anonymousId = options.anonymousId ?? randomId();
|
|
14
15
|
const sessionId = randomId();
|
package/dist/uuid.d.ts
ADDED
package/dist/uuid.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** HTTPS 优先使用原生 UUID;HTTP 环境用安全随机字节兼容,不降级为 Math.random。 */
|
|
2
|
+
export function randomUuid() {
|
|
3
|
+
const crypto = globalThis.crypto;
|
|
4
|
+
if (typeof crypto?.randomUUID === 'function')
|
|
5
|
+
return crypto.randomUUID();
|
|
6
|
+
if (typeof crypto?.getRandomValues !== 'function')
|
|
7
|
+
throw new Error('secure_random_unavailable');
|
|
8
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
9
|
+
// UUID v4 的版本位固定为 0100,变体位固定为 10,其他位保留随机性。
|
|
10
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
11
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
12
|
+
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
13
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
14
|
+
}
|