@tracelog/lib 2.10.0-rc.113.17 → 2.10.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/README.md +733 -160
- package/dist/browser/tracelog.esm.js +2763 -1241
- package/dist/browser/tracelog.esm.js.map +1 -1
- package/dist/browser/tracelog.js +2 -2
- package/dist/browser/tracelog.js.map +1 -1
- package/dist/public-api.cjs +2 -2
- package/dist/public-api.cjs.map +1 -1
- package/dist/public-api.d.mts +798 -72
- package/dist/public-api.d.ts +798 -72
- package/dist/public-api.js +2 -2
- package/dist/public-api.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# TraceLog
|
|
2
2
|
|
|
3
|
-
Lightweight web analytics library for tracking user behavior. Works standalone or with
|
|
3
|
+
Lightweight web analytics library for tracking user behavior. Works standalone or with optional backend integrations.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Zero-config**
|
|
8
|
-
- **Standalone
|
|
9
|
-
- **Privacy-first**
|
|
10
|
-
- **Cross-tab sessions**
|
|
11
|
-
- **Event-driven**
|
|
12
|
-
- **Lightweight**
|
|
7
|
+
- **Zero-config** - Auto-captures clicks, scrolls, page views, sessions, and performance metrics
|
|
8
|
+
- **Standalone** - No backend required; optional integrations (TraceLog SaaS, custom API)
|
|
9
|
+
- **Privacy-first** - PII sanitization, client-side sampling, `data-tlog-ignore` attribute
|
|
10
|
+
- **Cross-tab sessions** - BroadcastChannel sync with localStorage recovery
|
|
11
|
+
- **Event-driven** - Subscribe via `on()`/`off()` for real-time events
|
|
12
|
+
- **Lightweight** - Single dependency (`web-vitals`), ~62KB gzipped
|
|
13
13
|
|
|
14
14
|
## Live Demo
|
|
15
15
|
|
|
@@ -20,7 +20,6 @@ Lightweight web analytics library for tracking user behavior. Works standalone o
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
22
22
|
### NPM (Recommended)
|
|
23
|
-
|
|
24
23
|
```bash
|
|
25
24
|
npm install @tracelog/lib
|
|
26
25
|
```
|
|
@@ -41,7 +40,6 @@ const { sessionId } = await tracelog.init({
|
|
|
41
40
|
```
|
|
42
41
|
|
|
43
42
|
### CDN (Script Tag)
|
|
44
|
-
|
|
45
43
|
```html
|
|
46
44
|
<script src="https://cdn.jsdelivr.net/npm/@tracelog/lib@latest/dist/browser/tracelog.js"></script>
|
|
47
45
|
<script>
|
|
@@ -50,7 +48,6 @@ const { sessionId } = await tracelog.init({
|
|
|
50
48
|
```
|
|
51
49
|
|
|
52
50
|
### CDN (ES Module)
|
|
53
|
-
|
|
54
51
|
```html
|
|
55
52
|
<script type="module">
|
|
56
53
|
import { tracelog } from 'https://cdn.jsdelivr.net/npm/@tracelog/lib@latest/dist/browser/tracelog.esm.js';
|
|
@@ -58,52 +55,70 @@ const { sessionId } = await tracelog.init({
|
|
|
58
55
|
</script>
|
|
59
56
|
```
|
|
60
57
|
|
|
61
|
-
---
|
|
62
|
-
|
|
63
58
|
## Quick Start
|
|
64
59
|
|
|
65
60
|
### Initialization Order
|
|
66
61
|
|
|
67
|
-
Set up listeners **before** calling `init()`
|
|
62
|
+
**Important:** Set up listeners and transformers **before** calling `init()` to capture all events from the start:
|
|
68
63
|
|
|
69
64
|
```typescript
|
|
70
|
-
// 1. Obtain user consent
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
// 1. Obtain user consent FIRST (your responsibility)
|
|
66
|
+
// See "User Consent Management" section below for implementation details
|
|
67
|
+
const hasConsent = await getUserConsent(); // Your consent management system
|
|
68
|
+
|
|
69
|
+
if (!hasConsent) {
|
|
70
|
+
console.log('User declined tracking');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
73
|
|
|
74
|
-
// 2. Register listeners (before init)
|
|
74
|
+
// 2. Register event listeners SECOND (before init)
|
|
75
75
|
tracelog.on('event', (event) => {
|
|
76
76
|
console.log(event.type, event);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
// 3.
|
|
80
|
-
tracelog.
|
|
79
|
+
// 3. Configure transformers THIRD (if using custom backend)
|
|
80
|
+
tracelog.setTransformer('beforeSend', (event) => {
|
|
81
|
+
// Transform events before they're queued
|
|
82
|
+
return { ...event, custom_metadata: { app: 'v1' } };
|
|
83
|
+
});
|
|
81
84
|
|
|
82
|
-
// 4.
|
|
83
|
-
|
|
85
|
+
// 4. Configure custom headers FOURTH (if using custom backend with auth)
|
|
86
|
+
tracelog.setCustomHeaders(() => ({
|
|
87
|
+
'Authorization': `Bearer ${getAuthToken()}`
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
// 5. Initialize FIFTH (starts tracking immediately)
|
|
91
|
+
await tracelog.init({
|
|
84
92
|
integrations: {
|
|
85
|
-
|
|
93
|
+
custom: { collectApiUrl: 'https://api.example.com' }
|
|
86
94
|
}
|
|
87
95
|
});
|
|
88
96
|
|
|
89
|
-
//
|
|
90
|
-
tracelog.
|
|
97
|
+
// 6. Identify user (after init, optional)
|
|
98
|
+
tracelog.identify('cust_123', { name: 'Maria Garcia', plan: 'pro' });
|
|
99
|
+
|
|
100
|
+
// 7. Track custom events (after init)
|
|
101
|
+
tracelog.event('button_clicked', {
|
|
102
|
+
buttonId: 'signup-cta',
|
|
103
|
+
source: 'homepage'
|
|
104
|
+
});
|
|
91
105
|
|
|
92
|
-
//
|
|
106
|
+
// 8. On logout: reset identity
|
|
93
107
|
await tracelog.resetIdentity();
|
|
94
108
|
|
|
95
|
-
//
|
|
109
|
+
// 9. Cleanup (on consent revoke or app unmount)
|
|
96
110
|
tracelog.destroy();
|
|
97
111
|
```
|
|
98
112
|
|
|
99
|
-
**
|
|
113
|
+
**Why this order?** You must obtain user consent before initializing. Events like `SESSION_START` and `PAGE_VIEW` fire during initialization. Registering listeners, transformers, and custom headers before init ensures you capture, transform, and send these initial events with proper authentication.
|
|
100
114
|
|
|
101
|
-
|
|
115
|
+
**That's it!** TraceLog now automatically tracks:
|
|
116
|
+
- Page views & navigation (including SPA routes)
|
|
102
117
|
- Click interactions
|
|
103
118
|
- Scroll behavior
|
|
104
119
|
- User sessions
|
|
105
120
|
- Web Vitals (LCP, INP, CLS, FCP, TTFB)
|
|
106
|
-
- JavaScript errors
|
|
121
|
+
- JavaScript errors
|
|
107
122
|
|
|
108
123
|
---
|
|
109
124
|
|
|
@@ -111,40 +126,53 @@ tracelog.destroy();
|
|
|
111
126
|
|
|
112
127
|
| Method | Description |
|
|
113
128
|
|--------|-------------|
|
|
114
|
-
| `init(config?)` | Initialize tracking
|
|
115
|
-
| `event(name, metadata?, options?)` | Track
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
129
|
+
| `init(config?)` | Initialize tracking (see [Configuration](#configuration)) |
|
|
130
|
+
| `event(name, metadata?, options?)` | Track custom events. `options.critical: true` drains the queue via `sendBeacon` right after tracking, so the batch (the critical event + anything already queued) survives an imminent navigation. Subject to `sendBeacon`'s 64KB cap — oversized batches are persisted to `localStorage` and recovered on next `init()` via their idempotency token; the backend deduplicates by `event.id`. See [API_REFERENCE.md](./API_REFERENCE.md#eventname-metadata-options) for the full contract. |
|
|
131
|
+
| `flushImmediately()` | Force an async `fetch` flush of all pending events. Returns `Promise<boolean>`. |
|
|
132
|
+
| `flushImmediatelySync()` | Force a `sendBeacon` flush. Use for custom unload handlers; the library already wires this to `pagehide`/`beforeunload`/`visibilitychange`. |
|
|
133
|
+
| `updateGlobalMetadata(metadata)` | Replace all global metadata |
|
|
134
|
+
| `mergeGlobalMetadata(metadata)` | Merge with existing global metadata |
|
|
135
|
+
| `on(event, callback)` | Subscribe to events (`'event'` or `'queue'`) |
|
|
136
|
+
| `off(event, callback)` | Unsubscribe from events |
|
|
137
|
+
| `setTransformer(hook, fn)` | Transform events before sending (see [Transformers](#transformers)) |
|
|
138
|
+
| `removeTransformer(hook)` | Remove a previously set transformer |
|
|
139
|
+
| `setCustomHeaders(provider)` | Add custom HTTP headers to requests (see [Custom Headers](#custom-headers)) |
|
|
140
|
+
| `removeCustomHeaders()` | Remove custom headers provider |
|
|
141
|
+
| `identify(userId, traits?)` | Associate visitor with a known user identity |
|
|
142
|
+
| `resetIdentity()` | Clear identity, regenerate UUID, start new session |
|
|
143
|
+
| `isInitialized()` | Check initialization status |
|
|
144
|
+
| `getSessionId()` | Get current session ID (or null) |
|
|
145
|
+
| `setQaMode(enabled)` | Enable/disable QA mode (console logging) |
|
|
146
|
+
| `destroy()` | Stop tracking and cleanup |
|
|
124
147
|
|
|
125
148
|
**→ [Complete API Reference](./API_REFERENCE.md)**
|
|
126
149
|
|
|
127
150
|
---
|
|
128
151
|
|
|
129
|
-
## User Consent
|
|
152
|
+
## User Consent Management
|
|
130
153
|
|
|
131
|
-
TraceLog
|
|
154
|
+
TraceLog requires you to obtain user consent **before** calling `init()`. The library does not include a built-in consent management system.
|
|
132
155
|
|
|
133
156
|
```typescript
|
|
157
|
+
// Your responsibility: Obtain consent before initialization
|
|
134
158
|
const userConsent = await showCookieBanner(); // Your consent solution
|
|
135
159
|
|
|
136
160
|
if (userConsent.analytics) {
|
|
161
|
+
// Initialize only after consent
|
|
137
162
|
await tracelog.init({
|
|
138
|
-
integrations: {
|
|
163
|
+
integrations: {
|
|
164
|
+
tracelog: { projectId: 'your-project-id' }
|
|
165
|
+
}
|
|
139
166
|
});
|
|
140
167
|
} else {
|
|
141
|
-
// User rejected
|
|
168
|
+
// User rejected - don't initialize
|
|
169
|
+
console.log('Analytics consent denied');
|
|
142
170
|
}
|
|
143
171
|
|
|
144
|
-
// If
|
|
172
|
+
// If user revokes consent later
|
|
145
173
|
function handleConsentRevoke() {
|
|
146
|
-
tracelog.destroy();
|
|
147
|
-
localStorage.clear();
|
|
174
|
+
tracelog.destroy(); // Stop tracking immediately
|
|
175
|
+
localStorage.clear(); // Clear stored session data
|
|
148
176
|
}
|
|
149
177
|
```
|
|
150
178
|
|
|
@@ -152,76 +180,140 @@ function handleConsentRevoke() {
|
|
|
152
180
|
|
|
153
181
|
## Configuration
|
|
154
182
|
|
|
155
|
-
All configuration is optional
|
|
183
|
+
All configuration is **optional**. TraceLog works out-of-the-box with sensible defaults.
|
|
156
184
|
|
|
157
185
|
```typescript
|
|
158
186
|
await tracelog.init({
|
|
159
187
|
// Session
|
|
160
|
-
sessionTimeout: 900000,
|
|
188
|
+
sessionTimeout: 900000, // 15 min (default)
|
|
161
189
|
|
|
162
190
|
// Privacy
|
|
163
|
-
samplingRate: 1.0,
|
|
164
|
-
|
|
165
|
-
sensitiveQueryParams: ['token'], // Added to the 15-param default deny-list
|
|
166
|
-
|
|
167
|
-
// Throttles
|
|
168
|
-
pageViewThrottleMs: 1000, // Min interval between page_view events
|
|
169
|
-
clickThrottleMs: 300, // Min interval between click events per element
|
|
170
|
-
maxSameEventPerMinute: 60, // Per-name custom-event rate cap
|
|
191
|
+
samplingRate: 1.0, // 100% (default)
|
|
192
|
+
sensitiveQueryParams: ['token'], // Add to defaults
|
|
171
193
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
flushOnPageHidden: true, // Flush when document.hidden becomes true (mobile Safari coverage)
|
|
194
|
+
// Flush behavior (defaults shown)
|
|
195
|
+
flushOnSpaNavigation: false, // Opt-in: flush after pushState/replaceState/popstate/hashchange (default false; per-route flushing multiplies request volume on SPAs)
|
|
196
|
+
flushOnPageHidden: true, // Flush when document.hidden becomes true (mobile Safari coverage)
|
|
176
197
|
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
198
|
+
// Integrations (pick one, multiple, or none)
|
|
199
|
+
integrations: {
|
|
200
|
+
tracelog: { projectId: 'your-id' }, // TraceLog SaaS
|
|
201
|
+
custom: { collectApiUrl: 'https://api.com' }, // Custom backend
|
|
180
202
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
appName: 'MyApp'
|
|
203
|
+
// Multi-integration: Send to multiple backends simultaneously
|
|
204
|
+
// tracelog: { projectId: 'proj-123' }, // Analytics dashboard
|
|
205
|
+
// custom: { collectApiUrl: 'https://warehouse.com' } // Data warehouse
|
|
206
|
+
// Events sent to BOTH independently with separate error handling
|
|
186
207
|
},
|
|
187
208
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
209
|
+
// Web Vitals filtering
|
|
210
|
+
webVitalsMode: 'needs-improvement', // 'all' | 'needs-improvement' | 'poor'
|
|
211
|
+
|
|
212
|
+
// Viewport tracking (element visibility)
|
|
213
|
+
viewport: {
|
|
214
|
+
elements: [{ selector: '.cta', id: 'hero-cta' }],
|
|
215
|
+
threshold: 0.5, // 50% visible
|
|
216
|
+
minDwellTime: 1000 // 1 second
|
|
194
217
|
}
|
|
195
218
|
});
|
|
196
219
|
```
|
|
197
220
|
|
|
198
|
-
**→ [Full Configuration
|
|
221
|
+
**→ [Full Configuration Guide](./API_REFERENCE.md#configuration)**
|
|
199
222
|
|
|
200
223
|
---
|
|
201
224
|
|
|
202
225
|
## Automatic Event Types
|
|
203
226
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
|
207
|
-
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
227
|
+
TraceLog captures these events automatically (no code required):
|
|
228
|
+
|
|
229
|
+
| Event Type | What It Tracks |
|
|
230
|
+
|-------------------|---------------------------------------------|
|
|
231
|
+
| `page_view` | Navigation, SPA route changes |
|
|
232
|
+
| `click` | User interactions with elements |
|
|
233
|
+
| `session_start` | New session creation |
|
|
234
|
+
| `scroll` | Scroll depth, velocity, engagement |
|
|
235
|
+
| `web_vitals` | Core Web Vitals (LCP, INP, CLS, FCP, TTFB) |
|
|
236
|
+
| `error` | JavaScript errors, promise rejections |
|
|
237
|
+
| `viewport_visible`| Element visibility (requires `viewport` config) |
|
|
238
|
+
|
|
239
|
+
**Filtering Events:**
|
|
240
|
+
|
|
241
|
+
You can filter specific events before they're sent to your backend using the `beforeSend` transformer. This gives you complete control over what data is transmitted.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Filter out high-volume events (scroll, web_vitals)
|
|
245
|
+
tracelog.setTransformer('beforeSend', (event) => {
|
|
246
|
+
// Skip scroll and web vitals events
|
|
247
|
+
if (['scroll', 'web_vitals'].includes(event.type)) {
|
|
248
|
+
return null; // Returning null excludes the event from being sent
|
|
249
|
+
}
|
|
250
|
+
return event; // Send all other events normally
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
await tracelog.init({
|
|
254
|
+
integrations: {
|
|
255
|
+
custom: { collectApiUrl: 'https://api.example.com' }
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Advanced Filtering:**
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Conditional filtering based on custom logic
|
|
264
|
+
tracelog.setTransformer('beforeSend', (event) => {
|
|
265
|
+
// Only send errors in production
|
|
266
|
+
if (event.type === 'error' && process.env.NODE_ENV !== 'production') {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Only send 10% of scroll events (sampling)
|
|
271
|
+
if (event.type === 'scroll' && Math.random() > 0.1) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return event;
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Multi-Integration Behavior:**
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
// Transformers ONLY apply to custom backends
|
|
283
|
+
// TraceLog SaaS always receives all events unmodified
|
|
284
|
+
tracelog.setTransformer('beforeSend', (event) => {
|
|
285
|
+
if (['scroll', 'web_vitals'].includes(event.type)) {
|
|
286
|
+
return null; // Filtered from custom backend only
|
|
287
|
+
}
|
|
288
|
+
return event;
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
await tracelog.init({
|
|
292
|
+
integrations: {
|
|
293
|
+
tracelog: { projectId: 'proj-123' }, // Gets ALL events (unfiltered)
|
|
294
|
+
custom: { collectApiUrl: 'https://warehouse.com' } // Gets filtered events
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Important:** Transformers (`beforeSend`, `beforeBatch`) only apply to **custom backend integrations**. TraceLog SaaS always receives all events unmodified to maintain schema integrity and ensure complete analytics. This behavior is the same as the removed `disabledEvents` configuration.
|
|
212
300
|
|
|
213
|
-
**
|
|
301
|
+
**Use Cases:**
|
|
302
|
+
- Reduce bandwidth and backend costs for custom backends
|
|
303
|
+
- Already using Sentry/Datadog for errors (filter out `error` events from custom backend)
|
|
304
|
+
- Data warehouse doesn't need scroll/vitals granularity
|
|
305
|
+
- Minimize custom backend data volume for privacy compliance
|
|
306
|
+
- Custom sampling logic per event type
|
|
214
307
|
|
|
308
|
+
**Note:** Filtered events are still captured locally. Use `tracelog.on('event')` to access all events client-side, even those excluded from backend transmission.
|
|
309
|
+
|
|
310
|
+
**Custom Events:**
|
|
215
311
|
```typescript
|
|
216
312
|
tracelog.event('purchase_completed', {
|
|
217
313
|
orderId: 'ord-123',
|
|
218
314
|
total: 99.99,
|
|
219
315
|
currency: 'USD'
|
|
220
316
|
});
|
|
221
|
-
|
|
222
|
-
// Right before a navigation — guarantee delivery via sendBeacon
|
|
223
|
-
tracelog.event('purchase_completed', { orderId: 'ord-123' }, { critical: true });
|
|
224
|
-
window.location.href = '/thanks';
|
|
225
317
|
```
|
|
226
318
|
|
|
227
319
|
**→ [Event Types Reference](./API_REFERENCE.md#event-types)**
|
|
@@ -230,37 +322,441 @@ window.location.href = '/thanks';
|
|
|
230
322
|
|
|
231
323
|
## Global Metadata
|
|
232
324
|
|
|
233
|
-
|
|
325
|
+
Global metadata is automatically attached to **every event** sent to your backend, making it ideal for user context, environment info, or app-wide properties.
|
|
326
|
+
|
|
327
|
+
### Setting Initial Metadata
|
|
328
|
+
|
|
329
|
+
Configure global metadata during initialization:
|
|
234
330
|
|
|
235
331
|
```typescript
|
|
236
332
|
await tracelog.init({
|
|
237
333
|
globalMetadata: {
|
|
238
334
|
env: 'production',
|
|
239
335
|
version: '1.2.0',
|
|
240
|
-
|
|
336
|
+
appName: 'MyApp'
|
|
241
337
|
}
|
|
242
338
|
});
|
|
243
339
|
```
|
|
244
340
|
|
|
245
|
-
|
|
341
|
+
### Updating Metadata at Runtime
|
|
342
|
+
|
|
343
|
+
**Replace all metadata** (previous keys removed):
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// User login: Replace with user context
|
|
347
|
+
tracelog.updateGlobalMetadata({
|
|
348
|
+
userId: 'user-456',
|
|
349
|
+
plan: 'premium',
|
|
350
|
+
cohort: 'beta-testers'
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// User logout: Clear all metadata
|
|
354
|
+
tracelog.updateGlobalMetadata({});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Merge with existing metadata** (preserves other keys):
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// Add user ID while preserving env and version
|
|
361
|
+
tracelog.mergeGlobalMetadata({ userId: 'user-123' });
|
|
362
|
+
|
|
363
|
+
// Update version while preserving others
|
|
364
|
+
tracelog.mergeGlobalMetadata({ version: '1.3.0' });
|
|
365
|
+
|
|
366
|
+
// Add feature flags
|
|
367
|
+
tracelog.mergeGlobalMetadata({
|
|
368
|
+
feature_new_ui: true,
|
|
369
|
+
feature_dark_mode: false
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Use Cases
|
|
374
|
+
|
|
375
|
+
**User Authentication:**
|
|
376
|
+
```typescript
|
|
377
|
+
// Login
|
|
378
|
+
tracelog.mergeGlobalMetadata({
|
|
379
|
+
userId: user.id,
|
|
380
|
+
email: user.email,
|
|
381
|
+
plan: user.subscription.plan
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Logout
|
|
385
|
+
tracelog.updateGlobalMetadata({});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**A/B Testing:**
|
|
389
|
+
```typescript
|
|
390
|
+
tracelog.mergeGlobalMetadata({
|
|
391
|
+
experiment_checkout: 'variant-b',
|
|
392
|
+
experiment_pricing: 'control'
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Environment Context:**
|
|
397
|
+
```typescript
|
|
398
|
+
tracelog.mergeGlobalMetadata({
|
|
399
|
+
build: process.env.BUILD_NUMBER,
|
|
400
|
+
region: user.location.region,
|
|
401
|
+
language: navigator.language
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Validation Rules
|
|
246
406
|
|
|
247
|
-
- **Allowed
|
|
248
|
-
- **
|
|
249
|
-
- **Limits**:
|
|
407
|
+
- **Allowed Types**: Primitives (string, number, boolean), string arrays, nested objects (up to 10 levels)
|
|
408
|
+
- **NOT Allowed**: Functions, symbols, undefined, circular references
|
|
409
|
+
- **Limits**: Max 100 keys, 48KB serialized size, 500 items per array, 1000 chars per string
|
|
250
410
|
|
|
251
|
-
**→ [Metadata Reference](./API_REFERENCE.md#
|
|
411
|
+
**→ [Metadata API Reference](./API_REFERENCE.md#global-metadata)**
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Transformers
|
|
416
|
+
|
|
417
|
+
Transform events dynamically at runtime before they're sent to integrations. Useful for adding custom logic, enrichment, or filtering.
|
|
418
|
+
|
|
419
|
+
**Important**: Transformers are **integration-specific** to protect TraceLog SaaS schema integrity:
|
|
420
|
+
|
|
421
|
+
| Integration | `beforeSend` | `beforeBatch` | Notes |
|
|
422
|
+
|-------------|--------------|---------------|-------|
|
|
423
|
+
| **Standalone (no backend)** | ✅ Applied | ⚠️ Not supported | Only local event emission; `beforeBatch` requires backend |
|
|
424
|
+
| **TraceLog SaaS (only)** | ❌ Silently ignored | ❌ Silently ignored | Schema protection |
|
|
425
|
+
| **Custom Backend (only)** | ✅ Applied | ✅ Applied | Full control |
|
|
426
|
+
| **Multi-Integration** | ⚠️ Custom only | ⚠️ Custom only | SaaS gets original events, custom gets transformed |
|
|
427
|
+
|
|
428
|
+
**Multi-Integration Behavior:**
|
|
429
|
+
- When using both TraceLog SaaS + Custom backend simultaneously
|
|
430
|
+
- SaaS receives **original events** (transformers not applied)
|
|
431
|
+
- Custom backend receives **transformed events**
|
|
432
|
+
- Independent error handling and retry per integration
|
|
433
|
+
|
|
434
|
+
**Event Listeners and Transformers:**
|
|
435
|
+
|
|
436
|
+
Event listeners (`tracelog.on('event', ...)`) receive **original events**, not transformed events. Transformers only affect data sent to backends.
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
440
|
+
if ('type' in data) {
|
|
441
|
+
return { ...data, enrichedField: 'value' };
|
|
442
|
+
}
|
|
443
|
+
return data;
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
tracelog.on('event', (ev) => {
|
|
447
|
+
console.log(ev.enrichedField); // undefined - listeners receive original events
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Workaround for GTM/Third-Party Relay:**
|
|
452
|
+
|
|
453
|
+
If you need to forward enriched events to GTM or other systems, apply the transformation in your listener:
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// Define enrichment function once
|
|
457
|
+
const enrichEvent = (event) => ({
|
|
458
|
+
...event,
|
|
459
|
+
appVersion: '1.0.0',
|
|
460
|
+
environment: 'production'
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Use in transformer (for backend)
|
|
464
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
465
|
+
if ('type' in data) {
|
|
466
|
+
return enrichEvent(data);
|
|
467
|
+
}
|
|
468
|
+
return data;
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Use in listener (for GTM relay)
|
|
472
|
+
tracelog.on('event', (event) => {
|
|
473
|
+
const enrichedEvent = enrichEvent(event);
|
|
474
|
+
window.dataLayer?.push({ event: 'tracelog_event', ...enrichedEvent });
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Available Hooks
|
|
479
|
+
|
|
480
|
+
#### `beforeSend` - Per-Event Transformation
|
|
481
|
+
|
|
482
|
+
Transform individual events **before** deduplication, sampling, and queueing.
|
|
483
|
+
|
|
484
|
+
**Timing (depends on integration mode):**
|
|
485
|
+
- **Standalone mode (no backend)**: Runs in `EventManager.buildEventPayload()` before dedup/sampling/queueing
|
|
486
|
+
- **Custom-only mode**: Runs in `EventManager.buildEventPayload()` before dedup/sampling/queueing
|
|
487
|
+
- **Multi-integration mode (SaaS + Custom)**: Runs in `SenderManager` per-integration (SaaS skipped, Custom applied)
|
|
488
|
+
- **TraceLog SaaS-only mode**: Silently ignored (not applied)
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import { tracelog } from '@tracelog/lib';
|
|
492
|
+
import type { EventData, EventsQueue } from '@tracelog/lib';
|
|
493
|
+
|
|
494
|
+
// Add custom metadata to all events
|
|
495
|
+
tracelog.setTransformer('beforeSend', (data: EventData | EventsQueue) => {
|
|
496
|
+
if ('type' in data) {
|
|
497
|
+
return {
|
|
498
|
+
...data,
|
|
499
|
+
custom_event: {
|
|
500
|
+
...data.custom_event,
|
|
501
|
+
metadata: {
|
|
502
|
+
...data.custom_event?.metadata,
|
|
503
|
+
environment: 'production',
|
|
504
|
+
version: '1.0.0'
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return data;
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// Filter out sensitive events
|
|
513
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
514
|
+
if ('type' in data && data.custom_event?.name === 'internal_event') {
|
|
515
|
+
return null; // Event will be dropped
|
|
516
|
+
}
|
|
517
|
+
return data;
|
|
518
|
+
});
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
#### `beforeBatch` - Batch Transformation
|
|
522
|
+
|
|
523
|
+
Transform the entire batch before sending to backend. Runs once per batch (every 10s or 50 events).
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
// Add batch-level metadata
|
|
527
|
+
tracelog.setTransformer('beforeBatch', (data) => {
|
|
528
|
+
if ('events' in data) {
|
|
529
|
+
return {
|
|
530
|
+
...data,
|
|
531
|
+
global_metadata: {
|
|
532
|
+
...data.global_metadata,
|
|
533
|
+
batchSize: data.events.length,
|
|
534
|
+
batchTimestamp: Date.now()
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
return data;
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// Filter batch based on conditions
|
|
542
|
+
tracelog.setTransformer('beforeBatch', (data) => {
|
|
543
|
+
if ('events' in data && data.events.length < 5) {
|
|
544
|
+
return null; // Don't send small batches
|
|
545
|
+
}
|
|
546
|
+
return data;
|
|
547
|
+
});
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Removing Transformers
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
// Remove specific transformer
|
|
554
|
+
tracelog.removeTransformer('beforeSend');
|
|
555
|
+
tracelog.removeTransformer('beforeBatch');
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Error Handling & Validation
|
|
559
|
+
|
|
560
|
+
Transformers are designed to be resilient and flexible:
|
|
561
|
+
|
|
562
|
+
**Input Validation:**
|
|
563
|
+
- **Function type check**: `setTransformer()` throws error if `fn` is not a function
|
|
564
|
+
- **Example**: `tracelog.setTransformer('beforeSend', null)` → Throws `Error: [TraceLog] Transformer must be a function, received: object`
|
|
565
|
+
|
|
566
|
+
**Error Handling:**
|
|
567
|
+
- **Exceptions**: Caught and logged, original event/batch used
|
|
568
|
+
- **Invalid return**: Logged warning, original event/batch used
|
|
569
|
+
- **`null` return**: Event/batch filtered out (intended behavior)
|
|
570
|
+
|
|
571
|
+
**Validation:**
|
|
572
|
+
- **Minimal checks only**: `beforeSend` requires `'type'` field, `beforeBatch` requires `'events'` array
|
|
573
|
+
- **Custom schemas supported**: All other fields optional for maximum flexibility with custom backends
|
|
574
|
+
- **Use case**: Transform data to match your backend's schema (e.g., data warehouses, custom APIs)
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
// Safe transformer - errors won't break tracking
|
|
578
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
579
|
+
try {
|
|
580
|
+
// Complex transformation logic
|
|
581
|
+
return transformData(data);
|
|
582
|
+
} catch (error) {
|
|
583
|
+
console.error('Transformer error:', error);
|
|
584
|
+
return data; // Fallback to original
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Custom schema example - completely reshape for your backend
|
|
589
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
590
|
+
if ('type' in data) {
|
|
591
|
+
return {
|
|
592
|
+
type: 'analytics_event',
|
|
593
|
+
eventName: data.custom_event?.name,
|
|
594
|
+
timestamp: Date.now(),
|
|
595
|
+
// Your custom fields - TraceLog won't reject this!
|
|
596
|
+
customField1: 'value',
|
|
597
|
+
customField2: 123
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
return data;
|
|
601
|
+
});
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Use Cases
|
|
605
|
+
|
|
606
|
+
**Data Enrichment:**
|
|
607
|
+
```typescript
|
|
608
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
609
|
+
if ('type' in data) {
|
|
610
|
+
return {
|
|
611
|
+
...data,
|
|
612
|
+
custom_event: {
|
|
613
|
+
...data.custom_event,
|
|
614
|
+
metadata: {
|
|
615
|
+
...data.custom_event?.metadata,
|
|
616
|
+
userId: getCurrentUserId(),
|
|
617
|
+
sessionContext: getSessionContext()
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
return data;
|
|
623
|
+
});
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Event Filtering:**
|
|
627
|
+
```typescript
|
|
628
|
+
// Filter out bot traffic
|
|
629
|
+
tracelog.setTransformer('beforeBatch', (data) => {
|
|
630
|
+
if ('events' in data) {
|
|
631
|
+
const filteredEvents = data.events.filter(
|
|
632
|
+
event => !isBotUserAgent(navigator.userAgent)
|
|
633
|
+
);
|
|
634
|
+
return { ...data, events: filteredEvents };
|
|
635
|
+
}
|
|
636
|
+
return data;
|
|
637
|
+
});
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
**PII Sanitization (Custom Backend):**
|
|
641
|
+
```typescript
|
|
642
|
+
// Additional sanitization for custom backend
|
|
643
|
+
tracelog.setTransformer('beforeSend', (data) => {
|
|
644
|
+
if ('type' in data && data.custom_event?.metadata) {
|
|
645
|
+
const sanitized = { ...data.custom_event.metadata };
|
|
646
|
+
delete sanitized.email;
|
|
647
|
+
delete sanitized.phone;
|
|
648
|
+
return {
|
|
649
|
+
...data,
|
|
650
|
+
custom_event: {
|
|
651
|
+
...data.custom_event,
|
|
652
|
+
metadata: sanitized
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
return data;
|
|
657
|
+
});
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Custom Headers
|
|
663
|
+
|
|
664
|
+
Add custom HTTP headers to requests sent to custom backends. Useful for authentication, tenant identification, or API versioning.
|
|
665
|
+
|
|
666
|
+
**Important**: Custom headers **only apply to custom backend integrations**. TraceLog SaaS always receives requests without custom headers.
|
|
667
|
+
|
|
668
|
+
### Static Headers (Config)
|
|
669
|
+
|
|
670
|
+
Set fixed headers in configuration:
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
await tracelog.init({
|
|
674
|
+
integrations: {
|
|
675
|
+
custom: {
|
|
676
|
+
collectApiUrl: 'https://api.example.com/collect',
|
|
677
|
+
headers: {
|
|
678
|
+
'X-Tenant-Id': 'tenant-123',
|
|
679
|
+
'X-Brand': 'my-brand',
|
|
680
|
+
'X-API-Version': '2.0'
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Dynamic Headers (Provider)
|
|
688
|
+
|
|
689
|
+
Set headers dynamically at runtime (e.g., auth tokens that expire):
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
// Set before or after init
|
|
693
|
+
tracelog.setCustomHeaders(() => ({
|
|
694
|
+
'Authorization': `Bearer ${getAuthToken()}`,
|
|
695
|
+
'X-Request-ID': crypto.randomUUID()
|
|
696
|
+
}));
|
|
697
|
+
|
|
698
|
+
await tracelog.init({
|
|
699
|
+
integrations: {
|
|
700
|
+
custom: { collectApiUrl: 'https://api.example.com/collect' }
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### Static + Dynamic Headers
|
|
706
|
+
|
|
707
|
+
Combine both approaches. Dynamic headers override static on key collision:
|
|
708
|
+
|
|
709
|
+
```typescript
|
|
710
|
+
await tracelog.init({
|
|
711
|
+
integrations: {
|
|
712
|
+
custom: {
|
|
713
|
+
collectApiUrl: 'https://api.example.com/collect',
|
|
714
|
+
headers: {
|
|
715
|
+
'X-Brand': 'static-brand', // Static
|
|
716
|
+
'X-Tenant-Id': 'tenant-123' // Static
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
// Dynamic provider overrides 'X-Brand'
|
|
723
|
+
tracelog.setCustomHeaders(() => ({
|
|
724
|
+
'X-Brand': 'dynamic-brand', // Overrides static
|
|
725
|
+
'Authorization': 'Bearer token' // New header
|
|
726
|
+
}));
|
|
727
|
+
|
|
728
|
+
// Result: { 'X-Tenant-Id': 'tenant-123', 'X-Brand': 'dynamic-brand', 'Authorization': 'Bearer token' }
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### Removing Headers
|
|
732
|
+
|
|
733
|
+
```typescript
|
|
734
|
+
// Remove dynamic provider (static headers from config remain)
|
|
735
|
+
tracelog.removeCustomHeaders();
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### sendBeacon Limitation
|
|
739
|
+
|
|
740
|
+
⚠️ Custom headers are **NOT applied** to `sendBeacon()` requests (page unload). The browser API doesn't support custom headers. For scenarios requiring headers on all requests:
|
|
741
|
+
- Ensure async sends complete before page unload
|
|
742
|
+
- Use short-lived tokens that don't require refresh per request
|
|
743
|
+
|
|
744
|
+
**→ [Custom Headers API Reference](./API_REFERENCE.md#setcustomheadersprovider-customheadersprovider-void)**
|
|
252
745
|
|
|
253
746
|
---
|
|
254
747
|
|
|
255
748
|
## Integration Modes
|
|
256
749
|
|
|
257
|
-
|
|
750
|
+
TraceLog supports multiple integration modes. Choose what fits your needs:
|
|
751
|
+
|
|
752
|
+
### 1. Standalone (No Backend)
|
|
258
753
|
|
|
259
|
-
Default when no
|
|
754
|
+
**Default mode when no integrations configured.** Events captured and emitted locally without network requests.
|
|
260
755
|
|
|
261
756
|
```typescript
|
|
262
757
|
await tracelog.init();
|
|
263
758
|
|
|
759
|
+
// Consume events locally
|
|
264
760
|
tracelog.on('event', (event) => {
|
|
265
761
|
myAnalytics.track(event);
|
|
266
762
|
});
|
|
@@ -270,10 +766,15 @@ tracelog.on('queue', (batch) => {
|
|
|
270
766
|
});
|
|
271
767
|
```
|
|
272
768
|
|
|
273
|
-
|
|
769
|
+
**Behavior:**
|
|
770
|
+
- ✅ Events captured and queued normally
|
|
771
|
+
- ✅ `beforeSend` transformer applied (per-event transformation)
|
|
772
|
+
- ⚠️ `beforeBatch` transformer **NOT supported** (no SenderManager created)
|
|
773
|
+
- ✅ Events emitted to local listeners every 10 seconds or 50 events
|
|
774
|
+
- ❌ **NO network requests made** (no backends configured)
|
|
775
|
+
- ✅ Perfect for custom analytics pipelines, testing, or privacy-focused implementations
|
|
274
776
|
|
|
275
777
|
### 2. TraceLog SaaS
|
|
276
|
-
|
|
277
778
|
```typescript
|
|
278
779
|
await tracelog.init({
|
|
279
780
|
integrations: {
|
|
@@ -282,64 +783,126 @@ await tracelog.init({
|
|
|
282
783
|
});
|
|
283
784
|
```
|
|
284
785
|
|
|
285
|
-
|
|
786
|
+
### 3. Custom Backend
|
|
787
|
+
```typescript
|
|
788
|
+
await tracelog.init({
|
|
789
|
+
integrations: {
|
|
790
|
+
custom: {
|
|
791
|
+
collectApiUrl: 'https://api.example.com/collect',
|
|
792
|
+
allowHttp: false, // Only true for local testing
|
|
793
|
+
fetchCredentials: 'include' // Cookie policy: 'include' | 'same-origin' | 'omit'
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### 4. Multi-Integration (TraceLog SaaS + Custom Backend)
|
|
800
|
+
```typescript
|
|
801
|
+
await tracelog.init({
|
|
802
|
+
integrations: {
|
|
803
|
+
tracelog: { projectId: 'your-project-id' }, // Analytics dashboard
|
|
804
|
+
custom: { collectApiUrl: 'https://warehouse.com' } // Data warehouse
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// Events sent to BOTH endpoints independently
|
|
809
|
+
// - Independent error handling per integration
|
|
810
|
+
// - Independent retry/persistence per integration
|
|
811
|
+
// - Parallel sending (non-blocking)
|
|
812
|
+
```
|
|
286
813
|
|
|
287
|
-
**→ [Integration
|
|
814
|
+
**→ [Integration Setup Guide](./API_REFERENCE.md#integration-configuration)**
|
|
288
815
|
|
|
289
816
|
---
|
|
290
817
|
|
|
291
818
|
## Error Handling & Reliability
|
|
292
819
|
|
|
293
|
-
|
|
820
|
+
TraceLog implements intelligent error handling with automatic retries for transient failures:
|
|
294
821
|
|
|
295
|
-
|
|
822
|
+
### Automatic Retry Strategy
|
|
296
823
|
|
|
297
|
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
824
|
+
**Transient Errors** (5xx, timeouts, network failures):
|
|
825
|
+
- **Up to 2 retry attempts** per integration (3 total attempts)
|
|
826
|
+
- **Exponential backoff with jitter**: 200-300ms, 400-500ms
|
|
827
|
+
- **Independent retries** per integration (SaaS and Custom retry separately)
|
|
828
|
+
- **Persistence after exhaustion**: Events saved to localStorage for next-page recovery
|
|
300
829
|
|
|
301
|
-
**Rate
|
|
830
|
+
**Rate Limit** (429):
|
|
831
|
+
- **No in-session retries** — arms a 60-second cooldown instead
|
|
832
|
+
- **Cooldown is mirrored to localStorage** and shared across tabs/windows on the same origin (prevents every fresh page load from hammering the server's 429 window)
|
|
833
|
+
- **Events persisted immediately** to localStorage; retried once the cooldown elapses
|
|
834
|
+
- TraceLog SaaS deduplicates retries server-side; custom backends should implement idempotency
|
|
302
835
|
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
- Events
|
|
306
|
-
-
|
|
836
|
+
**Permanent Errors** (4xx except 408, 429):
|
|
837
|
+
- **No retries** (immediate failure)
|
|
838
|
+
- **Events discarded** (not persisted)
|
|
839
|
+
- **Exception**: 408 Request Timeout is treated as transient
|
|
307
840
|
|
|
308
|
-
|
|
841
|
+
```typescript
|
|
842
|
+
// Multi-backend example with automatic retries
|
|
843
|
+
await tracelog.init({
|
|
844
|
+
integrations: {
|
|
845
|
+
tracelog: { projectId: 'project-id' },
|
|
846
|
+
custom: { collectApiUrl: 'https://api.example.com/collect' }
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
// If tracelog SaaS returns 500:
|
|
851
|
+
// - Retries 2 times with backoff (200-300ms, 400-500ms)
|
|
852
|
+
// - If all fail → persists to localStorage for next page
|
|
309
853
|
|
|
310
|
-
|
|
311
|
-
-
|
|
854
|
+
// If custom backend succeeds:
|
|
855
|
+
// - Events removed from queue (optimistic removal)
|
|
856
|
+
// - Failed integration recovered on next page load
|
|
857
|
+
```
|
|
312
858
|
|
|
313
|
-
### Error
|
|
859
|
+
### Error Classification
|
|
314
860
|
|
|
315
|
-
| Status
|
|
316
|
-
|
|
317
|
-
| **2xx**
|
|
318
|
-
| **4xx** (except 408
|
|
319
|
-
| **408**
|
|
320
|
-
| **429**
|
|
321
|
-
| **5xx**
|
|
322
|
-
| **Network
|
|
323
|
-
| **Timeout**
|
|
861
|
+
| Status Code | Type | Retries | Persistence |
|
|
862
|
+
|-------------|------|---------|-------------|
|
|
863
|
+
| **2xx** | Success | None | Cleared |
|
|
864
|
+
| **4xx** (except 408, 429) | Permanent | ❌ None | ❌ Discarded |
|
|
865
|
+
| **408** Request Timeout | Transient | ✅ Up to 2 | ✅ After exhaustion |
|
|
866
|
+
| **429** Too Many Requests | Rate Limited (60s cooldown, shared across tabs) | ❌ None | ✅ Immediate |
|
|
867
|
+
| **5xx** | Transient | ✅ Up to 2 | ✅ After exhaustion |
|
|
868
|
+
| **Network Error** | Transient | ✅ Up to 2 | ✅ After exhaustion |
|
|
869
|
+
| **Timeout** | Transient | ✅ Up to 2 | ✅ After exhaustion |
|
|
324
870
|
|
|
325
|
-
###
|
|
871
|
+
### Optimistic Queue Management
|
|
326
872
|
|
|
327
|
-
|
|
873
|
+
**Multi-Integration Behavior:**
|
|
874
|
+
- Events removed from queue if **AT LEAST ONE** integration succeeds
|
|
875
|
+
- Failed integrations persist independently for next-page recovery
|
|
876
|
+
- Successful integration doesn't retry (performance optimization)
|
|
328
877
|
|
|
878
|
+
**Example Scenario:**
|
|
329
879
|
```typescript
|
|
330
|
-
//
|
|
331
|
-
//
|
|
880
|
+
// SaaS succeeds immediately → no retry needed
|
|
881
|
+
// Custom fails with 503 → retries 2 times → persists for recovery
|
|
882
|
+
// Events removed from queue (SaaS succeeded)
|
|
883
|
+
// Next page load → only Custom integration recovers persisted events
|
|
332
884
|
```
|
|
333
885
|
|
|
334
|
-
|
|
886
|
+
### Recovery on Page Load
|
|
335
887
|
|
|
336
|
-
|
|
888
|
+
Failed events automatically recovered on next `init()`:
|
|
889
|
+
|
|
890
|
+
```typescript
|
|
891
|
+
// Page 1: Events fail to send (5xx error after retries)
|
|
892
|
+
// → Persisted to localStorage per-integration
|
|
893
|
+
|
|
894
|
+
// Page 2: User navigates to new page
|
|
895
|
+
await tracelog.init({ /* same config */ });
|
|
896
|
+
// ✅ Automatically recovers and resends persisted events
|
|
897
|
+
// ✅ Independent recovery per integration
|
|
898
|
+
// ✅ Multi-tab protection (1s window prevents duplicates)
|
|
899
|
+
```
|
|
337
900
|
|
|
338
901
|
**→ [Full Error Handling Reference](./API_REFERENCE.md#error-handling)**
|
|
339
902
|
|
|
340
|
-
### Session
|
|
903
|
+
### Session Continuity (External Redirects)
|
|
341
904
|
|
|
342
|
-
TraceLog preserves sessions across external redirects (payment processors, OAuth flows, etc.) with zero developer action. Session data is mirrored to `sessionStorage` alongside `localStorage`, so when a user returns from an external site and `localStorage` is empty, the session is recovered from `sessionStorage` transparently.
|
|
905
|
+
TraceLog automatically preserves sessions across external redirects (payment processors, OAuth flows, etc.) with zero developer action. Session data is mirrored to `sessionStorage` alongside `localStorage`, so when a user returns from an external site and `localStorage` is empty, the session is recovered from `sessionStorage` transparently.
|
|
343
906
|
|
|
344
907
|
```typescript
|
|
345
908
|
// No special handling needed before redirect
|
|
@@ -351,7 +914,7 @@ tracelog.event('purchase', { orderId: '12345', amount: 99.99 });
|
|
|
351
914
|
// Same session as before the redirect
|
|
352
915
|
```
|
|
353
916
|
|
|
354
|
-
- Automatic
|
|
917
|
+
- Automatic: no API calls or developer action required
|
|
355
918
|
- `sessionStorage` mirror survives same-tab navigation (cleared on tab close)
|
|
356
919
|
- Session timeout still applies (expired sessions are not recovered)
|
|
357
920
|
|
|
@@ -359,26 +922,26 @@ tracelog.event('purchase', { orderId: '12345', amount: 99.99 });
|
|
|
359
922
|
|
|
360
923
|
## Privacy & Security
|
|
361
924
|
|
|
362
|
-
TraceLog is privacy-first by design:
|
|
925
|
+
TraceLog is **privacy-first** by design:
|
|
363
926
|
|
|
364
|
-
- ✅ **PII
|
|
365
|
-
- ✅ **Input
|
|
366
|
-
- ✅ **URL
|
|
367
|
-
- ✅ **Element
|
|
368
|
-
- ✅ **Client-
|
|
927
|
+
- ✅ **PII Sanitization** - Auto-redacts emails, phones, credit cards, API keys
|
|
928
|
+
- ✅ **Input Protection** - Never captures `<input>`, `<textarea>`, `<select>` values
|
|
929
|
+
- ✅ **URL Filtering** - Removes sensitive query params (15 defaults + custom)
|
|
930
|
+
- ✅ **Element Exclusion** - Use `data-tlog-ignore` to exclude sensitive areas
|
|
931
|
+
- ✅ **Client-Side Controls** - All sampling and validation happens in browser
|
|
369
932
|
|
|
933
|
+
**Example:**
|
|
370
934
|
```html
|
|
371
|
-
<!-- Exclude sensitive forms
|
|
935
|
+
<!-- Exclude sensitive forms -->
|
|
372
936
|
<div data-tlog-ignore>
|
|
373
937
|
<input type="password" name="password">
|
|
374
938
|
<input type="text" name="credit_card">
|
|
375
939
|
</div>
|
|
376
940
|
```
|
|
377
941
|
|
|
378
|
-
**Your
|
|
379
|
-
|
|
380
|
-
-
|
|
381
|
-
- Avoid PII in custom event metadata (TraceLog only sanitizes element text and error messages)
|
|
942
|
+
**Your Responsibilities:**
|
|
943
|
+
- Get user consent before calling `init()` (GDPR/CCPA)
|
|
944
|
+
- Sanitize custom event metadata (avoid PII)
|
|
382
945
|
- Call `destroy()` on consent revoke
|
|
383
946
|
|
|
384
947
|
**→ [Complete Security Guide](./SECURITY.md)**
|
|
@@ -387,20 +950,30 @@ TraceLog is privacy-first by design:
|
|
|
387
950
|
|
|
388
951
|
## QA Mode
|
|
389
952
|
|
|
390
|
-
QA mode
|
|
953
|
+
Enable QA mode for debugging and development:
|
|
391
954
|
|
|
392
|
-
### URL
|
|
955
|
+
### URL Activation
|
|
956
|
+
```bash
|
|
957
|
+
# Enable
|
|
958
|
+
?tlog_mode=qa
|
|
393
959
|
|
|
394
|
-
|
|
395
|
-
?tlog_mode=
|
|
396
|
-
|
|
960
|
+
# Disable
|
|
961
|
+
?tlog_mode=qa_off
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
### Programmatic API
|
|
965
|
+
```typescript
|
|
966
|
+
tracelog.setQaMode(true); // Enable
|
|
967
|
+
tracelog.setQaMode(false); // Disable
|
|
397
968
|
```
|
|
398
969
|
|
|
399
|
-
**
|
|
970
|
+
**Features:**
|
|
971
|
+
- Custom events logged to browser console
|
|
972
|
+
- Strict validation (throws errors instead of warnings)
|
|
973
|
+
- Session state visible in console
|
|
974
|
+
- Persistent across page reloads (sessionStorage)
|
|
400
975
|
|
|
401
|
-
|
|
402
|
-
- Strict validation: invalid custom-event payloads throw instead of being silently dropped
|
|
403
|
-
- Persists across navigations within the same tab (cleared on tab close)
|
|
976
|
+
**→ [QA Mode Documentation](./API_REFERENCE.md#setqamodeenabled-boolean-void)**
|
|
404
977
|
|
|
405
978
|
---
|
|
406
979
|
|
|
@@ -411,7 +984,7 @@ QA mode logs custom events to the browser console so you can verify tracking imp
|
|
|
411
984
|
- Safari 12+
|
|
412
985
|
- Edge 79+
|
|
413
986
|
|
|
414
|
-
**SSR/SSG
|
|
987
|
+
**SSR/SSG Compatible:** Safe to import in Angular Universal, Next.js, Nuxt, SvelteKit (no-ops in Node.js).
|
|
415
988
|
|
|
416
989
|
---
|
|
417
990
|
|
|
@@ -419,9 +992,9 @@ QA mode logs custom events to the browser console so you can verify tracking imp
|
|
|
419
992
|
|
|
420
993
|
```bash
|
|
421
994
|
npm install # Install dependencies
|
|
422
|
-
npm run build:all # Build ESM + CJS +
|
|
995
|
+
npm run build:all # Build ESM + CJS + Browser bundles
|
|
423
996
|
npm run check # Lint + format validation
|
|
424
|
-
npm test
|
|
997
|
+
npm run test # Run all tests
|
|
425
998
|
npm run test:coverage # Generate coverage report
|
|
426
999
|
```
|
|
427
1000
|
|
|
@@ -433,10 +1006,10 @@ npm run test:coverage # Generate coverage report
|
|
|
433
1006
|
|
|
434
1007
|
| Document | Description |
|
|
435
1008
|
|----------|-------------|
|
|
436
|
-
| **[API Reference](./API_REFERENCE.md)** | Complete API documentation
|
|
437
|
-
| **[Best Practices](./BEST_PRACTICES.md)** | Patterns, anti-patterns, optimization tips |
|
|
438
|
-
| **[Security Guide](./SECURITY.md)** | Privacy, GDPR compliance, security
|
|
439
|
-
| **[Changelog](./CHANGELOG.md)** | Release history |
|
|
1009
|
+
| **[API Reference](./API_REFERENCE.md)** | Complete API documentation with all methods, config options, and event types |
|
|
1010
|
+
| **[Best Practices](./BEST_PRACTICES.md)** | Patterns, anti-patterns, and optimization tips |
|
|
1011
|
+
| **[Security Guide](./SECURITY.md)** | Privacy, GDPR compliance, and security best practices |
|
|
1012
|
+
| **[Changelog](./CHANGELOG.md)** | Release history and migration guides |
|
|
440
1013
|
| **[Handlers](./src/handlers/README.md)** | Event capture implementation details |
|
|
441
1014
|
| **[Managers](./src/managers/README.md)** | Core component architecture |
|
|
442
1015
|
|