@tracelog/lib 2.10.0 → 3.0.0-rc.115.6

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  # TraceLog
2
2
 
3
- Lightweight web analytics library for tracking user behavior. Works standalone or with optional backend integrations.
3
+ Lightweight web analytics library for tracking user behavior. Works standalone or with the TraceLog SaaS backend.
4
4
 
5
5
  ## Features
6
6
 
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
7
+ - **Zero-config** Auto-captures clicks, scrolls, page views, sessions, performance metrics, and JavaScript errors
8
+ - **Standalone-first** Works without any backend; opt in to TraceLog SaaS by passing a `projectId`
9
+ - **Privacy-first** PII sanitization, client-side sampling, `data-tlog-ignore` attribute, sensitive URL param stripping
10
+ - **Cross-tab sessions** BroadcastChannel sync with localStorage + sessionStorage recovery
11
+ - **Event-driven** Subscribe via `on()` / `off()` for real-time consumption
12
+ - **Lightweight** Single dependency (`web-vitals`), ~62KB gzipped
13
13
 
14
14
  ## Live Demo
15
15
 
@@ -20,6 +20,7 @@ Lightweight web analytics library for tracking user behavior. Works standalone o
20
20
  ## Installation
21
21
 
22
22
  ### NPM (Recommended)
23
+
23
24
  ```bash
24
25
  npm install @tracelog/lib
25
26
  ```
@@ -40,6 +41,7 @@ const { sessionId } = await tracelog.init({
40
41
  ```
41
42
 
42
43
  ### CDN (Script Tag)
44
+
43
45
  ```html
44
46
  <script src="https://cdn.jsdelivr.net/npm/@tracelog/lib@latest/dist/browser/tracelog.js"></script>
45
47
  <script>
@@ -48,6 +50,7 @@ const { sessionId } = await tracelog.init({
48
50
  ```
49
51
 
50
52
  ### CDN (ES Module)
53
+
51
54
  ```html
52
55
  <script type="module">
53
56
  import { tracelog } from 'https://cdn.jsdelivr.net/npm/@tracelog/lib@latest/dist/browser/tracelog.esm.js';
@@ -55,70 +58,52 @@ const { sessionId } = await tracelog.init({
55
58
  </script>
56
59
  ```
57
60
 
61
+ ---
62
+
58
63
  ## Quick Start
59
64
 
60
65
  ### Initialization Order
61
66
 
62
- **Important:** Set up listeners and transformers **before** calling `init()` to capture all events from the start:
67
+ Set up listeners **before** calling `init()` so they receive the initial `SESSION_START` and `PAGE_VIEW` events that fire during init.
63
68
 
64
69
  ```typescript
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
- }
70
+ // 1. Obtain user consent first (your responsibility — see "User Consent" below)
71
+ const hasConsent = await getUserConsent();
72
+ if (!hasConsent) return;
73
73
 
74
- // 2. Register event listeners SECOND (before init)
74
+ // 2. Register listeners (before init)
75
75
  tracelog.on('event', (event) => {
76
76
  console.log(event.type, event);
77
77
  });
78
78
 
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
- });
84
-
85
- // 4. Configure custom headers FOURTH (if using custom backend with auth)
86
- tracelog.setCustomHeaders(() => ({
87
- 'Authorization': `Bearer ${getAuthToken()}`
88
- }));
79
+ // 3. Identify the user (optional can be called before or after init)
80
+ tracelog.identify('cust_123', { name: 'Maria Garcia', plan: 'pro' });
89
81
 
90
- // 5. Initialize FIFTH (starts tracking immediately)
91
- await tracelog.init({
82
+ // 4. Initialize (starts tracking immediately)
83
+ const { sessionId } = await tracelog.init({
92
84
  integrations: {
93
- custom: { collectApiUrl: 'https://api.example.com' }
85
+ tracelog: { projectId: 'your-project-id' }
94
86
  }
95
87
  });
96
88
 
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
- });
89
+ // 5. Track custom events at any point after init
90
+ tracelog.event('button_clicked', { buttonId: 'signup-cta', source: 'homepage' });
105
91
 
106
- // 8. On logout: reset identity
92
+ // 6. On logout: reset identity (clears identity, regenerates UUID, opens a new session)
107
93
  await tracelog.resetIdentity();
108
94
 
109
- // 9. Cleanup (on consent revoke or app unmount)
95
+ // 7. Cleanup on consent revoke or app unmount
110
96
  tracelog.destroy();
111
97
  ```
112
98
 
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.
99
+ **Auto-captured events** (no code required):
114
100
 
115
- **That's it!** TraceLog now automatically tracks:
116
- - Page views & navigation (including SPA routes)
101
+ - Page views & navigation (including SPA route changes)
117
102
  - Click interactions
118
103
  - Scroll behavior
119
104
  - User sessions
120
105
  - Web Vitals (LCP, INP, CLS, FCP, TTFB)
121
- - JavaScript errors
106
+ - JavaScript errors & unhandled promise rejections
122
107
 
123
108
  ---
124
109
 
@@ -126,53 +111,40 @@ tracelog.destroy();
126
111
 
127
112
  | Method | Description |
128
113
  |--------|-------------|
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 |
114
+ | `init(config?)` | Initialize tracking. Returns `Promise<{ sessionId }>`. See [Configuration](#configuration). |
115
+ | `event(name, metadata?, options?)` | Track a custom event. `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`. |
116
+ | `on(event, callback)` | Subscribe to events (`'event'` or `'queue'`). Local consumption independent of backend sends. |
117
+ | `off(event, callback)` | Unsubscribe. Must pass the exact callback reference used in `on()`. |
118
+ | `identify(userId, traits?)` | Associate the current visitor with a known user identity. `traits` accepts a `Record<string, string>`; non-string values are dropped silently. |
119
+ | `resetIdentity()` | Flush pending events under the old identity, clear identity, regenerate the visitor UUID, and start a new session. Use for logout flows. |
120
+ | `isInitialized()` | `true` after a successful `init()`, `false` otherwise (including during teardown). |
121
+ | `getSessionId()` | Current session ID, or `null` if not initialized. |
122
+ | `getUserId()` | Current visitor UUID, or `null` if not initialized. |
123
+ | `destroy()` | Stop tracking, drain pending events via `sendBeacon`, and release all resources. |
147
124
 
148
125
  **→ [Complete API Reference](./API_REFERENCE.md)**
149
126
 
150
127
  ---
151
128
 
152
- ## User Consent Management
129
+ ## User Consent
153
130
 
154
- TraceLog requires you to obtain user consent **before** calling `init()`. The library does not include a built-in consent management system.
131
+ TraceLog does not ship a consent manager. You are responsible for obtaining consent **before** calling `init()`.
155
132
 
156
133
  ```typescript
157
- // Your responsibility: Obtain consent before initialization
158
134
  const userConsent = await showCookieBanner(); // Your consent solution
159
135
 
160
136
  if (userConsent.analytics) {
161
- // Initialize only after consent
162
137
  await tracelog.init({
163
- integrations: {
164
- tracelog: { projectId: 'your-project-id' }
165
- }
138
+ integrations: { tracelog: { projectId: 'your-project-id' } }
166
139
  });
167
140
  } else {
168
- // User rejected - don't initialize
169
- console.log('Analytics consent denied');
141
+ // User rejected don't initialize
170
142
  }
171
143
 
172
- // If user revokes consent later
144
+ // If consent is revoked later
173
145
  function handleConsentRevoke() {
174
- tracelog.destroy(); // Stop tracking immediately
175
- localStorage.clear(); // Clear stored session data
146
+ tracelog.destroy();
147
+ localStorage.clear();
176
148
  }
177
149
  ```
178
150
 
@@ -180,140 +152,76 @@ function handleConsentRevoke() {
180
152
 
181
153
  ## Configuration
182
154
 
183
- All configuration is **optional**. TraceLog works out-of-the-box with sensible defaults.
155
+ All configuration is optional. TraceLog works out-of-the-box with sensible defaults.
184
156
 
185
157
  ```typescript
186
158
  await tracelog.init({
187
159
  // Session
188
- sessionTimeout: 900000, // 15 min (default)
160
+ sessionTimeout: 900000, // 15 min (default)
189
161
 
190
162
  // Privacy
191
- samplingRate: 1.0, // 100% (default)
192
- sensitiveQueryParams: ['token'], // Add to defaults
163
+ samplingRate: 1.0, // 100% (default)
164
+ errorSampling: 1.0, // 100% (default)
165
+ sensitiveQueryParams: ['token'], // Added to the 15-param default deny-list
193
166
 
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)
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
197
171
 
198
- // Integrations (pick one, multiple, or none)
199
- integrations: {
200
- tracelog: { projectId: 'your-id' }, // TraceLog SaaS
201
- custom: { collectApiUrl: 'https://api.com' }, // Custom backend
172
+ // Batch flush
173
+ sendIntervalMs: 10000, // Default batch interval
174
+ flushOnSpaNavigation: false, // Opt-in: flush after pushState / replaceState / popstate / hashchange
175
+ flushOnPageHidden: true, // Flush when document.hidden becomes true (mobile Safari coverage)
202
176
 
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
207
- },
177
+ // Web Vitals
178
+ webVitalsMode: 'needs-improvement', // 'all' | 'needs-improvement' | 'poor'
179
+ webVitalsThresholds: { LCP: 2500 }, // Optional per-metric overrides
208
180
 
209
- // Web Vitals filtering
210
- webVitalsMode: 'needs-improvement', // 'all' | 'needs-improvement' | 'poor'
181
+ // Global metadata appended to every event
182
+ globalMetadata: {
183
+ env: 'production',
184
+ version: '1.2.0',
185
+ appName: 'MyApp'
186
+ },
211
187
 
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
188
+ // Integration (omit for standalone mode)
189
+ integrations: {
190
+ tracelog: {
191
+ projectId: 'your-project-id',
192
+ shopify: false // Optional: enable Shopify cart attribute linking
193
+ }
217
194
  }
218
195
  });
219
196
  ```
220
197
 
221
- **→ [Full Configuration Guide](./API_REFERENCE.md#configuration)**
198
+ **→ [Full Configuration Reference](./API_REFERENCE.md#configuration)**
222
199
 
223
200
  ---
224
201
 
225
202
  ## Automatic Event Types
226
203
 
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.
204
+ | Event Type | What It Tracks |
205
+ |-----------------|-------------------------------------------------|
206
+ | `page_view` | Initial load, SPA route changes, hash changes |
207
+ | `click` | User interactions with elements |
208
+ | `session_start` | New session creation (server infers session end)|
209
+ | `scroll` | Depth and direction per scrollable container |
210
+ | `web_vitals` | Core Web Vitals (LCP, INP, CLS, FCP, TTFB) |
211
+ | `error` | JavaScript errors, unhandled promise rejections |
300
212
 
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
213
+ **Custom events:**
307
214
 
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:**
311
215
  ```typescript
312
216
  tracelog.event('purchase_completed', {
313
217
  orderId: 'ord-123',
314
218
  total: 99.99,
315
219
  currency: 'USD'
316
220
  });
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';
317
225
  ```
318
226
 
319
227
  **→ [Event Types Reference](./API_REFERENCE.md#event-types)**
@@ -322,441 +230,37 @@ tracelog.event('purchase_completed', {
322
230
 
323
231
  ## Global Metadata
324
232
 
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:
233
+ Set at `init()` time and attached to every event the library sends. Replace it by destroying and re-initializing TraceLog with a new config (typical at login / logout).
330
234
 
331
235
  ```typescript
332
236
  await tracelog.init({
333
237
  globalMetadata: {
334
238
  env: 'production',
335
239
  version: '1.2.0',
336
- appName: 'MyApp'
240
+ plan: user?.plan ?? 'anonymous'
337
241
  }
338
242
  });
339
243
  ```
340
244
 
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
245
+ **Validation rules:**
406
246
 
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
247
+ - **Allowed types**: primitives (string, number, boolean), string arrays, nested objects (up to 10 levels)
248
+ - **Disallowed**: functions, symbols, `undefined`, circular references
249
+ - **Limits**: max 100 keys, 48 KB serialized size, 500 items per array, 1000 chars per string
410
250
 
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)**
251
+ **→ [Metadata Reference](./API_REFERENCE.md#globalmetadata)**
745
252
 
746
253
  ---
747
254
 
748
255
  ## Integration Modes
749
256
 
750
- TraceLog supports multiple integration modes. Choose what fits your needs:
751
-
752
- ### 1. Standalone (No Backend)
257
+ ### 1. Standalone (no backend)
753
258
 
754
- **Default mode when no integrations configured.** Events captured and emitted locally without network requests.
259
+ Default when no `integrations` are configured. Events are captured, queued, and emitted locally no network requests.
755
260
 
756
261
  ```typescript
757
262
  await tracelog.init();
758
263
 
759
- // Consume events locally
760
264
  tracelog.on('event', (event) => {
761
265
  myAnalytics.track(event);
762
266
  });
@@ -766,143 +270,76 @@ tracelog.on('queue', (batch) => {
766
270
  });
767
271
  ```
768
272
 
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
273
+ Perfect for custom analytics pipelines, testing, or privacy-focused implementations where you want to ship events to your own destination via a listener.
776
274
 
777
275
  ### 2. TraceLog SaaS
778
- ```typescript
779
- await tracelog.init({
780
- integrations: {
781
- tracelog: { projectId: 'your-project-id' }
782
- }
783
- });
784
- ```
785
276
 
786
- ### 3. Custom Backend
787
277
  ```typescript
788
278
  await tracelog.init({
789
279
  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
- }
280
+ tracelog: { projectId: 'your-project-id' }
795
281
  }
796
282
  });
797
283
  ```
798
284
 
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
- ```
285
+ **Domain requirement.** The SaaS endpoint is derived from the host page's domain (`https://{projectId}.{rootDomain}/collect`), so `init()` rejects when called from `localhost` or a raw IP address. For local development, omit `integrations.tracelog` to run in standalone mode, or test against a staging domain mapped via `/etc/hosts`.
813
286
 
814
- **→ [Integration Setup Guide](./API_REFERENCE.md#integration-configuration)**
287
+ **→ [Integration Reference](./API_REFERENCE.md#integration-configuration)**
815
288
 
816
289
  ---
817
290
 
818
291
  ## Error Handling & Reliability
819
292
 
820
- TraceLog implements intelligent error handling with automatic retries for transient failures:
293
+ ### Automatic retry strategy
821
294
 
822
- ### Automatic Retry Strategy
295
+ **Transient errors** (5xx, timeouts, network failures):
823
296
 
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
297
+ - Up to 2 retry attempts (3 total)
298
+ - Exponential backoff with jitter (200–300 ms, 400–500 ms)
299
+ - Persisted to `localStorage` after exhaustion for next-page recovery
829
300
 
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
301
+ **Rate limit (429):**
835
302
 
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
303
+ - No in-session retries arms a 60-second cooldown instead
304
+ - Cooldown is mirrored to `localStorage` and shared across tabs/windows on the same origin (prevents every fresh page load from hammering the server during its 429 window)
305
+ - Events are persisted immediately and retried once the cooldown elapses
306
+ - The backend deduplicates retries via the batch idempotency token
840
307
 
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
308
+ **Permanent errors** (4xx except 408, 429):
853
309
 
854
- // If custom backend succeeds:
855
- // - Events removed from queue (optimistic removal)
856
- // - Failed integration recovered on next page load
857
- ```
310
+ - No retries events are discarded
311
+ - 408 Request Timeout is treated as transient
858
312
 
859
- ### Error Classification
313
+ ### Error classification
860
314
 
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 |
315
+ | Status | Type | Retries | Persistence |
316
+ |-----------------------|-------------|------------------|-------------------|
317
+ | **2xx** | Success | None | Cleared |
318
+ | **4xx** (except 408/429) | Permanent | ❌ None | ❌ Discarded |
319
+ | **408** | Transient | ✅ Up to 2 | ✅ After exhaustion |
320
+ | **429** | Rate Limit | None (60s cooldown, shared across tabs) | ✅ Immediate |
321
+ | **5xx** | Transient | ✅ Up to 2 | ✅ After exhaustion |
322
+ | **Network error** | Transient | ✅ Up to 2 | ✅ After exhaustion |
323
+ | **Timeout** | Transient | ✅ Up to 2 | ✅ After exhaustion |
870
324
 
871
- ### Optimistic Queue Management
325
+ ### Recovery on page load
872
326
 
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)
327
+ Failed events are recovered automatically on the next `init()`:
877
328
 
878
- **Example Scenario:**
879
329
  ```typescript
880
- // SaaS succeeds immediatelyno 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
330
+ // Page 1: events fail to send (5xx after retries) persisted with idempotency token
331
+ // Page 2: init() recovers and resends; backend deduplicates by idempotency token
884
332
  ```
885
333
 
886
- ### Recovery on Page Load
334
+ Multi-tab protection: a 1-second window prevents two tabs from re-sending the same persisted batch simultaneously.
887
335
 
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
- ```
336
+ **Circuit breaker.** After `MAX_CONSECUTIVE_NETWORK_FAILURES` consecutive network-level failures (DNS, connection refused), the sender opens its circuit and skips further requests until `CIRCUIT_BREAKER_COOLDOWN_MS` elapses. A single probe request is then allowed (half-open state) before fully closing.
900
337
 
901
338
  **→ [Full Error Handling Reference](./API_REFERENCE.md#error-handling)**
902
339
 
903
- ### Session Continuity (External Redirects)
340
+ ### Session continuity across external redirects
904
341
 
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.
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.
906
343
 
907
344
  ```typescript
908
345
  // No special handling needed before redirect
@@ -914,7 +351,7 @@ tracelog.event('purchase', { orderId: '12345', amount: 99.99 });
914
351
  // Same session as before the redirect
915
352
  ```
916
353
 
917
- - Automatic: no API calls or developer action required
354
+ - Automatic no API calls or developer action required
918
355
  - `sessionStorage` mirror survives same-tab navigation (cleared on tab close)
919
356
  - Session timeout still applies (expired sessions are not recovered)
920
357
 
@@ -922,26 +359,26 @@ tracelog.event('purchase', { orderId: '12345', amount: 99.99 });
922
359
 
923
360
  ## Privacy & Security
924
361
 
925
- TraceLog is **privacy-first** by design:
362
+ TraceLog is privacy-first by design:
926
363
 
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
364
+ - ✅ **PII sanitization** auto-redacts emails, phones, credit cards, IBANs, API keys, bearer tokens, and connection-string passwords from click text and error messages
365
+ - ✅ **Input protection** never captures values from `<input>`, `<textarea>`, `<select>`
366
+ - ✅ **URL filtering** removes 15 default sensitive query params (token, password, auth, secret, api_key, …) plus any you add via `sensitiveQueryParams`
367
+ - ✅ **Element exclusion** use `data-tlog-ignore` on any container to exclude its contents from click tracking
368
+ - ✅ **Client-side controls** sampling, dedup, and validation all happen in the browser
932
369
 
933
- **Example:**
934
370
  ```html
935
- <!-- Exclude sensitive forms -->
371
+ <!-- Exclude sensitive forms entirely -->
936
372
  <div data-tlog-ignore>
937
373
  <input type="password" name="password">
938
374
  <input type="text" name="credit_card">
939
375
  </div>
940
376
  ```
941
377
 
942
- **Your Responsibilities:**
943
- - Get user consent before calling `init()` (GDPR/CCPA)
944
- - Sanitize custom event metadata (avoid PII)
378
+ **Your responsibilities:**
379
+
380
+ - Obtain user consent before calling `init()` (GDPR / CCPA / LOPD)
381
+ - Avoid PII in custom event metadata (TraceLog only sanitizes element text and error messages)
945
382
  - Call `destroy()` on consent revoke
946
383
 
947
384
  **→ [Complete Security Guide](./SECURITY.md)**
@@ -950,30 +387,20 @@ TraceLog is **privacy-first** by design:
950
387
 
951
388
  ## QA Mode
952
389
 
953
- Enable QA mode for debugging and development:
390
+ QA mode logs custom events to the browser console so you can verify tracking implementation without inspecting the network tab.
954
391
 
955
- ### URL Activation
956
- ```bash
957
- # Enable
958
- ?tlog_mode=qa
392
+ ### URL activation
959
393
 
960
- # Disable
961
- ?tlog_mode=qa_off
962
- ```
963
-
964
- ### Programmatic API
965
- ```typescript
966
- tracelog.setQaMode(true); // Enable
967
- tracelog.setQaMode(false); // Disable
394
+ ```text
395
+ ?tlog_mode=qa # Enable (persists in sessionStorage for the tab)
396
+ ?tlog_mode=qa_off # Disable
968
397
  ```
969
398
 
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)
399
+ **Effects in QA mode:**
975
400
 
976
- **→ [QA Mode Documentation](./API_REFERENCE.md#setqamodeenabled-boolean-void)**
401
+ - Custom events logged to console with their name and metadata
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)
977
404
 
978
405
  ---
979
406
 
@@ -984,7 +411,7 @@ tracelog.setQaMode(false); // Disable
984
411
  - Safari 12+
985
412
  - Edge 79+
986
413
 
987
- **SSR/SSG Compatible:** Safe to import in Angular Universal, Next.js, Nuxt, SvelteKit (no-ops in Node.js).
414
+ **SSR/SSG compatible** safe to import in Angular Universal, Next.js, Nuxt, SvelteKit. All methods no-op in Node.js.
988
415
 
989
416
  ---
990
417
 
@@ -992,9 +419,9 @@ tracelog.setQaMode(false); // Disable
992
419
 
993
420
  ```bash
994
421
  npm install # Install dependencies
995
- npm run build:all # Build ESM + CJS + Browser bundles
422
+ npm run build:all # Build ESM + CJS + browser bundles
996
423
  npm run check # Lint + format validation
997
- npm run test # Run all tests
424
+ npm test # Run all tests
998
425
  npm run test:coverage # Generate coverage report
999
426
  ```
1000
427
 
@@ -1006,10 +433,10 @@ npm run test:coverage # Generate coverage report
1006
433
 
1007
434
  | Document | Description |
1008
435
  |----------|-------------|
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 |
436
+ | **[API Reference](./API_REFERENCE.md)** | Complete API documentation: methods, config options, event types |
437
+ | **[Best Practices](./BEST_PRACTICES.md)** | Patterns, anti-patterns, optimization tips |
438
+ | **[Security Guide](./SECURITY.md)** | Privacy, GDPR compliance, security checklist |
439
+ | **[Changelog](./CHANGELOG.md)** | Release history |
1013
440
  | **[Handlers](./src/handlers/README.md)** | Event capture implementation details |
1014
441
  | **[Managers](./src/managers/README.md)** | Core component architecture |
1015
442