@blotoutio/edgetag-sdk-js 1.53.0 → 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.
Files changed (3) hide show
  1. package/index.cjs.js +156 -1
  2. package/index.mjs +156 -1
  3. package/package.json +1 -1
package/index.cjs.js CHANGED
@@ -97,6 +97,40 @@ const upsert = (map, key, update, createDefault) => {
97
97
  return map.set(key, update(currentValue));
98
98
  };
99
99
 
100
+ /**
101
+ * Known ad-network click ID query parameters and the human-readable provider
102
+ * label each one maps to. Used by the CDN worker to resolve `inSessionTouch`
103
+ * and by the analytics API to classify paid-media touch sessions.
104
+ *
105
+ * Keep this list in sync with `defaultParams` in queryParams.ts when new
106
+ * click ID providers are added.
107
+ */
108
+ const CLICK_IDS = [
109
+ { param: 'fbclid', label: 'Facebook' }, // Facebook Ads
110
+ { param: 'gclid', label: 'Google' }, // Google Ads (click)
111
+ { param: 'gbraid', label: 'Google' }, // Google Ads (iOS app)
112
+ { param: 'wbraid', label: 'Google' }, // Google Ads (iOS web)
113
+ { param: 'msclkid', label: 'Bing' }, // Microsoft Bing Ads
114
+ { param: 'ttclid', label: 'TikTok' }, // TikTok Ads
115
+ { param: 'ScCid', label: 'Snapchat' }, // Snapchat Ads
116
+ { param: 'epik', label: 'Pinterest' }, // Pinterest Ads
117
+ { param: 'li_fat_id', label: 'LinkedIn' }, // LinkedIn Ads
118
+ { param: 'twclid', label: 'Twitter' }, // X (Twitter) Ads
119
+ { param: 'rdt_cid', label: 'Reddit' }, // Reddit Ads
120
+ { param: 'aleid', label: 'AppLovin' }, // AppLovin
121
+ { param: 'tabclid', label: 'Taboola' }, // Taboola
122
+ { param: 'obclid', label: 'Outbrain' }, // Outbrain
123
+ { param: 'trybe', label: 'Trybe' }, // Trybe
124
+ { param: '_kx', label: 'Klaviyo' }, // Klaviyo email campaigns
125
+ { param: 'mc_eid', label: 'Mailchimp' }, // Mailchimp email campaigns
126
+ { param: 'ttd_id', label: 'The Trade Desk' }, // The Trade Desk programmatic
127
+ { param: 'evsclid', label: 'EvoSearch' }, // EvoSearch
128
+ { param: 'li_did', label: 'Live Intent' }, // LiveIntent device-level ad
129
+ { param: '_raclid', label: 'Rumble' }, // Rumble Ads
130
+ { param: 'ref_id', label: 'StackAdapt' }, // StackAdapt programmatic
131
+ { param: 'duel_a', label: 'Duel' }, // Duel referral/advocacy
132
+ ];
133
+
100
134
  const expand = (str) => str.split(',').flatMap((entry) => {
101
135
  if (!entry.includes('-')) {
102
136
  return entry;
@@ -828,6 +862,127 @@ const evaluateEventRules = (rules, payload, providers, userData = {}) => {
828
862
  */
829
863
  const jsonClone = (json) => tryParse(JSON.stringify(json), undefined);
830
864
 
865
+ /**
866
+ * Exact utm_source normalization (lowercase key → canonical name).
867
+ *
868
+ * Use exact match when the key is short, generic, or must not accidentally
869
+ * absorb variant spellings (e.g. "impact" must not match "impact_radius").
870
+ *
871
+ * Ordering: Paid (search/social → ad networks → affiliates) →
872
+ * Organic (AI/discovery) → Retention (email → SMS → push → post-purchase)
873
+ */
874
+ const UTM_SOURCE_EXACT = {
875
+ // ── Paid – Search & Social ────────────────────────────────────────────────
876
+ google: 'Google',
877
+ adwords: 'Google',
878
+ youtube: 'Google',
879
+ yt: 'Google',
880
+ meta: 'Facebook',
881
+ facebook: 'Facebook',
882
+ instagram: 'Facebook',
883
+ ig: 'Facebook',
884
+ igshopping: 'Facebook',
885
+ threads: 'Facebook',
886
+ twitter: 'Twitter',
887
+ snapchat: 'Snapchat',
888
+ pinterest: 'Pinterest',
889
+ bing: 'Bing',
890
+ microsoft: 'Bing',
891
+ tiktok: 'TikTok',
892
+ // ── Paid – Ad Networks ───────────────────────────────────────────────────
893
+ rtbhouse: 'RTB House',
894
+ applovin: 'AppLovin',
895
+ ttd: 'The Trade Desk',
896
+ amazondsp: 'Amazon DSP',
897
+ axon: 'Axon',
898
+ duel: 'Duel',
899
+ // ── Paid – Affiliates ────────────────────────────────────────────────────
900
+ awin: 'Awin',
901
+ 'affiliate-cj': 'CJ Affiliate',
902
+ impact: 'Impact',
903
+ rakuten: 'Rakuten',
904
+ superfiliate: 'Superfiliate',
905
+ // ── Organic – AI & Discovery ─────────────────────────────────────────────
906
+ perplexity: 'Perplexity',
907
+ chatgpt: 'ChatGPT',
908
+ 'chatgpt.com': 'ChatGPT',
909
+ openai: 'ChatGPT',
910
+ 'copilot.com': 'Microsoft Copilot',
911
+ copilot: 'Microsoft Copilot',
912
+ applenews: 'Apple News',
913
+ whatsapp: 'WhatsApp',
914
+ podcast: 'Podcast',
915
+ // ── Retention – Email ────────────────────────────────────────────────────
916
+ mailchimp: 'Mailchimp',
917
+ omnisend: 'Omnisend',
918
+ iterable: 'Iterable',
919
+ listrak: 'Listrak',
920
+ sailthru: 'Sailthru',
921
+ // ── Retention – Push ─────────────────────────────────────────────────────
922
+ pushowl: 'PushOwl',
923
+ // ── Retention – Post-purchase / Payment ──────────────────────────────────
924
+ narvar: 'Narvar',
925
+ shop_app: 'Shop App',
926
+ salesforce: 'Salesforce',
927
+ yotpo: 'Yotpo',
928
+ };
929
+ /**
930
+ * Partial utm_source normalization (startsWith prefix check, lowercase).
931
+ *
932
+ * Checked after exact match so short exact keys (e.g. "ig", "yt") win first.
933
+ */
934
+ const UTM_SOURCE_PARTIAL = [
935
+ // ── Paid – Social ─────────────────────────────────────────────────────────
936
+ { prefix: 'tiktok', name: 'TikTok' },
937
+ // ── Paid – Ad Networks ───────────────────────────────────────────────────
938
+ { prefix: 'criteo', name: 'Criteo' },
939
+ // ── Retention – Email ────────────────────────────────────────────────────
940
+ { prefix: 'klaviyo', name: 'Klaviyo' },
941
+ // ── Retention – SMS ──────────────────────────────────────────────────────
942
+ { prefix: 'attentive', name: 'Attentive' },
943
+ { prefix: 'postscript', name: 'Postscript' },
944
+ // ── Retention – Post-purchase / Payment ──────────────────────────────────
945
+ { prefix: 'afterpay', name: 'Afterpay' },
946
+ { prefix: 'klarna', name: 'Klarna' },
947
+ ];
948
+ const ORGANIC_SEARCH_ENGINES = [
949
+ { match: 'google', name: 'Google' },
950
+ { match: 'bing', name: 'Bing' },
951
+ { match: 'yahoo', name: 'Yahoo' },
952
+ { match: 'duckduckgo', name: 'DuckDuckGo' },
953
+ { match: 'baidu', name: 'Baidu' },
954
+ { match: 'yandex', name: 'Yandex' },
955
+ { match: 'brave', name: 'Brave' },
956
+ ];
957
+ const KNOWN_REFERRAL_PLATFORMS = [
958
+ { match: 'facebook', name: 'Facebook' },
959
+ { match: 'instagram', name: 'Facebook' },
960
+ { match: 'twitter', name: 'Twitter' },
961
+ { match: 'x.com', name: 'Twitter' },
962
+ { match: 'tiktok', name: 'TikTok' },
963
+ { match: 'pinterest', name: 'Pinterest' },
964
+ { match: 'linkedin', name: 'LinkedIn' },
965
+ { match: 'youtube', name: 'YouTube' },
966
+ { match: 'chatgpt', name: 'ChatGPT' },
967
+ { match: 'claude', name: 'Claude' },
968
+ { match: 'perplexity', name: 'Perplexity' },
969
+ { match: 'shop.app', name: 'Shop App' },
970
+ { match: 'amazon', name: 'Amazon' },
971
+ { match: 'attentive', name: 'Attentive' },
972
+ ];
973
+ const OFFLINE_TOUCH = 'Blotout_Offline';
974
+ new Set([
975
+ ...CLICK_IDS.map((c) => c.label),
976
+ ...Object.values(UTM_SOURCE_EXACT),
977
+ ...UTM_SOURCE_PARTIAL.map((e) => e.name),
978
+ ...ORGANIC_SEARCH_ENGINES.map((e) => `Organic Search - ${e.name}`),
979
+ ...KNOWN_REFERRAL_PLATFORMS.map((e) => `Referral - ${e.name}`),
980
+ 'Referral - Other',
981
+ 'Direct Traffic',
982
+ 'Other',
983
+ OFFLINE_TOUCH,
984
+ ]);
985
+
831
986
  // eslint-disable-next-line @nx/enforce-module-boundaries
832
987
  const getGlobalSettings = () => {
833
988
  var _a;
@@ -1310,7 +1465,7 @@ const getStandardPayload = (destination, payload) => {
1310
1465
  referrer: getReferrer(destination),
1311
1466
  search: getSearch(destination),
1312
1467
  locale: getLocale(),
1313
- sdkVersion: "1.53.0" ,
1468
+ sdkVersion: "1.54.0" ,
1314
1469
  ...(payload || {}),
1315
1470
  };
1316
1471
  let storage = {};
package/index.mjs CHANGED
@@ -95,6 +95,40 @@ const upsert = (map, key, update, createDefault) => {
95
95
  return map.set(key, update(currentValue));
96
96
  };
97
97
 
98
+ /**
99
+ * Known ad-network click ID query parameters and the human-readable provider
100
+ * label each one maps to. Used by the CDN worker to resolve `inSessionTouch`
101
+ * and by the analytics API to classify paid-media touch sessions.
102
+ *
103
+ * Keep this list in sync with `defaultParams` in queryParams.ts when new
104
+ * click ID providers are added.
105
+ */
106
+ const CLICK_IDS = [
107
+ { param: 'fbclid', label: 'Facebook' }, // Facebook Ads
108
+ { param: 'gclid', label: 'Google' }, // Google Ads (click)
109
+ { param: 'gbraid', label: 'Google' }, // Google Ads (iOS app)
110
+ { param: 'wbraid', label: 'Google' }, // Google Ads (iOS web)
111
+ { param: 'msclkid', label: 'Bing' }, // Microsoft Bing Ads
112
+ { param: 'ttclid', label: 'TikTok' }, // TikTok Ads
113
+ { param: 'ScCid', label: 'Snapchat' }, // Snapchat Ads
114
+ { param: 'epik', label: 'Pinterest' }, // Pinterest Ads
115
+ { param: 'li_fat_id', label: 'LinkedIn' }, // LinkedIn Ads
116
+ { param: 'twclid', label: 'Twitter' }, // X (Twitter) Ads
117
+ { param: 'rdt_cid', label: 'Reddit' }, // Reddit Ads
118
+ { param: 'aleid', label: 'AppLovin' }, // AppLovin
119
+ { param: 'tabclid', label: 'Taboola' }, // Taboola
120
+ { param: 'obclid', label: 'Outbrain' }, // Outbrain
121
+ { param: 'trybe', label: 'Trybe' }, // Trybe
122
+ { param: '_kx', label: 'Klaviyo' }, // Klaviyo email campaigns
123
+ { param: 'mc_eid', label: 'Mailchimp' }, // Mailchimp email campaigns
124
+ { param: 'ttd_id', label: 'The Trade Desk' }, // The Trade Desk programmatic
125
+ { param: 'evsclid', label: 'EvoSearch' }, // EvoSearch
126
+ { param: 'li_did', label: 'Live Intent' }, // LiveIntent device-level ad
127
+ { param: '_raclid', label: 'Rumble' }, // Rumble Ads
128
+ { param: 'ref_id', label: 'StackAdapt' }, // StackAdapt programmatic
129
+ { param: 'duel_a', label: 'Duel' }, // Duel referral/advocacy
130
+ ];
131
+
98
132
  const expand = (str) => str.split(',').flatMap((entry) => {
99
133
  if (!entry.includes('-')) {
100
134
  return entry;
@@ -826,6 +860,127 @@ const evaluateEventRules = (rules, payload, providers, userData = {}) => {
826
860
  */
827
861
  const jsonClone = (json) => tryParse(JSON.stringify(json), undefined);
828
862
 
863
+ /**
864
+ * Exact utm_source normalization (lowercase key → canonical name).
865
+ *
866
+ * Use exact match when the key is short, generic, or must not accidentally
867
+ * absorb variant spellings (e.g. "impact" must not match "impact_radius").
868
+ *
869
+ * Ordering: Paid (search/social → ad networks → affiliates) →
870
+ * Organic (AI/discovery) → Retention (email → SMS → push → post-purchase)
871
+ */
872
+ const UTM_SOURCE_EXACT = {
873
+ // ── Paid – Search & Social ────────────────────────────────────────────────
874
+ google: 'Google',
875
+ adwords: 'Google',
876
+ youtube: 'Google',
877
+ yt: 'Google',
878
+ meta: 'Facebook',
879
+ facebook: 'Facebook',
880
+ instagram: 'Facebook',
881
+ ig: 'Facebook',
882
+ igshopping: 'Facebook',
883
+ threads: 'Facebook',
884
+ twitter: 'Twitter',
885
+ snapchat: 'Snapchat',
886
+ pinterest: 'Pinterest',
887
+ bing: 'Bing',
888
+ microsoft: 'Bing',
889
+ tiktok: 'TikTok',
890
+ // ── Paid – Ad Networks ───────────────────────────────────────────────────
891
+ rtbhouse: 'RTB House',
892
+ applovin: 'AppLovin',
893
+ ttd: 'The Trade Desk',
894
+ amazondsp: 'Amazon DSP',
895
+ axon: 'Axon',
896
+ duel: 'Duel',
897
+ // ── Paid – Affiliates ────────────────────────────────────────────────────
898
+ awin: 'Awin',
899
+ 'affiliate-cj': 'CJ Affiliate',
900
+ impact: 'Impact',
901
+ rakuten: 'Rakuten',
902
+ superfiliate: 'Superfiliate',
903
+ // ── Organic – AI & Discovery ─────────────────────────────────────────────
904
+ perplexity: 'Perplexity',
905
+ chatgpt: 'ChatGPT',
906
+ 'chatgpt.com': 'ChatGPT',
907
+ openai: 'ChatGPT',
908
+ 'copilot.com': 'Microsoft Copilot',
909
+ copilot: 'Microsoft Copilot',
910
+ applenews: 'Apple News',
911
+ whatsapp: 'WhatsApp',
912
+ podcast: 'Podcast',
913
+ // ── Retention – Email ────────────────────────────────────────────────────
914
+ mailchimp: 'Mailchimp',
915
+ omnisend: 'Omnisend',
916
+ iterable: 'Iterable',
917
+ listrak: 'Listrak',
918
+ sailthru: 'Sailthru',
919
+ // ── Retention – Push ─────────────────────────────────────────────────────
920
+ pushowl: 'PushOwl',
921
+ // ── Retention – Post-purchase / Payment ──────────────────────────────────
922
+ narvar: 'Narvar',
923
+ shop_app: 'Shop App',
924
+ salesforce: 'Salesforce',
925
+ yotpo: 'Yotpo',
926
+ };
927
+ /**
928
+ * Partial utm_source normalization (startsWith prefix check, lowercase).
929
+ *
930
+ * Checked after exact match so short exact keys (e.g. "ig", "yt") win first.
931
+ */
932
+ const UTM_SOURCE_PARTIAL = [
933
+ // ── Paid – Social ─────────────────────────────────────────────────────────
934
+ { prefix: 'tiktok', name: 'TikTok' },
935
+ // ── Paid – Ad Networks ───────────────────────────────────────────────────
936
+ { prefix: 'criteo', name: 'Criteo' },
937
+ // ── Retention – Email ────────────────────────────────────────────────────
938
+ { prefix: 'klaviyo', name: 'Klaviyo' },
939
+ // ── Retention – SMS ──────────────────────────────────────────────────────
940
+ { prefix: 'attentive', name: 'Attentive' },
941
+ { prefix: 'postscript', name: 'Postscript' },
942
+ // ── Retention – Post-purchase / Payment ──────────────────────────────────
943
+ { prefix: 'afterpay', name: 'Afterpay' },
944
+ { prefix: 'klarna', name: 'Klarna' },
945
+ ];
946
+ const ORGANIC_SEARCH_ENGINES = [
947
+ { match: 'google', name: 'Google' },
948
+ { match: 'bing', name: 'Bing' },
949
+ { match: 'yahoo', name: 'Yahoo' },
950
+ { match: 'duckduckgo', name: 'DuckDuckGo' },
951
+ { match: 'baidu', name: 'Baidu' },
952
+ { match: 'yandex', name: 'Yandex' },
953
+ { match: 'brave', name: 'Brave' },
954
+ ];
955
+ const KNOWN_REFERRAL_PLATFORMS = [
956
+ { match: 'facebook', name: 'Facebook' },
957
+ { match: 'instagram', name: 'Facebook' },
958
+ { match: 'twitter', name: 'Twitter' },
959
+ { match: 'x.com', name: 'Twitter' },
960
+ { match: 'tiktok', name: 'TikTok' },
961
+ { match: 'pinterest', name: 'Pinterest' },
962
+ { match: 'linkedin', name: 'LinkedIn' },
963
+ { match: 'youtube', name: 'YouTube' },
964
+ { match: 'chatgpt', name: 'ChatGPT' },
965
+ { match: 'claude', name: 'Claude' },
966
+ { match: 'perplexity', name: 'Perplexity' },
967
+ { match: 'shop.app', name: 'Shop App' },
968
+ { match: 'amazon', name: 'Amazon' },
969
+ { match: 'attentive', name: 'Attentive' },
970
+ ];
971
+ const OFFLINE_TOUCH = 'Blotout_Offline';
972
+ new Set([
973
+ ...CLICK_IDS.map((c) => c.label),
974
+ ...Object.values(UTM_SOURCE_EXACT),
975
+ ...UTM_SOURCE_PARTIAL.map((e) => e.name),
976
+ ...ORGANIC_SEARCH_ENGINES.map((e) => `Organic Search - ${e.name}`),
977
+ ...KNOWN_REFERRAL_PLATFORMS.map((e) => `Referral - ${e.name}`),
978
+ 'Referral - Other',
979
+ 'Direct Traffic',
980
+ 'Other',
981
+ OFFLINE_TOUCH,
982
+ ]);
983
+
829
984
  // eslint-disable-next-line @nx/enforce-module-boundaries
830
985
  const getGlobalSettings = () => {
831
986
  var _a;
@@ -1308,7 +1463,7 @@ const getStandardPayload = (destination, payload) => {
1308
1463
  referrer: getReferrer(destination),
1309
1464
  search: getSearch(destination),
1310
1465
  locale: getLocale(),
1311
- sdkVersion: "1.53.0" ,
1466
+ sdkVersion: "1.54.0" ,
1312
1467
  ...(payload || {}),
1313
1468
  };
1314
1469
  let storage = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blotoutio/edgetag-sdk-js",
3
- "version": "1.53.0",
3
+ "version": "1.54.0",
4
4
  "description": "JS SDK for EdgeTag",
5
5
  "author": "Blotout",
6
6
  "license": "MIT",