@tracelog/lib 2.8.5 → 2.9.0-rc.108.13
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 +7 -1
- package/dist/browser/tracelog-shopify-pixel.iife.js +1 -0
- package/dist/browser/tracelog-shopify-pixel.iife.js.map +1 -0
- package/dist/browser/tracelog.esm.js +1108 -857
- 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/pixel/index.cjs +3 -0
- package/dist/pixel/index.cjs.map +1 -0
- package/dist/pixel/index.d.mts +145 -0
- package/dist/pixel/index.d.ts +145 -0
- package/dist/pixel/index.js +3 -0
- package/dist/pixel/index.js.map +1 -0
- package/dist/public-api.cjs +2 -2
- package/dist/public-api.cjs.map +1 -1
- package/dist/public-api.d.mts +168 -5
- package/dist/public-api.d.ts +168 -5
- package/dist/public-api.js +2 -2
- package/dist/public-api.js.map +1 -1
- package/package.json +8 -2
package/dist/public-api.d.mts
CHANGED
|
@@ -301,6 +301,33 @@ interface CustomEventData {
|
|
|
301
301
|
/** Additional event metadata */
|
|
302
302
|
metadata?: Record<string, MetadataType> | Record<string, MetadataType>[];
|
|
303
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Optional flags for `tracelog.event()`.
|
|
306
|
+
*/
|
|
307
|
+
interface EventOptions {
|
|
308
|
+
/**
|
|
309
|
+
* If `true`, the event queue is flushed via `navigator.sendBeacon()`
|
|
310
|
+
* immediately after this event is tracked. The browser guarantees the
|
|
311
|
+
* request is queued for delivery even if the page is about to unload
|
|
312
|
+
* (e.g., `window.location.href = '/thanks'` right after tracking a
|
|
313
|
+
* purchase). Async fetch would be cancelled by the navigation;
|
|
314
|
+
* sendBeacon survives.
|
|
315
|
+
*
|
|
316
|
+
* Use for high-value events where loss is unacceptable (Purchase, Signup,
|
|
317
|
+
* AddPaymentInfo). The critical event plus any previously queued events
|
|
318
|
+
* are sent in a single request — this does not bypass the queue.
|
|
319
|
+
*
|
|
320
|
+
* **Limitations** (inherited from `sendBeacon`):
|
|
321
|
+
* - 64KB payload cap. If the combined queue + critical event exceeds it,
|
|
322
|
+
* the failed batch is persisted to localStorage and recovered on next
|
|
323
|
+
* `init()` via its idempotency token.
|
|
324
|
+
* - No retry on failure (fire-and-forget).
|
|
325
|
+
* - Custom headers are not applied (browser API limitation).
|
|
326
|
+
*
|
|
327
|
+
* @default false
|
|
328
|
+
*/
|
|
329
|
+
critical?: boolean;
|
|
330
|
+
}
|
|
304
331
|
/**
|
|
305
332
|
* Web performance metrics data
|
|
306
333
|
*/
|
|
@@ -318,6 +345,8 @@ interface ErrorData {
|
|
|
318
345
|
type: ErrorType;
|
|
319
346
|
/** Error message text */
|
|
320
347
|
message: string;
|
|
348
|
+
/** Error constructor name (TypeError, ReferenceError, etc.) when available */
|
|
349
|
+
name?: string;
|
|
321
350
|
/** Source file where error occurred */
|
|
322
351
|
filename?: string;
|
|
323
352
|
/** Line number in source file */
|
|
@@ -406,6 +435,22 @@ interface EventData {
|
|
|
406
435
|
/** Campaign tracking parameters */
|
|
407
436
|
utm?: UTM;
|
|
408
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Internal queue entry: an `EventData` enriched with the session ID frozen at
|
|
440
|
+
* `track()` time. Survives session renewal — when the user is idle past the
|
|
441
|
+
* timeout, `state.sessionId` is nulled but events already in the queue keep
|
|
442
|
+
* their original `_session_id`, so `EventManager.buildBatchesWithIds()` can
|
|
443
|
+
* still attribute them correctly instead of emitting `session_id: null` to the
|
|
444
|
+
* wire.
|
|
445
|
+
*
|
|
446
|
+
* **Internal only.** The leading underscore mirrors `EventsQueue._metadata` —
|
|
447
|
+
* this field is stripped from `events[]` at batch construction time, since the
|
|
448
|
+
* backend's `EventDto` uses `forbidNonWhitelisted: true` and the wrapper
|
|
449
|
+
* `EventsQueue.session_id` is the contract.
|
|
450
|
+
*/
|
|
451
|
+
interface QueuedEvent extends EventData {
|
|
452
|
+
_session_id: string;
|
|
453
|
+
}
|
|
409
454
|
|
|
410
455
|
/**
|
|
411
456
|
* Web Vitals filtering mode
|
|
@@ -449,6 +494,20 @@ interface Config {
|
|
|
449
494
|
webVitalsThresholds?: Partial<Record<WebVitalType, number>>;
|
|
450
495
|
/** Interval in milliseconds between event batch sends. @default 10000 (10 seconds) */
|
|
451
496
|
sendIntervalMs?: number;
|
|
497
|
+
/**
|
|
498
|
+
* If true, the event queue is flushed automatically after every SPA navigation
|
|
499
|
+
* (`pushState`, `replaceState`, `popstate`, `hashchange`). Prevents batch loss when the
|
|
500
|
+
* user closes the tab mid-buffer between SPA route changes. No-op for MPAs (no SPA nav).
|
|
501
|
+
* @default true
|
|
502
|
+
*/
|
|
503
|
+
flushOnSpaNavigation?: boolean;
|
|
504
|
+
/**
|
|
505
|
+
* If true, the event queue is flushed when `document.hidden` becomes `true`
|
|
506
|
+
* (tab switch, lock screen, app backgrounding). Especially relevant on mobile Safari
|
|
507
|
+
* where `pagehide`/`beforeunload` may not fire reliably.
|
|
508
|
+
* @default true
|
|
509
|
+
*/
|
|
510
|
+
flushOnPageHidden?: boolean;
|
|
452
511
|
/** Optional configuration for third-party integrations. */
|
|
453
512
|
integrations?: {
|
|
454
513
|
/** TraceLog integration options. */
|
|
@@ -1320,6 +1379,7 @@ declare class EventManager extends StateManager {
|
|
|
1320
1379
|
private rateLimitCounter;
|
|
1321
1380
|
private rateLimitWindowStart;
|
|
1322
1381
|
private lastSessionId;
|
|
1382
|
+
private pendingSyncFlush;
|
|
1323
1383
|
private sessionEventCounts;
|
|
1324
1384
|
private readonly saveSessionCountsDebounced;
|
|
1325
1385
|
/**
|
|
@@ -1527,6 +1587,44 @@ declare class EventManager extends StateManager {
|
|
|
1527
1587
|
* @see src/managers/README.md (lines 5-75) for flush details
|
|
1528
1588
|
*/
|
|
1529
1589
|
flushImmediatelySync(): boolean;
|
|
1590
|
+
/**
|
|
1591
|
+
* Sends ONLY the most recently queued event via `navigator.sendBeacon()` in
|
|
1592
|
+
* a dedicated single-event batch.
|
|
1593
|
+
*
|
|
1594
|
+
* **Purpose**: Guarantee delivery of an event that *must* survive an
|
|
1595
|
+
* imminent page unload (purchase confirmation, signup completion, etc.),
|
|
1596
|
+
* independent of the main queue's size or in-flight async send state.
|
|
1597
|
+
*
|
|
1598
|
+
* **Why a dedicated batch**: `flushImmediatelySync()` serialises the entire
|
|
1599
|
+
* queue into one `sendBeacon` call. If the queue is heavy (>64KB) the
|
|
1600
|
+
* beacon fails and the batch is persisted to `localStorage`, which is only
|
|
1601
|
+
* recovered on the next `init()` — useless for one-shot conversion events
|
|
1602
|
+
* where the user never returns.
|
|
1603
|
+
*
|
|
1604
|
+
* **Behaviour**:
|
|
1605
|
+
* - Reads the last entry of `eventsQueue` (the just-tracked event).
|
|
1606
|
+
* - Wraps it in its own `EventsQueue` and dispatches synchronously through
|
|
1607
|
+
* every `SenderManager` via `sendEventsQueueSync()`.
|
|
1608
|
+
* - Leaves the main queue untouched; the same event will be re-delivered by
|
|
1609
|
+
* the next periodic / unload flush. Idempotency is the caller-side
|
|
1610
|
+
* contract: the backend MUST deduplicate by `event.id` (e.g. unique index
|
|
1611
|
+
* on the ingestion collection) — same guarantee the library already
|
|
1612
|
+
* relies on for the persisted-events recovery path.
|
|
1613
|
+
*
|
|
1614
|
+
* **Use it after** `track()` so the event is in the queue. Caller is
|
|
1615
|
+
* responsible for verifying that `track()` actually queued the event
|
|
1616
|
+
* (it can be dropped by rate limiting / sampling / `beforeSend`).
|
|
1617
|
+
*
|
|
1618
|
+
* **Standalone mode** (no senders configured): returns `false` without
|
|
1619
|
+
* emitting. The subsequent main-queue drain (`flushImmediatelySync()`) is
|
|
1620
|
+
* responsible for delivering the event to local listeners — emitting here
|
|
1621
|
+
* too would surface the same event twice to a `tracelog.on('queue', ...)`
|
|
1622
|
+
* subscriber.
|
|
1623
|
+
*
|
|
1624
|
+
* @returns `true` if at least one sender delivered the beacon successfully,
|
|
1625
|
+
* `false` otherwise (including standalone mode — the drain handles it).
|
|
1626
|
+
*/
|
|
1627
|
+
flushLastEventSync(): boolean;
|
|
1530
1628
|
/**
|
|
1531
1629
|
* Sets the custom headers provider callback for the custom integration.
|
|
1532
1630
|
* Only affects requests to custom backend (not TraceLog SaaS).
|
|
@@ -1628,9 +1726,67 @@ declare class EventManager extends StateManager {
|
|
|
1628
1726
|
flushPendingEvents(): void;
|
|
1629
1727
|
private clearSendTimeout;
|
|
1630
1728
|
private isSuccessfulResult;
|
|
1729
|
+
/**
|
|
1730
|
+
* Groups the queue by frozen `_session_id`, preserving insertion order.
|
|
1731
|
+
* Single pass — `buildBatchesWithIds()` builds one batch + one eventIds list
|
|
1732
|
+
* per group, so the grouping cost is O(N) per flush regardless of session
|
|
1733
|
+
* count.
|
|
1734
|
+
*
|
|
1735
|
+
* **Self-heal**: any entry missing `_session_id` (an internal invariant
|
|
1736
|
+
* violation — `buildEventPayload` always stamps it) is removed from the
|
|
1737
|
+
* queue rather than left behind, otherwise a single corrupted entry would
|
|
1738
|
+
* keep `eventsQueue.length > 0` forever and re-trigger periodic sends.
|
|
1739
|
+
*/
|
|
1740
|
+
private groupQueuedEventsBySession;
|
|
1741
|
+
/**
|
|
1742
|
+
* Builds a parallel list of `(batch, eventIds)` for sending. The eventIds are
|
|
1743
|
+
* the original `_session_id`-tagged event IDs in the queue that map to this
|
|
1744
|
+
* batch — used for optimistic removal. We can't read them off the wrapper's
|
|
1745
|
+
* `events[]` because dedup may have removed some signatures.
|
|
1746
|
+
*/
|
|
1747
|
+
private buildBatchesWithIds;
|
|
1631
1748
|
private flushEvents;
|
|
1749
|
+
/**
|
|
1750
|
+
* Re-runs a sync flush that was deferred while an async send was in flight.
|
|
1751
|
+
*
|
|
1752
|
+
* Called from the `finally` blocks of `flushEvents(false)` and
|
|
1753
|
+
* `sendEventsQueue()`. If `pendingSyncFlush` is set, clears the flag and
|
|
1754
|
+
* invokes `flushImmediatelySync()` synchronously so any events that arrived
|
|
1755
|
+
* after the deferred sync call are delivered before the next event loop
|
|
1756
|
+
* tick. Critical for high-stakes events tracked mid-async-send.
|
|
1757
|
+
*/
|
|
1758
|
+
private drainPendingSyncFlush;
|
|
1759
|
+
/**
|
|
1760
|
+
* Sends one batch synchronously across all integrations (sendBeacon path).
|
|
1761
|
+
* Optimistic removal: if any integration succeeds, we remove the batch's
|
|
1762
|
+
* events from the queue and emit it locally. Failures persist per-integration.
|
|
1763
|
+
*/
|
|
1764
|
+
private sendBatchSync;
|
|
1765
|
+
/**
|
|
1766
|
+
* Sends one batch asynchronously across all integrations (fetch path).
|
|
1767
|
+
*/
|
|
1768
|
+
private sendBatchAsync;
|
|
1632
1769
|
private sendEventsQueue;
|
|
1633
|
-
|
|
1770
|
+
/**
|
|
1771
|
+
* Builds a single batch from a per-session group: dedup by signature,
|
|
1772
|
+
* SESSION_START first, then timestamp order, strip `_session_id`, apply
|
|
1773
|
+
* `beforeBatch` transformer when running standalone.
|
|
1774
|
+
*
|
|
1775
|
+
* **Why N batches per flush**: events freeze their `_session_id` at `track()`
|
|
1776
|
+
* time. If the session was renewed (idle timeout) between two `track()`
|
|
1777
|
+
* calls, the queue contains events from multiple sessions. `buildBatchesWithIds()`
|
|
1778
|
+
* emits one batch per session so the backend's `EventsQueueDto.session_id`
|
|
1779
|
+
* remains the single source of truth and stays consistent with the events it
|
|
1780
|
+
* carries.
|
|
1781
|
+
*
|
|
1782
|
+
* **Strip**: `_session_id` is removed from each event in the wrapper's
|
|
1783
|
+
* `events[]` because the backend uses `forbidNonWhitelisted: true` and would
|
|
1784
|
+
* reject the batch if the field leaked through.
|
|
1785
|
+
*
|
|
1786
|
+
* **Transformer note**: `beforeBatch` is invoked **once per session-batch**,
|
|
1787
|
+
* not once per flush. A queue spanning N sessions triggers N invocations.
|
|
1788
|
+
*/
|
|
1789
|
+
private buildBatchFromGroup;
|
|
1634
1790
|
private buildEventPayload;
|
|
1635
1791
|
private isDuplicateEvent;
|
|
1636
1792
|
private pruneOldFingerprints;
|
|
@@ -2333,6 +2489,11 @@ type BeforeSendTransformer = (event: EventData) => EventData | null;
|
|
|
2333
2489
|
* Transform entire batch before sending to backend.
|
|
2334
2490
|
* Applied per-batch (10s interval or 50 events), can filter by returning null.
|
|
2335
2491
|
*
|
|
2492
|
+
* **Multi-batch flushes**: when the queue spans more than one session (e.g.
|
|
2493
|
+
* after an idle-timeout renewal), one flush produces one batch per session,
|
|
2494
|
+
* and this transformer is invoked **once per session-batch**. Avoid stateful
|
|
2495
|
+
* transformers that assume a single invocation per flush.
|
|
2496
|
+
*
|
|
2336
2497
|
* @param batch - The batch to transform
|
|
2337
2498
|
* @returns Transformed batch or null to filter
|
|
2338
2499
|
*/
|
|
@@ -2384,8 +2545,8 @@ interface TraceLogTestBridge {
|
|
|
2384
2545
|
readonly initialized: boolean;
|
|
2385
2546
|
init(config?: Config): Promise<InitResult>;
|
|
2386
2547
|
destroy(force?: boolean): void;
|
|
2387
|
-
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
2388
|
-
event(name: string, metadata?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
2548
|
+
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[], options?: EventOptions): void;
|
|
2549
|
+
event(name: string, metadata?: Record<string, unknown> | Record<string, unknown>[], options?: EventOptions): void;
|
|
2389
2550
|
on(event: string, callback: (data: any) => void): void;
|
|
2390
2551
|
off(event: string, callback: (data: any) => void): void;
|
|
2391
2552
|
get<T extends keyof State>(key: T): State[T];
|
|
@@ -2572,7 +2733,7 @@ declare const getWebVitalsThresholds: (mode?: WebVitalsMode) => Record<WebVitalT
|
|
|
2572
2733
|
|
|
2573
2734
|
declare const tracelog: {
|
|
2574
2735
|
init: (config?: Config) => Promise<InitResult>;
|
|
2575
|
-
event: (name: string, metadata?: Record<string, MetadataType> | Record<string, MetadataType>[]) => void;
|
|
2736
|
+
event: (name: string, metadata?: Record<string, MetadataType> | Record<string, MetadataType>[], options?: EventOptions) => void;
|
|
2576
2737
|
on: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
2577
2738
|
off: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
2578
2739
|
setTransformer: typeof setTransformer;
|
|
@@ -2587,6 +2748,8 @@ declare const tracelog: {
|
|
|
2587
2748
|
mergeGlobalMetadata: (metadata: Record<string, MetadataType>) => void;
|
|
2588
2749
|
identify: (userId: string, traits?: Record<string, string>) => void;
|
|
2589
2750
|
resetIdentity: () => Promise<void>;
|
|
2751
|
+
flushImmediately: () => Promise<boolean>;
|
|
2752
|
+
flushImmediatelySync: () => boolean;
|
|
2590
2753
|
};
|
|
2591
2754
|
|
|
2592
|
-
export { AppConfigValidationError, type BeforeBatchTransformer, type BeforeSendTransformer, type ClickCoordinates, type ClickData, type ClickTrackingElementData, type Config, type CustomEventData, type CustomHeadersProvider, DEFAULT_SESSION_TIMEOUT, DEFAULT_WEB_VITALS_MODE, type DeviceInfo, DeviceType, type EmitterCallback, EmitterEvent, type EmitterMap, type ErrorData, ErrorType, type EventData, EventType, type EventTypeName, type EventsQueue, type IdentifyData, type InitResult, InitializationTimeoutError, IntegrationValidationError, MAX_ARRAY_LENGTH, MAX_CUSTOM_EVENT_ARRAY_SIZE, MAX_CUSTOM_EVENT_KEYS, MAX_CUSTOM_EVENT_NAME_LENGTH, MAX_CUSTOM_EVENT_STRING_SIZE, MAX_NESTED_OBJECT_KEYS, MAX_STRING_LENGTH, MAX_STRING_LENGTH_IN_ARRAY, type MetadataType, Mode, PII_PATTERNS, type PageViewData, PermanentError, type PersistedEventsQueue, type PrimaryScrollEvent, type QueueMetadata, RateLimitError, SamplingRateValidationError, type ScrollData, ScrollDirection, type SecondaryScrollEvent, type SessionEventCounts, SessionTimeoutValidationError, SpecialApiUrl, type State, TimeoutError, type TraceLogTestBridge, TraceLogValidationError, type TransformerHook, type TransformerMap, type UTM, type ViewportConfig, type ViewportElement, type ViewportEventData, WEB_VITALS_GOOD_THRESHOLDS, WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS, WEB_VITALS_POOR_THRESHOLDS, type WebVitalType, type WebVitalsData, type WebVitalsMode, getWebVitalsThresholds, isPrimaryScrollEvent, isSecondaryScrollEvent, tracelog };
|
|
2755
|
+
export { AppConfigValidationError, type BeforeBatchTransformer, type BeforeSendTransformer, type ClickCoordinates, type ClickData, type ClickTrackingElementData, type Config, type CustomEventData, type CustomHeadersProvider, DEFAULT_SESSION_TIMEOUT, DEFAULT_WEB_VITALS_MODE, type DeviceInfo, DeviceType, type EmitterCallback, EmitterEvent, type EmitterMap, type ErrorData, ErrorType, type EventData, type EventOptions, EventType, type EventTypeName, type EventsQueue, type IdentifyData, type InitResult, InitializationTimeoutError, IntegrationValidationError, MAX_ARRAY_LENGTH, MAX_CUSTOM_EVENT_ARRAY_SIZE, MAX_CUSTOM_EVENT_KEYS, MAX_CUSTOM_EVENT_NAME_LENGTH, MAX_CUSTOM_EVENT_STRING_SIZE, MAX_NESTED_OBJECT_KEYS, MAX_STRING_LENGTH, MAX_STRING_LENGTH_IN_ARRAY, type MetadataType, Mode, PII_PATTERNS, type PageViewData, PermanentError, type PersistedEventsQueue, type PrimaryScrollEvent, type QueueMetadata, type QueuedEvent, RateLimitError, SamplingRateValidationError, type ScrollData, ScrollDirection, type SecondaryScrollEvent, type SessionEventCounts, SessionTimeoutValidationError, SpecialApiUrl, type State, TimeoutError, type TraceLogTestBridge, TraceLogValidationError, type TransformerHook, type TransformerMap, type UTM, type ViewportConfig, type ViewportElement, type ViewportEventData, WEB_VITALS_GOOD_THRESHOLDS, WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS, WEB_VITALS_POOR_THRESHOLDS, type WebVitalType, type WebVitalsData, type WebVitalsMode, getWebVitalsThresholds, isPrimaryScrollEvent, isSecondaryScrollEvent, tracelog };
|
package/dist/public-api.d.ts
CHANGED
|
@@ -301,6 +301,33 @@ interface CustomEventData {
|
|
|
301
301
|
/** Additional event metadata */
|
|
302
302
|
metadata?: Record<string, MetadataType> | Record<string, MetadataType>[];
|
|
303
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Optional flags for `tracelog.event()`.
|
|
306
|
+
*/
|
|
307
|
+
interface EventOptions {
|
|
308
|
+
/**
|
|
309
|
+
* If `true`, the event queue is flushed via `navigator.sendBeacon()`
|
|
310
|
+
* immediately after this event is tracked. The browser guarantees the
|
|
311
|
+
* request is queued for delivery even if the page is about to unload
|
|
312
|
+
* (e.g., `window.location.href = '/thanks'` right after tracking a
|
|
313
|
+
* purchase). Async fetch would be cancelled by the navigation;
|
|
314
|
+
* sendBeacon survives.
|
|
315
|
+
*
|
|
316
|
+
* Use for high-value events where loss is unacceptable (Purchase, Signup,
|
|
317
|
+
* AddPaymentInfo). The critical event plus any previously queued events
|
|
318
|
+
* are sent in a single request — this does not bypass the queue.
|
|
319
|
+
*
|
|
320
|
+
* **Limitations** (inherited from `sendBeacon`):
|
|
321
|
+
* - 64KB payload cap. If the combined queue + critical event exceeds it,
|
|
322
|
+
* the failed batch is persisted to localStorage and recovered on next
|
|
323
|
+
* `init()` via its idempotency token.
|
|
324
|
+
* - No retry on failure (fire-and-forget).
|
|
325
|
+
* - Custom headers are not applied (browser API limitation).
|
|
326
|
+
*
|
|
327
|
+
* @default false
|
|
328
|
+
*/
|
|
329
|
+
critical?: boolean;
|
|
330
|
+
}
|
|
304
331
|
/**
|
|
305
332
|
* Web performance metrics data
|
|
306
333
|
*/
|
|
@@ -318,6 +345,8 @@ interface ErrorData {
|
|
|
318
345
|
type: ErrorType;
|
|
319
346
|
/** Error message text */
|
|
320
347
|
message: string;
|
|
348
|
+
/** Error constructor name (TypeError, ReferenceError, etc.) when available */
|
|
349
|
+
name?: string;
|
|
321
350
|
/** Source file where error occurred */
|
|
322
351
|
filename?: string;
|
|
323
352
|
/** Line number in source file */
|
|
@@ -406,6 +435,22 @@ interface EventData {
|
|
|
406
435
|
/** Campaign tracking parameters */
|
|
407
436
|
utm?: UTM;
|
|
408
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Internal queue entry: an `EventData` enriched with the session ID frozen at
|
|
440
|
+
* `track()` time. Survives session renewal — when the user is idle past the
|
|
441
|
+
* timeout, `state.sessionId` is nulled but events already in the queue keep
|
|
442
|
+
* their original `_session_id`, so `EventManager.buildBatchesWithIds()` can
|
|
443
|
+
* still attribute them correctly instead of emitting `session_id: null` to the
|
|
444
|
+
* wire.
|
|
445
|
+
*
|
|
446
|
+
* **Internal only.** The leading underscore mirrors `EventsQueue._metadata` —
|
|
447
|
+
* this field is stripped from `events[]` at batch construction time, since the
|
|
448
|
+
* backend's `EventDto` uses `forbidNonWhitelisted: true` and the wrapper
|
|
449
|
+
* `EventsQueue.session_id` is the contract.
|
|
450
|
+
*/
|
|
451
|
+
interface QueuedEvent extends EventData {
|
|
452
|
+
_session_id: string;
|
|
453
|
+
}
|
|
409
454
|
|
|
410
455
|
/**
|
|
411
456
|
* Web Vitals filtering mode
|
|
@@ -449,6 +494,20 @@ interface Config {
|
|
|
449
494
|
webVitalsThresholds?: Partial<Record<WebVitalType, number>>;
|
|
450
495
|
/** Interval in milliseconds between event batch sends. @default 10000 (10 seconds) */
|
|
451
496
|
sendIntervalMs?: number;
|
|
497
|
+
/**
|
|
498
|
+
* If true, the event queue is flushed automatically after every SPA navigation
|
|
499
|
+
* (`pushState`, `replaceState`, `popstate`, `hashchange`). Prevents batch loss when the
|
|
500
|
+
* user closes the tab mid-buffer between SPA route changes. No-op for MPAs (no SPA nav).
|
|
501
|
+
* @default true
|
|
502
|
+
*/
|
|
503
|
+
flushOnSpaNavigation?: boolean;
|
|
504
|
+
/**
|
|
505
|
+
* If true, the event queue is flushed when `document.hidden` becomes `true`
|
|
506
|
+
* (tab switch, lock screen, app backgrounding). Especially relevant on mobile Safari
|
|
507
|
+
* where `pagehide`/`beforeunload` may not fire reliably.
|
|
508
|
+
* @default true
|
|
509
|
+
*/
|
|
510
|
+
flushOnPageHidden?: boolean;
|
|
452
511
|
/** Optional configuration for third-party integrations. */
|
|
453
512
|
integrations?: {
|
|
454
513
|
/** TraceLog integration options. */
|
|
@@ -1320,6 +1379,7 @@ declare class EventManager extends StateManager {
|
|
|
1320
1379
|
private rateLimitCounter;
|
|
1321
1380
|
private rateLimitWindowStart;
|
|
1322
1381
|
private lastSessionId;
|
|
1382
|
+
private pendingSyncFlush;
|
|
1323
1383
|
private sessionEventCounts;
|
|
1324
1384
|
private readonly saveSessionCountsDebounced;
|
|
1325
1385
|
/**
|
|
@@ -1527,6 +1587,44 @@ declare class EventManager extends StateManager {
|
|
|
1527
1587
|
* @see src/managers/README.md (lines 5-75) for flush details
|
|
1528
1588
|
*/
|
|
1529
1589
|
flushImmediatelySync(): boolean;
|
|
1590
|
+
/**
|
|
1591
|
+
* Sends ONLY the most recently queued event via `navigator.sendBeacon()` in
|
|
1592
|
+
* a dedicated single-event batch.
|
|
1593
|
+
*
|
|
1594
|
+
* **Purpose**: Guarantee delivery of an event that *must* survive an
|
|
1595
|
+
* imminent page unload (purchase confirmation, signup completion, etc.),
|
|
1596
|
+
* independent of the main queue's size or in-flight async send state.
|
|
1597
|
+
*
|
|
1598
|
+
* **Why a dedicated batch**: `flushImmediatelySync()` serialises the entire
|
|
1599
|
+
* queue into one `sendBeacon` call. If the queue is heavy (>64KB) the
|
|
1600
|
+
* beacon fails and the batch is persisted to `localStorage`, which is only
|
|
1601
|
+
* recovered on the next `init()` — useless for one-shot conversion events
|
|
1602
|
+
* where the user never returns.
|
|
1603
|
+
*
|
|
1604
|
+
* **Behaviour**:
|
|
1605
|
+
* - Reads the last entry of `eventsQueue` (the just-tracked event).
|
|
1606
|
+
* - Wraps it in its own `EventsQueue` and dispatches synchronously through
|
|
1607
|
+
* every `SenderManager` via `sendEventsQueueSync()`.
|
|
1608
|
+
* - Leaves the main queue untouched; the same event will be re-delivered by
|
|
1609
|
+
* the next periodic / unload flush. Idempotency is the caller-side
|
|
1610
|
+
* contract: the backend MUST deduplicate by `event.id` (e.g. unique index
|
|
1611
|
+
* on the ingestion collection) — same guarantee the library already
|
|
1612
|
+
* relies on for the persisted-events recovery path.
|
|
1613
|
+
*
|
|
1614
|
+
* **Use it after** `track()` so the event is in the queue. Caller is
|
|
1615
|
+
* responsible for verifying that `track()` actually queued the event
|
|
1616
|
+
* (it can be dropped by rate limiting / sampling / `beforeSend`).
|
|
1617
|
+
*
|
|
1618
|
+
* **Standalone mode** (no senders configured): returns `false` without
|
|
1619
|
+
* emitting. The subsequent main-queue drain (`flushImmediatelySync()`) is
|
|
1620
|
+
* responsible for delivering the event to local listeners — emitting here
|
|
1621
|
+
* too would surface the same event twice to a `tracelog.on('queue', ...)`
|
|
1622
|
+
* subscriber.
|
|
1623
|
+
*
|
|
1624
|
+
* @returns `true` if at least one sender delivered the beacon successfully,
|
|
1625
|
+
* `false` otherwise (including standalone mode — the drain handles it).
|
|
1626
|
+
*/
|
|
1627
|
+
flushLastEventSync(): boolean;
|
|
1530
1628
|
/**
|
|
1531
1629
|
* Sets the custom headers provider callback for the custom integration.
|
|
1532
1630
|
* Only affects requests to custom backend (not TraceLog SaaS).
|
|
@@ -1628,9 +1726,67 @@ declare class EventManager extends StateManager {
|
|
|
1628
1726
|
flushPendingEvents(): void;
|
|
1629
1727
|
private clearSendTimeout;
|
|
1630
1728
|
private isSuccessfulResult;
|
|
1729
|
+
/**
|
|
1730
|
+
* Groups the queue by frozen `_session_id`, preserving insertion order.
|
|
1731
|
+
* Single pass — `buildBatchesWithIds()` builds one batch + one eventIds list
|
|
1732
|
+
* per group, so the grouping cost is O(N) per flush regardless of session
|
|
1733
|
+
* count.
|
|
1734
|
+
*
|
|
1735
|
+
* **Self-heal**: any entry missing `_session_id` (an internal invariant
|
|
1736
|
+
* violation — `buildEventPayload` always stamps it) is removed from the
|
|
1737
|
+
* queue rather than left behind, otherwise a single corrupted entry would
|
|
1738
|
+
* keep `eventsQueue.length > 0` forever and re-trigger periodic sends.
|
|
1739
|
+
*/
|
|
1740
|
+
private groupQueuedEventsBySession;
|
|
1741
|
+
/**
|
|
1742
|
+
* Builds a parallel list of `(batch, eventIds)` for sending. The eventIds are
|
|
1743
|
+
* the original `_session_id`-tagged event IDs in the queue that map to this
|
|
1744
|
+
* batch — used for optimistic removal. We can't read them off the wrapper's
|
|
1745
|
+
* `events[]` because dedup may have removed some signatures.
|
|
1746
|
+
*/
|
|
1747
|
+
private buildBatchesWithIds;
|
|
1631
1748
|
private flushEvents;
|
|
1749
|
+
/**
|
|
1750
|
+
* Re-runs a sync flush that was deferred while an async send was in flight.
|
|
1751
|
+
*
|
|
1752
|
+
* Called from the `finally` blocks of `flushEvents(false)` and
|
|
1753
|
+
* `sendEventsQueue()`. If `pendingSyncFlush` is set, clears the flag and
|
|
1754
|
+
* invokes `flushImmediatelySync()` synchronously so any events that arrived
|
|
1755
|
+
* after the deferred sync call are delivered before the next event loop
|
|
1756
|
+
* tick. Critical for high-stakes events tracked mid-async-send.
|
|
1757
|
+
*/
|
|
1758
|
+
private drainPendingSyncFlush;
|
|
1759
|
+
/**
|
|
1760
|
+
* Sends one batch synchronously across all integrations (sendBeacon path).
|
|
1761
|
+
* Optimistic removal: if any integration succeeds, we remove the batch's
|
|
1762
|
+
* events from the queue and emit it locally. Failures persist per-integration.
|
|
1763
|
+
*/
|
|
1764
|
+
private sendBatchSync;
|
|
1765
|
+
/**
|
|
1766
|
+
* Sends one batch asynchronously across all integrations (fetch path).
|
|
1767
|
+
*/
|
|
1768
|
+
private sendBatchAsync;
|
|
1632
1769
|
private sendEventsQueue;
|
|
1633
|
-
|
|
1770
|
+
/**
|
|
1771
|
+
* Builds a single batch from a per-session group: dedup by signature,
|
|
1772
|
+
* SESSION_START first, then timestamp order, strip `_session_id`, apply
|
|
1773
|
+
* `beforeBatch` transformer when running standalone.
|
|
1774
|
+
*
|
|
1775
|
+
* **Why N batches per flush**: events freeze their `_session_id` at `track()`
|
|
1776
|
+
* time. If the session was renewed (idle timeout) between two `track()`
|
|
1777
|
+
* calls, the queue contains events from multiple sessions. `buildBatchesWithIds()`
|
|
1778
|
+
* emits one batch per session so the backend's `EventsQueueDto.session_id`
|
|
1779
|
+
* remains the single source of truth and stays consistent with the events it
|
|
1780
|
+
* carries.
|
|
1781
|
+
*
|
|
1782
|
+
* **Strip**: `_session_id` is removed from each event in the wrapper's
|
|
1783
|
+
* `events[]` because the backend uses `forbidNonWhitelisted: true` and would
|
|
1784
|
+
* reject the batch if the field leaked through.
|
|
1785
|
+
*
|
|
1786
|
+
* **Transformer note**: `beforeBatch` is invoked **once per session-batch**,
|
|
1787
|
+
* not once per flush. A queue spanning N sessions triggers N invocations.
|
|
1788
|
+
*/
|
|
1789
|
+
private buildBatchFromGroup;
|
|
1634
1790
|
private buildEventPayload;
|
|
1635
1791
|
private isDuplicateEvent;
|
|
1636
1792
|
private pruneOldFingerprints;
|
|
@@ -2333,6 +2489,11 @@ type BeforeSendTransformer = (event: EventData) => EventData | null;
|
|
|
2333
2489
|
* Transform entire batch before sending to backend.
|
|
2334
2490
|
* Applied per-batch (10s interval or 50 events), can filter by returning null.
|
|
2335
2491
|
*
|
|
2492
|
+
* **Multi-batch flushes**: when the queue spans more than one session (e.g.
|
|
2493
|
+
* after an idle-timeout renewal), one flush produces one batch per session,
|
|
2494
|
+
* and this transformer is invoked **once per session-batch**. Avoid stateful
|
|
2495
|
+
* transformers that assume a single invocation per flush.
|
|
2496
|
+
*
|
|
2336
2497
|
* @param batch - The batch to transform
|
|
2337
2498
|
* @returns Transformed batch or null to filter
|
|
2338
2499
|
*/
|
|
@@ -2384,8 +2545,8 @@ interface TraceLogTestBridge {
|
|
|
2384
2545
|
readonly initialized: boolean;
|
|
2385
2546
|
init(config?: Config): Promise<InitResult>;
|
|
2386
2547
|
destroy(force?: boolean): void;
|
|
2387
|
-
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
2388
|
-
event(name: string, metadata?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
2548
|
+
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[], options?: EventOptions): void;
|
|
2549
|
+
event(name: string, metadata?: Record<string, unknown> | Record<string, unknown>[], options?: EventOptions): void;
|
|
2389
2550
|
on(event: string, callback: (data: any) => void): void;
|
|
2390
2551
|
off(event: string, callback: (data: any) => void): void;
|
|
2391
2552
|
get<T extends keyof State>(key: T): State[T];
|
|
@@ -2572,7 +2733,7 @@ declare const getWebVitalsThresholds: (mode?: WebVitalsMode) => Record<WebVitalT
|
|
|
2572
2733
|
|
|
2573
2734
|
declare const tracelog: {
|
|
2574
2735
|
init: (config?: Config) => Promise<InitResult>;
|
|
2575
|
-
event: (name: string, metadata?: Record<string, MetadataType> | Record<string, MetadataType>[]) => void;
|
|
2736
|
+
event: (name: string, metadata?: Record<string, MetadataType> | Record<string, MetadataType>[], options?: EventOptions) => void;
|
|
2576
2737
|
on: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
2577
2738
|
off: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
2578
2739
|
setTransformer: typeof setTransformer;
|
|
@@ -2587,6 +2748,8 @@ declare const tracelog: {
|
|
|
2587
2748
|
mergeGlobalMetadata: (metadata: Record<string, MetadataType>) => void;
|
|
2588
2749
|
identify: (userId: string, traits?: Record<string, string>) => void;
|
|
2589
2750
|
resetIdentity: () => Promise<void>;
|
|
2751
|
+
flushImmediately: () => Promise<boolean>;
|
|
2752
|
+
flushImmediatelySync: () => boolean;
|
|
2590
2753
|
};
|
|
2591
2754
|
|
|
2592
|
-
export { AppConfigValidationError, type BeforeBatchTransformer, type BeforeSendTransformer, type ClickCoordinates, type ClickData, type ClickTrackingElementData, type Config, type CustomEventData, type CustomHeadersProvider, DEFAULT_SESSION_TIMEOUT, DEFAULT_WEB_VITALS_MODE, type DeviceInfo, DeviceType, type EmitterCallback, EmitterEvent, type EmitterMap, type ErrorData, ErrorType, type EventData, EventType, type EventTypeName, type EventsQueue, type IdentifyData, type InitResult, InitializationTimeoutError, IntegrationValidationError, MAX_ARRAY_LENGTH, MAX_CUSTOM_EVENT_ARRAY_SIZE, MAX_CUSTOM_EVENT_KEYS, MAX_CUSTOM_EVENT_NAME_LENGTH, MAX_CUSTOM_EVENT_STRING_SIZE, MAX_NESTED_OBJECT_KEYS, MAX_STRING_LENGTH, MAX_STRING_LENGTH_IN_ARRAY, type MetadataType, Mode, PII_PATTERNS, type PageViewData, PermanentError, type PersistedEventsQueue, type PrimaryScrollEvent, type QueueMetadata, RateLimitError, SamplingRateValidationError, type ScrollData, ScrollDirection, type SecondaryScrollEvent, type SessionEventCounts, SessionTimeoutValidationError, SpecialApiUrl, type State, TimeoutError, type TraceLogTestBridge, TraceLogValidationError, type TransformerHook, type TransformerMap, type UTM, type ViewportConfig, type ViewportElement, type ViewportEventData, WEB_VITALS_GOOD_THRESHOLDS, WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS, WEB_VITALS_POOR_THRESHOLDS, type WebVitalType, type WebVitalsData, type WebVitalsMode, getWebVitalsThresholds, isPrimaryScrollEvent, isSecondaryScrollEvent, tracelog };
|
|
2755
|
+
export { AppConfigValidationError, type BeforeBatchTransformer, type BeforeSendTransformer, type ClickCoordinates, type ClickData, type ClickTrackingElementData, type Config, type CustomEventData, type CustomHeadersProvider, DEFAULT_SESSION_TIMEOUT, DEFAULT_WEB_VITALS_MODE, type DeviceInfo, DeviceType, type EmitterCallback, EmitterEvent, type EmitterMap, type ErrorData, ErrorType, type EventData, type EventOptions, EventType, type EventTypeName, type EventsQueue, type IdentifyData, type InitResult, InitializationTimeoutError, IntegrationValidationError, MAX_ARRAY_LENGTH, MAX_CUSTOM_EVENT_ARRAY_SIZE, MAX_CUSTOM_EVENT_KEYS, MAX_CUSTOM_EVENT_NAME_LENGTH, MAX_CUSTOM_EVENT_STRING_SIZE, MAX_NESTED_OBJECT_KEYS, MAX_STRING_LENGTH, MAX_STRING_LENGTH_IN_ARRAY, type MetadataType, Mode, PII_PATTERNS, type PageViewData, PermanentError, type PersistedEventsQueue, type PrimaryScrollEvent, type QueueMetadata, type QueuedEvent, RateLimitError, SamplingRateValidationError, type ScrollData, ScrollDirection, type SecondaryScrollEvent, type SessionEventCounts, SessionTimeoutValidationError, SpecialApiUrl, type State, TimeoutError, type TraceLogTestBridge, TraceLogValidationError, type TransformerHook, type TransformerMap, type UTM, type ViewportConfig, type ViewportElement, type ViewportEventData, WEB_VITALS_GOOD_THRESHOLDS, WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS, WEB_VITALS_POOR_THRESHOLDS, type WebVitalType, type WebVitalsData, type WebVitalsMode, getWebVitalsThresholds, isPrimaryScrollEvent, isSecondaryScrollEvent, tracelog };
|