@seekora-ai/search-sdk 0.2.6 → 0.2.8
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/cdn/seekora-sdk.js +30 -19
- package/cdn/seekora-sdk.min.js +12 -12
- package/dist/client.d.ts +130 -0
- package/dist/client.js +199 -5
- package/dist/generated/api.d.ts +33029 -8906
- package/dist/generated/api.js +46824 -1681
- package/dist/index.d.ts +1 -0
- package/dist/index.js +14 -1
- package/dist/ui-components.d.ts +74 -0
- package/dist/ui-components.js +165 -0
- package/package.json +1 -1
- package/dist/src/cdn.d.ts +0 -16
- package/dist/src/cdn.js +0 -26
- package/dist/src/client.d.ts +0 -709
- package/dist/src/client.js +0 -1548
- package/dist/src/config-loader.d.ts +0 -43
- package/dist/src/config-loader.js +0 -147
- package/dist/src/config.d.ts +0 -22
- package/dist/src/config.js +0 -58
- package/dist/src/context-collector.d.ts +0 -273
- package/dist/src/context-collector.js +0 -868
- package/dist/src/event-queue.d.ts +0 -195
- package/dist/src/event-queue.js +0 -424
- package/dist/src/index.d.ts +0 -14
- package/dist/src/index.js +0 -48
- package/dist/src/logger.d.ts +0 -61
- package/dist/src/logger.js +0 -172
- package/dist/src/utils.d.ts +0 -20
- package/dist/src/utils.js +0 -73
package/dist/client.d.ts
CHANGED
|
@@ -54,6 +54,58 @@ export interface SeekoraClientConfig {
|
|
|
54
54
|
* Event queue configuration
|
|
55
55
|
*/
|
|
56
56
|
eventQueue?: EventQueueConfig;
|
|
57
|
+
/**
|
|
58
|
+
* Default options for getSuggestions(); merged with per-call options (per-call overrides).
|
|
59
|
+
* Use to set e.g. include_dropdown_product_list: false, include_filtered_tabs: false for all suggestion calls.
|
|
60
|
+
*/
|
|
61
|
+
suggestionsDefaults?: {
|
|
62
|
+
include_dropdown_recommendations?: boolean;
|
|
63
|
+
include_dropdown_product_list?: boolean;
|
|
64
|
+
include_filtered_tabs?: boolean;
|
|
65
|
+
hitsPerPage?: number;
|
|
66
|
+
time_range?: '7d' | '30d' | '90d';
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Automatically track search result impressions
|
|
71
|
+
* When enabled, impression events are sent for all items in search results
|
|
72
|
+
* @default true
|
|
73
|
+
*/
|
|
74
|
+
autoTrackImpressions?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Delay before tracking impressions (ms)
|
|
77
|
+
* Useful to ensure items are actually visible to the user
|
|
78
|
+
* @default 0
|
|
79
|
+
*/
|
|
80
|
+
impressionTrackingDelay?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Enable event deduplication (industry standard: Segment, Amplitude pattern)
|
|
83
|
+
* When enabled, generates insert_id for backend deduplication
|
|
84
|
+
*
|
|
85
|
+
* Philosophy: Track everything by default, provide tools for deduplication
|
|
86
|
+
* - false (default): Track all events, no client-side filtering
|
|
87
|
+
* - true: Generate insert_id for optional backend deduplication
|
|
88
|
+
*
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
enableDeduplication?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Deduplication time window in milliseconds
|
|
94
|
+
* Events with same user+event_type+item within this window get same insert_id
|
|
95
|
+
*
|
|
96
|
+
* Industry standards:
|
|
97
|
+
* - Segment: 10 minutes (600000ms)
|
|
98
|
+
* - Mixpanel: 5 days (but we use shorter for engagement events)
|
|
99
|
+
* - Amplitude: 7 days (for all events)
|
|
100
|
+
*
|
|
101
|
+
* Recommended by event type:
|
|
102
|
+
* - Engagement (click, view, share): 5 minutes (300000ms)
|
|
103
|
+
* - Conversion (add_to_cart, wishlist): 5 minutes (300000ms)
|
|
104
|
+
* - Purchase: 30 days (2592000000ms) - handled separately
|
|
105
|
+
*
|
|
106
|
+
* @default 300000 (5 minutes)
|
|
107
|
+
*/
|
|
108
|
+
deduplicationWindow?: number;
|
|
57
109
|
}
|
|
58
110
|
/**
|
|
59
111
|
* Search context containing identifiers for linking events to searches
|
|
@@ -66,6 +118,8 @@ export interface SearchContext {
|
|
|
66
118
|
correlationId: string;
|
|
67
119
|
/** Search ID from backend (if present in response) */
|
|
68
120
|
searchId?: string;
|
|
121
|
+
/** Journey ID for session-level tracking */
|
|
122
|
+
journey_id?: string;
|
|
69
123
|
/** User ID if authenticated */
|
|
70
124
|
userId?: string;
|
|
71
125
|
/** Anonymous user ID */
|
|
@@ -89,6 +143,10 @@ export interface ExtendedEventPayload extends DataTypesEventPayload {
|
|
|
89
143
|
correlation_id?: string;
|
|
90
144
|
/** Search ID at top level (also in metadata for backward compat) */
|
|
91
145
|
search_id?: string;
|
|
146
|
+
/** Insert ID for event deduplication (industry standard: Segment/Amplitude pattern) */
|
|
147
|
+
insert_id?: string;
|
|
148
|
+
/** Conversion type for conversion events (add_to_cart, wishlist, purchase, etc.) */
|
|
149
|
+
conversion_type?: string;
|
|
92
150
|
}
|
|
93
151
|
export interface SearchOptions {
|
|
94
152
|
q: string;
|
|
@@ -269,6 +327,9 @@ export declare class SeekoraClient {
|
|
|
269
327
|
private cachedBrowserContext;
|
|
270
328
|
private enableEventQueue;
|
|
271
329
|
private eventQueue;
|
|
330
|
+
private clientConfig;
|
|
331
|
+
private enableDeduplication;
|
|
332
|
+
private deduplicationWindow;
|
|
272
333
|
constructor(config?: SeekoraClientConfig);
|
|
273
334
|
/**
|
|
274
335
|
* Search for documents
|
|
@@ -304,6 +365,10 @@ export declare class SeekoraClient {
|
|
|
304
365
|
time_range?: '7d' | '30d' | '90d';
|
|
305
366
|
disable_typo_tolerance?: boolean;
|
|
306
367
|
include_dropdown_recommendations?: boolean;
|
|
368
|
+
/** When false, omit product hits list from dropdown result (default true) */
|
|
369
|
+
include_dropdown_product_list?: boolean;
|
|
370
|
+
/** When false, omit filtered_tabs from dropdown extensions (default true) */
|
|
371
|
+
include_filtered_tabs?: boolean;
|
|
307
372
|
filtered_tabs?: Array<{
|
|
308
373
|
id?: string;
|
|
309
374
|
label: string;
|
|
@@ -456,6 +521,11 @@ export declare class SeekoraClient {
|
|
|
456
521
|
* Send a batch of events directly to the backend (bypasses queue)
|
|
457
522
|
*/
|
|
458
523
|
private sendEventsBatchDirect;
|
|
524
|
+
/**
|
|
525
|
+
* Automatically track search result impressions
|
|
526
|
+
* Creates a batch of view events for all items in search results
|
|
527
|
+
*/
|
|
528
|
+
private autoTrackSearchImpressions;
|
|
459
529
|
/**
|
|
460
530
|
* Get browser context (sync if cached, otherwise returns null)
|
|
461
531
|
* Use collectBrowserContext() for async collection
|
|
@@ -465,6 +535,19 @@ export declare class SeekoraClient {
|
|
|
465
535
|
* Collect browser context asynchronously
|
|
466
536
|
*/
|
|
467
537
|
collectBrowserContext(): Promise<BrowserContext>;
|
|
538
|
+
/**
|
|
539
|
+
* Generate insert_id for event deduplication (industry standard)
|
|
540
|
+
*
|
|
541
|
+
* Format: {user_key}_{event_type}_{item_id}_{timestamp_window}
|
|
542
|
+
* Similar to Segment, Amplitude, Mixpanel deduplication keys
|
|
543
|
+
*
|
|
544
|
+
* @param eventType - Event name (search, click, conversion, etc.)
|
|
545
|
+
* @param itemId - Item ID (optional, for item-specific events)
|
|
546
|
+
* @param userId - User ID or anonymous ID
|
|
547
|
+
* @param window - Deduplication window in milliseconds
|
|
548
|
+
* @returns insert_id string for backend deduplication
|
|
549
|
+
*/
|
|
550
|
+
private generateInsertId;
|
|
468
551
|
/**
|
|
469
552
|
* Build event payload with identifiers and browser context
|
|
470
553
|
* Ensures user_id or anon_id is present, and sets correlation_id/search_id at top level
|
|
@@ -569,6 +652,53 @@ export declare class SeekoraClient {
|
|
|
569
652
|
* @param context - Optional search context for linking events to searches
|
|
570
653
|
*/
|
|
571
654
|
trackCustom(eventName: string, payload?: Partial<DataTypesEventPayload>, context?: SearchContext): Promise<void>;
|
|
655
|
+
/**
|
|
656
|
+
* Track add to cart conversion event
|
|
657
|
+
*
|
|
658
|
+
* @param params - Add to cart parameters
|
|
659
|
+
* @param params.itemId - Item ID that was added to cart
|
|
660
|
+
* @param params.quantity - Quantity added (default: 1)
|
|
661
|
+
* @param params.value - Item value/price
|
|
662
|
+
* @param params.currency - Currency code (default: 'USD')
|
|
663
|
+
* @param params.position - Position in search results (if from search)
|
|
664
|
+
* @param params.searchContext - Search context for attribution
|
|
665
|
+
*/
|
|
666
|
+
trackAddToCart(params: {
|
|
667
|
+
itemId: string;
|
|
668
|
+
quantity?: number;
|
|
669
|
+
value?: number;
|
|
670
|
+
currency?: string;
|
|
671
|
+
position?: number;
|
|
672
|
+
searchContext?: SearchContext;
|
|
673
|
+
}): Promise<void>;
|
|
674
|
+
/**
|
|
675
|
+
* Track add to wishlist conversion event
|
|
676
|
+
*
|
|
677
|
+
* @param params - Add to wishlist parameters
|
|
678
|
+
* @param params.itemId - Item ID that was added to wishlist
|
|
679
|
+
* @param params.position - Position in search results (if from search)
|
|
680
|
+
* @param params.searchContext - Search context for attribution
|
|
681
|
+
*/
|
|
682
|
+
trackAddToWishlist(params: {
|
|
683
|
+
itemId: string;
|
|
684
|
+
position?: number;
|
|
685
|
+
searchContext?: SearchContext;
|
|
686
|
+
}): Promise<void>;
|
|
687
|
+
/**
|
|
688
|
+
* Track item share event
|
|
689
|
+
*
|
|
690
|
+
* @param params - Share parameters
|
|
691
|
+
* @param params.itemId - Item ID that was shared
|
|
692
|
+
* @param params.shareMethod - Share method (e.g., 'facebook', 'twitter', 'whatsapp', 'copy_link')
|
|
693
|
+
* @param params.position - Position in search results (if from search)
|
|
694
|
+
* @param params.searchContext - Search context for attribution
|
|
695
|
+
*/
|
|
696
|
+
trackShare(params: {
|
|
697
|
+
itemId: string;
|
|
698
|
+
shareMethod?: string;
|
|
699
|
+
position?: number;
|
|
700
|
+
searchContext?: SearchContext;
|
|
701
|
+
}): Promise<void>;
|
|
572
702
|
/**
|
|
573
703
|
* Validate an event payload before sending
|
|
574
704
|
* Useful for debugging and ensuring events are properly formatted
|
package/dist/client.js
CHANGED
|
@@ -25,6 +25,7 @@ class SeekoraClient {
|
|
|
25
25
|
constructor(config = {}) {
|
|
26
26
|
this.cachedBrowserContext = null;
|
|
27
27
|
this.eventQueue = null;
|
|
28
|
+
this.clientConfig = config;
|
|
28
29
|
// Load configuration from file, env, and code (in that order)
|
|
29
30
|
const mergedConfig = (0, config_loader_1.loadConfig)(config);
|
|
30
31
|
this.storeId = mergedConfig.storeId;
|
|
@@ -55,6 +56,9 @@ class SeekoraClient {
|
|
|
55
56
|
this.eventQueue.setLogger(this.logger);
|
|
56
57
|
this.eventQueue.setSender(this.createQueueSender());
|
|
57
58
|
}
|
|
59
|
+
// Initialize deduplication settings (industry standard: Segment/Amplitude pattern)
|
|
60
|
+
this.enableDeduplication = config.enableDeduplication || false;
|
|
61
|
+
this.deduplicationWindow = config.deduplicationWindow || 300000; // Default: 5 minutes
|
|
58
62
|
// Log identifier initialization
|
|
59
63
|
this.logger.verbose('Client identifiers initialized', {
|
|
60
64
|
hasUserId: !!this.userId,
|
|
@@ -62,7 +66,9 @@ class SeekoraClient {
|
|
|
62
66
|
sessionId: this.sessionId.substring(0, 8) + '...',
|
|
63
67
|
autoTrackSearch: this.autoTrackSearch,
|
|
64
68
|
enableContextCollection: this.enableContextCollection,
|
|
65
|
-
enableEventQueue: this.enableEventQueue
|
|
69
|
+
enableEventQueue: this.enableEventQueue,
|
|
70
|
+
enableDeduplication: this.enableDeduplication,
|
|
71
|
+
deduplicationWindow: this.deduplicationWindow
|
|
66
72
|
});
|
|
67
73
|
this.logger.verbose('Initializing SeekoraClient', {
|
|
68
74
|
storeId: this.storeId,
|
|
@@ -274,6 +280,22 @@ class SeekoraClient {
|
|
|
274
280
|
this.logger.warn('Failed to auto-track search event', { error: err.message });
|
|
275
281
|
});
|
|
276
282
|
}
|
|
283
|
+
// Automatically track search result impressions if enabled
|
|
284
|
+
if (this.clientConfig.autoTrackImpressions !== false && results.results && results.results.length > 0) {
|
|
285
|
+
const delay = this.clientConfig.impressionTrackingDelay || 0;
|
|
286
|
+
const trackImpressions = () => {
|
|
287
|
+
this.autoTrackSearchImpressions(results, context).catch((err) => {
|
|
288
|
+
// Log but don't fail the search if tracking fails
|
|
289
|
+
this.logger.warn('Failed to auto-track impressions', { error: err.message });
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
if (delay > 0) {
|
|
293
|
+
setTimeout(trackImpressions, delay);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
trackImpressions();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
277
299
|
return results;
|
|
278
300
|
}
|
|
279
301
|
catch (error) {
|
|
@@ -324,20 +346,25 @@ class SeekoraClient {
|
|
|
324
346
|
if (this.sessionId)
|
|
325
347
|
headers['x-session-id'] = this.sessionId;
|
|
326
348
|
const usePost = (options?.filtered_tabs && options.filtered_tabs.length > 0) ||
|
|
327
|
-
options?.include_dropdown_recommendations === true
|
|
349
|
+
options?.include_dropdown_recommendations === true ||
|
|
350
|
+
options?.include_dropdown_product_list === false ||
|
|
351
|
+
options?.include_filtered_tabs === false;
|
|
352
|
+
const defaults = this.clientConfig?.suggestionsDefaults;
|
|
328
353
|
const buildRequestBody = () => {
|
|
329
354
|
const body = {
|
|
330
355
|
query,
|
|
331
|
-
hitsPerPage: options?.hitsPerPage
|
|
356
|
+
hitsPerPage: options?.hitsPerPage ?? defaults?.hitsPerPage ?? 5,
|
|
332
357
|
page: options?.page,
|
|
333
358
|
include_categories: options?.include_categories,
|
|
334
359
|
include_facets: options?.include_facets,
|
|
335
360
|
max_categories: options?.max_categories,
|
|
336
361
|
max_facets: options?.max_facets,
|
|
337
362
|
min_popularity: options?.min_popularity,
|
|
338
|
-
time_range: options?.time_range,
|
|
363
|
+
time_range: options?.time_range ?? defaults?.time_range,
|
|
339
364
|
disable_typo_tolerance: options?.disable_typo_tolerance,
|
|
340
|
-
include_dropdown_recommendations: options?.include_dropdown_recommendations,
|
|
365
|
+
include_dropdown_recommendations: options?.include_dropdown_recommendations ?? defaults?.include_dropdown_recommendations,
|
|
366
|
+
include_dropdown_product_list: options?.include_dropdown_product_list ?? defaults?.include_dropdown_product_list,
|
|
367
|
+
include_filtered_tabs: options?.include_filtered_tabs ?? defaults?.include_filtered_tabs,
|
|
341
368
|
filtered_tabs: options?.filtered_tabs,
|
|
342
369
|
};
|
|
343
370
|
if (options?.analytics_tags) {
|
|
@@ -1020,6 +1047,52 @@ class SeekoraClient {
|
|
|
1020
1047
|
throw new Error(`Failed to send batch events: ${response.status}`);
|
|
1021
1048
|
}
|
|
1022
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Automatically track search result impressions
|
|
1052
|
+
* Creates a batch of view events for all items in search results
|
|
1053
|
+
*/
|
|
1054
|
+
async autoTrackSearchImpressions(searchResponse, context) {
|
|
1055
|
+
const items = searchResponse.results || [];
|
|
1056
|
+
if (items.length === 0) {
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
this.logger.verbose('Auto-tracking search impressions', {
|
|
1060
|
+
itemCount: items.length,
|
|
1061
|
+
query: searchResponse.query || searchResponse.data?.data?.query,
|
|
1062
|
+
searchId: context.searchId
|
|
1063
|
+
});
|
|
1064
|
+
// Create batch impression events
|
|
1065
|
+
const impressionEvents = items.map((item, index) => ({
|
|
1066
|
+
event_name: 'view',
|
|
1067
|
+
event_id: (0, utils_1.generateUUID)(),
|
|
1068
|
+
event_ts: new Date().toISOString(),
|
|
1069
|
+
// Link to search
|
|
1070
|
+
search_id: context.searchId,
|
|
1071
|
+
journey_id: context.journey_id,
|
|
1072
|
+
correlation_id: context.correlationId,
|
|
1073
|
+
session_id: context.sessionId || this.sessionId,
|
|
1074
|
+
// User context
|
|
1075
|
+
user_id: context.userId || this.userId,
|
|
1076
|
+
anonymous_id: context.anonId || this.anonId,
|
|
1077
|
+
// Item details
|
|
1078
|
+
clicked_item_id: item.id,
|
|
1079
|
+
position: index + 1,
|
|
1080
|
+
query: searchResponse.query || searchResponse.data?.data?.query,
|
|
1081
|
+
// Optional metadata
|
|
1082
|
+
analytics_tags: ['source:search', 'auto_tracked:true']
|
|
1083
|
+
}));
|
|
1084
|
+
try {
|
|
1085
|
+
await this.trackEvents(impressionEvents);
|
|
1086
|
+
this.logger.debug('Auto-tracked search impressions', { count: impressionEvents.length });
|
|
1087
|
+
}
|
|
1088
|
+
catch (error) {
|
|
1089
|
+
this.logger.error('Failed to auto-track impressions', {
|
|
1090
|
+
error: error.message,
|
|
1091
|
+
itemCount: items.length
|
|
1092
|
+
});
|
|
1093
|
+
throw error;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1023
1096
|
/**
|
|
1024
1097
|
* Get browser context (sync if cached, otherwise returns null)
|
|
1025
1098
|
* Use collectBrowserContext() for async collection
|
|
@@ -1034,6 +1107,31 @@ class SeekoraClient {
|
|
|
1034
1107
|
this.cachedBrowserContext = await this.contextCollector.collect();
|
|
1035
1108
|
return this.cachedBrowserContext;
|
|
1036
1109
|
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Generate insert_id for event deduplication (industry standard)
|
|
1112
|
+
*
|
|
1113
|
+
* Format: {user_key}_{event_type}_{item_id}_{timestamp_window}
|
|
1114
|
+
* Similar to Segment, Amplitude, Mixpanel deduplication keys
|
|
1115
|
+
*
|
|
1116
|
+
* @param eventType - Event name (search, click, conversion, etc.)
|
|
1117
|
+
* @param itemId - Item ID (optional, for item-specific events)
|
|
1118
|
+
* @param userId - User ID or anonymous ID
|
|
1119
|
+
* @param window - Deduplication window in milliseconds
|
|
1120
|
+
* @returns insert_id string for backend deduplication
|
|
1121
|
+
*/
|
|
1122
|
+
generateInsertId(eventType, itemId, userId, window) {
|
|
1123
|
+
// Round timestamp to nearest window (5 min default)
|
|
1124
|
+
const timestamp = Math.floor(Date.now() / window);
|
|
1125
|
+
// Format: userKey_eventType_itemId_timestampWindow
|
|
1126
|
+
// Item ID is optional (not all events have items)
|
|
1127
|
+
const parts = [
|
|
1128
|
+
userId.replace(/[^a-zA-Z0-9]/g, '_'), // Sanitize user ID
|
|
1129
|
+
eventType.replace(/[^a-zA-Z0-9]/g, '_'), // Sanitize event type
|
|
1130
|
+
itemId ? itemId.replace(/[^a-zA-Z0-9]/g, '_') : 'null',
|
|
1131
|
+
timestamp.toString()
|
|
1132
|
+
];
|
|
1133
|
+
return parts.join('_');
|
|
1134
|
+
}
|
|
1037
1135
|
/**
|
|
1038
1136
|
* Build event payload with identifiers and browser context
|
|
1039
1137
|
* Ensures user_id or anon_id is present, and sets correlation_id/search_id at top level
|
|
@@ -1115,6 +1213,26 @@ class SeekoraClient {
|
|
|
1115
1213
|
payload.is_tablet = ctx.is_tablet ? 1 : 0;
|
|
1116
1214
|
payload.is_touch_device = ctx.is_touch_device;
|
|
1117
1215
|
}
|
|
1216
|
+
// Generate insert_id for deduplication (industry standard: Segment/Amplitude pattern)
|
|
1217
|
+
// Always enable for purchase events (revenue must be accurate)
|
|
1218
|
+
// Optional for other events based on config
|
|
1219
|
+
const isPurchaseEvent = payload.event_name === 'conversion' &&
|
|
1220
|
+
(payload.conversion_type === 'purchase' || payload.conversion_type === 'checkout_completed');
|
|
1221
|
+
if (this.enableDeduplication || isPurchaseEvent) {
|
|
1222
|
+
const userKey = payload.user_id || payload.anon_id || this.anonId;
|
|
1223
|
+
const eventType = payload.event_name || 'unknown';
|
|
1224
|
+
const itemId = payload.clicked_item_id;
|
|
1225
|
+
// Use longer window for purchase events (30 days)
|
|
1226
|
+
const window = isPurchaseEvent ? 2592000000 : this.deduplicationWindow;
|
|
1227
|
+
payload.insert_id = this.generateInsertId(eventType, itemId, userKey, window);
|
|
1228
|
+
this.logger.verbose('Generated insert_id for deduplication', {
|
|
1229
|
+
event_name: eventType,
|
|
1230
|
+
item_id: itemId,
|
|
1231
|
+
insert_id: payload.insert_id,
|
|
1232
|
+
is_purchase: isPurchaseEvent,
|
|
1233
|
+
window_ms: window
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1118
1236
|
return payload;
|
|
1119
1237
|
}
|
|
1120
1238
|
/**
|
|
@@ -1321,6 +1439,82 @@ class SeekoraClient {
|
|
|
1321
1439
|
event_name: eventName,
|
|
1322
1440
|
}, context);
|
|
1323
1441
|
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Track add to cart conversion event
|
|
1444
|
+
*
|
|
1445
|
+
* @param params - Add to cart parameters
|
|
1446
|
+
* @param params.itemId - Item ID that was added to cart
|
|
1447
|
+
* @param params.quantity - Quantity added (default: 1)
|
|
1448
|
+
* @param params.value - Item value/price
|
|
1449
|
+
* @param params.currency - Currency code (default: 'USD')
|
|
1450
|
+
* @param params.position - Position in search results (if from search)
|
|
1451
|
+
* @param params.searchContext - Search context for attribution
|
|
1452
|
+
*/
|
|
1453
|
+
async trackAddToCart(params) {
|
|
1454
|
+
this.logger.verbose('Tracking add to cart event', {
|
|
1455
|
+
itemId: params.itemId,
|
|
1456
|
+
quantity: params.quantity,
|
|
1457
|
+
value: params.value
|
|
1458
|
+
});
|
|
1459
|
+
await this.trackEvent({
|
|
1460
|
+
event_name: 'conversion',
|
|
1461
|
+
conversion_type: 'add_to_cart',
|
|
1462
|
+
clicked_item_id: params.itemId,
|
|
1463
|
+
quantity: params.quantity || 1,
|
|
1464
|
+
value: params.value,
|
|
1465
|
+
currency: params.currency || 'USD',
|
|
1466
|
+
position: params.position,
|
|
1467
|
+
analytics_tags: ['action:add_to_cart'],
|
|
1468
|
+
}, params.searchContext);
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Track add to wishlist conversion event
|
|
1472
|
+
*
|
|
1473
|
+
* @param params - Add to wishlist parameters
|
|
1474
|
+
* @param params.itemId - Item ID that was added to wishlist
|
|
1475
|
+
* @param params.position - Position in search results (if from search)
|
|
1476
|
+
* @param params.searchContext - Search context for attribution
|
|
1477
|
+
*/
|
|
1478
|
+
async trackAddToWishlist(params) {
|
|
1479
|
+
this.logger.verbose('Tracking add to wishlist event', {
|
|
1480
|
+
itemId: params.itemId
|
|
1481
|
+
});
|
|
1482
|
+
await this.trackEvent({
|
|
1483
|
+
event_name: 'conversion',
|
|
1484
|
+
conversion_type: 'wishlist',
|
|
1485
|
+
clicked_item_id: params.itemId,
|
|
1486
|
+
position: params.position,
|
|
1487
|
+
analytics_tags: ['action:add_to_wishlist'],
|
|
1488
|
+
}, params.searchContext);
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Track item share event
|
|
1492
|
+
*
|
|
1493
|
+
* @param params - Share parameters
|
|
1494
|
+
* @param params.itemId - Item ID that was shared
|
|
1495
|
+
* @param params.shareMethod - Share method (e.g., 'facebook', 'twitter', 'whatsapp', 'copy_link')
|
|
1496
|
+
* @param params.position - Position in search results (if from search)
|
|
1497
|
+
* @param params.searchContext - Search context for attribution
|
|
1498
|
+
*/
|
|
1499
|
+
async trackShare(params) {
|
|
1500
|
+
this.logger.verbose('Tracking share event', {
|
|
1501
|
+
itemId: params.itemId,
|
|
1502
|
+
shareMethod: params.shareMethod
|
|
1503
|
+
});
|
|
1504
|
+
await this.trackEvent({
|
|
1505
|
+
event_name: 'custom',
|
|
1506
|
+
clicked_item_id: params.itemId,
|
|
1507
|
+
position: params.position,
|
|
1508
|
+
analytics_tags: [
|
|
1509
|
+
'action:share',
|
|
1510
|
+
`share_method:${params.shareMethod || 'unknown'}`
|
|
1511
|
+
],
|
|
1512
|
+
custom_json: JSON.stringify({
|
|
1513
|
+
share_method: params.shareMethod,
|
|
1514
|
+
action_type: 'share'
|
|
1515
|
+
}),
|
|
1516
|
+
}, params.searchContext);
|
|
1517
|
+
}
|
|
1324
1518
|
/**
|
|
1325
1519
|
* Validate an event payload before sending
|
|
1326
1520
|
* Useful for debugging and ensuring events are properly formatted
|