@shware/analytics 8.2.0 → 8.3.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 (76) hide show
  1. package/README.md +63 -2
  2. package/dist/click-id/index.cjs +57 -3
  3. package/dist/click-id/index.cjs.map +1 -1
  4. package/dist/click-id/index.d.cts +16 -1
  5. package/dist/click-id/index.d.cts.map +1 -1
  6. package/dist/click-id/index.d.mts +16 -1
  7. package/dist/click-id/index.d.mts.map +1 -1
  8. package/dist/click-id/index.mjs +54 -4
  9. package/dist/click-id/index.mjs.map +1 -1
  10. package/dist/server/google-data-manager.cjs +2 -0
  11. package/dist/server/google-data-manager.cjs.map +1 -1
  12. package/dist/server/google-data-manager.d.cts.map +1 -1
  13. package/dist/server/google-data-manager.d.mts.map +1 -1
  14. package/dist/server/google-data-manager.mjs +2 -0
  15. package/dist/server/google-data-manager.mjs.map +1 -1
  16. package/dist/server/index.cjs +8 -0
  17. package/dist/server/index.d.cts +5 -4
  18. package/dist/server/index.d.mts +5 -4
  19. package/dist/server/index.mjs +5 -4
  20. package/dist/server/microsoft-conversions-api.cjs +171 -0
  21. package/dist/server/microsoft-conversions-api.cjs.map +1 -0
  22. package/dist/server/microsoft-conversions-api.d.cts +117 -0
  23. package/dist/server/microsoft-conversions-api.d.cts.map +1 -0
  24. package/dist/server/microsoft-conversions-api.d.mts +117 -0
  25. package/dist/server/microsoft-conversions-api.d.mts.map +1 -0
  26. package/dist/server/microsoft-conversions-api.mjs +168 -0
  27. package/dist/server/microsoft-conversions-api.mjs.map +1 -0
  28. package/dist/tanstack/middleware.cjs +2 -1
  29. package/dist/tanstack/middleware.cjs.map +1 -1
  30. package/dist/tanstack/middleware.d.cts +2 -1
  31. package/dist/tanstack/middleware.d.cts.map +1 -1
  32. package/dist/tanstack/middleware.d.mts +2 -1
  33. package/dist/tanstack/middleware.d.mts.map +1 -1
  34. package/dist/tanstack/middleware.mjs +2 -1
  35. package/dist/tanstack/middleware.mjs.map +1 -1
  36. package/dist/third-parties/index.cjs +5 -0
  37. package/dist/third-parties/index.d.cts +3 -1
  38. package/dist/third-parties/index.d.mts +3 -1
  39. package/dist/third-parties/index.mjs +2 -1
  40. package/dist/third-parties/microsoft-uet.cjs +119 -0
  41. package/dist/third-parties/microsoft-uet.cjs.map +1 -0
  42. package/dist/third-parties/microsoft-uet.d.cts +70 -0
  43. package/dist/third-parties/microsoft-uet.d.cts.map +1 -0
  44. package/dist/third-parties/microsoft-uet.d.mts +70 -0
  45. package/dist/third-parties/microsoft-uet.d.mts.map +1 -0
  46. package/dist/third-parties/microsoft-uet.mjs +115 -0
  47. package/dist/third-parties/microsoft-uet.mjs.map +1 -0
  48. package/dist/third-parties/openai-pixel.cjs +2 -7
  49. package/dist/third-parties/openai-pixel.cjs.map +1 -1
  50. package/dist/third-parties/openai-pixel.d.cts.map +1 -1
  51. package/dist/third-parties/openai-pixel.d.mts.map +1 -1
  52. package/dist/third-parties/openai-pixel.mjs +1 -6
  53. package/dist/third-parties/openai-pixel.mjs.map +1 -1
  54. package/dist/track/uetq.cjs +80 -0
  55. package/dist/track/uetq.cjs.map +1 -0
  56. package/dist/track/uetq.d.cts +111 -0
  57. package/dist/track/uetq.d.cts.map +1 -0
  58. package/dist/track/uetq.d.mts +111 -0
  59. package/dist/track/uetq.d.mts.map +1 -0
  60. package/dist/track/uetq.mjs +78 -0
  61. package/dist/track/uetq.mjs.map +1 -0
  62. package/dist/utils/sha256.cjs +19 -0
  63. package/dist/utils/sha256.cjs.map +1 -0
  64. package/dist/utils/sha256.d.cts +13 -0
  65. package/dist/utils/sha256.d.cts.map +1 -0
  66. package/dist/utils/sha256.d.mts +13 -0
  67. package/dist/utils/sha256.d.mts.map +1 -0
  68. package/dist/utils/sha256.mjs +18 -0
  69. package/dist/utils/sha256.mjs.map +1 -0
  70. package/dist/web/index.cjs +1 -1
  71. package/dist/web/index.cjs.map +1 -1
  72. package/dist/web/index.d.cts.map +1 -1
  73. package/dist/web/index.d.mts.map +1 -1
  74. package/dist/web/index.mjs +2 -2
  75. package/dist/web/index.mjs.map +1 -1
  76. package/package.json +1 -1
@@ -0,0 +1,111 @@
1
+ import { Item } from "./gtag.mjs";
2
+ import { EventName, TrackName, TrackProperties } from "./types.mjs";
3
+ //#region src/track/uetq.d.ts
4
+ /**
5
+ * Microsoft Advertising Universal Event Tracking (UET) — the `uetq` queue that `bat.js` drains.
6
+ *
7
+ * UET's modern event API is gtag-shaped: `uetq.push('event', '<action>', { ...params })`, and the
8
+ * actions it knows (`purchase`, `add_to_cart`, `sign_up`, …) are GA4's recommended event names.
9
+ * Since this SDK's own event names are GA4's too, a standard event maps onto UET under the same
10
+ * name — only the parameter spellings differ (`value` → `revenue_value`, `Item` → `items[]`). An
11
+ * unknown name goes out unchanged as a custom event, which is what a UET *event goal* matches on.
12
+ *
13
+ * Parameter names below are taken from `bat.js` itself (`knownParams`); the tag silently drops
14
+ * any key it does not know, so an unmapped property is lost rather than an error. (bat.js's
15
+ * `knownEvents` list is not an allow-list — the tag only uses it to inherit page-level `set`
16
+ * parameters onto the events that carry them.)
17
+ *
18
+ * https://help.ads.microsoft.com/#apex/ads/en/56684/2 (custom events)
19
+ * https://help.ads.microsoft.com/#apex/ads/en/56910/1 (retail / dynamic remarketing)
20
+ * https://learn.microsoft.com/en-us/advertising/msa-help/hlp_ba_conc_uet_enhancedconversions
21
+ */
22
+ /** The retail `ecomm_pagetype` values UET validates against. */
23
+ type UETPageType = 'home' | 'searchresults' | 'category' | 'product' | 'cart' | 'purchase' | 'other';
24
+ /** One entry of the `items` array (`items.*` in bat.js's `knownParams`). */
25
+ interface UETItem {
26
+ id?: string;
27
+ name?: string;
28
+ brand?: string;
29
+ category?: string;
30
+ variant?: string;
31
+ price?: number;
32
+ quantity?: number;
33
+ list_name?: string;
34
+ list_position?: number;
35
+ location_id?: string;
36
+ }
37
+ /**
38
+ * Enhanced conversions identifiers. Sent RAW: the tag normalizes and SHA-256 hashes them in the
39
+ * browser before anything leaves the page (bat.js `validatePid`), and rejects a phone that is
40
+ * not E.164 after stripping separators.
41
+ */
42
+ interface UETPid {
43
+ em?: string;
44
+ ph?: string;
45
+ }
46
+ /** The parameters of one `uetq.push('event', action, params)` call. */
47
+ interface UETEventParams {
48
+ /** Deduplicates against the same event sent through the Conversions API. */
49
+ event_id?: string;
50
+ event_category?: string;
51
+ event_label?: string;
52
+ event_value?: number;
53
+ /** Variable revenue (`gv` on the wire). */
54
+ revenue_value?: number;
55
+ /** ISO 4217, uppercase (`gc` on the wire). */
56
+ currency?: string;
57
+ transaction_id?: string;
58
+ tax?: number;
59
+ shipping?: number;
60
+ coupon?: string;
61
+ items?: UETItem[];
62
+ search_term?: string;
63
+ method?: string;
64
+ content_type?: string;
65
+ content_id?: string;
66
+ ecomm_prodid?: string | string[];
67
+ ecomm_pagetype?: UETPageType;
68
+ ecomm_totalvalue?: number;
69
+ ecomm_category?: string;
70
+ pid?: UETPid;
71
+ }
72
+ type UETConsent = {
73
+ ad_storage: 'granted' | 'denied';
74
+ /**
75
+ * `default` only: milliseconds to hold events for an `update` before acting on the default,
76
+ * so a CMP that answers quickly does not cost the landing page. bat.js reads exactly this
77
+ * lowercase key (some community snippets spell it `Wait_for_update`, which it ignores) and
78
+ * caps it at 10,000.
79
+ */
80
+ wait_for_update?: number;
81
+ };
82
+ /**
83
+ * `window.uetq` is a plain array until `bat.js` loads (the snippet does `w[u] = w[u] || []`), then
84
+ * a `UET` instance that drains the queue; both expose `push`, which is all this SDK uses.
85
+ */
86
+ interface UETQ {
87
+ push(command: 'event', action: string, params?: UETEventParams): void;
88
+ push(command: 'pageLoad'): void;
89
+ push(command: 'set', params: {
90
+ pid?: UETPid;
91
+ }): void;
92
+ push(command: 'consent', mode: 'default' | 'update', consent: UETConsent): void;
93
+ }
94
+ /** GA4 `Item` → UET `items[]` entry. Undefined fields are left for the caller to strip. */
95
+ declare function mapItems(items?: Item[]): undefined | UETItem[];
96
+ /**
97
+ * Map an internal track event onto a UET action and its parameters.
98
+ *
99
+ * The action is always the internal name: for the GA4-named standard events that is also the
100
+ * name UET knows, and for everything else it is the custom action a UET event goal is configured
101
+ * with. Keeping the name identical on both channels is what lets the Conversions API sender
102
+ * (`eventName`) deduplicate against the browser (`event_id`).
103
+ *
104
+ * `page_view` is special to the tag: bat.js routes that action to its page-load beacon (the same
105
+ * one the snippet fires on load and `enableAutoSpaTracking` fires on history changes), so the
106
+ * browser tracker does not forward it — see `sendUETEvent`.
107
+ */
108
+ declare function mapUETEvent<T extends EventName>(name: TrackName<T>, properties?: TrackProperties<T>, eventId?: string): [string, UETEventParams];
109
+ //#endregion
110
+ export { UETConsent, UETEventParams, UETItem, UETPageType, UETPid, UETQ, mapItems, mapUETEvent };
111
+ //# sourceMappingURL=uetq.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uetq.d.mts","names":[],"sources":["../../src/track/uetq.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;KAuBY;;UAUK;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;UAQe;EACf;EACA;;;UAIe;;EAEf;EACA;EACA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA,iBAAiB;EACjB;EACA;EACA,MAAM;;KAGI;EACV;;;;;;;EAOA;;;;;;UAOe;EACf,KAAK,kBAAkB,gBAAgB,SAAS;EAChD,KAAK;EACL,KAAK,gBAAgB;IAAU,MAAM;;EACrC,KAAK,oBAAoB,4BAA4B,SAAS;;;iBAIhD,SAAS,QAAQ,qBAAqB;;;;;;;;;;;;;iBAuDtC,YAAY,UAAU,WACpC,MAAM,UAAU,IAChB,aAAa,gBAAgB,IAC7B,4BACU"}
@@ -0,0 +1,78 @@
1
+ //#region src/track/uetq.ts
2
+ /** GA4 `Item` → UET `items[]` entry. Undefined fields are left for the caller to strip. */
3
+ function mapItems(items) {
4
+ if (!items || items.length === 0) return void 0;
5
+ return items.map((item) => ({
6
+ id: item.item_id,
7
+ name: item.item_name,
8
+ brand: item.item_brand,
9
+ category: item.item_category,
10
+ variant: item.item_variant,
11
+ price: item.price,
12
+ quantity: item.quantity,
13
+ list_name: item.item_list_name,
14
+ list_position: item.index,
15
+ location_id: item.location_id
16
+ }));
17
+ }
18
+ function number(value) {
19
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
20
+ }
21
+ function string(value) {
22
+ return typeof value === "string" && value !== "" ? value : void 0;
23
+ }
24
+ function currency(value) {
25
+ return string(value)?.toUpperCase();
26
+ }
27
+ const PAGE_TYPES = [
28
+ "home",
29
+ "searchresults",
30
+ "category",
31
+ "product",
32
+ "cart",
33
+ "purchase",
34
+ "other"
35
+ ];
36
+ function pageType(value) {
37
+ const lowered = string(value)?.toLowerCase();
38
+ return lowered && PAGE_TYPES.includes(lowered) ? lowered : void 0;
39
+ }
40
+ /**
41
+ * Map an internal track event onto a UET action and its parameters.
42
+ *
43
+ * The action is always the internal name: for the GA4-named standard events that is also the
44
+ * name UET knows, and for everything else it is the custom action a UET event goal is configured
45
+ * with. Keeping the name identical on both channels is what lets the Conversions API sender
46
+ * (`eventName`) deduplicate against the browser (`event_id`).
47
+ *
48
+ * `page_view` is special to the tag: bat.js routes that action to its page-load beacon (the same
49
+ * one the snippet fires on load and `enableAutoSpaTracking` fires on history changes), so the
50
+ * browser tracker does not forward it — see `sendUETEvent`.
51
+ */
52
+ function mapUETEvent(name, properties, eventId) {
53
+ const p = properties ?? {};
54
+ const params = { event_id: eventId };
55
+ params.revenue_value = number(p.value) ?? number(p.revenue_value);
56
+ params.currency = currency(p.currency);
57
+ params.transaction_id = string(p.transaction_id);
58
+ params.tax = number(p.tax);
59
+ params.shipping = number(p.shipping);
60
+ params.coupon = string(p.coupon);
61
+ params.items = Array.isArray(p.items) ? mapItems(p.items) : void 0;
62
+ params.search_term = string(p.search_term);
63
+ params.method = string(p.method);
64
+ params.content_type = string(p.content_type);
65
+ params.content_id = string(p.content_id);
66
+ params.event_category = string(p.event_category);
67
+ params.event_label = string(p.event_label);
68
+ params.event_value = number(p.event_value);
69
+ params.ecomm_pagetype = pageType(p.ecomm_pagetype);
70
+ params.ecomm_prodid = params.ecomm_pagetype ? Array.isArray(p.ecomm_prodid) ? p.ecomm_prodid : string(p.ecomm_prodid) : void 0;
71
+ params.ecomm_totalvalue = number(p.ecomm_totalvalue);
72
+ params.ecomm_category = string(p.ecomm_category);
73
+ return [name, params];
74
+ }
75
+ //#endregion
76
+ export { mapItems, mapUETEvent };
77
+
78
+ //# sourceMappingURL=uetq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uetq.mjs","names":[],"sources":["../../src/track/uetq.ts"],"sourcesContent":["import type { Item } from './gtag';\nimport type { EventName, TrackName, TrackProperties } from './types';\n\n/**\n * Microsoft Advertising Universal Event Tracking (UET) — the `uetq` queue that `bat.js` drains.\n *\n * UET's modern event API is gtag-shaped: `uetq.push('event', '<action>', { ...params })`, and the\n * actions it knows (`purchase`, `add_to_cart`, `sign_up`, …) are GA4's recommended event names.\n * Since this SDK's own event names are GA4's too, a standard event maps onto UET under the same\n * name — only the parameter spellings differ (`value` → `revenue_value`, `Item` → `items[]`). An\n * unknown name goes out unchanged as a custom event, which is what a UET *event goal* matches on.\n *\n * Parameter names below are taken from `bat.js` itself (`knownParams`); the tag silently drops\n * any key it does not know, so an unmapped property is lost rather than an error. (bat.js's\n * `knownEvents` list is not an allow-list — the tag only uses it to inherit page-level `set`\n * parameters onto the events that carry them.)\n *\n * https://help.ads.microsoft.com/#apex/ads/en/56684/2 (custom events)\n * https://help.ads.microsoft.com/#apex/ads/en/56910/1 (retail / dynamic remarketing)\n * https://learn.microsoft.com/en-us/advertising/msa-help/hlp_ba_conc_uet_enhancedconversions\n */\n\n/** The retail `ecomm_pagetype` values UET validates against. */\nexport type UETPageType =\n | 'home'\n | 'searchresults'\n | 'category'\n | 'product'\n | 'cart'\n | 'purchase'\n | 'other';\n\n/** One entry of the `items` array (`items.*` in bat.js's `knownParams`). */\nexport interface UETItem {\n id?: string;\n name?: string;\n brand?: string;\n category?: string;\n variant?: string;\n price?: number;\n quantity?: number;\n list_name?: string;\n list_position?: number;\n location_id?: string;\n}\n\n/**\n * Enhanced conversions identifiers. Sent RAW: the tag normalizes and SHA-256 hashes them in the\n * browser before anything leaves the page (bat.js `validatePid`), and rejects a phone that is\n * not E.164 after stripping separators.\n */\nexport interface UETPid {\n em?: string;\n ph?: string;\n}\n\n/** The parameters of one `uetq.push('event', action, params)` call. */\nexport interface UETEventParams {\n /** Deduplicates against the same event sent through the Conversions API. */\n event_id?: string;\n event_category?: string;\n event_label?: string;\n event_value?: number;\n /** Variable revenue (`gv` on the wire). */\n revenue_value?: number;\n /** ISO 4217, uppercase (`gc` on the wire). */\n currency?: string;\n transaction_id?: string;\n tax?: number;\n shipping?: number;\n coupon?: string;\n items?: UETItem[];\n search_term?: string;\n method?: string;\n content_type?: string;\n content_id?: string;\n ecomm_prodid?: string | string[];\n ecomm_pagetype?: UETPageType;\n ecomm_totalvalue?: number;\n ecomm_category?: string;\n pid?: UETPid;\n}\n\nexport type UETConsent = {\n ad_storage: 'granted' | 'denied';\n /**\n * `default` only: milliseconds to hold events for an `update` before acting on the default,\n * so a CMP that answers quickly does not cost the landing page. bat.js reads exactly this\n * lowercase key (some community snippets spell it `Wait_for_update`, which it ignores) and\n * caps it at 10,000.\n */\n wait_for_update?: number;\n};\n\n/**\n * `window.uetq` is a plain array until `bat.js` loads (the snippet does `w[u] = w[u] || []`), then\n * a `UET` instance that drains the queue; both expose `push`, which is all this SDK uses.\n */\nexport interface UETQ {\n push(command: 'event', action: string, params?: UETEventParams): void;\n push(command: 'pageLoad'): void;\n push(command: 'set', params: { pid?: UETPid }): void;\n push(command: 'consent', mode: 'default' | 'update', consent: UETConsent): void;\n}\n\n/** GA4 `Item` → UET `items[]` entry. Undefined fields are left for the caller to strip. */\nexport function mapItems(items?: Item[]): undefined | UETItem[] {\n if (!items || items.length === 0) return undefined;\n return items.map((item) => ({\n id: item.item_id,\n name: item.item_name,\n brand: item.item_brand,\n category: item.item_category,\n variant: item.item_variant,\n price: item.price,\n quantity: item.quantity,\n list_name: item.item_list_name,\n list_position: item.index,\n location_id: item.location_id,\n }));\n}\n\nfunction number(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction string(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\nfunction currency(value: unknown): string | undefined {\n return string(value)?.toUpperCase();\n}\n\nconst PAGE_TYPES: string[] = [\n 'home',\n 'searchresults',\n 'category',\n 'product',\n 'cart',\n 'purchase',\n 'other',\n];\n\nfunction pageType(value: unknown): UETPageType | undefined {\n const lowered = string(value)?.toLowerCase();\n return lowered && PAGE_TYPES.includes(lowered) ? (lowered as UETPageType) : undefined;\n}\n\n/**\n * Map an internal track event onto a UET action and its parameters.\n *\n * The action is always the internal name: for the GA4-named standard events that is also the\n * name UET knows, and for everything else it is the custom action a UET event goal is configured\n * with. Keeping the name identical on both channels is what lets the Conversions API sender\n * (`eventName`) deduplicate against the browser (`event_id`).\n *\n * `page_view` is special to the tag: bat.js routes that action to its page-load beacon (the same\n * one the snippet fires on load and `enableAutoSpaTracking` fires on history changes), so the\n * browser tracker does not forward it — see `sendUETEvent`.\n */\nexport function mapUETEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n): [string, UETEventParams] {\n // A loose view of whatever the caller passed: every branch below reads only the keys it\n // knows and validates their types, so an event with foreign properties cannot leak them.\n const p = (properties ?? {}) as Record<string, unknown>;\n const params: UETEventParams = { event_id: eventId };\n\n // GA4's commerce parameters, spelled the UET way. `value` is GA4's revenue field and becomes\n // UET's variable revenue; every key here is one bat.js lists for the events that carry it.\n params.revenue_value = number(p.value) ?? number(p.revenue_value);\n params.currency = currency(p.currency);\n params.transaction_id = string(p.transaction_id);\n params.tax = number(p.tax);\n params.shipping = number(p.shipping);\n params.coupon = string(p.coupon);\n params.items = Array.isArray(p.items) ? mapItems(p.items as Item[]) : undefined;\n params.search_term = string(p.search_term);\n params.method = string(p.method);\n params.content_type = string(p.content_type);\n params.content_id = string(p.content_id);\n\n // The custom-event goal fields and the retail (dynamic remarketing) fields pass through under\n // their UET names, so a host can attach them to any event it defines.\n params.event_category = string(p.event_category);\n params.event_label = string(p.event_label);\n params.event_value = number(p.event_value);\n // bat.js throws on a `prodid` without a `pagetype` (`missingPageTypeException`), which would\n // cost the whole event; a product id on its own is dropped instead.\n params.ecomm_pagetype = pageType(p.ecomm_pagetype);\n params.ecomm_prodid = params.ecomm_pagetype\n ? Array.isArray(p.ecomm_prodid)\n ? (p.ecomm_prodid as string[])\n : string(p.ecomm_prodid)\n : undefined;\n params.ecomm_totalvalue = number(p.ecomm_totalvalue);\n params.ecomm_category = string(p.ecomm_category);\n\n return [name, params];\n}\n"],"mappings":";;AA0GA,SAAgB,SAAS,OAAuC;CAC9D,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,KAAA;CACzC,OAAO,MAAM,KAAK,UAAU;EAC1B,IAAI,KAAK;EACT,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,SAAS,KAAK;EACd,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,eAAe,KAAK;EACpB,aAAa,KAAK;CACpB,EAAE;AACJ;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ,KAAA;AACvE;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,OAAO,UAAU,YAAY,UAAU,KAAK,QAAQ,KAAA;AAC7D;AAEA,SAAS,SAAS,OAAoC;CACpD,OAAO,OAAO,KAAK,CAAC,EAAE,YAAY;AACpC;AAEA,MAAM,aAAuB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,SAAS,OAAyC;CACzD,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,YAAY;CAC3C,OAAO,WAAW,WAAW,SAAS,OAAO,IAAK,UAA0B,KAAA;AAC9E;;;;;;;;;;;;;AAcA,SAAgB,YACd,MACA,YACA,SAC0B;CAG1B,MAAM,IAAK,cAAc,CAAC;CAC1B,MAAM,SAAyB,EAAE,UAAU,QAAQ;CAInD,OAAO,gBAAgB,OAAO,EAAE,KAAK,KAAK,OAAO,EAAE,aAAa;CAChE,OAAO,WAAW,SAAS,EAAE,QAAQ;CACrC,OAAO,iBAAiB,OAAO,EAAE,cAAc;CAC/C,OAAO,MAAM,OAAO,EAAE,GAAG;CACzB,OAAO,WAAW,OAAO,EAAE,QAAQ;CACnC,OAAO,SAAS,OAAO,EAAE,MAAM;CAC/B,OAAO,QAAQ,MAAM,QAAQ,EAAE,KAAK,IAAI,SAAS,EAAE,KAAe,IAAI,KAAA;CACtE,OAAO,cAAc,OAAO,EAAE,WAAW;CACzC,OAAO,SAAS,OAAO,EAAE,MAAM;CAC/B,OAAO,eAAe,OAAO,EAAE,YAAY;CAC3C,OAAO,aAAa,OAAO,EAAE,UAAU;CAIvC,OAAO,iBAAiB,OAAO,EAAE,cAAc;CAC/C,OAAO,cAAc,OAAO,EAAE,WAAW;CACzC,OAAO,cAAc,OAAO,EAAE,WAAW;CAGzC,OAAO,iBAAiB,SAAS,EAAE,cAAc;CACjD,OAAO,eAAe,OAAO,iBACzB,MAAM,QAAQ,EAAE,YAAY,IACzB,EAAE,eACH,OAAO,EAAE,YAAY,IACvB,KAAA;CACJ,OAAO,mBAAmB,OAAO,EAAE,gBAAgB;CACnD,OAAO,iBAAiB,OAAO,EAAE,cAAc;CAE/C,OAAO,CAAC,MAAM,MAAM;AACtB"}
@@ -0,0 +1,19 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/utils/sha256.ts
3
+ /**
4
+ * SHA-256 hex digest, lowercase, computed in the browser with SubtleCrypto — the format every
5
+ * ad platform expects for a hashed identifier, and the same digest the server-side senders
6
+ * produce with `node:crypto`, so a value hashed on either side matches.
7
+ *
8
+ * Asynchronous because `crypto.subtle.digest` is; it rejects where SubtleCrypto is unavailable
9
+ * (an insecure context), so callers decide what to send without the hash.
10
+ */
11
+ async function sha256(value) {
12
+ const bytes = new TextEncoder().encode(value);
13
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
14
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
15
+ }
16
+ //#endregion
17
+ exports.sha256 = sha256;
18
+
19
+ //# sourceMappingURL=sha256.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.cjs","names":[],"sources":["../../src/utils/sha256.ts"],"sourcesContent":["/**\n * SHA-256 hex digest, lowercase, computed in the browser with SubtleCrypto — the format every\n * ad platform expects for a hashed identifier, and the same digest the server-side senders\n * produce with `node:crypto`, so a value hashed on either side matches.\n *\n * Asynchronous because `crypto.subtle.digest` is; it rejects where SubtleCrypto is unavailable\n * (an insecure context), so callers decide what to send without the hash.\n */\nexport async function sha256(value: string): Promise<string> {\n const bytes = new TextEncoder().encode(value);\n const digest = await crypto.subtle.digest('SHA-256', bytes);\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n"],"mappings":";;;;;;;;;;AAQA,eAAsB,OAAO,OAAgC;CAC3D,MAAM,QAAQ,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;CAC5C,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK;CAC1D,OAAO,MAAM,KAAK,IAAI,WAAW,MAAM,CAAC,CAAC,CACtC,KAAK,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CACjD,KAAK,EAAE;AACZ"}
@@ -0,0 +1,13 @@
1
+ //#region src/utils/sha256.d.ts
2
+ /**
3
+ * SHA-256 hex digest, lowercase, computed in the browser with SubtleCrypto — the format every
4
+ * ad platform expects for a hashed identifier, and the same digest the server-side senders
5
+ * produce with `node:crypto`, so a value hashed on either side matches.
6
+ *
7
+ * Asynchronous because `crypto.subtle.digest` is; it rejects where SubtleCrypto is unavailable
8
+ * (an insecure context), so callers decide what to send without the hash.
9
+ */
10
+ declare function sha256(value: string): Promise<string>;
11
+ //#endregion
12
+ export { sha256 };
13
+ //# sourceMappingURL=sha256.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.d.cts","names":[],"sources":["../../src/utils/sha256.ts"],"mappings":";;;;;;;;;iBAQsB,OAAO,gBAAgB"}
@@ -0,0 +1,13 @@
1
+ //#region src/utils/sha256.d.ts
2
+ /**
3
+ * SHA-256 hex digest, lowercase, computed in the browser with SubtleCrypto — the format every
4
+ * ad platform expects for a hashed identifier, and the same digest the server-side senders
5
+ * produce with `node:crypto`, so a value hashed on either side matches.
6
+ *
7
+ * Asynchronous because `crypto.subtle.digest` is; it rejects where SubtleCrypto is unavailable
8
+ * (an insecure context), so callers decide what to send without the hash.
9
+ */
10
+ declare function sha256(value: string): Promise<string>;
11
+ //#endregion
12
+ export { sha256 };
13
+ //# sourceMappingURL=sha256.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.d.mts","names":[],"sources":["../../src/utils/sha256.ts"],"mappings":";;;;;;;;;iBAQsB,OAAO,gBAAgB"}
@@ -0,0 +1,18 @@
1
+ //#region src/utils/sha256.ts
2
+ /**
3
+ * SHA-256 hex digest, lowercase, computed in the browser with SubtleCrypto — the format every
4
+ * ad platform expects for a hashed identifier, and the same digest the server-side senders
5
+ * produce with `node:crypto`, so a value hashed on either side matches.
6
+ *
7
+ * Asynchronous because `crypto.subtle.digest` is; it rejects where SubtleCrypto is unavailable
8
+ * (an insecure context), so callers decide what to send without the hash.
9
+ */
10
+ async function sha256(value) {
11
+ const bytes = new TextEncoder().encode(value);
12
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
13
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
14
+ }
15
+ //#endregion
16
+ export { sha256 };
17
+
18
+ //# sourceMappingURL=sha256.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.mjs","names":[],"sources":["../../src/utils/sha256.ts"],"sourcesContent":["/**\n * SHA-256 hex digest, lowercase, computed in the browser with SubtleCrypto — the format every\n * ad platform expects for a hashed identifier, and the same digest the server-side senders\n * produce with `node:crypto`, so a value hashed on either side matches.\n *\n * Asynchronous because `crypto.subtle.digest` is; it rejects where SubtleCrypto is unavailable\n * (an insecure context), so callers decide what to send without the hash.\n */\nexport async function sha256(value: string): Promise<string> {\n const bytes = new TextEncoder().encode(value);\n const digest = await crypto.subtle.digest('SHA-256', bytes);\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n"],"mappings":";;;;;;;;;AAQA,eAAsB,OAAO,OAAgC;CAC3D,MAAM,QAAQ,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;CAC5C,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK;CAC1D,OAAO,MAAM,KAAK,IAAI,WAAW,MAAM,CAAC,CAAC,CACtC,KAAK,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CACjD,KAAK,EAAE;AACZ"}
@@ -87,7 +87,7 @@ async function getTags() {
87
87
  li_fat_id: params.get("li_fat_id") ?? parsed.li_fat_id ?? void 0,
88
88
  dclid: params.get("dclid") ?? void 0,
89
89
  ko_click_id: params.get("ko_click_id") ?? void 0,
90
- msclkid: params.get("msclkid") ?? void 0,
90
+ msclkid: params.get("msclkid") ?? require_click_id_index.parseUetMsclkid(parsed._uetmsclkid),
91
91
  sccid: params.get("sccid") ?? void 0,
92
92
  ttclid: params.get("ttclid") ?? void 0,
93
93
  twclid: params.get("twclid") ?? void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["config","keys","uuidv4","getLink","Bowser","parseCookie","parseGcl"],"sources":["../../src/web/index.ts"],"sourcesContent":["import Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { parseGcl } from '../click-id/index';\nimport { keys } from '../constants/storage';\nimport { type Link, getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = config.storage.getItem(keys.device_id);\n if (cached) return cached;\n // lib.dom types `crypto.randomUUID` as always present, but it is missing in\n // insecure contexts and older browsers, so probe it before use.\n const id = (crypto as Partial<Crypto>).randomUUID ? crypto.randomUUID() : uuidv4();\n config.storage.setItem(keys.device_id, id);\n return id;\n}\n\nconst links = new Map<string, Promise<Link | null>>();\n\n/**\n * `getTags` runs once per event now, and the link a `?s=` id points at cannot change while the\n * page is open, so an uncached lookup would put an identical request on the wire for every\n * event the page sends.\n */\nfunction getCachedLink(id: string) {\n const cached = links.get(id);\n if (cached) return cached;\n const link = getLink(id).then((result) => {\n // A lookup that came back empty must not stick for the lifetime of the page. `getLink`\n // answers null for a network failure as well as for a link that does not exist, and losing\n // the link's utm params for every later event costs more than repeating a rare request.\n if (!result) links.delete(id);\n return result;\n });\n links.set(id, link);\n return link;\n}\n\n/** The user agent cannot change while the page is open, so it is parsed once. */\nlet parser: ReturnType<typeof Bowser.getParser> | undefined;\n\nexport async function getTags() {\n // Read the page before the first await: `getTags` runs when the event happens, and a single\n // page app can navigate while the link lookup below is still in flight.\n const page_location = window.location.href;\n const page_referrer = document.referrer || undefined;\n const page_title = document.title;\n\n parser ??= Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getCachedLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n page_location,\n page_referrer,\n page_title,\n // Meta Ads — _fbc is set server-side (see @shware/analytics/server resolveClickIdCookies)\n fbc: parsed._fbc ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads — _gcl_aw/_gcl_gb are written by gtag and kept alive server-side (see\n // @shware/analytics/server resolveClickIdCookies); the URL wins, the cookie carries the\n // click id to every later page of the visit and to returning visits.\n gclid: params.get('gclid') ?? parseGcl(parsed._gcl_aw)?.clickId,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads — _rdt_cid is set server-side (see @shware/analytics/server resolveClickIdCookies)\n rdt_cid: params.get('rdt_cid') ?? parsed._rdt_cid ?? undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n msclkid: params.get('msclkid') ?? undefined,\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? parseGcl(parsed._gcl_gb)?.clickId,\n gbraid: params.get('gbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["config","keys","uuidv4","getLink","Bowser","parseCookie","parseGcl","parseUetMsclkid"],"sources":["../../src/web/index.ts"],"sourcesContent":["import Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { parseGcl, parseUetMsclkid } from '../click-id/index';\nimport { keys } from '../constants/storage';\nimport { type Link, getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = config.storage.getItem(keys.device_id);\n if (cached) return cached;\n // lib.dom types `crypto.randomUUID` as always present, but it is missing in\n // insecure contexts and older browsers, so probe it before use.\n const id = (crypto as Partial<Crypto>).randomUUID ? crypto.randomUUID() : uuidv4();\n config.storage.setItem(keys.device_id, id);\n return id;\n}\n\nconst links = new Map<string, Promise<Link | null>>();\n\n/**\n * `getTags` runs once per event now, and the link a `?s=` id points at cannot change while the\n * page is open, so an uncached lookup would put an identical request on the wire for every\n * event the page sends.\n */\nfunction getCachedLink(id: string) {\n const cached = links.get(id);\n if (cached) return cached;\n const link = getLink(id).then((result) => {\n // A lookup that came back empty must not stick for the lifetime of the page. `getLink`\n // answers null for a network failure as well as for a link that does not exist, and losing\n // the link's utm params for every later event costs more than repeating a rare request.\n if (!result) links.delete(id);\n return result;\n });\n links.set(id, link);\n return link;\n}\n\n/** The user agent cannot change while the page is open, so it is parsed once. */\nlet parser: ReturnType<typeof Bowser.getParser> | undefined;\n\nexport async function getTags() {\n // Read the page before the first await: `getTags` runs when the event happens, and a single\n // page app can navigate while the link lookup below is still in flight.\n const page_location = window.location.href;\n const page_referrer = document.referrer || undefined;\n const page_title = document.title;\n\n parser ??= Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getCachedLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n page_location,\n page_referrer,\n page_title,\n // Meta Ads — _fbc is set server-side (see @shware/analytics/server resolveClickIdCookies)\n fbc: parsed._fbc ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads — _gcl_aw/_gcl_gb are written by gtag and kept alive server-side (see\n // @shware/analytics/server resolveClickIdCookies); the URL wins, the cookie carries the\n // click id to every later page of the visit and to returning visits.\n gclid: params.get('gclid') ?? parseGcl(parsed._gcl_aw)?.clickId,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads — _rdt_cid is set server-side (see @shware/analytics/server resolveClickIdCookies)\n rdt_cid: params.get('rdt_cid') ?? parsed._rdt_cid ?? undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n // Microsoft Ads — _uetmsclkid is written by the UET tag and kept alive server-side (see\n // @shware/analytics/server resolveClickIdCookies); the URL wins, the cookie carries the\n // click id to every later page of the visit and to returning visits.\n msclkid: params.get('msclkid') ?? parseUetMsclkid(parsed._uetmsclkid),\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? parseGcl(parsed._gcl_gb)?.clickId,\n gbraid: params.get('gbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/web/index.ts"],"mappings":";;;iBASgB;iBAkCM,WAAO,QAAA;cA6FhB,SAAS"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/web/index.ts"],"mappings":";;;iBASgB;iBAkCM,WAAO,QAAA;cAgGhB,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/web/index.ts"],"mappings":";;;iBASgB;iBAkCM,WAAO,QAAA;cA6FhB,SAAS"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/web/index.ts"],"mappings":";;;iBASgB;iBAkCM,WAAO,QAAA;cAgGhB,SAAS"}
@@ -2,7 +2,7 @@ import { __toESM } from "../_virtual/_rolldown/runtime.mjs";
2
2
  import { cache, config } from "../setup/index.mjs";
3
3
  import { keys } from "../constants/storage.mjs";
4
4
  import { getLink } from "../link/index.mjs";
5
- import { parseGcl } from "../click-id/index.mjs";
5
+ import { parseGcl, parseUetMsclkid } from "../click-id/index.mjs";
6
6
  import { require_es5 } from "../node_modules/.pnpm/bowser@2.14.1/node_modules/bowser/es5.mjs";
7
7
  import { v4 } from "uuid";
8
8
  import { parseCookie } from "cookie";
@@ -86,7 +86,7 @@ async function getTags() {
86
86
  li_fat_id: params.get("li_fat_id") ?? parsed.li_fat_id ?? void 0,
87
87
  dclid: params.get("dclid") ?? void 0,
88
88
  ko_click_id: params.get("ko_click_id") ?? void 0,
89
- msclkid: params.get("msclkid") ?? void 0,
89
+ msclkid: params.get("msclkid") ?? parseUetMsclkid(parsed._uetmsclkid),
90
90
  sccid: params.get("sccid") ?? void 0,
91
91
  ttclid: params.get("ttclid") ?? void 0,
92
92
  twclid: params.get("twclid") ?? void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["uuidv4","Bowser"],"sources":["../../src/web/index.ts"],"sourcesContent":["import Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { parseGcl } from '../click-id/index';\nimport { keys } from '../constants/storage';\nimport { type Link, getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = config.storage.getItem(keys.device_id);\n if (cached) return cached;\n // lib.dom types `crypto.randomUUID` as always present, but it is missing in\n // insecure contexts and older browsers, so probe it before use.\n const id = (crypto as Partial<Crypto>).randomUUID ? crypto.randomUUID() : uuidv4();\n config.storage.setItem(keys.device_id, id);\n return id;\n}\n\nconst links = new Map<string, Promise<Link | null>>();\n\n/**\n * `getTags` runs once per event now, and the link a `?s=` id points at cannot change while the\n * page is open, so an uncached lookup would put an identical request on the wire for every\n * event the page sends.\n */\nfunction getCachedLink(id: string) {\n const cached = links.get(id);\n if (cached) return cached;\n const link = getLink(id).then((result) => {\n // A lookup that came back empty must not stick for the lifetime of the page. `getLink`\n // answers null for a network failure as well as for a link that does not exist, and losing\n // the link's utm params for every later event costs more than repeating a rare request.\n if (!result) links.delete(id);\n return result;\n });\n links.set(id, link);\n return link;\n}\n\n/** The user agent cannot change while the page is open, so it is parsed once. */\nlet parser: ReturnType<typeof Bowser.getParser> | undefined;\n\nexport async function getTags() {\n // Read the page before the first await: `getTags` runs when the event happens, and a single\n // page app can navigate while the link lookup below is still in flight.\n const page_location = window.location.href;\n const page_referrer = document.referrer || undefined;\n const page_title = document.title;\n\n parser ??= Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getCachedLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n page_location,\n page_referrer,\n page_title,\n // Meta Ads — _fbc is set server-side (see @shware/analytics/server resolveClickIdCookies)\n fbc: parsed._fbc ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads — _gcl_aw/_gcl_gb are written by gtag and kept alive server-side (see\n // @shware/analytics/server resolveClickIdCookies); the URL wins, the cookie carries the\n // click id to every later page of the visit and to returning visits.\n gclid: params.get('gclid') ?? parseGcl(parsed._gcl_aw)?.clickId,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads — _rdt_cid is set server-side (see @shware/analytics/server resolveClickIdCookies)\n rdt_cid: params.get('rdt_cid') ?? parsed._rdt_cid ?? undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n msclkid: params.get('msclkid') ?? undefined,\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? parseGcl(parsed._gcl_gb)?.clickId,\n gbraid: params.get('gbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["uuidv4","Bowser"],"sources":["../../src/web/index.ts"],"sourcesContent":["import Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { parseGcl, parseUetMsclkid } from '../click-id/index';\nimport { keys } from '../constants/storage';\nimport { type Link, getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = config.storage.getItem(keys.device_id);\n if (cached) return cached;\n // lib.dom types `crypto.randomUUID` as always present, but it is missing in\n // insecure contexts and older browsers, so probe it before use.\n const id = (crypto as Partial<Crypto>).randomUUID ? crypto.randomUUID() : uuidv4();\n config.storage.setItem(keys.device_id, id);\n return id;\n}\n\nconst links = new Map<string, Promise<Link | null>>();\n\n/**\n * `getTags` runs once per event now, and the link a `?s=` id points at cannot change while the\n * page is open, so an uncached lookup would put an identical request on the wire for every\n * event the page sends.\n */\nfunction getCachedLink(id: string) {\n const cached = links.get(id);\n if (cached) return cached;\n const link = getLink(id).then((result) => {\n // A lookup that came back empty must not stick for the lifetime of the page. `getLink`\n // answers null for a network failure as well as for a link that does not exist, and losing\n // the link's utm params for every later event costs more than repeating a rare request.\n if (!result) links.delete(id);\n return result;\n });\n links.set(id, link);\n return link;\n}\n\n/** The user agent cannot change while the page is open, so it is parsed once. */\nlet parser: ReturnType<typeof Bowser.getParser> | undefined;\n\nexport async function getTags() {\n // Read the page before the first await: `getTags` runs when the event happens, and a single\n // page app can navigate while the link lookup below is still in flight.\n const page_location = window.location.href;\n const page_referrer = document.referrer || undefined;\n const page_title = document.title;\n\n parser ??= Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getCachedLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n page_location,\n page_referrer,\n page_title,\n // Meta Ads — _fbc is set server-side (see @shware/analytics/server resolveClickIdCookies)\n fbc: parsed._fbc ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads — _gcl_aw/_gcl_gb are written by gtag and kept alive server-side (see\n // @shware/analytics/server resolveClickIdCookies); the URL wins, the cookie carries the\n // click id to every later page of the visit and to returning visits.\n gclid: params.get('gclid') ?? parseGcl(parsed._gcl_aw)?.clickId,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads — _rdt_cid is set server-side (see @shware/analytics/server resolveClickIdCookies)\n rdt_cid: params.get('rdt_cid') ?? parsed._rdt_cid ?? undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n // Microsoft Ads — _uetmsclkid is written by the UET tag and kept alive server-side (see\n // @shware/analytics/server resolveClickIdCookies); the URL wins, the cookie carries the\n // click id to every later page of the visit and to returning visits.\n msclkid: params.get('msclkid') ?? parseUetMsclkid(parsed._uetmsclkid),\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? parseGcl(parsed._gcl_gb)?.clickId,\n gbraid: params.get('gbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shware/analytics",
3
- "version": "8.2.0",
3
+ "version": "8.3.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",