@tracelog/lib 0.8.3 → 0.9.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.
Files changed (76) hide show
  1. package/README.md +142 -14
  2. package/dist/browser/tracelog.esm.js +561 -509
  3. package/dist/browser/tracelog.esm.js.map +1 -1
  4. package/dist/browser/tracelog.js +2 -2
  5. package/dist/browser/tracelog.js.map +1 -1
  6. package/dist/cjs/api.js +0 -2
  7. package/dist/cjs/api.js.map +1 -1
  8. package/dist/cjs/constants/config.constants.d.ts +2 -0
  9. package/dist/cjs/constants/config.constants.d.ts.map +1 -1
  10. package/dist/cjs/constants/config.constants.js +2 -0
  11. package/dist/cjs/constants/config.constants.js.map +1 -1
  12. package/dist/cjs/constants/error.constants.d.ts +1 -1
  13. package/dist/cjs/constants/error.constants.d.ts.map +1 -1
  14. package/dist/cjs/constants/error.constants.js +1 -1
  15. package/dist/cjs/constants/error.constants.js.map +1 -1
  16. package/dist/cjs/handlers/error.handler.d.ts.map +1 -1
  17. package/dist/cjs/handlers/error.handler.js +1 -5
  18. package/dist/cjs/handlers/error.handler.js.map +1 -1
  19. package/dist/cjs/handlers/performance.handler.d.ts.map +1 -1
  20. package/dist/cjs/handlers/performance.handler.js +0 -6
  21. package/dist/cjs/handlers/performance.handler.js.map +1 -1
  22. package/dist/cjs/handlers/scroll.handler.d.ts +9 -2
  23. package/dist/cjs/handlers/scroll.handler.d.ts.map +1 -1
  24. package/dist/cjs/handlers/scroll.handler.js +133 -35
  25. package/dist/cjs/handlers/scroll.handler.js.map +1 -1
  26. package/dist/cjs/managers/event.manager.d.ts.map +1 -1
  27. package/dist/cjs/managers/event.manager.js +0 -14
  28. package/dist/cjs/managers/event.manager.js.map +1 -1
  29. package/dist/cjs/managers/sender.manager.d.ts.map +1 -1
  30. package/dist/cjs/managers/sender.manager.js +1 -15
  31. package/dist/cjs/managers/sender.manager.js.map +1 -1
  32. package/dist/cjs/types/config.types.d.ts +4 -4
  33. package/dist/cjs/types/config.types.d.ts.map +1 -1
  34. package/dist/cjs/types/event.types.d.ts +18 -0
  35. package/dist/cjs/types/event.types.d.ts.map +1 -1
  36. package/dist/cjs/types/event.types.js +9 -0
  37. package/dist/cjs/types/event.types.js.map +1 -1
  38. package/dist/cjs/utils/validations/config-validations.utils.d.ts.map +1 -1
  39. package/dist/cjs/utils/validations/config-validations.utils.js +15 -77
  40. package/dist/cjs/utils/validations/config-validations.utils.js.map +1 -1
  41. package/dist/esm/api.js +0 -2
  42. package/dist/esm/api.js.map +1 -1
  43. package/dist/esm/constants/config.constants.d.ts +2 -0
  44. package/dist/esm/constants/config.constants.d.ts.map +1 -1
  45. package/dist/esm/constants/config.constants.js +2 -0
  46. package/dist/esm/constants/config.constants.js.map +1 -1
  47. package/dist/esm/constants/error.constants.d.ts +1 -1
  48. package/dist/esm/constants/error.constants.d.ts.map +1 -1
  49. package/dist/esm/constants/error.constants.js +1 -1
  50. package/dist/esm/constants/error.constants.js.map +1 -1
  51. package/dist/esm/handlers/error.handler.d.ts.map +1 -1
  52. package/dist/esm/handlers/error.handler.js +2 -6
  53. package/dist/esm/handlers/error.handler.js.map +1 -1
  54. package/dist/esm/handlers/performance.handler.d.ts.map +1 -1
  55. package/dist/esm/handlers/performance.handler.js +0 -6
  56. package/dist/esm/handlers/performance.handler.js.map +1 -1
  57. package/dist/esm/handlers/scroll.handler.d.ts +9 -2
  58. package/dist/esm/handlers/scroll.handler.d.ts.map +1 -1
  59. package/dist/esm/handlers/scroll.handler.js +133 -35
  60. package/dist/esm/handlers/scroll.handler.js.map +1 -1
  61. package/dist/esm/managers/event.manager.d.ts.map +1 -1
  62. package/dist/esm/managers/event.manager.js +0 -14
  63. package/dist/esm/managers/event.manager.js.map +1 -1
  64. package/dist/esm/managers/sender.manager.d.ts.map +1 -1
  65. package/dist/esm/managers/sender.manager.js +1 -15
  66. package/dist/esm/managers/sender.manager.js.map +1 -1
  67. package/dist/esm/types/config.types.d.ts +4 -4
  68. package/dist/esm/types/config.types.d.ts.map +1 -1
  69. package/dist/esm/types/event.types.d.ts +18 -0
  70. package/dist/esm/types/event.types.d.ts.map +1 -1
  71. package/dist/esm/types/event.types.js +7 -0
  72. package/dist/esm/types/event.types.js.map +1 -1
  73. package/dist/esm/utils/validations/config-validations.utils.d.ts.map +1 -1
  74. package/dist/esm/utils/validations/config-validations.utils.js +15 -77
  75. package/dist/esm/utils/validations/config-validations.utils.js.map +1 -1
  76. package/package.json +1 -1
package/README.md CHANGED
@@ -5,12 +5,15 @@ A lightweight TypeScript library for web analytics and user behavior tracking. A
5
5
  ## Features
6
6
 
7
7
  - **Zero-config tracking** - Automatically captures clicks, scrolls, page navigation, and web vitals out of the box.
8
- - **Cross-tab session management** - Maintains consistent user sessions across multiple browser tabs with automatic recovery.
8
+ - **Cross-tab session management** - Maintains consistent user sessions across multiple browser tabs with BroadcastChannel API and automatic localStorage recovery.
9
9
  - **Client-only architecture** - Fully autonomous with optional backend integrations (TraceLog SaaS, custom API, Google Analytics).
10
- - **Privacy-first** - Built-in PII sanitization and client-side sampling controls.
10
+ - **Privacy-first** - Built-in PII sanitization (emails, phone numbers, credit cards, API keys) and client-side sampling controls.
11
11
  - **Framework agnostic** - Works with vanilla JS, React, Vue, Angular, or any web application.
12
12
  - **Lightweight** - Only one dependency (`web-vitals`) with dual ESM/CJS support.
13
13
  - **Event-driven** - Real-time event subscription with `on()` and `off()` methods for custom integrations.
14
+ - **Rate limiting** - Client-side rate limiting (200 events/second) with exemptions for critical events (SESSION_START/END).
15
+ - **Event recovery** - Automatic recovery of persisted events from localStorage after crashes or network failures.
16
+ - **Smart filtering** - Threshold-based web vitals reporting, deduplication (10px click precision), and intelligent queue management.
14
17
 
15
18
  ## Installation
16
19
 
@@ -140,18 +143,23 @@ await tracelog.init({
140
143
  - `destroy(): Promise<void>` - Clean up and remove listeners
141
144
 
142
145
  **Config (all optional):**
143
- - `sessionTimeout`: Session timeout in ms (default: 900000)
146
+ - `sessionTimeout`: Session timeout in ms (default: 900000 / 15 minutes, range: 30s - 24 hours)
144
147
  - `globalMetadata`: Metadata attached to all events
145
148
  - `samplingRate`: Event sampling rate 0-1 (default: 1.0)
146
- - `errorSampling`: Error sampling rate 0-1 (default: 1.0)
149
+ - `errorSampling`: Error sampling rate 0-1 (default: 1.0 / 100%)
147
150
  - `sensitiveQueryParams`: Query params to remove from URLs
148
- - `scrollContainerSelectors`: Custom scroll containers
151
+ - `primaryScrollSelector`: Override automatic primary scroll container detection (e.g., `.mat-sidenav-content`, `window`)
149
152
  - `integrations`:
150
153
  - `tracelog.projectId`: TraceLog SaaS
151
154
  - `custom.collectApiUrl`: Custom backend
152
155
  - `custom.allowHttp`: Enable HTTP for testing
153
156
  - `googleAnalytics.measurementId`: GA4
154
157
 
158
+ **📚 For detailed configuration and implementation, see:**
159
+ - [Handlers Documentation](./src/handlers/README.md) - Event capture logic
160
+ - [Managers Documentation](./src/managers/README.md) - Core components
161
+ - [Listeners Documentation](./src/listeners/README.md) - Activity tracking
162
+
155
163
  ## Event Data Structure
156
164
 
157
165
  Each event contains a base structure with type-specific data:
@@ -185,20 +193,40 @@ Each event contains a base structure with type-specific data:
185
193
  - **`SCROLL`**: Scroll engagement
186
194
  - `scroll_data.depth`: Scroll depth percentage (0-100)
187
195
  - `scroll_data.direction`: Scroll direction (up/down)
196
+ - `scroll_data.container_selector`: CSS selector identifying scroll container (e.g., `window`, `.mat-sidenav-content`, `#main`)
197
+ - `scroll_data.is_primary`: Whether this is the main scroll container (true for primary content, false for sidebars/modals)
198
+ - `scroll_data.velocity`: Scroll speed in pixels per second (for engagement analysis)
199
+ - `scroll_data.max_depth_reached`: Maximum scroll depth reached in current session (0-100)
200
+
201
+ ### Scroll Container Detection
202
+
203
+ **Primary Container Logic**:
204
+ - If `window` is scrollable → `window` is primary (`is_primary: true`)
205
+ - If `window` NOT scrollable → First detected container is primary (e.g., `.mat-sidenav-content` in Angular Material)
206
+ - All other containers are secondary (`is_primary: false`)
188
207
 
189
- - **`SESSION_START`**: Session initialization
190
- - No additional data
208
+ **Important Notes**:
209
+ - `is_primary` is calculated ONCE when container is detected
210
+ - Does not re-calculate if layout changes dynamically during session
211
+ - Pages without scroll: Window is marked primary but generates no events (scroll impossible)
212
+ - Historical events maintain their original `is_primary` value for consistency
191
213
 
192
- - **`SESSION_END`**: Session termination
193
- - `session_end_reason`: Reason (timeout, manual, tab_close)
214
+ - **`SESSION_START`**: Session initialization (cross-tab synchronized)
215
+ - Session ID format: `{timestamp}-{9-char-base36}` (e.g., `1728488234567-kx9f2m1bq`)
216
+ - See [SessionManager docs](./src/managers/README.md#sessionmanager) for cross-tab sync details
217
+
218
+ - **`SESSION_END`**: Session termination (synchronous flush before page unload)
219
+ - `session_end_reason`: `inactivity`, `page_unload`, `manual_stop`, `orphaned_cleanup`, or `tab_closed`
194
220
 
195
221
  - **`CUSTOM`**: Business-specific events
196
222
  - `custom_event.name`: Event name
197
223
  - `custom_event.metadata`: Custom data (any JSON-serializable value)
198
224
 
199
- - **`WEB_VITALS`**: Performance metrics
225
+ - **`WEB_VITALS`**: Performance metrics (only sent when exceeding quality thresholds)
200
226
  - `web_vitals.type`: Metric type (LCP, CLS, INP, FCP, TTFB, LONG_TASK)
201
- - `web_vitals.value`: Metric value in milliseconds
227
+ - `web_vitals.value`: Metric value in milliseconds or unitless (CLS)
228
+ - **Thresholds**: LCP >4000ms, FCP >1800ms, CLS >0.25, INP >200ms, TTFB >800ms, LONG_TASK >50ms
229
+ - See [PerformanceHandler docs](./src/handlers/README.md#performancehandler) for details
202
230
 
203
231
  - **`ERROR`**: JavaScript errors
204
232
  - `error_data.type`: Error type (js_error, promise_rejection)
@@ -218,6 +246,67 @@ await tracelog.init();
218
246
 
219
247
  **Note**: Listeners are buffered if registered before `init()`, ensuring you don't miss initial events.
220
248
 
249
+ **Filter scroll events by primary container:**
250
+ ```typescript
251
+ tracelog.on('event', (event) => {
252
+ if (event.type === 'scroll') {
253
+ const { is_primary, container_selector, depth, velocity } = event.scroll_data;
254
+
255
+ if (is_primary) {
256
+ // Track main content engagement
257
+ console.log(`Primary scroll (${container_selector}): ${depth}%`);
258
+
259
+ // Identify engaged users (slow scroll = reading)
260
+ if (velocity < 500 && depth > 50) {
261
+ console.log('User is reading deeply');
262
+ }
263
+
264
+ // Identify bouncing users (fast scroll = scanning)
265
+ if (velocity > 2000) {
266
+ console.log('User is quickly scanning');
267
+ }
268
+ } else {
269
+ // Track auxiliary UI interaction
270
+ console.log(`Secondary scroll (${container_selector}): ${depth}%`);
271
+ }
272
+ }
273
+ });
274
+ ```
275
+
276
+ **Analytics queries example:**
277
+ ```typescript
278
+ // In your analytics backend
279
+ const primaryScrollEvents = events.filter(e =>
280
+ e.type === 'scroll' && e.scroll_data.is_primary
281
+ );
282
+
283
+ const avgPrimaryDepth = primaryScrollEvents.reduce((sum, e) =>
284
+ sum + e.scroll_data.depth, 0
285
+ ) / primaryScrollEvents.length;
286
+
287
+ console.log(`Average primary content depth: ${avgPrimaryDepth}%`);
288
+ ```
289
+
290
+ **TypeScript type helpers:**
291
+ ```typescript
292
+ import { tracelog, isPrimaryScrollEvent, isSecondaryScrollEvent, EmitterEvent } from '@tracelog/lib';
293
+
294
+ tracelog.on(EmitterEvent.EVENT, (event) => {
295
+ // Type guard provides type safety
296
+ if (isPrimaryScrollEvent(event)) {
297
+ // TypeScript knows event.scroll_data.is_primary === true
298
+ const depth = event.scroll_data.depth;
299
+ console.log(`Primary scroll depth: ${depth}%`);
300
+ }
301
+
302
+ if (isSecondaryScrollEvent(event)) {
303
+ // TypeScript knows event.scroll_data.is_primary === false
304
+ const selector = event.scroll_data.container_selector;
305
+ console.log(`Secondary UI scroll: ${selector}`);
306
+ }
307
+ });
308
+ ```
309
+
221
310
  **Multiple integrations:**
222
311
  ```typescript
223
312
  await tracelog.init({
@@ -233,6 +322,32 @@ await tracelog.init({
233
322
  window.__traceLogDisabled = true;
234
323
  ```
235
324
 
325
+ ### Manual Primary Container Selection
326
+
327
+ For edge cases requiring manual override of automatic primary container detection:
328
+
329
+ ```typescript
330
+ await tracelog.init({
331
+ primaryScrollSelector: '#custom-content' // Override auto-detection
332
+ });
333
+
334
+ // To mark window as primary:
335
+ await tracelog.init({
336
+ primaryScrollSelector: 'window'
337
+ });
338
+ ```
339
+
340
+ **When to use**: In 99% of cases, automatic detection is sufficient. Use manual selection only when:
341
+ - You need to override automatic detection for a specific business requirement
342
+ - Your UI has multiple main content areas and you want to prioritize one
343
+
344
+ **Important Notes:**
345
+ - `primaryScrollSelector` is applied during `init()` and affects all subsequently detected containers
346
+ - The selector is applied BEFORE automatic detection to ensure priority and avoid race conditions
347
+ - If you call `init()` multiple times, only the LAST `primaryScrollSelector` will be active
348
+ - To change primary container dynamically, call `destroy()` then `init()` with new selector
349
+ - Calling `init()` does NOT clear previously detected containers unless `destroy()` is called first
350
+
236
351
  ## Compatibility
237
352
 
238
353
  - **Runtime**: Modern browsers (Chrome 60+, Firefox 55+, Safari 12+)
@@ -268,14 +383,18 @@ TraceLog uses a hybrid sendBeacon/fetch strategy with intelligent error handling
268
383
  - **Permanent errors (4xx)**: Events are discarded immediately
269
384
  - `400 Bad Request`, `403 Forbidden`, `404 Not Found` → No persistence, no retry
270
385
  - Prevents infinite retry loops for configuration issues (e.g., excluded IPs, invalid projects)
386
+ - Throttled logging (1 log per status code per minute) to prevent console spam
271
387
 
272
388
  - **Temporary errors (5xx, network failures)**: Events persist in localStorage
273
389
  - `500`, `502`, `503`, `504` → Events saved for recovery on next page load
274
390
  - Network failures → Events persist and recover automatically
275
- - No automatic in-session retries to avoid performance impact
391
+ - **No automatic in-session retries** to avoid performance impact and battery drain
392
+ - Recovery attempted on next page load via `recoverPersistedEvents()`
276
393
 
277
394
  - **Event expiry**: Persisted events expire after 2 hours to prevent stale data recovery
278
395
 
396
+ See [SenderManager docs](./src/managers/README.md#sendermanager) for complete error handling details.
397
+
279
398
  ### sendBeacon Limitations
280
399
 
281
400
  When using `sendBeacon` (page unload scenarios):
@@ -286,11 +405,20 @@ When using `sendBeacon` (page unload scenarios):
286
405
 
287
406
  ## Troubleshooting
288
407
 
289
- - **Session issues**: Check localStorage availability and session timeout
408
+ - **Session issues**: Check localStorage availability and session timeout. See [SessionManager docs](./src/managers/README.md#sessionmanager)
290
409
  - **Memory usage**: Reduce `sessionTimeout`, lower `samplingRate`, call `destroy()` on cleanup
291
- - **Events not sending**: Check browser console for `PermanentError` logs indicating 4xx errors
410
+ - **Events not sending**:
411
+ - Check browser console for `PermanentError` logs indicating 4xx errors
412
+ - Verify integration configuration (`projectId` or `collectApiUrl`)
413
+ - Check network tab for failed requests to `/collect` endpoint
414
+ - **Rate limiting**: If events are being dropped, check console for rate limit warnings (200 events/second max)
415
+ - **Scroll detection issues**: Use `primaryScrollSelector` to manually specify main content container
292
416
  - **CI failures**: Verify Playwright installation and Node.js ≥20
293
417
 
418
+ **Detailed troubleshooting guides:**
419
+ - [Handlers troubleshooting](./src/handlers/README.md) - Event capture issues
420
+ - [Managers troubleshooting](./src/managers/README.md) - State, storage, and network issues
421
+
294
422
  ## Development & Contributing
295
423
 
296
424
  ### Development Setup