@releval/tracker 1.0.0-bootstrap.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.
@@ -0,0 +1,762 @@
1
+ //#region src/logging/Logger.d.ts
2
+ /**
3
+ * Logging interface for internal tracker diagnostics. Implement this to
4
+ * integrate the tracker's log output with your application's logging system,
5
+ * then pass it as the `logger` option. To fan out to several destinations
6
+ * (e.g. the console AND your own service), pass a logger that wraps both.
7
+ */
8
+ interface Logger {
9
+ /**
10
+ * Logs a debug message
11
+ * @param msg the message
12
+ * @param data the additional data
13
+ */
14
+ debug(msg: string, ...data: any[]): void;
15
+ /**
16
+ * Logs an info message
17
+ * @param msg the message
18
+ * @param data the additional data
19
+ */
20
+ info(msg: string, ...data: any[]): void;
21
+ /**
22
+ * Logs a warn message
23
+ * @param msg the message
24
+ * @param data the additional data
25
+ */
26
+ warn(msg: string, ...data: any[]): void;
27
+ /**
28
+ * Logs an error message
29
+ * @param msg the message
30
+ * @param data the additional data
31
+ */
32
+ error(msg: string, ...data: any[]): void;
33
+ }
34
+ //#endregion
35
+ //#region src/attribution/AttributionStore.d.ts
36
+ /**
37
+ * The search context recorded for a result when it was clicked, used to
38
+ * attribute later conversion events (add_to_cart, purchase) for the same
39
+ * object back to the originating query.
40
+ */
41
+ type AttributionRecord = {
42
+ /** The server-issued query id of the search that produced the result. */
43
+ queryId: string;
44
+ /** The absolute 1-based rank the result was clicked at, if recorded. */
45
+ ordinal?: number;
46
+ /** The query text as the user entered it, if recorded. */
47
+ query?: string;
48
+ };
49
+ //#endregion
50
+ //#region src/types/Event.d.ts
51
+ /**
52
+ * The core UBI event structure that is enriched and emitted to sinks.
53
+ *
54
+ * Only `action_name` is required when dispatching - the dispatcher auto-sets
55
+ * `timestamp` and enrichers populate contextual fields like `application`,
56
+ * `session_id`, `client_id`, and browser/page metadata before the event
57
+ * reaches the sink.
58
+ *
59
+ * @see https://o19s.github.io/ubi/schema/1.3.0/event.schema.json
60
+ */
61
+ interface Event {
62
+ /**
63
+ * Name of the application integrated with UBI. Distinguishes event sources
64
+ * when multiple search UIs feed into the same backend.
65
+ * @example "primary-search"
66
+ * @example "type-ahead"
67
+ */
68
+ application?: string;
69
+ /**
70
+ * The name of the action that triggered the event.
71
+ * Common values: `"click"`, `"add_to_cart"`, `"purchase"`, `"impression"`, `"view"`, `"watch"`.
72
+ * Any custom string is also accepted.
73
+ */
74
+ action_name: string;
75
+ /**
76
+ * The unique identifier of the query that this event is associated with,
77
+ * typically a UUID. Links events back to the originating search.
78
+ * @example "00112233-4455-6677-8899-aabbccddeeff"
79
+ * @example "1234-user-5678"
80
+ */
81
+ query_id?: string;
82
+ /**
83
+ * The session identifier, used to correlate interactions across page navigations
84
+ * and to track unique visits for both authenticated and anonymous users.
85
+ * Populated automatically by the SessionEnricher.
86
+ * @example "84266fdbd31d4c2c6d0665f7e8380fa3"
87
+ */
88
+ session_id?: string;
89
+ /**
90
+ * A stable anonymous identifier for the client issuing events.
91
+ * This could be a unique browser, a microservice, or a crawling bot.
92
+ * Populated automatically by the ClientIdEnricher.
93
+ * @example "5e3b2a1c-8b7d-4f2e-a3d4-c9b2e1f3a4b5"
94
+ * @example "quepid-nightly-bot"
95
+ */
96
+ client_id?: string;
97
+ /**
98
+ * The authenticated user identifier, if available.
99
+ * May be `undefined` for anonymous/unauthenticated users.
100
+ * @example "5e3b2a1c-8b7d-4f2e-a3d4-c9b2e1f3a4b5"
101
+ */
102
+ user_id?: string;
103
+ /**
104
+ * When the event took place, formatted as an ISO 8601 date-time string.
105
+ * Auto-set by the dispatcher if not provided.
106
+ * @example "2018-11-13T20:20:39+00:00"
107
+ * @example "2018-11-13T20:20:39Z"
108
+ */
109
+ timestamp?: string;
110
+ /**
111
+ * Groups related `action_name` values into logical categories.
112
+ * @example "QUERY"
113
+ * @example "CONVERSION"
114
+ */
115
+ message_type?: string;
116
+ /**
117
+ * Optional text message for the log entry. For a `message_type` of `"QUERY"`,
118
+ * this would typically contain the search text.
119
+ */
120
+ message?: string;
121
+ /** The query as the user entered it, before any normalization or processing. */
122
+ user_query?: string;
123
+ /**
124
+ * Contextual data attached to a tracked event describing what was interacted with
125
+ * and where it appeared on the page. Additional custom properties can be added via
126
+ * the index signature.
127
+ */
128
+ event_attributes?: EventAttributes;
129
+ /**
130
+ * The public Site identifier issued by Releval when registering a Site.
131
+ * Stamped automatically by the OptionsEnricher from `TrackerOptions.siteId`.
132
+ * A Releval extension to the UBI schema: the server drops events whose
133
+ * `site_id` does not match a registered Site.
134
+ * @example "01J8ZP4Q9K7X2M5N6R8T0V3W1Y"
135
+ */
136
+ site_id?: string;
137
+ }
138
+ /**
139
+ * Contextual data attached to a tracked event describing what was interacted with
140
+ * and where it appeared on the page. Additional custom properties can be added via
141
+ * the index signature.
142
+ */
143
+ interface EventAttributes {
144
+ /**
145
+ * Identifies the object (e.g. product, document) that was interacted with.
146
+ * Additional custom properties can be added via the index signature.
147
+ */
148
+ object?: EventObject;
149
+ /**
150
+ * Describes where an interaction occurred relative to other items on the page.
151
+ * Use `ordinal` for list/grid rank and `x`/`y` for coordinate-based positioning.
152
+ * Additional custom properties can be added via the index signature.
153
+ */
154
+ position?: EventPosition;
155
+ /**
156
+ * A stable per-event identifier, set once by the dispatcher when the event
157
+ * is created. A Releval extension to the UBI schema. It lives under
158
+ * `event_attributes` because that is where the server preserves unknown
159
+ * fields, so delivery retries (which can duplicate) stay deduplicable in
160
+ * analysis: `LIMIT 1 BY event_attributes.event_id`.
161
+ * @example "01J8ZP4Q9K7X2M5N6R8T0V3W1Y"
162
+ */
163
+ event_id?: string;
164
+ /**
165
+ * The tracker build that produced the event (`{ version }`), stamped by the
166
+ * dispatcher. A Releval extension, for supportability once several tracker
167
+ * versions are deployed across customer sites.
168
+ */
169
+ tracker?: any;
170
+ [key: string]: any;
171
+ }
172
+ /**
173
+ * Identifies the object (e.g. product, document) that was interacted with.
174
+ * Additional custom properties can be added via the index signature.
175
+ */
176
+ interface EventObject {
177
+ /**
178
+ * The identifier that uniquely locates the object within the document corpus.
179
+ * Variants should be incorporated, so for a red t-shirt use the SKU-level identifier.
180
+ * Always a string on the wire: the Releval server binds it strictly and rejects
181
+ * the whole batch when a number arrives.
182
+ * @example "XYZ-12345"
183
+ * @example "ISBN 0-061-96436-0"
184
+ */
185
+ object_id: string;
186
+ /**
187
+ * The type/namespace of the object identifier.
188
+ * Common values: `"product"`, `"user"`, `"post"`, `"comment"`, `"video"`.
189
+ */
190
+ object_id_type?: string;
191
+ /**
192
+ * The name of the field that stores the object identifier in the backend data store.
193
+ * If omitted, the search index's default primary identifier is used (e.g. `_id` in OpenSearch).
194
+ */
195
+ object_id_field?: string;
196
+ /**
197
+ * The internal identifier that the search engine uses to index the object.
198
+ * For example, the `_id` field in OpenSearch indices - pass numeric ids as
199
+ * strings. Always a string on the wire: the Releval server binds it strictly
200
+ * and a number would reject the whole batch, the same hole `object_id` closes.
201
+ * @example "1"
202
+ * @example "123456"
203
+ */
204
+ internal_id?: string;
205
+ [key: string]: any;
206
+ }
207
+ /**
208
+ * Describes where an interaction occurred relative to other items on the page.
209
+ * Use `ordinal` for list/grid rank and `x`/`y` for coordinate-based positioning.
210
+ * Additional custom properties can be added via the index signature.
211
+ */
212
+ interface EventPosition {
213
+ /**
214
+ * The absolute, 1-based rank of the item across pagination:
215
+ * `(page - 1) * pageSize + positionOnPage` with `page` 1-based. It must equal
216
+ * index + 1 of the object in the `query_response_hit_ids` the backend sent to
217
+ * track-query for this `query_id`. For grid layouts this is left to right,
218
+ * ignoring wrapping.
219
+ * @example 1
220
+ * @example 21
221
+ */
222
+ ordinal?: number;
223
+ /**
224
+ * The x coordinate on the screen where the event was triggered.
225
+ * Matches the Releval server's flat `x` field.
226
+ */
227
+ x?: number;
228
+ /**
229
+ * The y coordinate on the screen where the event was triggered.
230
+ * Matches the Releval server's flat `y` field.
231
+ */
232
+ y?: number;
233
+ [key: string]: any;
234
+ }
235
+ //#endregion
236
+ //#region src/enrichers/Enricher.d.ts
237
+ /**
238
+ * Mutates a UBI event to add contextual data before it reaches the sink. Built-in enrichers
239
+ * add fields like `application`, `session_id`, `client_id`, and browser metadata. Implement
240
+ * this interface to add custom fields (e.g. A/B test variant, feature flags) and register
241
+ * via `tracker.addEnricher()`.
242
+ */
243
+ interface Enricher {
244
+ /**
245
+ * Enriches the event.
246
+ * @param event the event to enrich
247
+ */
248
+ enrich(event: Event): void;
249
+ }
250
+ //#endregion
251
+ //#region src/sinks/Sink.d.ts
252
+ /**
253
+ * A destination that receives enriched UBI events. Built-in sinks send events to an API
254
+ * endpoint (BatchSink) or to the browser console (ConsoleSink). Implement this
255
+ * interface to send events to a custom destination and register via `tracker.addSink()`.
256
+ */
257
+ interface Sink {
258
+ /**
259
+ * Emit the specified event to the destination.
260
+ * @remarks Implementations should allow errors to propagate. These are logged by the configured tracker logger.
261
+ * @param event The event to emit
262
+ */
263
+ emit(event: Event): void;
264
+ }
265
+ //#endregion
266
+ //#region src/collectors/resolveResult.d.ts
267
+ /**
268
+ * The result data resolved from a DOM element by the default data-attribute
269
+ * convention shared by the declarative collectors.
270
+ */
271
+ type ResolvedResultData = {
272
+ /** The identifier of the result object (e.g. product/SKU) - `data-object-id`. */
273
+ objectId: string;
274
+ /**
275
+ * The absolute, 1-based rank of the result across pagination -
276
+ * `data-ordinal`. `undefined` when missing, empty or not a positive
277
+ * integer; the collectors validate before anything is emitted.
278
+ */
279
+ ordinal?: number;
280
+ /** The backend field the object id maps to - `data-object-id-field`. */
281
+ objectIdField?: string;
282
+ /**
283
+ * Overrides the emitted `action_name` (e.g. `"add_to_cart"`) -
284
+ * `data-action-name`. Clicks default to `"click"`.
285
+ */
286
+ actionName?: string;
287
+ /**
288
+ * The server-issued `query_id` that produced the result - `data-query-id`
289
+ * on the nearest ancestor carrying it (the element itself counts).
290
+ */
291
+ queryId?: string;
292
+ /** The query text as the user entered it - `data-query` on that ancestor. */
293
+ query?: string;
294
+ /**
295
+ * Any additional keys - `data-event-*` attributes read by the default
296
+ * resolver, or extra keys returned by a custom one - are persisted as-is
297
+ * under the event's `event_attributes`.
298
+ */
299
+ [key: string]: unknown;
300
+ };
301
+ /**
302
+ * Reads the documented data-attribute convention off a result element:
303
+ *
304
+ * - `data-object-id`, `data-ordinal`, `data-object-id-field` and (optionally)
305
+ * `data-action-name` on the result element itself;
306
+ * - `data-event-*` on the result element for custom event attributes:
307
+ * `data-event-badge="sale"` becomes `badge: "sale"` (hyphens camelCase,
308
+ * underscores survive - `data-event-sale_price` -> `sale_price`); a value
309
+ * that looks like a JSON object or array is parsed as one
310
+ * (`data-event-filters='{"brand":"acme"}'`), any other value - scalars
311
+ * included - stays a string;
312
+ * - `data-query-id` and `data-query` on the nearest ancestor carrying
313
+ * `data-query-id` (typically the results container, rendered server-side).
314
+ *
315
+ * A missing, empty or non-positive-integer `data-ordinal` yields
316
+ * `undefined`, never 0 - a
317
+ * fabricated rank is indistinguishable from data downstream. The collectors
318
+ * validate what they need and warn (once) when a required field is absent.
319
+ *
320
+ * @param logger receives the one-time malformed-JSON warning; the collectors
321
+ * pass the tracker's logger.
322
+ */
323
+ declare const readResultData: (element: HTMLElement, logger?: Logger) => ResolvedResultData;
324
+ //#endregion
325
+ //#region src/collectors/ResultClickCollector.d.ts
326
+ /**
327
+ * The result data resolved for a clicked element. `actionName` defaults to
328
+ * `"click"`; a click resolved to any other action (e.g. `"add_to_cart"`) is
329
+ * routed through `trackResultEvent`, where a missing `queryId`/`ordinal` is
330
+ * resolved from the attribution recorded when the result was clicked.
331
+ */
332
+ type ResolvedResultClick = ResolvedResultData;
333
+ /**
334
+ * Resolves the result data for a clicked element. Return `undefined` to skip
335
+ * the click entirely. The default implementation reads the data-attribute
336
+ * convention described on {@link readResultData}.
337
+ */
338
+ type ResultClickResolve = (element: HTMLElement, event: MouseEvent) => ResolvedResultClick | undefined;
339
+ /** Options for `Tracker.trackResultClicks`. */
340
+ type TrackResultClicksOptions = {
341
+ /**
342
+ * CSS selector of result elements. Clicks are matched via `closest()`, so a
343
+ * click on any descendant of a matching element counts.
344
+ */
345
+ selector: string;
346
+ /** The root the single delegated listener attaches to. Defaults to `document`. */
347
+ root?: Document | Element;
348
+ /**
349
+ * Selector for elements whose clicks must NOT be reported as result clicks,
350
+ * checked inside the matched result (e.g. `'[data-add-to-cart]'` for a
351
+ * button nested in the card). Without it a click on the nested button would
352
+ * emit both its own event and a result click, inflating CTR.
353
+ */
354
+ ignore?: string;
355
+ /** Custom resolver; defaults to the data-attribute convention. */
356
+ resolve?: ResultClickResolve;
357
+ };
358
+ //#endregion
359
+ //#region src/tracker.d.ts
360
+ /**
361
+ * Options shared by every {@link TrackerOptions} variant. Not used directly -
362
+ * see {@link TrackerOptions} for the endpoint pairing rules.
363
+ */
364
+ type TrackerBaseOptions = {
365
+ /** The name of the application */
366
+ application: string;
367
+ /**
368
+ * The initial user id attached to events (`user_id`). Must be an opaque,
369
+ * pseudonymous identifier - never an email address or name. Change it
370
+ * mid-session (login/logout) with {@link Tracker.setUserId}.
371
+ */
372
+ userId?: string;
373
+ /** Session inactivity timeout in milliseconds. Defaults to 30 minutes. */
374
+ sessionInactivityTimeoutMs?: number;
375
+ /** Maximum session duration in milliseconds. Defaults to 24 hours. */
376
+ maxSessionDurationMs?: number;
377
+ /**
378
+ * When true, the default console logger also prints debug/info
379
+ * diagnostics: every dispatched event and every delivery outcome. This is
380
+ * the one-line install check against a server that answers 202 to
381
+ * everything. Warnings and errors are printed regardless. Applies to the
382
+ * default `ConsoleLogger` only - a supplied `logger` receives all levels
383
+ * and filters for itself. Defaults to false.
384
+ */
385
+ debug?: boolean;
386
+ /**
387
+ * A logger to receive internal diagnostics. Replaces the default
388
+ * `ConsoleLogger`, so pass one here to route (or silence) tracker logging
389
+ * instead of writing to the console. To fan out to several destinations,
390
+ * pass a logger that wraps them.
391
+ */
392
+ logger?: Logger;
393
+ };
394
+ /**
395
+ * Configuration passed to `new Tracker()`. Delivering to Releval requires the
396
+ * pair: `endpointHost` (the base URL browsers use to reach the Releval
397
+ * deployment - not your site's own origin) and `siteId` (the public Site
398
+ * identifier issued by Releval). The server silently drops events whose
399
+ * `site_id` does not match a registered Site, so the type requires the two
400
+ * together. Omit `endpointHost` for development mode: events go to the
401
+ * console - or only to sinks added with `addSink`, once any are added - and
402
+ * nothing leaves the page.
403
+ */
404
+ type TrackerOptions = TrackerBaseOptions & ({
405
+ /**
406
+ * Base URL browsers use to reach the Releval deployment, e.g.
407
+ * `"https://releval.example.com"` - not your site's own origin. A
408
+ * path prefix for a reverse proxy is allowed; events POST to
409
+ * `<endpointHost>/api/v1/ubi/track-event`.
410
+ */
411
+ endpointHost: string;
412
+ /**
413
+ * The public Site identifier issued by Releval when registering a
414
+ * Site. Stamped on every event in the request body; identity travels
415
+ * in the payload, not a header, because `navigator.sendBeacon`
416
+ * cannot set custom headers on the unload path.
417
+ */
418
+ siteId: string;
419
+ } | {
420
+ /** Omit to log events to the console instead of delivering (development mode). */
421
+ endpointHost?: undefined;
422
+ /** Without an endpoint the site id is optional; it is stamped when present. */
423
+ siteId?: string;
424
+ });
425
+ /**
426
+ * A reference to a search result that was interacted with. Used by the
427
+ * high-level `trackResult*` methods to build the UBI `object` + `position`
428
+ * attributes so callers do not hand-roll the event shape.
429
+ */
430
+ type ResultRef = {
431
+ /** The identifier of the object (e.g. product/SKU) that was interacted with. */
432
+ objectId: string;
433
+ /**
434
+ * The absolute, 1-based rank of the result across pagination -
435
+ * `(page - 1) * pageSize + positionOnPage` with `page` 1-based - not the
436
+ * per-page position. It must equal index + 1 of this object in the
437
+ * `query_response_hit_ids` your backend sent to track-query for this
438
+ * `query_id`: either one query_id spans all pages with absolute ordinals,
439
+ * or each page fetch is its own track-query call with its own query_id.
440
+ */
441
+ ordinal: number;
442
+ /** The backend field the object id maps to (UBI `object_id_field`). Optional. */
443
+ objectIdField?: string;
444
+ /**
445
+ * Any additional keys are persisted as-is under the event's
446
+ * `event_attributes` (e.g. `badge: "sale"`). Values must be
447
+ * JSON-serialisable. Keys the tracker itself owns - `object`, `position`,
448
+ * `event_id`, `tracker`, `page`, `browser` - are overwritten by it; use
449
+ * {@link Tracker.dispatch} for full control of the event shape.
450
+ */
451
+ [key: string]: unknown;
452
+ };
453
+ /** Options for {@link Tracker.trackResultEvent}. */
454
+ type TrackResultEventOptions = {
455
+ /** The UBI `action_name` (e.g. `"add_to_cart"`, `"purchase"`, `"view"`). */
456
+ actionName: string;
457
+ /** The identifier of the object (e.g. product/SKU) the event is about. */
458
+ objectId: string;
459
+ /** The backend field the object id maps to (UBI `object_id_field`). Optional. */
460
+ objectIdField?: string;
461
+ /**
462
+ * The absolute, 1-based rank the result was returned at. Optional: when
463
+ * omitted it is resolved from the attribution recorded by
464
+ * {@link Tracker.trackResultClick} for the same `objectId`. When neither is
465
+ * available the event carries no position.
466
+ */
467
+ ordinal?: number;
468
+ /**
469
+ * The server-issued `query_id` that produced this result. Optional: when
470
+ * omitted it is resolved from the attribution recorded by
471
+ * {@link Tracker.trackResultClick} for the same `objectId`, so a conversion
472
+ * on a later page joins to the originating query without the integrator
473
+ * threading it by hand. When neither is available the event is sent
474
+ * unattributed and a warning is logged once.
475
+ */
476
+ queryId?: string;
477
+ /** The query text as the user entered it, if known (populates `user_query`). */
478
+ query?: string;
479
+ /**
480
+ * Any additional keys are persisted as-is under the event's
481
+ * `event_attributes` (e.g. `badge: "sale"`). Values must be
482
+ * JSON-serialisable. Keys the tracker itself owns - `object`, `position`,
483
+ * `event_id`, `tracker`, `page`, `browser` - are overwritten by it; use
484
+ * {@link Tracker.dispatch} for full control of the event shape.
485
+ */
486
+ [key: string]: unknown;
487
+ };
488
+ /** Options for {@link Tracker.trackResultClick}. */
489
+ type TrackResultClickOptions = ResultRef & {
490
+ /** The server-issued `query_id` that produced this result. Required. */
491
+ queryId: string;
492
+ /** The query text as the user entered it, if known (populates `user_query`). */
493
+ query?: string;
494
+ /** Override the `action_name`. Defaults to `"click"`. */
495
+ actionName?: string;
496
+ };
497
+ /** Options for {@link Tracker.trackResultImpression}. */
498
+ type TrackResultImpressionOptions = {
499
+ /** The results that became visible, each with its object id and absolute rank. */
500
+ items: ResultRef[];
501
+ /** The server-issued `query_id` that produced these results. Required. */
502
+ queryId: string;
503
+ /** The query text as the user entered it, if known (populates `user_query`). */
504
+ query?: string;
505
+ };
506
+ /** Options for {@link Tracker.trackSearch}. */
507
+ type TrackSearchOptions = {
508
+ /** The query text as the user entered it. */
509
+ query: string;
510
+ /** The server-issued `query_id` for this search. Required. */
511
+ queryId: string;
512
+ /**
513
+ * Any additional keys are persisted as-is under the event's
514
+ * `event_attributes` (e.g. `badge: "sale"`). Values must be
515
+ * JSON-serialisable. Keys the tracker itself owns - `object`, `position`,
516
+ * `event_id`, `tracker`, `page`, `browser` - are overwritten by it; use
517
+ * {@link Tracker.dispatch} for full control of the event shape.
518
+ */
519
+ [key: string]: unknown;
520
+ };
521
+ /**
522
+ * The entry point of `@releval/tracker`: collects User Behavior Insights
523
+ * events - searches, result impressions, result clicks and the conversions
524
+ * that follow - in the canonical joinable shape
525
+ * (`query_id` + `object_id` + `ordinal`) and delivers them to a Releval
526
+ * deployment's track-event API.
527
+ *
528
+ * Events flow through a small pipeline: the high-level `track*` methods (or
529
+ * the declarative collectors) build canonical events, enrichers stamp
530
+ * context (application, session, client id, page, browser), and sinks
531
+ * deliver - batched with retry to the endpoint, or to the console in
532
+ * development. Nothing is delivered before {@link Tracker.start}; earlier
533
+ * dispatches are buffered and replayed.
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * const tracker = new Tracker({
538
+ * application: "primary-search",
539
+ * siteId: "YOUR_SITE_ID",
540
+ * endpointHost: "https://releval.example.com",
541
+ * });
542
+ * tracker.start();
543
+ *
544
+ * tracker.trackSearch({ query, queryId }); // ids come from your backend
545
+ * tracker.trackResultClick({ objectId, ordinal, queryId });
546
+ * tracker.trackResultEvent({ actionName: "add_to_cart", objectId });
547
+ * ```
548
+ *
549
+ * The full integration flow is documented at
550
+ * https://releval.co/docs/user-behavior-insights/browser-tracker
551
+ */
552
+ declare class Tracker {
553
+ private readonly enrichers;
554
+ private readonly options;
555
+ private readonly _sessionManager;
556
+ private started;
557
+ private hasEverStarted;
558
+ private collectors;
559
+ private readonly detaches;
560
+ private sink?;
561
+ private batchSink?;
562
+ private readonly logger;
563
+ private dispatcher?;
564
+ private _clientId?;
565
+ private _userId?;
566
+ private _attribution?;
567
+ private hasWarnedMissingQueryId;
568
+ private hasWarnedUnattributedResult;
569
+ private hasWarnedInvalidOrdinal;
570
+ private readonly preStartBuffer;
571
+ private hasWarnedPreStartOverflow;
572
+ private readonly seenImpressions;
573
+ private readonly extraSinks;
574
+ constructor(options: TrackerOptions);
575
+ private _localStorage?;
576
+ /**
577
+ * The tracker's local storage (with fallbacks when Web Storage is
578
+ * unavailable). Internal: collectors and stores receive it via options.
579
+ */
580
+ private get localStorage();
581
+ private _sessionStorage?;
582
+ /**
583
+ * The tracker's session storage (with fallbacks when Web Storage is
584
+ * unavailable). Internal: collectors and stores receive it via options.
585
+ */
586
+ private get sessionStorage();
587
+ /**
588
+ * Gets the session identifier. Automatically rotates on inactivity or max duration.
589
+ */
590
+ get sessionId(): string;
591
+ /**
592
+ * Gets the stable anonymous client/device ID. Persisted in localStorage across sessions.
593
+ */
594
+ get clientId(): string;
595
+ /**
596
+ * Sets (or clears, with `undefined`) the user id stamped on events - for a
597
+ * login or logout that happens without a page reload. Applies to events
598
+ * dispatched after the call; events already queued or persisted for retry
599
+ * keep the identity they were stamped with. An explicit `user_id` on a
600
+ * dispatched event still wins. Callable before `start()`. Does not rotate
601
+ * the session or touch the client id. The value must be an opaque,
602
+ * pseudonymous identifier - never an email address or name.
603
+ */
604
+ setUserId(userId: string | undefined): void;
605
+ /**
606
+ * The object-keyed attribution store, created on first use so construction
607
+ * performs no storage IO for it.
608
+ */
609
+ private get attribution();
610
+ /**
611
+ * Returns the attribution recorded by {@link Tracker.trackResultClick} for
612
+ * an object in the current session, or undefined. Use it to build custom
613
+ * conversion payloads (e.g. a checkout page resolving several line items).
614
+ */
615
+ getResultAttribution(objectId: string): AttributionRecord | undefined;
616
+ /**
617
+ * Reports that a search ran, with the server-issued `query_id`. Call this
618
+ * after your search backend returns results.
619
+ *
620
+ * Dispatches a `search` event (`message_type: "QUERY"`) carrying `query_id`
621
+ * and `user_query`. Without it a query that gets no impression and no click
622
+ * leaves no event at all, so abandonment and reformulation cannot be
623
+ * analysed downstream.
624
+ */
625
+ trackSearch(options: TrackSearchOptions): void;
626
+ /**
627
+ * Dispatches a UBI event for a search result in the canonical shape
628
+ * (`query_id`, `event_attributes.object.object_id`, `event_attributes.position.ordinal`),
629
+ * so callers never hand-build it. Prefer {@link Tracker.trackResultClick} /
630
+ * {@link Tracker.trackResultImpression}; use this directly for conversions
631
+ * (e.g. `actionName: "add_to_cart"` or `"purchase"`), where `queryId` and
632
+ * `ordinal` may be omitted and are resolved from the attribution recorded
633
+ * when the result was clicked - including on a later page. When `queryId`
634
+ * IS supplied and matches the recorded click, a missing `ordinal`/`query`
635
+ * is still borrowed from it, so the row shape does not depend on which
636
+ * page supplied the id.
637
+ */
638
+ trackResultEvent(options: TrackResultEventOptions): void;
639
+ /**
640
+ * Dispatches a `click` event for a clicked search result, attributed to
641
+ * `queryId` (required at the call site by design), and records the
642
+ * attribution for the object so later conversion events for it - on this
643
+ * page or a later one - resolve the originating query automatically.
644
+ */
645
+ trackResultClick(options: TrackResultClickOptions): void;
646
+ /**
647
+ * Dispatches an `impression` event for each result that became visible,
648
+ * attributed to `queryId`. Emits one canonical event per item so impressions
649
+ * join to clicks on `object_id`/`ordinal`.
650
+ *
651
+ * Each `(queryId, objectId)` pair is reported ONCE per Tracker instance:
652
+ * re-renders, virtualized-list remounts and re-discovered elements do not
653
+ * inflate the impression count (the CTR denominator), while a new `queryId`
654
+ * re-fires for results returned by consecutive searches. A full page load
655
+ * builds a fresh Tracker and so starts fresh; when revisits matter,
656
+ * deduplicate downstream on distinct `(query_id, object_id)`.
657
+ */
658
+ trackResultImpression(options: TrackResultImpressionOptions): void;
659
+ /**
660
+ * Adds an enricher that runs on every event before emission (e.g. stamping
661
+ * an A/B variant, store or locale - the split key for any comparison).
662
+ * Returns a disposer that removes it again.
663
+ */
664
+ addEnricher(enricher: Enricher): () => void;
665
+ /**
666
+ * Registers a collector so it attaches at start() (or immediately when the
667
+ * tracker is already started), and returns a function that detaches and
668
+ * unregisters it again.
669
+ */
670
+ private registerCollector;
671
+ /**
672
+ * Binds declarative result-click collection to the DOM: one delegated
673
+ * listener reports clicks on elements matching `selector` in the canonical
674
+ * joinable shape, reading `data-object-id` / `data-ordinal` from the result
675
+ * element and `data-query-id` from its nearest ancestor (or a custom
676
+ * `resolve`). A resolved `data-action-name` other than `click` (e.g.
677
+ * `add_to_cart`) is routed through {@link Tracker.trackResultEvent}, so its
678
+ * attribution can resolve from the recorded click.
679
+ *
680
+ * Use `ignore` for interactive descendants of a result (an add-to-cart
681
+ * button inside the card) so their clicks are not double-reported as result
682
+ * clicks.
683
+ *
684
+ * @returns a function that stops this collection again.
685
+ */
686
+ trackResultClicks(options: TrackResultClicksOptions): () => void;
687
+ /** Routes a resolved declarative click through the high-level API. */
688
+ private emitResolvedResultClick;
689
+ /**
690
+ * Binds declarative result-impression collection to the DOM: each element
691
+ * matching `selector` emits one canonical `impression` event the first time
692
+ * it enters the viewport, attributed via the same data-attribute convention
693
+ * as {@link Tracker.trackResultClicks} (or a custom `resolve`). Elements
694
+ * added or re-rendered after `start()` are discovered automatically.
695
+ *
696
+ * @returns a function that stops this collection again.
697
+ */
698
+ trackResultImpressions(options: TrackResultImpressionsOptions): () => void;
699
+ /**
700
+ * Adds a sink that receives every emitted event (e.g. mirroring into your
701
+ * own analytics, or a test spy). Returns a disposer that removes it again.
702
+ */
703
+ addSink(sink: Sink): () => void;
704
+ /**
705
+ * starts the tracker and attaches all collectors.
706
+ */
707
+ start(): void;
708
+ /**
709
+ * Builds the effective sink from the configured endpoint plus any user sinks.
710
+ * ConsoleSink is used only when there is neither an endpoint nor a user sink
711
+ * (the development default). The result is always an AggregateSink so its
712
+ * reference stays stable across addSink() calls.
713
+ */
714
+ private buildSink;
715
+ /**
716
+ * Dispatches an event through the tracker pipeline (enrichers → sinks).
717
+ * Use this to send custom events that aren't captured by a collector.
718
+ * Safe to call before start(): the event is buffered (bounded), stamped
719
+ * with the timestamp of the moment it happened, and replayed once start()
720
+ * runs.
721
+ * @param event The event to dispatch.
722
+ */
723
+ dispatch(event: Event): void;
724
+ /**
725
+ * Forces immediate delivery of any queued events, rather than waiting for the
726
+ * next batch interval. Useful before a critical action or a hard navigation.
727
+ * Resolves once the current batch has been sent (or scheduled for retry).
728
+ * A no-op that resolves immediately when there is no batching sink (events go
729
+ * to the console) or the tracker is stopped.
730
+ */
731
+ flush(): Promise<void>;
732
+ /**
733
+ * stops the tracker and detaches all collectors.
734
+ */
735
+ stop(): void;
736
+ }
737
+ //#endregion
738
+ //#region src/collectors/ResultImpressionCollector.d.ts
739
+ /**
740
+ * The result data resolved for an element that became visible: the shared
741
+ * {@link ResolvedResultData} shape. `actionName` is ignored here - an
742
+ * impression is always an `impression`.
743
+ */
744
+ type ResolvedResultImpression = ResolvedResultData;
745
+ /**
746
+ * Resolves the result data for a visible element. Return `undefined` to skip
747
+ * it. The default implementation reads the data-attribute convention described
748
+ * on {@link readResultData}.
749
+ */
750
+ type ResultImpressionResolve = (element: HTMLElement) => ResolvedResultImpression | undefined;
751
+ /** Options for `Tracker.trackResultImpressions`. */
752
+ type TrackResultImpressionsOptions = {
753
+ /** CSS selector of result elements to observe for viewport visibility. */
754
+ selector: string;
755
+ /** The root observed for matching elements. Defaults to `document`. */
756
+ root?: Document | Element;
757
+ /** Custom resolver; defaults to the data-attribute convention. */
758
+ resolve?: ResultImpressionResolve;
759
+ };
760
+ //#endregion
761
+ export { AttributionRecord as C, EventPosition as S, Sink as _, TrackResultClickOptions as a, EventAttributes as b, TrackSearchOptions as c, TrackerOptions as d, ResolvedResultClick as f, readResultData as g, ResolvedResultData as h, ResultRef as i, Tracker as l, TrackResultClicksOptions as m, ResultImpressionResolve as n, TrackResultEventOptions as o, ResultClickResolve as p, TrackResultImpressionsOptions as r, TrackResultImpressionOptions as s, ResolvedResultImpression as t, TrackerBaseOptions as u, Enricher as v, Logger as w, EventObject as x, Event as y };
762
+ //# sourceMappingURL=ResultImpressionCollector.d.mts.map