@traffical/js-client 0.1.2
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 +209 -0
- package/dist/client.d.ts +167 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +429 -0
- package/dist/client.js.map +1 -0
- package/dist/error-boundary.d.ts +47 -0
- package/dist/error-boundary.d.ts.map +1 -0
- package/dist/error-boundary.js +118 -0
- package/dist/error-boundary.js.map +1 -0
- package/dist/event-logger.d.ts +75 -0
- package/dist/event-logger.d.ts.map +1 -0
- package/dist/event-logger.js +186 -0
- package/dist/event-logger.js.map +1 -0
- package/dist/exposure-dedup.d.ts +49 -0
- package/dist/exposure-dedup.d.ts.map +1 -0
- package/dist/exposure-dedup.js +94 -0
- package/dist/exposure-dedup.js.map +1 -0
- package/dist/global.d.ts +38 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/global.js +67 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/decision-tracking.d.ts +68 -0
- package/dist/plugins/decision-tracking.d.ts.map +1 -0
- package/dist/plugins/decision-tracking.js +83 -0
- package/dist/plugins/decision-tracking.js.map +1 -0
- package/dist/plugins/dom-binding.d.ts +48 -0
- package/dist/plugins/dom-binding.d.ts.map +1 -0
- package/dist/plugins/dom-binding.js +211 -0
- package/dist/plugins/dom-binding.js.map +1 -0
- package/dist/plugins/index.d.ts +70 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +194 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +62 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +7 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/stable-id.d.ts +44 -0
- package/dist/stable-id.d.ts.map +1 -0
- package/dist/stable-id.js +129 -0
- package/dist/stable-id.js.map +1 -0
- package/dist/storage.d.ts +41 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +144 -0
- package/dist/storage.js.map +1 -0
- package/dist/traffical.min.js +3 -0
- package/dist/traffical.min.js.map +7 -0
- package/package.json +51 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventLogger - Smart event batching with browser-specific features.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Batches events (flush every N events or M seconds)
|
|
6
|
+
* - Uses navigator.sendBeacon() on page unload
|
|
7
|
+
* - Persists failed events to localStorage
|
|
8
|
+
* - Retries failed events on next session
|
|
9
|
+
* - Visibility-aware: flushes on visibilitychange to hidden
|
|
10
|
+
*/
|
|
11
|
+
import type { TrackableEvent } from "@traffical/core";
|
|
12
|
+
import type { StorageProvider } from "./storage.js";
|
|
13
|
+
export interface EventLoggerOptions {
|
|
14
|
+
/** API endpoint for events */
|
|
15
|
+
endpoint: string;
|
|
16
|
+
/** API key for authentication */
|
|
17
|
+
apiKey: string;
|
|
18
|
+
/** Storage provider for failed events */
|
|
19
|
+
storage: StorageProvider;
|
|
20
|
+
/** Max events before auto-flush (default: 10) */
|
|
21
|
+
batchSize?: number;
|
|
22
|
+
/** Auto-flush interval in ms (default: 30000) */
|
|
23
|
+
flushIntervalMs?: number;
|
|
24
|
+
/** Callback on flush error */
|
|
25
|
+
onError?: (error: Error) => void;
|
|
26
|
+
}
|
|
27
|
+
export declare class EventLogger {
|
|
28
|
+
private _endpoint;
|
|
29
|
+
private _apiKey;
|
|
30
|
+
private _storage;
|
|
31
|
+
private _batchSize;
|
|
32
|
+
private _flushIntervalMs;
|
|
33
|
+
private _onError?;
|
|
34
|
+
private _queue;
|
|
35
|
+
private _flushTimer;
|
|
36
|
+
private _isFlushing;
|
|
37
|
+
constructor(options: EventLoggerOptions);
|
|
38
|
+
/**
|
|
39
|
+
* Log an event (added to batch queue).
|
|
40
|
+
*/
|
|
41
|
+
log(event: TrackableEvent): void;
|
|
42
|
+
/**
|
|
43
|
+
* Flush all queued events immediately.
|
|
44
|
+
*/
|
|
45
|
+
flush(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Flush using fetch with keepalive (for page unload).
|
|
48
|
+
*
|
|
49
|
+
* We use fetch with keepalive: true instead of sendBeacon because:
|
|
50
|
+
* - sendBeacon cannot send custom headers (like Authorization)
|
|
51
|
+
* - keepalive ensures the request completes even as the page unloads
|
|
52
|
+
* - Same reliability guarantees as sendBeacon
|
|
53
|
+
*
|
|
54
|
+
* Returns true if request was initiated, false otherwise.
|
|
55
|
+
*/
|
|
56
|
+
flushBeacon(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get the number of events in the queue.
|
|
59
|
+
*/
|
|
60
|
+
get queueSize(): number;
|
|
61
|
+
/**
|
|
62
|
+
* Destroy the logger (cleanup timers and listeners).
|
|
63
|
+
*/
|
|
64
|
+
destroy(): void;
|
|
65
|
+
private _sendEvents;
|
|
66
|
+
private _persistFailedEvents;
|
|
67
|
+
private _retryFailedEvents;
|
|
68
|
+
private _startFlushTimer;
|
|
69
|
+
private _setupListeners;
|
|
70
|
+
private _removeListeners;
|
|
71
|
+
private _onPageHide;
|
|
72
|
+
private _onVisibilityChange;
|
|
73
|
+
private _onBeforeUnload;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=event-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-logger.d.ts","sourceRoot":"","sources":["../src/event-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAOpD,MAAM,WAAW,kBAAkB;IACjC,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,CAAyB;IAE1C,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,kBAAkB;IAkBvC;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAShC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB5B;;;;;;;;;OASG;IACH,WAAW,IAAI,OAAO;IAgCtB;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,OAAO,IAAI,IAAI;YAQD,WAAW;IAezB,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,WAAW,CAEjB;IAEF,OAAO,CAAC,mBAAmB,CAIzB;IAEF,OAAO,CAAC,eAAe,CAErB;CACH"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventLogger - Smart event batching with browser-specific features.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Batches events (flush every N events or M seconds)
|
|
6
|
+
* - Uses navigator.sendBeacon() on page unload
|
|
7
|
+
* - Persists failed events to localStorage
|
|
8
|
+
* - Retries failed events on next session
|
|
9
|
+
* - Visibility-aware: flushes on visibilitychange to hidden
|
|
10
|
+
*/
|
|
11
|
+
const FAILED_EVENTS_KEY = "failed_events";
|
|
12
|
+
const DEFAULT_BATCH_SIZE = 10;
|
|
13
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 30000; // 30 seconds
|
|
14
|
+
const MAX_FAILED_EVENTS = 100;
|
|
15
|
+
export class EventLogger {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this._queue = [];
|
|
18
|
+
this._flushTimer = null;
|
|
19
|
+
this._isFlushing = false;
|
|
20
|
+
this._onPageHide = () => {
|
|
21
|
+
this.flushBeacon();
|
|
22
|
+
};
|
|
23
|
+
this._onVisibilityChange = () => {
|
|
24
|
+
if (document.visibilityState === "hidden") {
|
|
25
|
+
this.flushBeacon();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
this._onBeforeUnload = () => {
|
|
29
|
+
this.flushBeacon();
|
|
30
|
+
};
|
|
31
|
+
this._endpoint = options.endpoint;
|
|
32
|
+
this._apiKey = options.apiKey;
|
|
33
|
+
this._storage = options.storage;
|
|
34
|
+
this._batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
35
|
+
this._flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
36
|
+
this._onError = options.onError;
|
|
37
|
+
// Set up browser event listeners
|
|
38
|
+
this._setupListeners();
|
|
39
|
+
// Retry failed events from previous session
|
|
40
|
+
this._retryFailedEvents();
|
|
41
|
+
// Start flush timer
|
|
42
|
+
this._startFlushTimer();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Log an event (added to batch queue).
|
|
46
|
+
*/
|
|
47
|
+
log(event) {
|
|
48
|
+
this._queue.push(event);
|
|
49
|
+
// Auto-flush if batch is full
|
|
50
|
+
if (this._queue.length >= this._batchSize) {
|
|
51
|
+
this.flush();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Flush all queued events immediately.
|
|
56
|
+
*/
|
|
57
|
+
async flush() {
|
|
58
|
+
if (this._isFlushing || this._queue.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this._isFlushing = true;
|
|
62
|
+
// Take current queue
|
|
63
|
+
const events = [...this._queue];
|
|
64
|
+
this._queue = [];
|
|
65
|
+
try {
|
|
66
|
+
await this._sendEvents(events);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Persist failed events for retry
|
|
70
|
+
this._persistFailedEvents(events);
|
|
71
|
+
this._onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
this._isFlushing = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Flush using fetch with keepalive (for page unload).
|
|
79
|
+
*
|
|
80
|
+
* We use fetch with keepalive: true instead of sendBeacon because:
|
|
81
|
+
* - sendBeacon cannot send custom headers (like Authorization)
|
|
82
|
+
* - keepalive ensures the request completes even as the page unloads
|
|
83
|
+
* - Same reliability guarantees as sendBeacon
|
|
84
|
+
*
|
|
85
|
+
* Returns true if request was initiated, false otherwise.
|
|
86
|
+
*/
|
|
87
|
+
flushBeacon() {
|
|
88
|
+
if (this._queue.length === 0) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (typeof fetch === "undefined") {
|
|
92
|
+
// Fetch not available - try async flush
|
|
93
|
+
this.flush();
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const events = [...this._queue];
|
|
97
|
+
this._queue = [];
|
|
98
|
+
// Use fetch with keepalive instead of sendBeacon
|
|
99
|
+
// This allows us to include the Authorization header
|
|
100
|
+
fetch(this._endpoint, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
Authorization: `Bearer ${this._apiKey}`,
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({ events }),
|
|
107
|
+
keepalive: true,
|
|
108
|
+
}).catch(() => {
|
|
109
|
+
// Persist for retry on next session
|
|
110
|
+
this._persistFailedEvents(events);
|
|
111
|
+
});
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the number of events in the queue.
|
|
116
|
+
*/
|
|
117
|
+
get queueSize() {
|
|
118
|
+
return this._queue.length;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Destroy the logger (cleanup timers and listeners).
|
|
122
|
+
*/
|
|
123
|
+
destroy() {
|
|
124
|
+
if (this._flushTimer) {
|
|
125
|
+
clearInterval(this._flushTimer);
|
|
126
|
+
this._flushTimer = null;
|
|
127
|
+
}
|
|
128
|
+
this._removeListeners();
|
|
129
|
+
}
|
|
130
|
+
async _sendEvents(events) {
|
|
131
|
+
const response = await fetch(this._endpoint, {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: {
|
|
134
|
+
"Content-Type": "application/json",
|
|
135
|
+
Authorization: `Bearer ${this._apiKey}`,
|
|
136
|
+
},
|
|
137
|
+
body: JSON.stringify({ events }),
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
_persistFailedEvents(events) {
|
|
144
|
+
const existing = this._storage.get(FAILED_EVENTS_KEY) ?? [];
|
|
145
|
+
// Limit total stored events
|
|
146
|
+
const combined = [...existing, ...events].slice(-MAX_FAILED_EVENTS);
|
|
147
|
+
this._storage.set(FAILED_EVENTS_KEY, combined);
|
|
148
|
+
}
|
|
149
|
+
_retryFailedEvents() {
|
|
150
|
+
const failed = this._storage.get(FAILED_EVENTS_KEY);
|
|
151
|
+
if (!failed || failed.length === 0) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// Clear stored events
|
|
155
|
+
this._storage.remove(FAILED_EVENTS_KEY);
|
|
156
|
+
// Add to queue for retry
|
|
157
|
+
this._queue.push(...failed);
|
|
158
|
+
}
|
|
159
|
+
_startFlushTimer() {
|
|
160
|
+
if (this._flushIntervalMs <= 0)
|
|
161
|
+
return;
|
|
162
|
+
this._flushTimer = setInterval(() => {
|
|
163
|
+
this.flush().catch(() => {
|
|
164
|
+
// Errors handled in flush
|
|
165
|
+
});
|
|
166
|
+
}, this._flushIntervalMs);
|
|
167
|
+
}
|
|
168
|
+
_setupListeners() {
|
|
169
|
+
if (typeof window === "undefined")
|
|
170
|
+
return;
|
|
171
|
+
// Flush on page hide (covers tab close, navigation, etc.)
|
|
172
|
+
window.addEventListener("pagehide", this._onPageHide);
|
|
173
|
+
// Flush when page becomes hidden (tab switch, minimize)
|
|
174
|
+
document.addEventListener("visibilitychange", this._onVisibilityChange);
|
|
175
|
+
// Fallback: beforeunload for older browsers
|
|
176
|
+
window.addEventListener("beforeunload", this._onBeforeUnload);
|
|
177
|
+
}
|
|
178
|
+
_removeListeners() {
|
|
179
|
+
if (typeof window === "undefined")
|
|
180
|
+
return;
|
|
181
|
+
window.removeEventListener("pagehide", this._onPageHide);
|
|
182
|
+
document.removeEventListener("visibilitychange", this._onVisibilityChange);
|
|
183
|
+
window.removeEventListener("beforeunload", this._onBeforeUnload);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=event-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-logger.js","sourceRoot":"","sources":["../src/event-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExposureDeduplicator - Prevents duplicate exposure events.
|
|
3
|
+
*
|
|
4
|
+
* Same user seeing same variant should only count as 1 exposure.
|
|
5
|
+
* Uses session-based deduplication with localStorage persistence.
|
|
6
|
+
*/
|
|
7
|
+
import type { StorageProvider } from "./storage.js";
|
|
8
|
+
export interface ExposureDeduplicatorOptions {
|
|
9
|
+
/** Storage provider for persistence */
|
|
10
|
+
storage: StorageProvider;
|
|
11
|
+
/** Session TTL in milliseconds (default: 30 minutes) */
|
|
12
|
+
sessionTtlMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class ExposureDeduplicator {
|
|
15
|
+
private _storage;
|
|
16
|
+
private _sessionTtlMs;
|
|
17
|
+
private _seen;
|
|
18
|
+
private _sessionStart;
|
|
19
|
+
constructor(options: ExposureDeduplicatorOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Generate a deduplication key for an exposure.
|
|
22
|
+
*
|
|
23
|
+
* Key format: {unitKey}:{policyId}:{variant}
|
|
24
|
+
*/
|
|
25
|
+
static createKey(unitKey: string, policyId: string, variant: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Check if an exposure should be tracked (not a duplicate).
|
|
28
|
+
* Returns true if this is a new exposure, false if duplicate.
|
|
29
|
+
*/
|
|
30
|
+
shouldTrack(key: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Check and mark in one operation.
|
|
33
|
+
* Returns true if this was a new exposure (and is now marked as seen).
|
|
34
|
+
*/
|
|
35
|
+
checkAndMark(unitKey: string, policyId: string, variant: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all seen exposures (useful for testing or logout).
|
|
38
|
+
*/
|
|
39
|
+
clear(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get the number of unique exposures in the current session.
|
|
42
|
+
*/
|
|
43
|
+
get size(): number;
|
|
44
|
+
private _isSessionExpired;
|
|
45
|
+
private _resetSession;
|
|
46
|
+
private _persist;
|
|
47
|
+
private _restore;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=exposure-dedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exposure-dedup.d.ts","sourceRoot":"","sources":["../src/exposure-dedup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAKpD,MAAM,WAAW,2BAA2B;IAC1C,uCAAuC;IACvC,OAAO,EAAE,eAAe,CAAC;IACzB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AASD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,aAAa,CAAS;gBAElB,OAAO,EAAE,2BAA2B;IAUhD;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAI5E;;;OAGG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAiBjC;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAKzE;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,QAAQ;CAejB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExposureDeduplicator - Prevents duplicate exposure events.
|
|
3
|
+
*
|
|
4
|
+
* Same user seeing same variant should only count as 1 exposure.
|
|
5
|
+
* Uses session-based deduplication with localStorage persistence.
|
|
6
|
+
*/
|
|
7
|
+
const STORAGE_KEY = "exposure_dedup";
|
|
8
|
+
const DEFAULT_SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
9
|
+
export class ExposureDeduplicator {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this._storage = options.storage;
|
|
12
|
+
this._sessionTtlMs = options.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;
|
|
13
|
+
this._seen = new Set();
|
|
14
|
+
this._sessionStart = Date.now();
|
|
15
|
+
// Restore from storage
|
|
16
|
+
this._restore();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate a deduplication key for an exposure.
|
|
20
|
+
*
|
|
21
|
+
* Key format: {unitKey}:{policyId}:{variant}
|
|
22
|
+
*/
|
|
23
|
+
static createKey(unitKey, policyId, variant) {
|
|
24
|
+
return `${unitKey}:${policyId}:${variant}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if an exposure should be tracked (not a duplicate).
|
|
28
|
+
* Returns true if this is a new exposure, false if duplicate.
|
|
29
|
+
*/
|
|
30
|
+
shouldTrack(key) {
|
|
31
|
+
// Check if session has expired
|
|
32
|
+
if (this._isSessionExpired()) {
|
|
33
|
+
this._resetSession();
|
|
34
|
+
}
|
|
35
|
+
if (this._seen.has(key)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
// Mark as seen
|
|
39
|
+
this._seen.add(key);
|
|
40
|
+
this._persist();
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check and mark in one operation.
|
|
45
|
+
* Returns true if this was a new exposure (and is now marked as seen).
|
|
46
|
+
*/
|
|
47
|
+
checkAndMark(unitKey, policyId, variant) {
|
|
48
|
+
const key = ExposureDeduplicator.createKey(unitKey, policyId, variant);
|
|
49
|
+
return this.shouldTrack(key);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Clear all seen exposures (useful for testing or logout).
|
|
53
|
+
*/
|
|
54
|
+
clear() {
|
|
55
|
+
this._seen.clear();
|
|
56
|
+
this._storage.remove(STORAGE_KEY);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the number of unique exposures in the current session.
|
|
60
|
+
*/
|
|
61
|
+
get size() {
|
|
62
|
+
return this._seen.size;
|
|
63
|
+
}
|
|
64
|
+
_isSessionExpired() {
|
|
65
|
+
return Date.now() - this._sessionStart > this._sessionTtlMs;
|
|
66
|
+
}
|
|
67
|
+
_resetSession() {
|
|
68
|
+
this._seen.clear();
|
|
69
|
+
this._sessionStart = Date.now();
|
|
70
|
+
this._storage.remove(STORAGE_KEY);
|
|
71
|
+
}
|
|
72
|
+
_persist() {
|
|
73
|
+
const state = {
|
|
74
|
+
seen: Array.from(this._seen),
|
|
75
|
+
sessionStart: this._sessionStart,
|
|
76
|
+
};
|
|
77
|
+
this._storage.set(STORAGE_KEY, state, this._sessionTtlMs);
|
|
78
|
+
}
|
|
79
|
+
_restore() {
|
|
80
|
+
const state = this._storage.get(STORAGE_KEY);
|
|
81
|
+
if (!state)
|
|
82
|
+
return;
|
|
83
|
+
// Check if stored session is still valid
|
|
84
|
+
const sessionAge = Date.now() - state.sessionStart;
|
|
85
|
+
if (sessionAge > this._sessionTtlMs) {
|
|
86
|
+
this._storage.remove(STORAGE_KEY);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Restore state
|
|
90
|
+
this._seen = new Set(state.seen);
|
|
91
|
+
this._sessionStart = state.sessionStart;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=exposure-dedup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exposure-dedup.js","sourceRoot":"","sources":["../src/exposure-dedup.ts"],"names":[],"mappings":"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"}
|
package/dist/global.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global entry point for IIFE bundle.
|
|
3
|
+
*
|
|
4
|
+
* Exports `window.Traffical` for script tag usage:
|
|
5
|
+
*
|
|
6
|
+
* ```html
|
|
7
|
+
* <script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>
|
|
8
|
+
* <script>
|
|
9
|
+
* Traffical.init({ ... }).then(function(client) {
|
|
10
|
+
* var params = client.getParams({ ... });
|
|
11
|
+
* });
|
|
12
|
+
* </script>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { TrafficalClient, type TrafficalClientOptions } from "./client.js";
|
|
16
|
+
import type { TrafficalPlugin } from "./plugins/index.js";
|
|
17
|
+
import { createDOMBindingPlugin, type DOMBindingPlugin, type DOMBindingPluginOptions } from "./plugins/dom-binding.js";
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the Traffical client (async).
|
|
20
|
+
* Returns the client instance.
|
|
21
|
+
*/
|
|
22
|
+
declare function init(options: TrafficalClientOptions): Promise<TrafficalClient>;
|
|
23
|
+
/**
|
|
24
|
+
* Initialize the Traffical client (sync).
|
|
25
|
+
* Returns the client instance immediately, but config fetch happens async.
|
|
26
|
+
*/
|
|
27
|
+
declare function initSync(options: TrafficalClientOptions): TrafficalClient;
|
|
28
|
+
/**
|
|
29
|
+
* Get the singleton client instance.
|
|
30
|
+
* Returns null if not initialized.
|
|
31
|
+
*/
|
|
32
|
+
declare function instance(): TrafficalClient | null;
|
|
33
|
+
/**
|
|
34
|
+
* Destroy the singleton instance.
|
|
35
|
+
*/
|
|
36
|
+
declare function destroy(): void;
|
|
37
|
+
export { init, initSync, instance, destroy, TrafficalClient, type TrafficalClientOptions, type TrafficalPlugin, createDOMBindingPlugin, type DOMBindingPlugin, type DOMBindingPluginOptions, };
|
|
38
|
+
//# sourceMappingURL=global.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../src/global.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EAGf,KAAK,sBAAsB,EAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC7B,MAAM,0BAA0B,CAAC;AAKlC;;;GAGG;AACH,iBAAe,IAAI,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CAQ7E;AAED;;;GAGG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAclE;AAED;;;GAGG;AACH,iBAAS,QAAQ,IAAI,eAAe,GAAG,IAAI,CAE1C;AAED;;GAEG;AACH,iBAAS,OAAO,IAAI,IAAI,CAKvB;AAGD,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EAEpB,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC7B,CAAC"}
|
package/dist/global.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global entry point for IIFE bundle.
|
|
3
|
+
*
|
|
4
|
+
* Exports `window.Traffical` for script tag usage:
|
|
5
|
+
*
|
|
6
|
+
* ```html
|
|
7
|
+
* <script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>
|
|
8
|
+
* <script>
|
|
9
|
+
* Traffical.init({ ... }).then(function(client) {
|
|
10
|
+
* var params = client.getParams({ ... });
|
|
11
|
+
* });
|
|
12
|
+
* </script>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { TrafficalClient, createTrafficalClient, createTrafficalClientSync, } from "./client.js";
|
|
16
|
+
import { createDOMBindingPlugin, } from "./plugins/dom-binding.js";
|
|
17
|
+
// Global state for singleton pattern
|
|
18
|
+
let _instance = null;
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the Traffical client (async).
|
|
21
|
+
* Returns the client instance.
|
|
22
|
+
*/
|
|
23
|
+
async function init(options) {
|
|
24
|
+
if (_instance) {
|
|
25
|
+
console.warn("[Traffical] Client already initialized. Returning existing instance.");
|
|
26
|
+
return _instance;
|
|
27
|
+
}
|
|
28
|
+
_instance = await createTrafficalClient(options);
|
|
29
|
+
return _instance;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize the Traffical client (sync).
|
|
33
|
+
* Returns the client instance immediately, but config fetch happens async.
|
|
34
|
+
*/
|
|
35
|
+
function initSync(options) {
|
|
36
|
+
if (_instance) {
|
|
37
|
+
console.warn("[Traffical] Client already initialized. Returning existing instance.");
|
|
38
|
+
return _instance;
|
|
39
|
+
}
|
|
40
|
+
_instance = createTrafficalClientSync(options);
|
|
41
|
+
// Start async initialization in background
|
|
42
|
+
_instance.initialize().catch((error) => {
|
|
43
|
+
console.warn("[Traffical] Initialization error:", error);
|
|
44
|
+
});
|
|
45
|
+
return _instance;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the singleton client instance.
|
|
49
|
+
* Returns null if not initialized.
|
|
50
|
+
*/
|
|
51
|
+
function instance() {
|
|
52
|
+
return _instance;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Destroy the singleton instance.
|
|
56
|
+
*/
|
|
57
|
+
function destroy() {
|
|
58
|
+
if (_instance) {
|
|
59
|
+
_instance.destroy();
|
|
60
|
+
_instance = null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Export the Traffical global object
|
|
64
|
+
export { init, initSync, instance, destroy, TrafficalClient,
|
|
65
|
+
// DOM binding plugin
|
|
66
|
+
createDOMBindingPlugin, };
|
|
67
|
+
//# sourceMappingURL=global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global.js","sourceRoot":"","sources":["../src/global.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,yBAAyB,GAE1B,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,sBAAsB,GAGvB,MAAM,0BAA0B,CAAC;AAElC,qCAAqC;AACrC,IAAI,SAAS,GAA2B,IAAI,CAAC;AAE7C;;;GAGG;AACH,KAAK,UAAU,IAAI,CAAC,OAA+B;IACjD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACrF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,SAAS,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,OAA+B;IAC/C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACrF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,SAAS,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAE/C,2CAA2C;IAC3C,SAAS,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ;IACf,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,OAAO;IACd,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,OAAO,EAAE,CAAC;QACpB,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;AACH,CAAC;AAED,qCAAqC;AACrC,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,eAAe;AAGf,qBAAqB;AACrB,sBAAsB,GAGvB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/js-client
|
|
3
|
+
*
|
|
4
|
+
* Traffical JavaScript SDK for browser environments.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Error boundary wrapping (P0) - SDK errors never crash your app
|
|
8
|
+
* - Exposure deduplication (P0) - Same user/variant = 1 exposure
|
|
9
|
+
* - Smart event batching (P1) - Batches events, uses sendBeacon on unload
|
|
10
|
+
* - Plugin system (P2) - Extensible via plugins
|
|
11
|
+
* - DOM binding plugin - Auto-apply parameters to DOM elements
|
|
12
|
+
* - Auto stable ID - Anonymous user identification
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { createTrafficalClient, createDOMBindingPlugin } from '@traffical/js-client';
|
|
17
|
+
*
|
|
18
|
+
* const traffical = await createTrafficalClient({
|
|
19
|
+
* orgId: 'org_123',
|
|
20
|
+
* projectId: 'proj_456',
|
|
21
|
+
* env: 'production',
|
|
22
|
+
* apiKey: 'pk_...',
|
|
23
|
+
* plugins: [createDOMBindingPlugin()],
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* const params = traffical.getParams({
|
|
27
|
+
* context: { userId: 'user_789' },
|
|
28
|
+
* defaults: { 'ui.button.color': '#000' },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export * from "@traffical/core";
|
|
33
|
+
export { TrafficalClient, createTrafficalClient, createTrafficalClientSync, type TrafficalClientOptions, } from "./client.js";
|
|
34
|
+
export { ErrorBoundary, type ErrorBoundaryOptions } from "./error-boundary.js";
|
|
35
|
+
export { EventLogger, type EventLoggerOptions } from "./event-logger.js";
|
|
36
|
+
export { ExposureDeduplicator, type ExposureDeduplicatorOptions } from "./exposure-dedup.js";
|
|
37
|
+
export { StableIdProvider, type StableIdProviderOptions } from "./stable-id.js";
|
|
38
|
+
export { createStorageProvider, LocalStorageProvider, MemoryStorageProvider, type StorageProvider, } from "./storage.js";
|
|
39
|
+
export { PluginManager, type TrafficalPlugin, type PluginOptions } from "./plugins/index.js";
|
|
40
|
+
export { createDOMBindingPlugin, type DOMBindingPlugin, type DOMBindingPluginOptions, } from "./plugins/dom-binding.js";
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,cAAc,iBAAiB,CAAC;AAGhC,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,KAAK,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG7F,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/js-client
|
|
3
|
+
*
|
|
4
|
+
* Traffical JavaScript SDK for browser environments.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Error boundary wrapping (P0) - SDK errors never crash your app
|
|
8
|
+
* - Exposure deduplication (P0) - Same user/variant = 1 exposure
|
|
9
|
+
* - Smart event batching (P1) - Batches events, uses sendBeacon on unload
|
|
10
|
+
* - Plugin system (P2) - Extensible via plugins
|
|
11
|
+
* - DOM binding plugin - Auto-apply parameters to DOM elements
|
|
12
|
+
* - Auto stable ID - Anonymous user identification
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { createTrafficalClient, createDOMBindingPlugin } from '@traffical/js-client';
|
|
17
|
+
*
|
|
18
|
+
* const traffical = await createTrafficalClient({
|
|
19
|
+
* orgId: 'org_123',
|
|
20
|
+
* projectId: 'proj_456',
|
|
21
|
+
* env: 'production',
|
|
22
|
+
* apiKey: 'pk_...',
|
|
23
|
+
* plugins: [createDOMBindingPlugin()],
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* const params = traffical.getParams({
|
|
27
|
+
* context: { userId: 'user_789' },
|
|
28
|
+
* defaults: { 'ui.button.color': '#000' },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
// Re-export everything from core
|
|
33
|
+
export * from "@traffical/core";
|
|
34
|
+
// Export client
|
|
35
|
+
export { TrafficalClient, createTrafficalClient, createTrafficalClientSync, } from "./client.js";
|
|
36
|
+
// Export components for advanced usage
|
|
37
|
+
export { ErrorBoundary } from "./error-boundary.js";
|
|
38
|
+
export { EventLogger } from "./event-logger.js";
|
|
39
|
+
export { ExposureDeduplicator } from "./exposure-dedup.js";
|
|
40
|
+
export { StableIdProvider } from "./stable-id.js";
|
|
41
|
+
export { createStorageProvider, LocalStorageProvider, MemoryStorageProvider, } from "./storage.js";
|
|
42
|
+
// Export plugin system
|
|
43
|
+
export { PluginManager } from "./plugins/index.js";
|
|
44
|
+
// Export DOM binding plugin
|
|
45
|
+
export { createDOMBindingPlugin, } from "./plugins/dom-binding.js";
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,iCAAiC;AACjC,cAAc,iBAAiB,CAAC;AAEhC,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,yBAAyB,GAE1B,MAAM,aAAa,CAAC;AAErB,uCAAuC;AACvC,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAoC,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAgC,MAAM,gBAAgB,CAAC;AAChF,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,cAAc,CAAC;AAEtB,uBAAuB;AACvB,OAAO,EAAE,aAAa,EAA4C,MAAM,oBAAoB,CAAC;AAE7F,4BAA4B;AAC5B,OAAO,EACL,sBAAsB,GAGvB,MAAM,0BAA0B,CAAC"}
|