@yuanze_dev/tracker 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autocapture.js +8 -8
- package/dist/context.js +8 -8
- package/dist/events.d.ts +8 -8
- package/dist/node.js +1 -1
- package/dist/react.js +3 -3
- package/dist/tracker.d.ts +5 -0
- package/dist/tracker.js +33 -6
- package/package.json +1 -1
package/dist/autocapture.js
CHANGED
|
@@ -39,9 +39,9 @@ export function setupAutocapture(tracker, options = {}) {
|
|
|
39
39
|
return;
|
|
40
40
|
const { captureClicks = true, allow = DEFAULT_ALLOW, deny = DEFAULT_DENY, sampleRate = 1, maxTextLength = 64, } = options;
|
|
41
41
|
const sendPageview = () => tracker.track("$pageview", {
|
|
42
|
-
url: redactUrl(location.pathname + location.search),
|
|
43
|
-
title: document.title,
|
|
44
|
-
referrer: redactUrl(document.referrer),
|
|
42
|
+
$url: redactUrl(location.pathname + location.search),
|
|
43
|
+
$title: document.title,
|
|
44
|
+
$referrer: redactUrl(document.referrer),
|
|
45
45
|
});
|
|
46
46
|
sendPageview();
|
|
47
47
|
const patch = (key) => {
|
|
@@ -82,16 +82,16 @@ export function setupAutocapture(tracker, options = {}) {
|
|
|
82
82
|
if (sampleRate < 1 && Math.random() >= sampleRate)
|
|
83
83
|
return;
|
|
84
84
|
const props = {
|
|
85
|
-
tag: el.tagName.toLowerCase(),
|
|
86
|
-
selector: elementSelector(el),
|
|
87
|
-
url: location.pathname,
|
|
85
|
+
$tag: el.tagName.toLowerCase(),
|
|
86
|
+
$selector: elementSelector(el),
|
|
87
|
+
$url: location.pathname,
|
|
88
88
|
};
|
|
89
89
|
const text = sanitizeText(el.textContent || "", maxTextLength);
|
|
90
90
|
if (text)
|
|
91
|
-
props
|
|
91
|
+
props.$text = text;
|
|
92
92
|
const href = el.getAttribute("href");
|
|
93
93
|
if (href && !href.startsWith("javascript:"))
|
|
94
|
-
props
|
|
94
|
+
props.$href = redactUrl(href);
|
|
95
95
|
tracker.trackUnsafe("$autocapture", props);
|
|
96
96
|
}, true);
|
|
97
97
|
}
|
package/dist/context.js
CHANGED
|
@@ -38,18 +38,18 @@ export function collectEnvContext() {
|
|
|
38
38
|
if (typeof navigator === "undefined")
|
|
39
39
|
return ctx;
|
|
40
40
|
const ua = navigator.userAgent || "";
|
|
41
|
-
ctx
|
|
42
|
-
ctx
|
|
41
|
+
ctx["$os"] = detectOS(ua);
|
|
42
|
+
ctx["$browser"] = detectBrowser(ua);
|
|
43
43
|
if (navigator.language)
|
|
44
|
-
ctx
|
|
44
|
+
ctx["$language"] = navigator.language;
|
|
45
45
|
if (typeof screen !== "undefined" && screen.width) {
|
|
46
|
-
ctx
|
|
47
|
-
ctx
|
|
46
|
+
ctx["$screen_width"] = screen.width;
|
|
47
|
+
ctx["$screen_height"] = screen.height;
|
|
48
48
|
}
|
|
49
49
|
if (typeof document !== "undefined" && document.referrer) {
|
|
50
|
-
ctx
|
|
50
|
+
ctx["$referrer"] = document.referrer;
|
|
51
51
|
try {
|
|
52
|
-
ctx
|
|
52
|
+
ctx["$referrer_domain"] = new URL(document.referrer).hostname;
|
|
53
53
|
}
|
|
54
54
|
catch {
|
|
55
55
|
}
|
|
@@ -60,7 +60,7 @@ export function collectEnvContext() {
|
|
|
60
60
|
for (const k of UTM_KEYS) {
|
|
61
61
|
const v = params.get(k);
|
|
62
62
|
if (v)
|
|
63
|
-
ctx[k] = v;
|
|
63
|
+
ctx[`$${k}`] = v;
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
catch {
|
package/dist/events.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export interface SystemEvents {
|
|
2
2
|
$pageview: {
|
|
3
|
-
url: string;
|
|
4
|
-
title?: string;
|
|
5
|
-
referrer?: string;
|
|
3
|
+
$url: string;
|
|
4
|
+
$title?: string;
|
|
5
|
+
$referrer?: string;
|
|
6
6
|
};
|
|
7
7
|
$autocapture: {
|
|
8
|
-
tag: string;
|
|
9
|
-
selector: string;
|
|
10
|
-
url: string;
|
|
11
|
-
text?: string;
|
|
12
|
-
href?: string;
|
|
8
|
+
$tag: string;
|
|
9
|
+
$selector: string;
|
|
10
|
+
$url: string;
|
|
11
|
+
$text?: string;
|
|
12
|
+
$href?: string;
|
|
13
13
|
};
|
|
14
14
|
$experiment_exposure: {
|
|
15
15
|
experiment: string;
|
package/dist/node.js
CHANGED
|
@@ -13,7 +13,7 @@ export function machineDistinctId(salt = "yuanze-tracker") {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
export function nodeEnvContext() {
|
|
16
|
-
return { os: platform(), runtime: `node/${process.versions.node}` };
|
|
16
|
+
return { $os: platform(), $runtime: `node/${process.versions.node}` };
|
|
17
17
|
}
|
|
18
18
|
export function createTracker(options) {
|
|
19
19
|
const { envContext = true, flushOnExit = true, ...rest } = options;
|
package/dist/react.js
CHANGED
|
@@ -49,9 +49,9 @@ export function useRouteTracking(path) {
|
|
|
49
49
|
return;
|
|
50
50
|
last.current = path;
|
|
51
51
|
tracker.track("$pageview", {
|
|
52
|
-
url: path,
|
|
53
|
-
title: typeof document !== "undefined" ? document.title : undefined,
|
|
54
|
-
referrer: typeof document !== "undefined" ? document.referrer : undefined,
|
|
52
|
+
$url: path,
|
|
53
|
+
$title: typeof document !== "undefined" ? document.title : undefined,
|
|
54
|
+
$referrer: typeof document !== "undefined" ? document.referrer : undefined,
|
|
55
55
|
});
|
|
56
56
|
}, [tracker, path]);
|
|
57
57
|
}
|
package/dist/tracker.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type TrackerOptions = {
|
|
|
10
10
|
flushMs?: number;
|
|
11
11
|
autocapture?: boolean;
|
|
12
12
|
sender?: Sender;
|
|
13
|
+
maxQueueSize?: number;
|
|
13
14
|
};
|
|
14
15
|
export declare class Tracker {
|
|
15
16
|
private queue;
|
|
@@ -23,6 +24,9 @@ export declare class Tracker {
|
|
|
23
24
|
readonly appVersion?: string;
|
|
24
25
|
readonly maxBatch: number;
|
|
25
26
|
readonly flushMs: number;
|
|
27
|
+
readonly maxQueueSize: number;
|
|
28
|
+
private flushing;
|
|
29
|
+
private retryCount;
|
|
26
30
|
constructor(opts: TrackerOptions);
|
|
27
31
|
private anonId;
|
|
28
32
|
register(props: Record<string, unknown>): void;
|
|
@@ -32,6 +36,7 @@ export declare class Tracker {
|
|
|
32
36
|
track<K extends EventName>(name: K, properties?: EventProps<K>): void;
|
|
33
37
|
trackUnsafe(name: string, properties?: Record<string, unknown>): void;
|
|
34
38
|
private enqueue;
|
|
39
|
+
private trim;
|
|
35
40
|
private schedule;
|
|
36
41
|
flush(unloading?: boolean): Promise<void>;
|
|
37
42
|
}
|
package/dist/tracker.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const MAX_BATCH_SIZE = 50;
|
|
2
|
+
const MAX_BACKOFF_MS = 30_000;
|
|
1
3
|
function uuid() {
|
|
2
4
|
return globalThis.crypto.randomUUID();
|
|
3
5
|
}
|
|
@@ -13,6 +15,9 @@ export class Tracker {
|
|
|
13
15
|
appVersion;
|
|
14
16
|
maxBatch;
|
|
15
17
|
flushMs;
|
|
18
|
+
maxQueueSize;
|
|
19
|
+
flushing = false;
|
|
20
|
+
retryCount = 0;
|
|
16
21
|
constructor(opts) {
|
|
17
22
|
this.endpoint = opts.endpoint;
|
|
18
23
|
this.writeKey = opts.writeKey;
|
|
@@ -20,6 +25,7 @@ export class Tracker {
|
|
|
20
25
|
this.appVersion = opts.appVersion;
|
|
21
26
|
this.maxBatch = opts.maxBatch ?? 5;
|
|
22
27
|
this.flushMs = opts.flushMs ?? 5000;
|
|
28
|
+
this.maxQueueSize = opts.maxQueueSize ?? 500;
|
|
23
29
|
this.send = opts.sender ?? defaultFetchSender;
|
|
24
30
|
this.distinctId = opts.distinctId || this.anonId();
|
|
25
31
|
if (typeof document !== "undefined") {
|
|
@@ -80,34 +86,55 @@ export class Tracker {
|
|
|
80
86
|
clientTime: new Date().toISOString(),
|
|
81
87
|
properties: { ...this.superProps, ...properties },
|
|
82
88
|
});
|
|
89
|
+
this.trim();
|
|
83
90
|
if (this.queue.length >= this.maxBatch)
|
|
84
91
|
this.flush();
|
|
85
92
|
else
|
|
86
93
|
this.schedule();
|
|
87
94
|
}
|
|
88
|
-
|
|
95
|
+
trim() {
|
|
96
|
+
const overflow = this.queue.length - this.maxQueueSize;
|
|
97
|
+
if (overflow > 0) {
|
|
98
|
+
this.queue.splice(0, overflow);
|
|
99
|
+
console.warn(`[tracker] 队列超过 ${this.maxQueueSize} 条,丢弃最旧的 ${overflow} 条`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
schedule(delayMs = this.flushMs) {
|
|
89
103
|
if (this.timer)
|
|
90
104
|
return;
|
|
91
|
-
this.timer = setTimeout(() => this.flush(),
|
|
105
|
+
this.timer = setTimeout(() => this.flush(), delayMs);
|
|
92
106
|
}
|
|
93
107
|
async flush(unloading = false) {
|
|
94
108
|
if (this.timer) {
|
|
95
109
|
clearTimeout(this.timer);
|
|
96
110
|
this.timer = null;
|
|
97
111
|
}
|
|
98
|
-
if (this.queue.length === 0)
|
|
112
|
+
if (this.queue.length === 0 || (this.flushing && !unloading))
|
|
99
113
|
return;
|
|
100
|
-
|
|
114
|
+
this.flushing = true;
|
|
115
|
+
const batch = this.queue.splice(0, Math.min(MAX_BATCH_SIZE, this.queue.length));
|
|
101
116
|
const body = JSON.stringify({ events: batch });
|
|
102
117
|
const headers = { "Content-Type": "application/json", "x-write-key": this.writeKey };
|
|
103
118
|
try {
|
|
104
119
|
await this.send(this.endpoint, body, headers, unloading);
|
|
120
|
+
this.retryCount = 0;
|
|
105
121
|
}
|
|
106
122
|
catch (err) {
|
|
107
|
-
if (err?.retryable !== false)
|
|
123
|
+
if (err?.retryable !== false) {
|
|
108
124
|
this.queue.unshift(...batch);
|
|
109
|
-
|
|
125
|
+
this.trim();
|
|
126
|
+
this.retryCount += 1;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
110
129
|
console.warn("[tracker] 丢弃一批(不可重试):", err?.status);
|
|
130
|
+
this.retryCount = 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
this.flushing = false;
|
|
135
|
+
}
|
|
136
|
+
if (this.queue.length > 0 && !unloading) {
|
|
137
|
+
this.schedule(Math.min(this.flushMs * 2 ** this.retryCount, MAX_BACKOFF_MS));
|
|
111
138
|
}
|
|
112
139
|
}
|
|
113
140
|
}
|