@seekora-ai/search-sdk 0.2.22 → 0.2.23
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/dist/analytics/activeFilters.d.ts +33 -0
- package/dist/analytics/activeFilters.js +145 -0
- package/dist/analytics/events.d.ts +26 -0
- package/dist/analytics/events.js +38 -0
- package/dist/analytics/index.d.ts +6 -0
- package/dist/analytics/index.js +55 -0
- package/dist/analytics/loadAnalytics.d.ts +37 -0
- package/dist/analytics/loadAnalytics.js +70 -0
- package/dist/analytics/searchId.d.ts +26 -0
- package/dist/analytics/searchId.js +102 -0
- package/dist/analytics/track.d.ts +59 -0
- package/dist/analytics/track.js +130 -0
- package/dist/analytics/types.gen.d.ts +781 -0
- package/dist/analytics/types.gen.js +6 -0
- package/dist/client.d.ts +82 -17
- package/dist/client.js +588 -414
- package/dist/index.d.ts +13 -12
- package/dist/index.js +39 -1
- package/package.json +9 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Canonical V4 emission wrapper around @segment/analytics-next.
|
|
3
|
+
//
|
|
4
|
+
// Every Seekora SDK callsite that emits a clickstream event MUST go through
|
|
5
|
+
// `track()` (or one of the named helpers below). The wrapper guarantees:
|
|
6
|
+
//
|
|
7
|
+
// - Tenant context (orgcode + xstoreid) is stamped on every event so
|
|
8
|
+
// downstream Rotor functions don't have to infer it.
|
|
9
|
+
// - search_id is auto-resolved from the 3-layer chain (in-memory →
|
|
10
|
+
// sessionStorage → URL) when the caller hasn't supplied it. Callers can
|
|
11
|
+
// override by passing an explicit `search_id` in properties.
|
|
12
|
+
//
|
|
13
|
+
// The wrapper is intentionally tiny — type checking lives in the codegen'd
|
|
14
|
+
// AnalyticsEvent union (re-exported from ./events), and validation happens
|
|
15
|
+
// downstream in the Rotor `seekora-event-validate` function. This wrapper
|
|
16
|
+
// is the lightest possible shim so unit-level tests can drive it.
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.trackFilterViewed = exports.trackObjectViewed = exports.trackProductViewed = exports.trackOrderCompleted = exports.trackProductAdded = exports.trackFilterConverted = exports.trackObjectConvertedOutsideSearch = exports.trackObjectConverted = exports.trackObjectClickedOutsideSearch = exports.trackSuggestionClicked = exports.trackSearchRefined = exports.trackSearchEmpty = exports.trackFacetApplied = exports.trackObjectClicked = exports.trackSearchImpression = exports.trackSearchSubmitted = void 0;
|
|
19
|
+
exports.track = track;
|
|
20
|
+
exports.emitFilterConvertedForActiveFilters = emitFilterConvertedForActiveFilters;
|
|
21
|
+
const events_1 = require("./events");
|
|
22
|
+
const searchId_1 = require("./searchId");
|
|
23
|
+
const activeFilters_1 = require("./activeFilters");
|
|
24
|
+
/**
|
|
25
|
+
* Emit a track event with canonical Seekora fields auto-stamped.
|
|
26
|
+
*
|
|
27
|
+
* - Tenant context (orgcode, xstoreid) is always merged in.
|
|
28
|
+
* - search_id is auto-resolved via resolveSearchId() when absent. Callers
|
|
29
|
+
* that already know the search_id should pass it explicitly to skip the
|
|
30
|
+
* resolver work.
|
|
31
|
+
*/
|
|
32
|
+
function track(ctx, event, properties = {}) {
|
|
33
|
+
const enriched = {
|
|
34
|
+
...properties,
|
|
35
|
+
orgcode: ctx.orgcode,
|
|
36
|
+
xstoreid: ctx.xstoreid,
|
|
37
|
+
};
|
|
38
|
+
// Auto-resolve search_id when the caller didn't supply a non-empty
|
|
39
|
+
// value. Treating `search_id: undefined` (or `null`/`""`) as "supplied"
|
|
40
|
+
// would skip the 3-layer resolver and the event would land in
|
|
41
|
+
// ClickHouse with no search_id -- breaking funnel attribution for
|
|
42
|
+
// every Object Clicked that came from a ProductCard which doesn't pass
|
|
43
|
+
// searchId explicitly (the common case). The `in` operator alone
|
|
44
|
+
// returns true for `{search_id: undefined}` because the key is
|
|
45
|
+
// present, so check the value too.
|
|
46
|
+
const explicit = enriched.search_id;
|
|
47
|
+
if (explicit == null || explicit === "") {
|
|
48
|
+
const sid = (0, searchId_1.resolveSearchId)();
|
|
49
|
+
if (sid) {
|
|
50
|
+
enriched.search_id = sid;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Drop the empty key entirely so analytics-next doesn't ship a
|
|
54
|
+
// `"search_id": null` that downstream queries can confuse with
|
|
55
|
+
// an explicit "no-attribution" intent.
|
|
56
|
+
delete enriched.search_id;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
ctx.analytics.track(event, enriched);
|
|
60
|
+
}
|
|
61
|
+
// --- Named helpers --------------------------------------------------------
|
|
62
|
+
// Thin sugar so callsites don't have to import EVENT_NAMES alongside track().
|
|
63
|
+
const trackSearchSubmitted = (ctx, p) => track(ctx, events_1.EVENT_NAMES.SearchSubmitted, p);
|
|
64
|
+
exports.trackSearchSubmitted = trackSearchSubmitted;
|
|
65
|
+
const trackSearchImpression = (ctx, p) => track(ctx, events_1.EVENT_NAMES.SearchImpression, p);
|
|
66
|
+
exports.trackSearchImpression = trackSearchImpression;
|
|
67
|
+
const trackObjectClicked = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ObjectClicked, p);
|
|
68
|
+
exports.trackObjectClicked = trackObjectClicked;
|
|
69
|
+
const trackFacetApplied = (ctx, p) => track(ctx, events_1.EVENT_NAMES.FacetApplied, p);
|
|
70
|
+
exports.trackFacetApplied = trackFacetApplied;
|
|
71
|
+
const trackSearchEmpty = (ctx, p) => track(ctx, events_1.EVENT_NAMES.SearchEmpty, p);
|
|
72
|
+
exports.trackSearchEmpty = trackSearchEmpty;
|
|
73
|
+
const trackSearchRefined = (ctx, p) => track(ctx, events_1.EVENT_NAMES.SearchRefined, p);
|
|
74
|
+
exports.trackSearchRefined = trackSearchRefined;
|
|
75
|
+
const trackSuggestionClicked = (ctx, p) => track(ctx, events_1.EVENT_NAMES.SuggestionClicked, p);
|
|
76
|
+
exports.trackSuggestionClicked = trackSuggestionClicked;
|
|
77
|
+
const trackObjectClickedOutsideSearch = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ObjectClickedOutsideSearch, p);
|
|
78
|
+
exports.trackObjectClickedOutsideSearch = trackObjectClickedOutsideSearch;
|
|
79
|
+
const trackObjectConverted = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ObjectConverted, p);
|
|
80
|
+
exports.trackObjectConverted = trackObjectConverted;
|
|
81
|
+
const trackObjectConvertedOutsideSearch = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ObjectConvertedOutsideSearch, p);
|
|
82
|
+
exports.trackObjectConvertedOutsideSearch = trackObjectConvertedOutsideSearch;
|
|
83
|
+
const trackFilterConverted = (ctx, p) => track(ctx, events_1.EVENT_NAMES.FilterConverted, p);
|
|
84
|
+
exports.trackFilterConverted = trackFilterConverted;
|
|
85
|
+
/**
|
|
86
|
+
* Emit one `Filter Converted` event per currently-active filter.
|
|
87
|
+
*
|
|
88
|
+
* Algolia hybrid pattern: a single conversion (addToCart / purchase /
|
|
89
|
+
* wishlist) gets attributed to EVERY filter that was active in the
|
|
90
|
+
* 30-min attribution window — one event per filter so funnel queries
|
|
91
|
+
* can answer "which filter values converted at what rate".
|
|
92
|
+
*
|
|
93
|
+
* `baseProps` carry the conversion metadata that's identical across
|
|
94
|
+
* all of the emitted events (conversion_type, value, currency, plus
|
|
95
|
+
* any extras the caller wants threaded through). Each emit then
|
|
96
|
+
* stamps the per-filter `filter_attribute` + `filter_value`.
|
|
97
|
+
*
|
|
98
|
+
* If no filters are active (empty session-state OR every filter
|
|
99
|
+
* expired), this is a no-op — the regular Object Converted / Product
|
|
100
|
+
* Added event still goes out (the caller's responsibility), but no
|
|
101
|
+
* Filter Converted gets emitted.
|
|
102
|
+
*
|
|
103
|
+
* Returns the number of events emitted, which is handy for unit tests
|
|
104
|
+
* and for callers that want to log "attributed N filters".
|
|
105
|
+
*/
|
|
106
|
+
function emitFilterConvertedForActiveFilters(ctx, baseProps = {}) {
|
|
107
|
+
const filters = (0, activeFilters_1.getActiveFilters)();
|
|
108
|
+
for (const f of filters) {
|
|
109
|
+
track(ctx, events_1.EVENT_NAMES.FilterConverted, {
|
|
110
|
+
...baseProps,
|
|
111
|
+
filter_attribute: f.filter_attribute,
|
|
112
|
+
filter_value: f.filter_value,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return filters.length;
|
|
116
|
+
}
|
|
117
|
+
const trackProductAdded = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ProductAdded, p);
|
|
118
|
+
exports.trackProductAdded = trackProductAdded;
|
|
119
|
+
// NOTE: Order Line Completed is synthesized downstream by the rotor
|
|
120
|
+
// seekora-orderline-explode function from each Order Completed's
|
|
121
|
+
// `products[]` array. There is NO client helper for it -- callsites
|
|
122
|
+
// only emit Order Completed.
|
|
123
|
+
const trackOrderCompleted = (ctx, p) => track(ctx, events_1.EVENT_NAMES.OrderCompleted, p);
|
|
124
|
+
exports.trackOrderCompleted = trackOrderCompleted;
|
|
125
|
+
const trackProductViewed = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ProductViewed, p);
|
|
126
|
+
exports.trackProductViewed = trackProductViewed;
|
|
127
|
+
const trackObjectViewed = (ctx, p) => track(ctx, events_1.EVENT_NAMES.ObjectViewed, p);
|
|
128
|
+
exports.trackObjectViewed = trackObjectViewed;
|
|
129
|
+
const trackFilterViewed = (ctx, p) => track(ctx, events_1.EVENT_NAMES.FilterViewed, p);
|
|
130
|
+
exports.trackFilterViewed = trackFilterViewed;
|