@blotoutio/providers-evo-search-sdk 1.53.1 → 1.54.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.
- package/core.cjs.js +160 -1
- package/core.js +160 -1
- package/core.mjs +160 -1
- package/hooks.cjs.js +160 -1
- package/hooks.d.ts +36 -13
- package/hooks.mjs +160 -1
- package/index.cjs.js +195 -4
- package/index.js +195 -4
- package/index.mjs +195 -4
- package/package.json +1 -1
package/core.cjs.js
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Known ad-network click ID query parameters and the human-readable provider
|
|
5
|
+
* label each one maps to. Used by the CDN worker to resolve `inSessionTouch`
|
|
6
|
+
* and by the analytics API to classify paid-media touch sessions.
|
|
7
|
+
*
|
|
8
|
+
* Keep this list in sync with `defaultParams` in queryParams.ts when new
|
|
9
|
+
* click ID providers are added.
|
|
10
|
+
*/
|
|
11
|
+
const CLICK_IDS = [
|
|
12
|
+
{ param: 'fbclid', label: 'Facebook' }, // Facebook Ads
|
|
13
|
+
{ param: 'gclid', label: 'Google' }, // Google Ads (click)
|
|
14
|
+
{ param: 'gbraid', label: 'Google' }, // Google Ads (iOS app)
|
|
15
|
+
{ param: 'wbraid', label: 'Google' }, // Google Ads (iOS web)
|
|
16
|
+
{ param: 'msclkid', label: 'Bing' }, // Microsoft Bing Ads
|
|
17
|
+
{ param: 'ttclid', label: 'TikTok' }, // TikTok Ads
|
|
18
|
+
{ param: 'ScCid', label: 'Snapchat' }, // Snapchat Ads
|
|
19
|
+
{ param: 'epik', label: 'Pinterest' }, // Pinterest Ads
|
|
20
|
+
{ param: 'li_fat_id', label: 'LinkedIn' }, // LinkedIn Ads
|
|
21
|
+
{ param: 'twclid', label: 'Twitter' }, // X (Twitter) Ads
|
|
22
|
+
{ param: 'rdt_cid', label: 'Reddit' }, // Reddit Ads
|
|
23
|
+
{ param: 'aleid', label: 'AppLovin' }, // AppLovin
|
|
24
|
+
{ param: 'tabclid', label: 'Taboola' }, // Taboola
|
|
25
|
+
{ param: 'obclid', label: 'Outbrain' }, // Outbrain
|
|
26
|
+
{ param: 'trybe', label: 'Trybe' }, // Trybe
|
|
27
|
+
{ param: '_kx', label: 'Klaviyo' }, // Klaviyo email campaigns
|
|
28
|
+
{ param: 'mc_eid', label: 'Mailchimp' }, // Mailchimp email campaigns
|
|
29
|
+
{ param: 'ttd_id', label: 'The Trade Desk' }, // The Trade Desk programmatic
|
|
30
|
+
{ param: 'evsclid', label: 'EvoSearch' }, // EvoSearch
|
|
31
|
+
{ param: 'li_did', label: 'Live Intent' }, // LiveIntent device-level ad
|
|
32
|
+
{ param: '_raclid', label: 'Rumble' }, // Rumble Ads
|
|
33
|
+
{ param: 'ref_id', label: 'StackAdapt' }, // StackAdapt programmatic
|
|
34
|
+
{ param: 'duel_a', label: 'Duel' }, // Duel referral/advocacy
|
|
35
|
+
];
|
|
36
|
+
|
|
3
37
|
const expand = (str) => str.split(',').flatMap((entry) => {
|
|
4
38
|
if (!entry.includes('-')) {
|
|
5
39
|
return entry;
|
|
@@ -356,8 +390,133 @@ const usStates = new Map([
|
|
|
356
390
|
]);
|
|
357
391
|
new Set([...isoCountries.keys(), ...usStates.keys()]);
|
|
358
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Exact utm_source normalization (lowercase key → canonical name).
|
|
395
|
+
*
|
|
396
|
+
* Use exact match when the key is short, generic, or must not accidentally
|
|
397
|
+
* absorb variant spellings (e.g. "impact" must not match "impact_radius").
|
|
398
|
+
*
|
|
399
|
+
* Ordering: Paid (search/social → ad networks → affiliates) →
|
|
400
|
+
* Organic (AI/discovery) → Retention (email → SMS → push → post-purchase)
|
|
401
|
+
*/
|
|
402
|
+
const UTM_SOURCE_EXACT = {
|
|
403
|
+
// ── Paid – Search & Social ────────────────────────────────────────────────
|
|
404
|
+
google: 'Google',
|
|
405
|
+
adwords: 'Google',
|
|
406
|
+
youtube: 'Google',
|
|
407
|
+
yt: 'Google',
|
|
408
|
+
meta: 'Facebook',
|
|
409
|
+
facebook: 'Facebook',
|
|
410
|
+
instagram: 'Facebook',
|
|
411
|
+
ig: 'Facebook',
|
|
412
|
+
igshopping: 'Facebook',
|
|
413
|
+
threads: 'Facebook',
|
|
414
|
+
twitter: 'Twitter',
|
|
415
|
+
snapchat: 'Snapchat',
|
|
416
|
+
pinterest: 'Pinterest',
|
|
417
|
+
bing: 'Bing',
|
|
418
|
+
microsoft: 'Bing',
|
|
419
|
+
tiktok: 'TikTok',
|
|
420
|
+
// ── Paid – Ad Networks ───────────────────────────────────────────────────
|
|
421
|
+
rtbhouse: 'RTB House',
|
|
422
|
+
applovin: 'AppLovin',
|
|
423
|
+
ttd: 'The Trade Desk',
|
|
424
|
+
amazondsp: 'Amazon DSP',
|
|
425
|
+
axon: 'Axon',
|
|
426
|
+
duel: 'Duel',
|
|
427
|
+
// ── Paid – Affiliates ────────────────────────────────────────────────────
|
|
428
|
+
awin: 'Awin',
|
|
429
|
+
'affiliate-cj': 'CJ Affiliate',
|
|
430
|
+
impact: 'Impact',
|
|
431
|
+
rakuten: 'Rakuten',
|
|
432
|
+
superfiliate: 'Superfiliate',
|
|
433
|
+
// ── Organic – AI & Discovery ─────────────────────────────────────────────
|
|
434
|
+
perplexity: 'Perplexity',
|
|
435
|
+
chatgpt: 'ChatGPT',
|
|
436
|
+
'chatgpt.com': 'ChatGPT',
|
|
437
|
+
openai: 'ChatGPT',
|
|
438
|
+
'copilot.com': 'Microsoft Copilot',
|
|
439
|
+
copilot: 'Microsoft Copilot',
|
|
440
|
+
applenews: 'Apple News',
|
|
441
|
+
whatsapp: 'WhatsApp',
|
|
442
|
+
podcast: 'Podcast',
|
|
443
|
+
// ── Retention – Email ────────────────────────────────────────────────────
|
|
444
|
+
mailchimp: 'Mailchimp',
|
|
445
|
+
omnisend: 'Omnisend',
|
|
446
|
+
iterable: 'Iterable',
|
|
447
|
+
listrak: 'Listrak',
|
|
448
|
+
sailthru: 'Sailthru',
|
|
449
|
+
// ── Retention – Push ─────────────────────────────────────────────────────
|
|
450
|
+
pushowl: 'PushOwl',
|
|
451
|
+
// ── Retention – Post-purchase / Payment ──────────────────────────────────
|
|
452
|
+
narvar: 'Narvar',
|
|
453
|
+
shop_app: 'Shop App',
|
|
454
|
+
salesforce: 'Salesforce',
|
|
455
|
+
yotpo: 'Yotpo',
|
|
456
|
+
};
|
|
457
|
+
/**
|
|
458
|
+
* Partial utm_source normalization (startsWith prefix check, lowercase).
|
|
459
|
+
*
|
|
460
|
+
* Checked after exact match so short exact keys (e.g. "ig", "yt") win first.
|
|
461
|
+
*/
|
|
462
|
+
const UTM_SOURCE_PARTIAL = [
|
|
463
|
+
// ── Paid – Social ─────────────────────────────────────────────────────────
|
|
464
|
+
{ prefix: 'tiktok', name: 'TikTok' },
|
|
465
|
+
// ── Paid – Ad Networks ───────────────────────────────────────────────────
|
|
466
|
+
{ prefix: 'criteo', name: 'Criteo' },
|
|
467
|
+
// ── Retention – Email ────────────────────────────────────────────────────
|
|
468
|
+
{ prefix: 'klaviyo', name: 'Klaviyo' },
|
|
469
|
+
// ── Retention – SMS ──────────────────────────────────────────────────────
|
|
470
|
+
{ prefix: 'attentive', name: 'Attentive' },
|
|
471
|
+
{ prefix: 'postscript', name: 'Postscript' },
|
|
472
|
+
// ── Retention – Post-purchase / Payment ──────────────────────────────────
|
|
473
|
+
{ prefix: 'afterpay', name: 'Afterpay' },
|
|
474
|
+
{ prefix: 'klarna', name: 'Klarna' },
|
|
475
|
+
];
|
|
476
|
+
const ORGANIC_SEARCH_ENGINES = [
|
|
477
|
+
{ match: 'google', name: 'Google' },
|
|
478
|
+
{ match: 'bing', name: 'Bing' },
|
|
479
|
+
{ match: 'yahoo', name: 'Yahoo' },
|
|
480
|
+
{ match: 'duckduckgo', name: 'DuckDuckGo' },
|
|
481
|
+
{ match: 'baidu', name: 'Baidu' },
|
|
482
|
+
{ match: 'yandex', name: 'Yandex' },
|
|
483
|
+
{ match: 'brave', name: 'Brave' },
|
|
484
|
+
];
|
|
485
|
+
const KNOWN_REFERRAL_PLATFORMS = [
|
|
486
|
+
{ match: 'facebook', name: 'Facebook' },
|
|
487
|
+
{ match: 'instagram', name: 'Facebook' },
|
|
488
|
+
{ match: 'twitter', name: 'Twitter' },
|
|
489
|
+
{ match: 'x.com', name: 'Twitter' },
|
|
490
|
+
{ match: 'tiktok', name: 'TikTok' },
|
|
491
|
+
{ match: 'pinterest', name: 'Pinterest' },
|
|
492
|
+
{ match: 'linkedin', name: 'LinkedIn' },
|
|
493
|
+
{ match: 'youtube', name: 'YouTube' },
|
|
494
|
+
{ match: 'chatgpt', name: 'ChatGPT' },
|
|
495
|
+
{ match: 'claude', name: 'Claude' },
|
|
496
|
+
{ match: 'perplexity', name: 'Perplexity' },
|
|
497
|
+
{ match: 'shop.app', name: 'Shop App' },
|
|
498
|
+
{ match: 'amazon', name: 'Amazon' },
|
|
499
|
+
{ match: 'attentive', name: 'Attentive' },
|
|
500
|
+
];
|
|
501
|
+
const OFFLINE_TOUCH = 'Blotout_Offline';
|
|
502
|
+
new Set([
|
|
503
|
+
...CLICK_IDS.map((c) => c.label),
|
|
504
|
+
...Object.values(UTM_SOURCE_EXACT),
|
|
505
|
+
...UTM_SOURCE_PARTIAL.map((e) => e.name),
|
|
506
|
+
...ORGANIC_SEARCH_ENGINES.map((e) => `Organic Search - ${e.name}`),
|
|
507
|
+
...KNOWN_REFERRAL_PLATFORMS.map((e) => `Referral - ${e.name}`),
|
|
508
|
+
'Referral - Other',
|
|
509
|
+
'Direct Traffic',
|
|
510
|
+
'Other',
|
|
511
|
+
OFFLINE_TOUCH,
|
|
512
|
+
]);
|
|
513
|
+
|
|
359
514
|
const uiActions = new Set([
|
|
360
515
|
'evoSearchProductRecommendationClicked',
|
|
516
|
+
'evoSearchProductClicked',
|
|
517
|
+
'evoSearchProductAddedToCart',
|
|
518
|
+
'evoSearchProductPurchased',
|
|
519
|
+
'evoSearchSearchIssued',
|
|
361
520
|
]);
|
|
362
521
|
new Set([...uiActions]);
|
|
363
522
|
|
|
@@ -515,7 +674,7 @@ const createEvoSearchAPI = ({ fetch: fetchImpl = window.fetch, baseURL, userId,
|
|
|
515
674
|
throw new Error(`Search failed - ${response.status}`);
|
|
516
675
|
}
|
|
517
676
|
const data = (await response.json());
|
|
518
|
-
logger.info(`EvoSearch API: Search success - ${data.length} products`);
|
|
677
|
+
logger.info(`EvoSearch API: Search success - ${data.results.length} products`);
|
|
519
678
|
return data;
|
|
520
679
|
};
|
|
521
680
|
const sendEvent = (action, currency, actionData, clickData, beacon) => {
|
package/core.js
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
var ProvidersEvoSearchSdk = (function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Known ad-network click ID query parameters and the human-readable provider
|
|
6
|
+
* label each one maps to. Used by the CDN worker to resolve `inSessionTouch`
|
|
7
|
+
* and by the analytics API to classify paid-media touch sessions.
|
|
8
|
+
*
|
|
9
|
+
* Keep this list in sync with `defaultParams` in queryParams.ts when new
|
|
10
|
+
* click ID providers are added.
|
|
11
|
+
*/
|
|
12
|
+
const CLICK_IDS = [
|
|
13
|
+
{ param: 'fbclid', label: 'Facebook' }, // Facebook Ads
|
|
14
|
+
{ param: 'gclid', label: 'Google' }, // Google Ads (click)
|
|
15
|
+
{ param: 'gbraid', label: 'Google' }, // Google Ads (iOS app)
|
|
16
|
+
{ param: 'wbraid', label: 'Google' }, // Google Ads (iOS web)
|
|
17
|
+
{ param: 'msclkid', label: 'Bing' }, // Microsoft Bing Ads
|
|
18
|
+
{ param: 'ttclid', label: 'TikTok' }, // TikTok Ads
|
|
19
|
+
{ param: 'ScCid', label: 'Snapchat' }, // Snapchat Ads
|
|
20
|
+
{ param: 'epik', label: 'Pinterest' }, // Pinterest Ads
|
|
21
|
+
{ param: 'li_fat_id', label: 'LinkedIn' }, // LinkedIn Ads
|
|
22
|
+
{ param: 'twclid', label: 'Twitter' }, // X (Twitter) Ads
|
|
23
|
+
{ param: 'rdt_cid', label: 'Reddit' }, // Reddit Ads
|
|
24
|
+
{ param: 'aleid', label: 'AppLovin' }, // AppLovin
|
|
25
|
+
{ param: 'tabclid', label: 'Taboola' }, // Taboola
|
|
26
|
+
{ param: 'obclid', label: 'Outbrain' }, // Outbrain
|
|
27
|
+
{ param: 'trybe', label: 'Trybe' }, // Trybe
|
|
28
|
+
{ param: '_kx', label: 'Klaviyo' }, // Klaviyo email campaigns
|
|
29
|
+
{ param: 'mc_eid', label: 'Mailchimp' }, // Mailchimp email campaigns
|
|
30
|
+
{ param: 'ttd_id', label: 'The Trade Desk' }, // The Trade Desk programmatic
|
|
31
|
+
{ param: 'evsclid', label: 'EvoSearch' }, // EvoSearch
|
|
32
|
+
{ param: 'li_did', label: 'Live Intent' }, // LiveIntent device-level ad
|
|
33
|
+
{ param: '_raclid', label: 'Rumble' }, // Rumble Ads
|
|
34
|
+
{ param: 'ref_id', label: 'StackAdapt' }, // StackAdapt programmatic
|
|
35
|
+
{ param: 'duel_a', label: 'Duel' }, // Duel referral/advocacy
|
|
36
|
+
];
|
|
37
|
+
|
|
4
38
|
const expand = (str) => str.split(',').flatMap((entry) => {
|
|
5
39
|
if (!entry.includes('-')) {
|
|
6
40
|
return entry;
|
|
@@ -357,8 +391,133 @@ var ProvidersEvoSearchSdk = (function () {
|
|
|
357
391
|
]);
|
|
358
392
|
new Set([...isoCountries.keys(), ...usStates.keys()]);
|
|
359
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Exact utm_source normalization (lowercase key → canonical name).
|
|
396
|
+
*
|
|
397
|
+
* Use exact match when the key is short, generic, or must not accidentally
|
|
398
|
+
* absorb variant spellings (e.g. "impact" must not match "impact_radius").
|
|
399
|
+
*
|
|
400
|
+
* Ordering: Paid (search/social → ad networks → affiliates) →
|
|
401
|
+
* Organic (AI/discovery) → Retention (email → SMS → push → post-purchase)
|
|
402
|
+
*/
|
|
403
|
+
const UTM_SOURCE_EXACT = {
|
|
404
|
+
// ── Paid – Search & Social ────────────────────────────────────────────────
|
|
405
|
+
google: 'Google',
|
|
406
|
+
adwords: 'Google',
|
|
407
|
+
youtube: 'Google',
|
|
408
|
+
yt: 'Google',
|
|
409
|
+
meta: 'Facebook',
|
|
410
|
+
facebook: 'Facebook',
|
|
411
|
+
instagram: 'Facebook',
|
|
412
|
+
ig: 'Facebook',
|
|
413
|
+
igshopping: 'Facebook',
|
|
414
|
+
threads: 'Facebook',
|
|
415
|
+
twitter: 'Twitter',
|
|
416
|
+
snapchat: 'Snapchat',
|
|
417
|
+
pinterest: 'Pinterest',
|
|
418
|
+
bing: 'Bing',
|
|
419
|
+
microsoft: 'Bing',
|
|
420
|
+
tiktok: 'TikTok',
|
|
421
|
+
// ── Paid – Ad Networks ───────────────────────────────────────────────────
|
|
422
|
+
rtbhouse: 'RTB House',
|
|
423
|
+
applovin: 'AppLovin',
|
|
424
|
+
ttd: 'The Trade Desk',
|
|
425
|
+
amazondsp: 'Amazon DSP',
|
|
426
|
+
axon: 'Axon',
|
|
427
|
+
duel: 'Duel',
|
|
428
|
+
// ── Paid – Affiliates ────────────────────────────────────────────────────
|
|
429
|
+
awin: 'Awin',
|
|
430
|
+
'affiliate-cj': 'CJ Affiliate',
|
|
431
|
+
impact: 'Impact',
|
|
432
|
+
rakuten: 'Rakuten',
|
|
433
|
+
superfiliate: 'Superfiliate',
|
|
434
|
+
// ── Organic – AI & Discovery ─────────────────────────────────────────────
|
|
435
|
+
perplexity: 'Perplexity',
|
|
436
|
+
chatgpt: 'ChatGPT',
|
|
437
|
+
'chatgpt.com': 'ChatGPT',
|
|
438
|
+
openai: 'ChatGPT',
|
|
439
|
+
'copilot.com': 'Microsoft Copilot',
|
|
440
|
+
copilot: 'Microsoft Copilot',
|
|
441
|
+
applenews: 'Apple News',
|
|
442
|
+
whatsapp: 'WhatsApp',
|
|
443
|
+
podcast: 'Podcast',
|
|
444
|
+
// ── Retention – Email ────────────────────────────────────────────────────
|
|
445
|
+
mailchimp: 'Mailchimp',
|
|
446
|
+
omnisend: 'Omnisend',
|
|
447
|
+
iterable: 'Iterable',
|
|
448
|
+
listrak: 'Listrak',
|
|
449
|
+
sailthru: 'Sailthru',
|
|
450
|
+
// ── Retention – Push ─────────────────────────────────────────────────────
|
|
451
|
+
pushowl: 'PushOwl',
|
|
452
|
+
// ── Retention – Post-purchase / Payment ──────────────────────────────────
|
|
453
|
+
narvar: 'Narvar',
|
|
454
|
+
shop_app: 'Shop App',
|
|
455
|
+
salesforce: 'Salesforce',
|
|
456
|
+
yotpo: 'Yotpo',
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* Partial utm_source normalization (startsWith prefix check, lowercase).
|
|
460
|
+
*
|
|
461
|
+
* Checked after exact match so short exact keys (e.g. "ig", "yt") win first.
|
|
462
|
+
*/
|
|
463
|
+
const UTM_SOURCE_PARTIAL = [
|
|
464
|
+
// ── Paid – Social ─────────────────────────────────────────────────────────
|
|
465
|
+
{ prefix: 'tiktok', name: 'TikTok' },
|
|
466
|
+
// ── Paid – Ad Networks ───────────────────────────────────────────────────
|
|
467
|
+
{ prefix: 'criteo', name: 'Criteo' },
|
|
468
|
+
// ── Retention – Email ────────────────────────────────────────────────────
|
|
469
|
+
{ prefix: 'klaviyo', name: 'Klaviyo' },
|
|
470
|
+
// ── Retention – SMS ──────────────────────────────────────────────────────
|
|
471
|
+
{ prefix: 'attentive', name: 'Attentive' },
|
|
472
|
+
{ prefix: 'postscript', name: 'Postscript' },
|
|
473
|
+
// ── Retention – Post-purchase / Payment ──────────────────────────────────
|
|
474
|
+
{ prefix: 'afterpay', name: 'Afterpay' },
|
|
475
|
+
{ prefix: 'klarna', name: 'Klarna' },
|
|
476
|
+
];
|
|
477
|
+
const ORGANIC_SEARCH_ENGINES = [
|
|
478
|
+
{ match: 'google', name: 'Google' },
|
|
479
|
+
{ match: 'bing', name: 'Bing' },
|
|
480
|
+
{ match: 'yahoo', name: 'Yahoo' },
|
|
481
|
+
{ match: 'duckduckgo', name: 'DuckDuckGo' },
|
|
482
|
+
{ match: 'baidu', name: 'Baidu' },
|
|
483
|
+
{ match: 'yandex', name: 'Yandex' },
|
|
484
|
+
{ match: 'brave', name: 'Brave' },
|
|
485
|
+
];
|
|
486
|
+
const KNOWN_REFERRAL_PLATFORMS = [
|
|
487
|
+
{ match: 'facebook', name: 'Facebook' },
|
|
488
|
+
{ match: 'instagram', name: 'Facebook' },
|
|
489
|
+
{ match: 'twitter', name: 'Twitter' },
|
|
490
|
+
{ match: 'x.com', name: 'Twitter' },
|
|
491
|
+
{ match: 'tiktok', name: 'TikTok' },
|
|
492
|
+
{ match: 'pinterest', name: 'Pinterest' },
|
|
493
|
+
{ match: 'linkedin', name: 'LinkedIn' },
|
|
494
|
+
{ match: 'youtube', name: 'YouTube' },
|
|
495
|
+
{ match: 'chatgpt', name: 'ChatGPT' },
|
|
496
|
+
{ match: 'claude', name: 'Claude' },
|
|
497
|
+
{ match: 'perplexity', name: 'Perplexity' },
|
|
498
|
+
{ match: 'shop.app', name: 'Shop App' },
|
|
499
|
+
{ match: 'amazon', name: 'Amazon' },
|
|
500
|
+
{ match: 'attentive', name: 'Attentive' },
|
|
501
|
+
];
|
|
502
|
+
const OFFLINE_TOUCH = 'Blotout_Offline';
|
|
503
|
+
new Set([
|
|
504
|
+
...CLICK_IDS.map((c) => c.label),
|
|
505
|
+
...Object.values(UTM_SOURCE_EXACT),
|
|
506
|
+
...UTM_SOURCE_PARTIAL.map((e) => e.name),
|
|
507
|
+
...ORGANIC_SEARCH_ENGINES.map((e) => `Organic Search - ${e.name}`),
|
|
508
|
+
...KNOWN_REFERRAL_PLATFORMS.map((e) => `Referral - ${e.name}`),
|
|
509
|
+
'Referral - Other',
|
|
510
|
+
'Direct Traffic',
|
|
511
|
+
'Other',
|
|
512
|
+
OFFLINE_TOUCH,
|
|
513
|
+
]);
|
|
514
|
+
|
|
360
515
|
const uiActions = new Set([
|
|
361
516
|
'evoSearchProductRecommendationClicked',
|
|
517
|
+
'evoSearchProductClicked',
|
|
518
|
+
'evoSearchProductAddedToCart',
|
|
519
|
+
'evoSearchProductPurchased',
|
|
520
|
+
'evoSearchSearchIssued',
|
|
362
521
|
]);
|
|
363
522
|
new Set([...uiActions]);
|
|
364
523
|
|
|
@@ -516,7 +675,7 @@ var ProvidersEvoSearchSdk = (function () {
|
|
|
516
675
|
throw new Error(`Search failed - ${response.status}`);
|
|
517
676
|
}
|
|
518
677
|
const data = (await response.json());
|
|
519
|
-
logger.info(`EvoSearch API: Search success - ${data.length} products`);
|
|
678
|
+
logger.info(`EvoSearch API: Search success - ${data.results.length} products`);
|
|
520
679
|
return data;
|
|
521
680
|
};
|
|
522
681
|
const sendEvent = (action, currency, actionData, clickData, beacon) => {
|
package/core.mjs
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known ad-network click ID query parameters and the human-readable provider
|
|
3
|
+
* label each one maps to. Used by the CDN worker to resolve `inSessionTouch`
|
|
4
|
+
* and by the analytics API to classify paid-media touch sessions.
|
|
5
|
+
*
|
|
6
|
+
* Keep this list in sync with `defaultParams` in queryParams.ts when new
|
|
7
|
+
* click ID providers are added.
|
|
8
|
+
*/
|
|
9
|
+
const CLICK_IDS = [
|
|
10
|
+
{ param: 'fbclid', label: 'Facebook' }, // Facebook Ads
|
|
11
|
+
{ param: 'gclid', label: 'Google' }, // Google Ads (click)
|
|
12
|
+
{ param: 'gbraid', label: 'Google' }, // Google Ads (iOS app)
|
|
13
|
+
{ param: 'wbraid', label: 'Google' }, // Google Ads (iOS web)
|
|
14
|
+
{ param: 'msclkid', label: 'Bing' }, // Microsoft Bing Ads
|
|
15
|
+
{ param: 'ttclid', label: 'TikTok' }, // TikTok Ads
|
|
16
|
+
{ param: 'ScCid', label: 'Snapchat' }, // Snapchat Ads
|
|
17
|
+
{ param: 'epik', label: 'Pinterest' }, // Pinterest Ads
|
|
18
|
+
{ param: 'li_fat_id', label: 'LinkedIn' }, // LinkedIn Ads
|
|
19
|
+
{ param: 'twclid', label: 'Twitter' }, // X (Twitter) Ads
|
|
20
|
+
{ param: 'rdt_cid', label: 'Reddit' }, // Reddit Ads
|
|
21
|
+
{ param: 'aleid', label: 'AppLovin' }, // AppLovin
|
|
22
|
+
{ param: 'tabclid', label: 'Taboola' }, // Taboola
|
|
23
|
+
{ param: 'obclid', label: 'Outbrain' }, // Outbrain
|
|
24
|
+
{ param: 'trybe', label: 'Trybe' }, // Trybe
|
|
25
|
+
{ param: '_kx', label: 'Klaviyo' }, // Klaviyo email campaigns
|
|
26
|
+
{ param: 'mc_eid', label: 'Mailchimp' }, // Mailchimp email campaigns
|
|
27
|
+
{ param: 'ttd_id', label: 'The Trade Desk' }, // The Trade Desk programmatic
|
|
28
|
+
{ param: 'evsclid', label: 'EvoSearch' }, // EvoSearch
|
|
29
|
+
{ param: 'li_did', label: 'Live Intent' }, // LiveIntent device-level ad
|
|
30
|
+
{ param: '_raclid', label: 'Rumble' }, // Rumble Ads
|
|
31
|
+
{ param: 'ref_id', label: 'StackAdapt' }, // StackAdapt programmatic
|
|
32
|
+
{ param: 'duel_a', label: 'Duel' }, // Duel referral/advocacy
|
|
33
|
+
];
|
|
34
|
+
|
|
1
35
|
const expand = (str) => str.split(',').flatMap((entry) => {
|
|
2
36
|
if (!entry.includes('-')) {
|
|
3
37
|
return entry;
|
|
@@ -354,8 +388,133 @@ const usStates = new Map([
|
|
|
354
388
|
]);
|
|
355
389
|
new Set([...isoCountries.keys(), ...usStates.keys()]);
|
|
356
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Exact utm_source normalization (lowercase key → canonical name).
|
|
393
|
+
*
|
|
394
|
+
* Use exact match when the key is short, generic, or must not accidentally
|
|
395
|
+
* absorb variant spellings (e.g. "impact" must not match "impact_radius").
|
|
396
|
+
*
|
|
397
|
+
* Ordering: Paid (search/social → ad networks → affiliates) →
|
|
398
|
+
* Organic (AI/discovery) → Retention (email → SMS → push → post-purchase)
|
|
399
|
+
*/
|
|
400
|
+
const UTM_SOURCE_EXACT = {
|
|
401
|
+
// ── Paid – Search & Social ────────────────────────────────────────────────
|
|
402
|
+
google: 'Google',
|
|
403
|
+
adwords: 'Google',
|
|
404
|
+
youtube: 'Google',
|
|
405
|
+
yt: 'Google',
|
|
406
|
+
meta: 'Facebook',
|
|
407
|
+
facebook: 'Facebook',
|
|
408
|
+
instagram: 'Facebook',
|
|
409
|
+
ig: 'Facebook',
|
|
410
|
+
igshopping: 'Facebook',
|
|
411
|
+
threads: 'Facebook',
|
|
412
|
+
twitter: 'Twitter',
|
|
413
|
+
snapchat: 'Snapchat',
|
|
414
|
+
pinterest: 'Pinterest',
|
|
415
|
+
bing: 'Bing',
|
|
416
|
+
microsoft: 'Bing',
|
|
417
|
+
tiktok: 'TikTok',
|
|
418
|
+
// ── Paid – Ad Networks ───────────────────────────────────────────────────
|
|
419
|
+
rtbhouse: 'RTB House',
|
|
420
|
+
applovin: 'AppLovin',
|
|
421
|
+
ttd: 'The Trade Desk',
|
|
422
|
+
amazondsp: 'Amazon DSP',
|
|
423
|
+
axon: 'Axon',
|
|
424
|
+
duel: 'Duel',
|
|
425
|
+
// ── Paid – Affiliates ────────────────────────────────────────────────────
|
|
426
|
+
awin: 'Awin',
|
|
427
|
+
'affiliate-cj': 'CJ Affiliate',
|
|
428
|
+
impact: 'Impact',
|
|
429
|
+
rakuten: 'Rakuten',
|
|
430
|
+
superfiliate: 'Superfiliate',
|
|
431
|
+
// ── Organic – AI & Discovery ─────────────────────────────────────────────
|
|
432
|
+
perplexity: 'Perplexity',
|
|
433
|
+
chatgpt: 'ChatGPT',
|
|
434
|
+
'chatgpt.com': 'ChatGPT',
|
|
435
|
+
openai: 'ChatGPT',
|
|
436
|
+
'copilot.com': 'Microsoft Copilot',
|
|
437
|
+
copilot: 'Microsoft Copilot',
|
|
438
|
+
applenews: 'Apple News',
|
|
439
|
+
whatsapp: 'WhatsApp',
|
|
440
|
+
podcast: 'Podcast',
|
|
441
|
+
// ── Retention – Email ────────────────────────────────────────────────────
|
|
442
|
+
mailchimp: 'Mailchimp',
|
|
443
|
+
omnisend: 'Omnisend',
|
|
444
|
+
iterable: 'Iterable',
|
|
445
|
+
listrak: 'Listrak',
|
|
446
|
+
sailthru: 'Sailthru',
|
|
447
|
+
// ── Retention – Push ─────────────────────────────────────────────────────
|
|
448
|
+
pushowl: 'PushOwl',
|
|
449
|
+
// ── Retention – Post-purchase / Payment ──────────────────────────────────
|
|
450
|
+
narvar: 'Narvar',
|
|
451
|
+
shop_app: 'Shop App',
|
|
452
|
+
salesforce: 'Salesforce',
|
|
453
|
+
yotpo: 'Yotpo',
|
|
454
|
+
};
|
|
455
|
+
/**
|
|
456
|
+
* Partial utm_source normalization (startsWith prefix check, lowercase).
|
|
457
|
+
*
|
|
458
|
+
* Checked after exact match so short exact keys (e.g. "ig", "yt") win first.
|
|
459
|
+
*/
|
|
460
|
+
const UTM_SOURCE_PARTIAL = [
|
|
461
|
+
// ── Paid – Social ─────────────────────────────────────────────────────────
|
|
462
|
+
{ prefix: 'tiktok', name: 'TikTok' },
|
|
463
|
+
// ── Paid – Ad Networks ───────────────────────────────────────────────────
|
|
464
|
+
{ prefix: 'criteo', name: 'Criteo' },
|
|
465
|
+
// ── Retention – Email ────────────────────────────────────────────────────
|
|
466
|
+
{ prefix: 'klaviyo', name: 'Klaviyo' },
|
|
467
|
+
// ── Retention – SMS ──────────────────────────────────────────────────────
|
|
468
|
+
{ prefix: 'attentive', name: 'Attentive' },
|
|
469
|
+
{ prefix: 'postscript', name: 'Postscript' },
|
|
470
|
+
// ── Retention – Post-purchase / Payment ──────────────────────────────────
|
|
471
|
+
{ prefix: 'afterpay', name: 'Afterpay' },
|
|
472
|
+
{ prefix: 'klarna', name: 'Klarna' },
|
|
473
|
+
];
|
|
474
|
+
const ORGANIC_SEARCH_ENGINES = [
|
|
475
|
+
{ match: 'google', name: 'Google' },
|
|
476
|
+
{ match: 'bing', name: 'Bing' },
|
|
477
|
+
{ match: 'yahoo', name: 'Yahoo' },
|
|
478
|
+
{ match: 'duckduckgo', name: 'DuckDuckGo' },
|
|
479
|
+
{ match: 'baidu', name: 'Baidu' },
|
|
480
|
+
{ match: 'yandex', name: 'Yandex' },
|
|
481
|
+
{ match: 'brave', name: 'Brave' },
|
|
482
|
+
];
|
|
483
|
+
const KNOWN_REFERRAL_PLATFORMS = [
|
|
484
|
+
{ match: 'facebook', name: 'Facebook' },
|
|
485
|
+
{ match: 'instagram', name: 'Facebook' },
|
|
486
|
+
{ match: 'twitter', name: 'Twitter' },
|
|
487
|
+
{ match: 'x.com', name: 'Twitter' },
|
|
488
|
+
{ match: 'tiktok', name: 'TikTok' },
|
|
489
|
+
{ match: 'pinterest', name: 'Pinterest' },
|
|
490
|
+
{ match: 'linkedin', name: 'LinkedIn' },
|
|
491
|
+
{ match: 'youtube', name: 'YouTube' },
|
|
492
|
+
{ match: 'chatgpt', name: 'ChatGPT' },
|
|
493
|
+
{ match: 'claude', name: 'Claude' },
|
|
494
|
+
{ match: 'perplexity', name: 'Perplexity' },
|
|
495
|
+
{ match: 'shop.app', name: 'Shop App' },
|
|
496
|
+
{ match: 'amazon', name: 'Amazon' },
|
|
497
|
+
{ match: 'attentive', name: 'Attentive' },
|
|
498
|
+
];
|
|
499
|
+
const OFFLINE_TOUCH = 'Blotout_Offline';
|
|
500
|
+
new Set([
|
|
501
|
+
...CLICK_IDS.map((c) => c.label),
|
|
502
|
+
...Object.values(UTM_SOURCE_EXACT),
|
|
503
|
+
...UTM_SOURCE_PARTIAL.map((e) => e.name),
|
|
504
|
+
...ORGANIC_SEARCH_ENGINES.map((e) => `Organic Search - ${e.name}`),
|
|
505
|
+
...KNOWN_REFERRAL_PLATFORMS.map((e) => `Referral - ${e.name}`),
|
|
506
|
+
'Referral - Other',
|
|
507
|
+
'Direct Traffic',
|
|
508
|
+
'Other',
|
|
509
|
+
OFFLINE_TOUCH,
|
|
510
|
+
]);
|
|
511
|
+
|
|
357
512
|
const uiActions = new Set([
|
|
358
513
|
'evoSearchProductRecommendationClicked',
|
|
514
|
+
'evoSearchProductClicked',
|
|
515
|
+
'evoSearchProductAddedToCart',
|
|
516
|
+
'evoSearchProductPurchased',
|
|
517
|
+
'evoSearchSearchIssued',
|
|
359
518
|
]);
|
|
360
519
|
new Set([...uiActions]);
|
|
361
520
|
|
|
@@ -513,7 +672,7 @@ const createEvoSearchAPI = ({ fetch: fetchImpl = window.fetch, baseURL, userId,
|
|
|
513
672
|
throw new Error(`Search failed - ${response.status}`);
|
|
514
673
|
}
|
|
515
674
|
const data = (await response.json());
|
|
516
|
-
logger.info(`EvoSearch API: Search success - ${data.length} products`);
|
|
675
|
+
logger.info(`EvoSearch API: Search success - ${data.results.length} products`);
|
|
517
676
|
return data;
|
|
518
677
|
};
|
|
519
678
|
const sendEvent = (action, currency, actionData, clickData, beacon) => {
|