@shware/analytics 8.2.0 → 8.5.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 (128) hide show
  1. package/README.md +78 -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/hooks/use-web-analytics.cjs +11 -5
  11. package/dist/hooks/use-web-analytics.cjs.map +1 -1
  12. package/dist/hooks/use-web-analytics.d.cts +4 -1
  13. package/dist/hooks/use-web-analytics.d.cts.map +1 -1
  14. package/dist/hooks/use-web-analytics.d.mts +4 -1
  15. package/dist/hooks/use-web-analytics.d.mts.map +1 -1
  16. package/dist/hooks/use-web-analytics.mjs +11 -5
  17. package/dist/hooks/use-web-analytics.mjs.map +1 -1
  18. package/dist/next/index.cjs +38 -2
  19. package/dist/next/index.cjs.map +1 -1
  20. package/dist/next/index.d.cts +3 -1
  21. package/dist/next/index.d.cts.map +1 -1
  22. package/dist/next/index.d.mts +3 -1
  23. package/dist/next/index.d.mts.map +1 -1
  24. package/dist/next/index.mjs +39 -3
  25. package/dist/next/index.mjs.map +1 -1
  26. package/dist/react-router/index.cjs +25 -3
  27. package/dist/react-router/index.cjs.map +1 -1
  28. package/dist/react-router/index.d.cts +3 -1
  29. package/dist/react-router/index.d.cts.map +1 -1
  30. package/dist/react-router/index.d.mts +3 -1
  31. package/dist/react-router/index.d.mts.map +1 -1
  32. package/dist/react-router/index.mjs +25 -3
  33. package/dist/react-router/index.mjs.map +1 -1
  34. package/dist/schema/index.cjs +1 -0
  35. package/dist/schema/index.cjs.map +1 -1
  36. package/dist/schema/index.d.cts +4 -0
  37. package/dist/schema/index.d.cts.map +1 -1
  38. package/dist/schema/index.d.mts +4 -0
  39. package/dist/schema/index.d.mts.map +1 -1
  40. package/dist/schema/index.mjs +1 -0
  41. package/dist/schema/index.mjs.map +1 -1
  42. package/dist/server/google-data-manager.cjs +2 -0
  43. package/dist/server/google-data-manager.cjs.map +1 -1
  44. package/dist/server/google-data-manager.d.cts.map +1 -1
  45. package/dist/server/google-data-manager.d.mts.map +1 -1
  46. package/dist/server/google-data-manager.mjs +2 -0
  47. package/dist/server/google-data-manager.mjs.map +1 -1
  48. package/dist/server/index.cjs +8 -0
  49. package/dist/server/index.d.cts +5 -4
  50. package/dist/server/index.d.mts +5 -4
  51. package/dist/server/index.mjs +5 -4
  52. package/dist/server/microsoft-conversions-api.cjs +171 -0
  53. package/dist/server/microsoft-conversions-api.cjs.map +1 -0
  54. package/dist/server/microsoft-conversions-api.d.cts +124 -0
  55. package/dist/server/microsoft-conversions-api.d.cts.map +1 -0
  56. package/dist/server/microsoft-conversions-api.d.mts +124 -0
  57. package/dist/server/microsoft-conversions-api.d.mts.map +1 -0
  58. package/dist/server/microsoft-conversions-api.mjs +168 -0
  59. package/dist/server/microsoft-conversions-api.mjs.map +1 -0
  60. package/dist/tanstack/analytics.cjs +25 -3
  61. package/dist/tanstack/analytics.cjs.map +1 -1
  62. package/dist/tanstack/analytics.d.cts +3 -1
  63. package/dist/tanstack/analytics.d.cts.map +1 -1
  64. package/dist/tanstack/analytics.d.mts +3 -1
  65. package/dist/tanstack/analytics.d.mts.map +1 -1
  66. package/dist/tanstack/analytics.mjs +25 -3
  67. package/dist/tanstack/analytics.mjs.map +1 -1
  68. package/dist/tanstack/middleware.cjs +2 -1
  69. package/dist/tanstack/middleware.cjs.map +1 -1
  70. package/dist/tanstack/middleware.d.cts +2 -1
  71. package/dist/tanstack/middleware.d.cts.map +1 -1
  72. package/dist/tanstack/middleware.d.mts +2 -1
  73. package/dist/tanstack/middleware.d.mts.map +1 -1
  74. package/dist/tanstack/middleware.mjs +2 -1
  75. package/dist/tanstack/middleware.mjs.map +1 -1
  76. package/dist/third-parties/index.cjs +5 -0
  77. package/dist/third-parties/index.d.cts +3 -1
  78. package/dist/third-parties/index.d.mts +3 -1
  79. package/dist/third-parties/index.mjs +2 -1
  80. package/dist/third-parties/microsoft-uet.cjs +119 -0
  81. package/dist/third-parties/microsoft-uet.cjs.map +1 -0
  82. package/dist/third-parties/microsoft-uet.d.cts +70 -0
  83. package/dist/third-parties/microsoft-uet.d.cts.map +1 -0
  84. package/dist/third-parties/microsoft-uet.d.mts +70 -0
  85. package/dist/third-parties/microsoft-uet.d.mts.map +1 -0
  86. package/dist/third-parties/microsoft-uet.mjs +115 -0
  87. package/dist/third-parties/microsoft-uet.mjs.map +1 -0
  88. package/dist/third-parties/openai-pixel.cjs +2 -7
  89. package/dist/third-parties/openai-pixel.cjs.map +1 -1
  90. package/dist/third-parties/openai-pixel.d.cts.map +1 -1
  91. package/dist/third-parties/openai-pixel.d.mts.map +1 -1
  92. package/dist/third-parties/openai-pixel.mjs +1 -6
  93. package/dist/third-parties/openai-pixel.mjs.map +1 -1
  94. package/dist/track/types.d.cts +7 -0
  95. package/dist/track/types.d.cts.map +1 -1
  96. package/dist/track/types.d.mts +7 -0
  97. package/dist/track/types.d.mts.map +1 -1
  98. package/dist/track/uetq.cjs +80 -0
  99. package/dist/track/uetq.cjs.map +1 -0
  100. package/dist/track/uetq.d.cts +111 -0
  101. package/dist/track/uetq.d.cts.map +1 -0
  102. package/dist/track/uetq.d.mts +111 -0
  103. package/dist/track/uetq.d.mts.map +1 -0
  104. package/dist/track/uetq.mjs +78 -0
  105. package/dist/track/uetq.mjs.map +1 -0
  106. package/dist/utils/sha256.cjs +19 -0
  107. package/dist/utils/sha256.cjs.map +1 -0
  108. package/dist/utils/sha256.d.cts +13 -0
  109. package/dist/utils/sha256.d.cts.map +1 -0
  110. package/dist/utils/sha256.d.mts +13 -0
  111. package/dist/utils/sha256.d.mts.map +1 -0
  112. package/dist/utils/sha256.mjs +18 -0
  113. package/dist/utils/sha256.mjs.map +1 -0
  114. package/dist/web/index.cjs +26 -2
  115. package/dist/web/index.cjs.map +1 -1
  116. package/dist/web/index.d.cts.map +1 -1
  117. package/dist/web/index.d.mts.map +1 -1
  118. package/dist/web/index.mjs +27 -3
  119. package/dist/web/index.mjs.map +1 -1
  120. package/dist/web/page-key.cjs +53 -0
  121. package/dist/web/page-key.cjs.map +1 -0
  122. package/dist/web/page-key.d.cts +24 -0
  123. package/dist/web/page-key.d.cts.map +1 -0
  124. package/dist/web/page-key.d.mts +24 -0
  125. package/dist/web/page-key.d.mts.map +1 -0
  126. package/dist/web/page-key.mjs +52 -0
  127. package/dist/web/page-key.mjs.map +1 -0
  128. package/package.json +1 -1
@@ -0,0 +1,168 @@
1
+ import { IGNORED_EVENTS } from "../third-parties/ignored-events.mjs";
2
+ import { formatMsclkid } from "../click-id/index.mjs";
3
+ import { pageLocation } from "./page-location.mjs";
4
+ import { mapUETEvent } from "../track/uetq.mjs";
5
+ import { chunk, fetch } from "@shware/utils";
6
+ import { createHash } from "node:crypto";
7
+ //#region src/server/microsoft-conversions-api.ts
8
+ /**
9
+ * Microsoft Advertising Conversions API (CAPI) sender — the server-side counterpart of the UET
10
+ * tag, in the same shape as the other senders here: a pure per-event builder, a never-throws
11
+ * `sendEvents`, the token in a header and never in a URL or a log line.
12
+ *
13
+ * Microsoft's recommended deployment is hybrid: keep the UET JavaScript on the page and send the
14
+ * same conversions here too, deduplicated by (`tagId`, `eventName`, `eventId`). Both channels in
15
+ * this SDK are fed from the same `track()` call — the browser sends the internal name as the UET
16
+ * action with the event id as `event_id`, and this builder sends the same name as `eventName`
17
+ * with the same id — so the pair agrees by construction.
18
+ *
19
+ * The token comes from the UET tag's "Use Conversions API" setup step, or from the Campaign
20
+ * Management API's `UetTagAuthKey/Query`. Access is provisioned per customer account: an account
21
+ * whose tag setup has no "Use Conversions API" option has to ask Microsoft Advertising support.
22
+ *
23
+ * https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration
24
+ */
25
+ const ENDPOINT = "https://capi.uet.microsoft.com/v1";
26
+ /** The API's hard limit per request. */
27
+ const MAX_EVENTS_PER_REQUEST = 1e3;
28
+ const SHA256 = /^[a-f0-9]{64}$/;
29
+ const sha256 = (value) => createHash("sha256").update(value).digest("hex");
30
+ /**
31
+ * Microsoft's email normalization: trim, drop the dots and any `+alias` from the user part —
32
+ * for EVERY domain, unlike Google's gmail-only rule — lowercase, SHA-256. A value that is
33
+ * already a SHA-256 hex digest passes through.
34
+ * https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration#hashed-identifiers
35
+ */
36
+ function normalizeEmail(input) {
37
+ const email = input.trim().toLowerCase();
38
+ const at = email.lastIndexOf("@");
39
+ if (at === -1) return email;
40
+ return `${email.slice(0, at).replace(/\./g, "").split("+", 1)[0] ?? ""}@${email.slice(at + 1)}`;
41
+ }
42
+ function hashEmail(input) {
43
+ const value = input?.trim().toLowerCase();
44
+ if (!value) return void 0;
45
+ return SHA256.test(value) ? value : sha256(normalizeEmail(value));
46
+ }
47
+ /**
48
+ * Phones are E.164 by the schema (`+` then 11–15 digits, the range bat.js validates too); the
49
+ * separators bat.js tolerates are stripped, and anything else is dropped rather than guessed —
50
+ * a number with no country code hashed under an assumed one can never match.
51
+ */
52
+ function hashPhone(input) {
53
+ const value = input?.trim().toLowerCase();
54
+ if (!value) return void 0;
55
+ if (SHA256.test(value)) return value;
56
+ const phone = value.replace(/[-() \t]/g, "");
57
+ return /^\+\d{11,15}$/.test(phone) ? sha256(phone) : void 0;
58
+ }
59
+ function getUserData(event, data) {
60
+ const { tags, platform, visitor_id } = event;
61
+ const email = Array.isArray(data.email) ? data.email[0] : data.email;
62
+ const phone = Array.isArray(data.phone_number) ? data.phone_number[0] : data.phone_number;
63
+ return {
64
+ msclkid: tags.msclkid ? formatMsclkid(tags.msclkid) : void 0,
65
+ em: hashEmail(email),
66
+ ph: hashPhone(phone),
67
+ anonymousId: visitor_id || void 0,
68
+ externalId: data.user_id ? sha256(data.user_id) : void 0,
69
+ clientUserAgent: data.user_agent,
70
+ clientIpAddress: typeof tags.ip_address === "string" && tags.ip_address ? tags.ip_address : data.ip_address,
71
+ idfa: platform === "ios" ? tags.advertising_id : void 0,
72
+ gaid: platform === "android" ? tags.advertising_id : void 0
73
+ };
74
+ }
75
+ function getCustomData(event) {
76
+ const [, params] = mapUETEvent(event.name, event.properties);
77
+ const customData = {
78
+ eventCategory: params.event_category,
79
+ eventLabel: params.event_label,
80
+ eventValue: params.event_value,
81
+ searchTerm: params.search_term,
82
+ transactionId: params.transaction_id,
83
+ value: params.revenue_value,
84
+ currency: params.currency,
85
+ items: params.items?.map(({ id, name, price, quantity }) => ({
86
+ id,
87
+ name,
88
+ price,
89
+ quantity
90
+ })),
91
+ itemIds: Array.isArray(params.ecomm_prodid) ? params.ecomm_prodid : params.ecomm_prodid ? [params.ecomm_prodid] : void 0,
92
+ pageType: params.ecomm_pagetype,
93
+ ecommTotalValue: params.ecomm_totalvalue,
94
+ ecommCategory: params.ecomm_category
95
+ };
96
+ return Object.values(customData).some((value) => value !== void 0) ? customData : void 0;
97
+ }
98
+ function getServerEvent(event, data = {}, options = {}) {
99
+ const { consent, pageLoads = false } = typeof options === "string" ? { consent: options } : options;
100
+ const { id, name, tags, created_at } = event;
101
+ const isPageLoad = name === "page_view";
102
+ return {
103
+ eventType: isPageLoad ? "pageLoad" : "custom",
104
+ eventId: tags.idempotency_key ?? id,
105
+ eventName: isPageLoad ? void 0 : name,
106
+ eventTime: Math.round(new Date(created_at).getTime() / 1e3),
107
+ eventSourceUrl: pageLocation(tags),
108
+ pageLoadId: pageLoads ? tags.page_load_id : void 0,
109
+ referrerUrl: tags.page_referrer,
110
+ pageTitle: tags.page_title,
111
+ adStorageConsent: consent === "granted" ? "G" : consent === "denied" ? "D" : void 0,
112
+ userData: getUserData(event, data),
113
+ customData: isPageLoad ? void 0 : getCustomData(event)
114
+ };
115
+ }
116
+ /** Strip undefined fields so the wire payload carries only what was set. */
117
+ function compact(value) {
118
+ return JSON.parse(JSON.stringify(value));
119
+ }
120
+ /**
121
+ * Send events to `POST /v1/{tagId}/events`. Batches above the API's 1,000-event limit are split
122
+ * into consecutive requests; one response is returned per request made. Never throws: an HTTP
123
+ * or network failure is logged (without the token) and yields no response for that request.
124
+ */
125
+ async function sendEvents(token, tagId, events, data = {}, options = {}) {
126
+ const { consent, pageLoads = false, dataProvider, continueOnValidationError = true } = options;
127
+ const capiEvents = events.filter((event) => pageLoads && event.name === "page_view" || !IGNORED_EVENTS.includes(event.name)).map((event) => getServerEvent(event, data, {
128
+ consent,
129
+ pageLoads
130
+ })).filter((event) => event.eventType !== "pageLoad" || event.eventSourceUrl);
131
+ if (capiEvents.length === 0) return [];
132
+ const responses = [];
133
+ for (const batch of chunk(capiEvents, MAX_EVENTS_PER_REQUEST)) {
134
+ const body = compact({
135
+ data: batch,
136
+ continueOnValidationError,
137
+ dataProvider
138
+ });
139
+ try {
140
+ const response = await fetch(`${ENDPOINT}/${tagId}/events`, {
141
+ method: "POST",
142
+ headers: {
143
+ "Content-Type": "application/json",
144
+ Accept: "application/json",
145
+ Authorization: `Bearer ${token}`
146
+ },
147
+ body: JSON.stringify(body)
148
+ });
149
+ if (response.ok) {
150
+ const result = await response.json();
151
+ if (result.error?.details?.length) console.warn(`Microsoft conversion accepted with validation issues: ${JSON.stringify(result.error.details)}`);
152
+ responses.push(result);
153
+ continue;
154
+ }
155
+ const { status } = response;
156
+ const message = await response.text();
157
+ console.error(`Failed to send Microsoft conversion, status: ${status}, body: ${message}`);
158
+ } catch (error) {
159
+ const message = error instanceof Error ? error.message : String(error);
160
+ console.error(`Failed to send Microsoft conversion, network error: ${message}`);
161
+ }
162
+ }
163
+ return responses;
164
+ }
165
+ //#endregion
166
+ export { getServerEvent, normalizeEmail, sendEvents };
167
+
168
+ //# sourceMappingURL=microsoft-conversions-api.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microsoft-conversions-api.mjs","names":[],"sources":["../../src/server/microsoft-conversions-api.ts"],"sourcesContent":["/**\n * Microsoft Advertising Conversions API (CAPI) sender — the server-side counterpart of the UET\n * tag, in the same shape as the other senders here: a pure per-event builder, a never-throws\n * `sendEvents`, the token in a header and never in a URL or a log line.\n *\n * Microsoft's recommended deployment is hybrid: keep the UET JavaScript on the page and send the\n * same conversions here too, deduplicated by (`tagId`, `eventName`, `eventId`). Both channels in\n * this SDK are fed from the same `track()` call — the browser sends the internal name as the UET\n * action with the event id as `event_id`, and this builder sends the same name as `eventName`\n * with the same id — so the pair agrees by construction.\n *\n * The token comes from the UET tag's \"Use Conversions API\" setup step, or from the Campaign\n * Management API's `UetTagAuthKey/Query`. Access is provisioned per customer account: an account\n * whose tag setup has no \"Use Conversions API\" option has to ask Microsoft Advertising support.\n *\n * https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration\n */\nimport { createHash } from 'node:crypto';\nimport { chunk, fetch } from '@shware/utils';\nimport { formatMsclkid } from '../click-id/index';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { mapUETEvent } from '../track/uetq';\nimport { pageLocation } from './page-location';\n\nconst ENDPOINT = 'https://capi.uet.microsoft.com/v1';\n\n/** The API's hard limit per request. */\nconst MAX_EVENTS_PER_REQUEST = 1000;\n\nconst SHA256 = /^[a-f0-9]{64}$/;\n\nconst sha256 = (value: string) => createHash('sha256').update(value).digest('hex');\n\n/**\n * Microsoft's email normalization: trim, drop the dots and any `+alias` from the user part —\n * for EVERY domain, unlike Google's gmail-only rule — lowercase, SHA-256. A value that is\n * already a SHA-256 hex digest passes through.\n * https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration#hashed-identifiers\n */\nexport function normalizeEmail(input: string): string {\n const email = input.trim().toLowerCase();\n const at = email.lastIndexOf('@');\n if (at === -1) return email;\n const user = email.slice(0, at).replace(/\\./g, '').split('+', 1)[0] ?? '';\n return `${user}@${email.slice(at + 1)}`;\n}\n\nfunction hashEmail(input: string | undefined): string | undefined {\n const value = input?.trim().toLowerCase();\n if (!value) return undefined;\n return SHA256.test(value) ? value : sha256(normalizeEmail(value));\n}\n\n/**\n * Phones are E.164 by the schema (`+` then 11–15 digits, the range bat.js validates too); the\n * separators bat.js tolerates are stripped, and anything else is dropped rather than guessed —\n * a number with no country code hashed under an assumed one can never match.\n */\nfunction hashPhone(input: string | undefined): string | undefined {\n const value = input?.trim().toLowerCase();\n if (!value) return undefined;\n if (SHA256.test(value)) return value;\n const phone = value.replace(/[-() \\t]/g, '');\n return /^\\+\\d{11,15}$/.test(phone) ? sha256(phone) : undefined;\n}\n\nexport interface MicrosoftUserData {\n /** Microsoft click id, as a dashed UUID. */\n msclkid?: string;\n /** SHA-256 of the normalized email. */\n em?: string;\n /** SHA-256 of the E.164 phone number. */\n ph?: string;\n /** The SDK visitor id — what the client-side ID Sync pixel (`sendUETIdSync`) sends as `VID`. */\n anonymousId?: string;\n /** SHA-256 of the signed-in user id — what `sendUETIdSync` sends as `UID`. */\n externalId?: string;\n clientUserAgent?: string;\n clientIpAddress?: string;\n idfa?: string;\n gaid?: string;\n}\n\nexport interface MicrosoftItem {\n id?: string;\n name?: string;\n price?: number;\n quantity?: number;\n}\n\nexport interface MicrosoftCustomData {\n eventCategory?: string;\n eventLabel?: string;\n eventValue?: number;\n searchTerm?: string;\n transactionId?: string;\n value?: number;\n currency?: string;\n items?: MicrosoftItem[];\n itemIds?: string[];\n pageType?: string;\n ecommTotalValue?: number;\n ecommCategory?: string;\n}\n\n/** One entry of the `data` array POSTed to `/v1/{tagId}/events`. */\nexport interface MicrosoftEvent {\n eventType: 'custom' | 'pageLoad';\n eventId: string;\n /** The UET action; absent on `pageLoad` events. */\n eventName?: string;\n /** UNIX seconds, UTC; the API rejects anything older than 7 days. */\n eventTime: number;\n eventSourceUrl?: string;\n /** Links a `custom` event to its `pageLoad` event; a v4 UUID (the SDK's `page_load_id` tag). */\n pageLoadId?: string;\n referrerUrl?: string;\n pageTitle?: string;\n /** `G` granted, `D` denied. Absent means granted. */\n adStorageConsent?: 'G' | 'D';\n userData: MicrosoftUserData;\n customData?: MicrosoftCustomData;\n}\n\nexport interface MicrosoftConversionsOptions {\n /**\n * EEA/UK/CH consent for the whole batch. `denied` events are accepted but used for nothing —\n * no attribution, no audiences. Absent means granted, which is also the API's default.\n */\n consent?: 'granted' | 'denied';\n /**\n * Also send `page_view` events, as CAPI `pageLoad` events, and link every custom event to the\n * page load it happened on (`pageLoadId`, from the `page_load_id` tag). Off by default because\n * the UET JavaScript already reports every page load and the two would double-count\n * destination-URL goals; turn it on for a CAPI-only site with no tag on the page. Off, no\n * `pageLoadId` is sent either: it would point at a page load Microsoft only saw from the tag,\n * under the tag's own id.\n *\n * Not yet modelled: the revenue-only custom event a `pageLoad` needs for a destination goal\n * with variable revenue.\n */\n pageLoads?: boolean;\n /** Identifies a third-party sender in Microsoft's monitoring; leave unset for a first-party build. */\n dataProvider?: string;\n /**\n * Process the valid events of a batch and report the invalid ones, instead of the API's default\n * all-or-nothing rejection. On by default — one malformed event should cost itself, not the\n * batch it travelled with.\n */\n continueOnValidationError?: boolean;\n}\n\nexport interface MicrosoftValidationDetail {\n index?: number;\n propertyName?: string;\n attemptedValue?: unknown;\n errorMessage?: string;\n errorCode?: string;\n isWarning?: boolean;\n}\n\nexport interface MicrosoftConversionsResponse {\n eventsReceived?: number;\n error?: { code?: string; message?: string; details?: MicrosoftValidationDetail[] };\n}\n\nfunction getUserData(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): MicrosoftUserData {\n const { tags, platform, visitor_id } = event;\n const email = Array.isArray(data.email) ? data.email[0] : data.email;\n const phone = Array.isArray(data.phone_number) ? data.phone_number[0] : data.phone_number;\n\n return {\n msclkid: tags.msclkid ? formatMsclkid(tags.msclkid) : undefined,\n em: hashEmail(email),\n ph: hashPhone(phone),\n // Microsoft requires this to equal the `VID` of the client-side ID Sync pixel; both sides use\n // the SDK visitor id, which every event carries and the client can always read.\n anonymousId: visitor_id || undefined,\n // Microsoft asks for an anonymized id and never a real one. SHA-256, the same digest\n // `sendUETIdSync` applies client-side, so `externalId` and the pixel's `UID` agree too.\n externalId: data.user_id ? sha256(data.user_id) : undefined,\n clientUserAgent: data.user_agent,\n clientIpAddress:\n typeof tags.ip_address === 'string' && tags.ip_address ? tags.ip_address : data.ip_address,\n idfa: platform === 'ios' ? tags.advertising_id : undefined,\n gaid: platform === 'android' ? tags.advertising_id : undefined,\n };\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getCustomData(event: TrackEvent<any>): MicrosoftCustomData | undefined {\n const [, params] = mapUETEvent(event.name, event.properties);\n const customData: MicrosoftCustomData = {\n eventCategory: params.event_category,\n eventLabel: params.event_label,\n eventValue: params.event_value,\n searchTerm: params.search_term,\n transactionId: params.transaction_id,\n value: params.revenue_value,\n currency: params.currency,\n items: params.items?.map(({ id, name, price, quantity }) => ({ id, name, price, quantity })),\n itemIds: Array.isArray(params.ecomm_prodid)\n ? params.ecomm_prodid\n : params.ecomm_prodid\n ? [params.ecomm_prodid]\n : undefined,\n pageType: params.ecomm_pagetype,\n ecommTotalValue: params.ecomm_totalvalue,\n ecommCategory: params.ecomm_category,\n };\n return Object.values(customData).some((value) => value !== undefined) ? customData : undefined;\n}\n\n/**\n * Build one CAPI event. `page_view` becomes a `pageLoad` event (the caller decides whether to\n * send those, see `MicrosoftConversionsOptions.pageLoads`); everything else is a `custom` event\n * named after the track event, which is also the UET action the browser sent.\n */\nexport type MicrosoftEventOptions = Pick<MicrosoftConversionsOptions, 'consent' | 'pageLoads'>;\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData = {},\n /** Options, or — the 8.3.0 signature, still accepted — the bare consent value. */\n options: MicrosoftEventOptions | MicrosoftEventOptions['consent'] = {}\n): MicrosoftEvent {\n const { consent, pageLoads = false } =\n typeof options === 'string' ? { consent: options } : options;\n const { id, name, tags, created_at } = event;\n const isPageLoad = name === 'page_view';\n\n return {\n eventType: isPageLoad ? 'pageLoad' : 'custom',\n eventId: tags.idempotency_key ?? id,\n eventName: isPageLoad ? undefined : name,\n eventTime: Math.round(new Date(created_at).getTime() / 1000),\n eventSourceUrl: pageLocation(tags),\n pageLoadId: pageLoads ? tags.page_load_id : undefined,\n referrerUrl: tags.page_referrer,\n pageTitle: tags.page_title,\n adStorageConsent: consent === 'granted' ? 'G' : consent === 'denied' ? 'D' : undefined,\n userData: getUserData(event, data),\n customData: isPageLoad ? undefined : getCustomData(event),\n };\n}\n\n/** Strip undefined fields so the wire payload carries only what was set. */\nfunction compact<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/**\n * Send events to `POST /v1/{tagId}/events`. Batches above the API's 1,000-event limit are split\n * into consecutive requests; one response is returned per request made. Never throws: an HTTP\n * or network failure is logged (without the token) and yields no response for that request.\n */\nexport async function sendEvents(\n token: string,\n tagId: string | number,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n options: MicrosoftConversionsOptions = {}\n): Promise<MicrosoftConversionsResponse[]> {\n const { consent, pageLoads = false, dataProvider, continueOnValidationError = true } = options;\n\n const capiEvents = events\n .filter(\n (event) => (pageLoads && event.name === 'page_view') || !IGNORED_EVENTS.includes(event.name)\n )\n .map((event) => getServerEvent(event, data, { consent, pageLoads }))\n // A pageLoad without a URL is rejected by the API, and a page-less event has nothing to say.\n .filter((event) => event.eventType !== 'pageLoad' || event.eventSourceUrl);\n if (capiEvents.length === 0) return [];\n\n const responses: MicrosoftConversionsResponse[] = [];\n for (const batch of chunk(capiEvents, MAX_EVENTS_PER_REQUEST)) {\n const body = compact({ data: batch, continueOnValidationError, dataProvider });\n try {\n const response = await fetch(`${ENDPOINT}/${tagId}/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(body),\n });\n if (response.ok) {\n const result = (await response.json()) as MicrosoftConversionsResponse;\n // A 200 can still carry field-level warnings (the field was removed) and, with\n // `continueOnValidationError`, skipped events. Surface them: silent partial data loss is\n // the one failure mode the caller cannot see from the status code.\n if (result.error?.details?.length) {\n console.warn(\n `Microsoft conversion accepted with validation issues: ${JSON.stringify(result.error.details)}`\n );\n }\n responses.push(result);\n continue;\n }\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send Microsoft conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`Failed to send Microsoft conversion, network error: ${message}`);\n }\n }\n return responses;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,WAAW;;AAGjB,MAAM,yBAAyB;AAE/B,MAAM,SAAS;AAEf,MAAM,UAAU,UAAkB,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;;;;;;;AAQjF,SAAgB,eAAe,OAAuB;CACpD,MAAM,QAAQ,MAAM,KAAK,CAAC,CAAC,YAAY;CACvC,MAAM,KAAK,MAAM,YAAY,GAAG;CAChC,IAAI,OAAO,IAAI,OAAO;CAEtB,OAAO,GADM,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,GACxD,GAAG,MAAM,MAAM,KAAK,CAAC;AACtC;AAEA,SAAS,UAAU,OAA+C;CAChE,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC,YAAY;CACxC,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,OAAO,OAAO,KAAK,KAAK,IAAI,QAAQ,OAAO,eAAe,KAAK,CAAC;AAClE;;;;;;AAOA,SAAS,UAAU,OAA+C;CAChE,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC,YAAY;CACxC,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,IAAI,OAAO,KAAK,KAAK,GAAG,OAAO;CAC/B,MAAM,QAAQ,MAAM,QAAQ,aAAa,EAAE;CAC3C,OAAO,gBAAgB,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI,KAAA;AACvD;AAsGA,SAAS,YAEP,OACA,MACmB;CACnB,MAAM,EAAE,MAAM,UAAU,eAAe;CACvC,MAAM,QAAQ,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK;CAC/D,MAAM,QAAQ,MAAM,QAAQ,KAAK,YAAY,IAAI,KAAK,aAAa,KAAK,KAAK;CAE7E,OAAO;EACL,SAAS,KAAK,UAAU,cAAc,KAAK,OAAO,IAAI,KAAA;EACtD,IAAI,UAAU,KAAK;EACnB,IAAI,UAAU,KAAK;EAGnB,aAAa,cAAc,KAAA;EAG3B,YAAY,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI,KAAA;EAClD,iBAAiB,KAAK;EACtB,iBACE,OAAO,KAAK,eAAe,YAAY,KAAK,aAAa,KAAK,aAAa,KAAK;EAClF,MAAM,aAAa,QAAQ,KAAK,iBAAiB,KAAA;EACjD,MAAM,aAAa,YAAY,KAAK,iBAAiB,KAAA;CACvD;AACF;AAGA,SAAS,cAAc,OAAyD;CAC9E,MAAM,GAAG,UAAU,YAAY,MAAM,MAAM,MAAM,UAAU;CAC3D,MAAM,aAAkC;EACtC,eAAe,OAAO;EACtB,YAAY,OAAO;EACnB,YAAY,OAAO;EACnB,YAAY,OAAO;EACnB,eAAe,OAAO;EACtB,OAAO,OAAO;EACd,UAAU,OAAO;EACjB,OAAO,OAAO,OAAO,KAAK,EAAE,IAAI,MAAM,OAAO,gBAAgB;GAAE;GAAI;GAAM;GAAO;EAAS,EAAE;EAC3F,SAAS,MAAM,QAAQ,OAAO,YAAY,IACtC,OAAO,eACP,OAAO,eACL,CAAC,OAAO,YAAY,IACpB,KAAA;EACN,UAAU,OAAO;EACjB,iBAAiB,OAAO;EACxB,eAAe,OAAO;CACxB;CACA,OAAO,OAAO,OAAO,UAAU,CAAC,CAAC,MAAM,UAAU,UAAU,KAAA,CAAS,IAAI,aAAa,KAAA;AACvF;AASA,SAAgB,eAEd,OACA,OAAyB,CAAC,GAE1B,UAAoE,CAAC,GACrD;CAChB,MAAM,EAAE,SAAS,YAAY,UAC3B,OAAO,YAAY,WAAW,EAAE,SAAS,QAAQ,IAAI;CACvD,MAAM,EAAE,IAAI,MAAM,MAAM,eAAe;CACvC,MAAM,aAAa,SAAS;CAE5B,OAAO;EACL,WAAW,aAAa,aAAa;EACrC,SAAS,KAAK,mBAAmB;EACjC,WAAW,aAAa,KAAA,IAAY;EACpC,WAAW,KAAK,MAAM,IAAI,KAAK,UAAU,CAAC,CAAC,QAAQ,IAAI,GAAI;EAC3D,gBAAgB,aAAa,IAAI;EACjC,YAAY,YAAY,KAAK,eAAe,KAAA;EAC5C,aAAa,KAAK;EAClB,WAAW,KAAK;EAChB,kBAAkB,YAAY,YAAY,MAAM,YAAY,WAAW,MAAM,KAAA;EAC7E,UAAU,YAAY,OAAO,IAAI;EACjC,YAAY,aAAa,KAAA,IAAY,cAAc,KAAK;CAC1D;AACF;;AAGA,SAAS,QAAW,OAAa;CAC/B,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;;;;;;AAOA,eAAsB,WACpB,OACA,OAEA,QACA,OAAyB,CAAC,GAC1B,UAAuC,CAAC,GACC;CACzC,MAAM,EAAE,SAAS,YAAY,OAAO,cAAc,4BAA4B,SAAS;CAEvF,MAAM,aAAa,OAChB,QACE,UAAW,aAAa,MAAM,SAAS,eAAgB,CAAC,eAAe,SAAS,MAAM,IAAI,CAC7F,CAAC,CACA,KAAK,UAAU,eAAe,OAAO,MAAM;EAAE;EAAS;CAAU,CAAC,CAAC,CAAC,CAEnE,QAAQ,UAAU,MAAM,cAAc,cAAc,MAAM,cAAc;CAC3E,IAAI,WAAW,WAAW,GAAG,OAAO,CAAC;CAErC,MAAM,YAA4C,CAAC;CACnD,KAAK,MAAM,SAAS,MAAM,YAAY,sBAAsB,GAAG;EAC7D,MAAM,OAAO,QAAQ;GAAE,MAAM;GAAO;GAA2B;EAAa,CAAC;EAC7E,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,GAAG,SAAS,GAAG,MAAM,UAAU;IAC1D,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,QAAQ;KACR,eAAe,UAAU;IAC3B;IACA,MAAM,KAAK,UAAU,IAAI;GAC3B,CAAC;GACD,IAAI,SAAS,IAAI;IACf,MAAM,SAAU,MAAM,SAAS,KAAK;IAIpC,IAAI,OAAO,OAAO,SAAS,QACzB,QAAQ,KACN,yDAAyD,KAAK,UAAU,OAAO,MAAM,OAAO,GAC9F;IAEF,UAAU,KAAK,MAAM;IACrB;GACF;GACA,MAAM,EAAE,WAAW;GACnB,MAAM,UAAU,MAAM,SAAS,KAAK;GACpC,QAAQ,MAAM,gDAAgD,OAAO,UAAU,SAAS;EAC1F,SAAS,OAAO;GACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACrE,QAAQ,MAAM,uDAAuD,SAAS;EAChF;CACF;CACA,OAAO;AACT"}
@@ -6,9 +6,9 @@ const require_hooks_use_web_analytics = require("../hooks/use-web-analytics.cjs"
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
7
  let _tanstack_react_router = require("@tanstack/react-router");
8
8
  //#region src/tanstack/analytics.tsx
9
- function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, hotjarId, facebookAppId, reportWebVitals = true }) {
10
- const { pathname } = (0, _tanstack_react_router.useLocation)();
11
- require_hooks_use_web_analytics.useWebAnalytics(pathname);
9
+ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, uetTagId, hotjarId, facebookAppId, reportWebVitals = true }) {
10
+ const { pathname, searchStr } = (0, _tanstack_react_router.useLocation)();
11
+ require_hooks_use_web_analytics.useWebAnalytics(pathname, searchStr);
12
12
  require_hooks_use_outbound_click_analytics.useOutboundClickAnalytics();
13
13
  require_hooks_use_report_web_vitals.useReportWebVitals((metric) => {
14
14
  if (!reportWebVitals) return;
@@ -130,6 +130,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId,
130
130
  b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js";
131
131
  s.parentNode.insertBefore(b, s);
132
132
  })(window.lintrk);
133
+ ` }
134
+ }),
135
+ uetTagId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("script", {
136
+ async: true,
137
+ id: "uet-tag",
138
+ dangerouslySetInnerHTML: { __html: `
139
+ (function (w, d, t, u, o) {
140
+ w[u] = w[u] || [], o.ts = (new Date).getTime();
141
+ var n = d.createElement(t);
142
+ n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
143
+ n.async = 1, n.onload = n.onreadystatechange = function() {
144
+ var s = this.readyState;
145
+ s && "loaded" !== s && "complete" !== s ||
146
+ (o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
147
+ n.onload = n.onreadystatechange = null)
148
+ };
149
+ var i = d.getElementsByTagName(t)[0];
150
+ i.parentNode.insertBefore(n, i);
151
+ })(window, document, "script", "uetq", {
152
+ ti: "${uetTagId}",
153
+ enableAutoSpaTracking: true
154
+ });
133
155
  ` }
134
156
  }),
135
157
  hotjarId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("script", {
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.cjs","names":["useLocation"],"sources":["../../src/tanstack/analytics.tsx"],"sourcesContent":["import { useLocation } from '@tanstack/react-router';\nimport { useOutboundClickAnalytics } from '../hooks/use-outbound-click-analytics';\nimport { useReportWebVitals } from '../hooks/use-report-web-vitals';\nimport { useWebAnalytics } from '../hooks/use-web-analytics';\nimport type { PixelId as MetaPixelId } from '../track/fbq';\nimport type { GaId, GtmId } from '../track/gtag';\nimport { track } from '../track/index';\nimport type { PixelId as RedditPixelId } from '../track/rdt';\n\ninterface Props {\n gaId?: GaId;\n gaSrc?: string;\n gtmId?: GtmId;\n metaPixelId?: MetaPixelId;\n openaiPixelId?: string;\n redditPixelId?: RedditPixelId;\n linkedInPartnerId?: `${number}`;\n hotjarId?: `${number}`;\n facebookAppId?: string;\n nonce?: string;\n debugMode?: boolean;\n reportWebVitals?: boolean;\n}\n\nexport function Analytics({\n gaId,\n gaSrc,\n nonce,\n debugMode,\n metaPixelId,\n openaiPixelId,\n redditPixelId,\n linkedInPartnerId,\n hotjarId,\n facebookAppId,\n reportWebVitals = true,\n}: Props) {\n const { pathname } = useLocation();\n useWebAnalytics(pathname);\n useOutboundClickAnalytics();\n\n useReportWebVitals((metric) => {\n if (!reportWebVitals) return;\n const properties = {\n value: metric.delta,\n metric_id: metric.id,\n metric_value: metric.value,\n metric_delta: metric.delta,\n metric_rating: metric.rating,\n metric_navigation_type: metric.navigationType,\n non_interaction: true, // avoids affecting bounce rate.\n };\n track(metric.name, properties);\n });\n\n return (\n <>\n {facebookAppId && <meta property=\"fb:app_id\" content={facebookAppId} />}\n {gaId && (\n <>\n <script\n async\n id=\"gtag\"\n nonce={nonce}\n src={gaSrc ?? `https://www.googletagmanager.com/gtag/js?id=${gaId}`}\n />\n <script\n async\n nonce={nonce}\n id=\"gtag-init\"\n dangerouslySetInnerHTML={{\n __html: `\n window.dataLayer = window.dataLayer || [];\n function gtag(){dataLayer.push(arguments);}\n gtag('js', new Date());\n gtag('config', '${gaId}'${debugMode ? \" ,{ 'debug_mode': true }\" : ''});\n `,\n }}\n />\n </>\n )}\n {metaPixelId && (\n <script\n async\n id=\"meta-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !(function (f, b, e, v, n, t, s) {\n if (f.fbq) return;\n n = f.fbq = function () {\n n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);\n };\n if (!f._fbq) f._fbq = n;\n n.push = n;\n n.loaded = !0;\n n.version = '2.0';\n n.queue = [];\n t = b.createElement(e);\n t.async = !0;\n t.src = v;\n s = b.getElementsByTagName(e)[0];\n s.parentNode.insertBefore(t, s);\n })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');\n fbq('init', '${metaPixelId}');\n fbq('track', 'PageView');`,\n }}\n />\n )}\n {openaiPixelId && (\n <script\n async\n id=\"openai-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, s, u) {\n if (w.oaiq) return;\n var q = function () {\n q.q.push(arguments);\n };\n q.q = [];\n w.oaiq = q;\n var js = d.createElement(s);\n js.async = true;\n js.src = u;\n var f = d.getElementsByTagName(s)[0];\n f.parentNode.insertBefore(js, f);\n })(window, document, \"script\", \"https://bzrcdn.openai.com/sdk/oaiq.min.js\");\n\n oaiq(\"init\", { pixelId: \"${openaiPixelId}\" });\n oaiq(\"measure\", \"page_viewed\", { type: \"contents\" });\n `,\n }}\n />\n )}\n {redditPixelId && (\n <script\n async\n id=\"reddit-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !function(w,d) {\n if(!w.rdt) {\n var p = w.rdt = function() {\n p.sendEvent ? p.sendEvent.apply(p,arguments) : p.callQueue.push(arguments)\n };\n p.callQueue = [];\n var t = d.createElement(\"script\");\n t.src = \"https://www.redditstatic.com/ads/pixel.js\";\n t.async = !0;\n var s = d.getElementsByTagName(\"script\")[0];\n s.parentNode.insertBefore(t,s)\n }\n }(window, document);\n rdt('init', '${redditPixelId}');\n rdt('track', 'PageVisit');`,\n }}\n />\n )}\n {linkedInPartnerId && (\n <script\n async\n id=\"linkedin-insight-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n _linkedin_partner_id = \"${linkedInPartnerId}\";\n window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];\n window._linkedin_data_partner_ids.push(_linkedin_partner_id);\n\n (function(l) {\n if (!l){\n window.lintrk = function(a,b){\n window.lintrk.q.push([a,b])\n };\n window.lintrk.q=[]\n }\n var s = document.getElementsByTagName(\"script\")[0];\n var b = document.createElement(\"script\");\n b.type = \"text/javascript\";b.async = true;\n b.src = \"https://snap.licdn.com/li.lms-analytics/insight.min.js\";\n s.parentNode.insertBefore(b, s);\n })(window.lintrk);\n `,\n }}\n />\n )}\n {hotjarId && (\n <script\n async\n id=\"hotjar\"\n dangerouslySetInnerHTML={{\n __html: `\n (function(h,o,t,j,a,r){\n h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};\n h._hjSettings={hjid:${hotjarId},hjsv:6};\n a=o.getElementsByTagName('head')[0];\n r=o.createElement('script');r.async=1;\n r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;\n a.appendChild(r);\n })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');\n `,\n }}\n />\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;AAwBA,SAAgB,UAAU,EACxB,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,eACA,kBAAkB,QACV;CACR,MAAM,EAAE,cAAA,GAAaA,uBAAAA,YAAAA,CAAY;CACjC,gCAAA,gBAAgB,QAAQ;CACxB,2CAAA,0BAA0B;CAE1B,oCAAA,oBAAoB,WAAW;EAC7B,IAAI,CAAC,iBAAiB;EACtB,MAAM,aAAa;GACjB,OAAO,OAAO;GACd,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,cAAc,OAAO;GACrB,eAAe,OAAO;GACtB,wBAAwB,OAAO;GAC/B,iBAAiB;EACnB;EACA,oBAAA,MAAM,OAAO,MAAM,UAAU;CAC/B,CAAC;CAED,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACG,iBAAiB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,UAAS;GAAY,SAAS;EAAgB,CAAA;EACrE,QACC,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACI;GACP,KAAK,SAAS,+CAA+C;EAC9D,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACO;GACP,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;gCAIU,KAAK,GAAG,YAAY,6BAA6B,GAAG;cAExE;EACD,CAAA,CACD,EAAA,CAAA;EAEH,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;;;2BAiBO,YAAY;uCAE7B;EACD,CAAA;EAEF,iBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;2CAeuB,cAAc;;cAG/C;EACD,CAAA;EAEF,iBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;2BAcO,cAAc;wCAE/B;EACD,CAAA;EAEF,qBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;sCACkB,kBAAkB;;;;;;;;;;;;;;;;;cAkB9C;EACD,CAAA;EAEF,YACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;oCAGgB,SAAS;;;;;;cAOnC;EACD,CAAA;CAEH,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"analytics.cjs","names":["useLocation"],"sources":["../../src/tanstack/analytics.tsx"],"sourcesContent":["import { useLocation } from '@tanstack/react-router';\nimport { useOutboundClickAnalytics } from '../hooks/use-outbound-click-analytics';\nimport { useReportWebVitals } from '../hooks/use-report-web-vitals';\nimport { useWebAnalytics } from '../hooks/use-web-analytics';\nimport type { PixelId as MetaPixelId } from '../track/fbq';\nimport type { GaId, GtmId } from '../track/gtag';\nimport { track } from '../track/index';\nimport type { PixelId as RedditPixelId } from '../track/rdt';\n\ninterface Props {\n gaId?: GaId;\n gaSrc?: string;\n gtmId?: GtmId;\n metaPixelId?: MetaPixelId;\n openaiPixelId?: string;\n redditPixelId?: RedditPixelId;\n linkedInPartnerId?: `${number}`;\n /** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */\n uetTagId?: `${number}`;\n hotjarId?: `${number}`;\n facebookAppId?: string;\n nonce?: string;\n debugMode?: boolean;\n reportWebVitals?: boolean;\n}\n\nexport function Analytics({\n gaId,\n gaSrc,\n nonce,\n debugMode,\n metaPixelId,\n openaiPixelId,\n redditPixelId,\n linkedInPartnerId,\n uetTagId,\n hotjarId,\n facebookAppId,\n reportWebVitals = true,\n}: Props) {\n const { pathname, searchStr } = useLocation();\n useWebAnalytics(pathname, searchStr);\n useOutboundClickAnalytics();\n\n useReportWebVitals((metric) => {\n if (!reportWebVitals) return;\n const properties = {\n value: metric.delta,\n metric_id: metric.id,\n metric_value: metric.value,\n metric_delta: metric.delta,\n metric_rating: metric.rating,\n metric_navigation_type: metric.navigationType,\n non_interaction: true, // avoids affecting bounce rate.\n };\n track(metric.name, properties);\n });\n\n return (\n <>\n {facebookAppId && <meta property=\"fb:app_id\" content={facebookAppId} />}\n {gaId && (\n <>\n <script\n async\n id=\"gtag\"\n nonce={nonce}\n src={gaSrc ?? `https://www.googletagmanager.com/gtag/js?id=${gaId}`}\n />\n <script\n async\n nonce={nonce}\n id=\"gtag-init\"\n dangerouslySetInnerHTML={{\n __html: `\n window.dataLayer = window.dataLayer || [];\n function gtag(){dataLayer.push(arguments);}\n gtag('js', new Date());\n gtag('config', '${gaId}'${debugMode ? \" ,{ 'debug_mode': true }\" : ''});\n `,\n }}\n />\n </>\n )}\n {metaPixelId && (\n <script\n async\n id=\"meta-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !(function (f, b, e, v, n, t, s) {\n if (f.fbq) return;\n n = f.fbq = function () {\n n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);\n };\n if (!f._fbq) f._fbq = n;\n n.push = n;\n n.loaded = !0;\n n.version = '2.0';\n n.queue = [];\n t = b.createElement(e);\n t.async = !0;\n t.src = v;\n s = b.getElementsByTagName(e)[0];\n s.parentNode.insertBefore(t, s);\n })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');\n fbq('init', '${metaPixelId}');\n fbq('track', 'PageView');`,\n }}\n />\n )}\n {openaiPixelId && (\n <script\n async\n id=\"openai-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, s, u) {\n if (w.oaiq) return;\n var q = function () {\n q.q.push(arguments);\n };\n q.q = [];\n w.oaiq = q;\n var js = d.createElement(s);\n js.async = true;\n js.src = u;\n var f = d.getElementsByTagName(s)[0];\n f.parentNode.insertBefore(js, f);\n })(window, document, \"script\", \"https://bzrcdn.openai.com/sdk/oaiq.min.js\");\n\n oaiq(\"init\", { pixelId: \"${openaiPixelId}\" });\n oaiq(\"measure\", \"page_viewed\", { type: \"contents\" });\n `,\n }}\n />\n )}\n {redditPixelId && (\n <script\n async\n id=\"reddit-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !function(w,d) {\n if(!w.rdt) {\n var p = w.rdt = function() {\n p.sendEvent ? p.sendEvent.apply(p,arguments) : p.callQueue.push(arguments)\n };\n p.callQueue = [];\n var t = d.createElement(\"script\");\n t.src = \"https://www.redditstatic.com/ads/pixel.js\";\n t.async = !0;\n var s = d.getElementsByTagName(\"script\")[0];\n s.parentNode.insertBefore(t,s)\n }\n }(window, document);\n rdt('init', '${redditPixelId}');\n rdt('track', 'PageVisit');`,\n }}\n />\n )}\n {linkedInPartnerId && (\n <script\n async\n id=\"linkedin-insight-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n _linkedin_partner_id = \"${linkedInPartnerId}\";\n window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];\n window._linkedin_data_partner_ids.push(_linkedin_partner_id);\n\n (function(l) {\n if (!l){\n window.lintrk = function(a,b){\n window.lintrk.q.push([a,b])\n };\n window.lintrk.q=[]\n }\n var s = document.getElementsByTagName(\"script\")[0];\n var b = document.createElement(\"script\");\n b.type = \"text/javascript\";b.async = true;\n b.src = \"https://snap.licdn.com/li.lms-analytics/insight.min.js\";\n s.parentNode.insertBefore(b, s);\n })(window.lintrk);\n `,\n }}\n />\n )}\n {uetTagId && (\n <script\n async\n id=\"uet-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, t, u, o) {\n w[u] = w[u] || [], o.ts = (new Date).getTime();\n var n = d.createElement(t);\n n.src = \"https://bat.bing.net/bat.js?ti=\" + o.ti + (\"uetq\" != u ? \"&q=\" + u : \"\"),\n n.async = 1, n.onload = n.onreadystatechange = function() {\n var s = this.readyState;\n s && \"loaded\" !== s && \"complete\" !== s ||\n (o.q = w[u], w[u] = new UET(o), w[u].push(\"pageLoad\"),\n n.onload = n.onreadystatechange = null)\n };\n var i = d.getElementsByTagName(t)[0];\n i.parentNode.insertBefore(n, i);\n })(window, document, \"script\", \"uetq\", {\n ti: \"${uetTagId}\",\n enableAutoSpaTracking: true\n });\n `,\n }}\n />\n )}\n {hotjarId && (\n <script\n async\n id=\"hotjar\"\n dangerouslySetInnerHTML={{\n __html: `\n (function(h,o,t,j,a,r){\n h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};\n h._hjSettings={hjid:${hotjarId},hjsv:6};\n a=o.getElementsByTagName('head')[0];\n r=o.createElement('script');r.async=1;\n r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;\n a.appendChild(r);\n })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');\n `,\n }}\n />\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;AA0BA,SAAgB,UAAU,EACxB,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,UACA,eACA,kBAAkB,QACV;CACR,MAAM,EAAE,UAAU,eAAA,GAAcA,uBAAAA,YAAAA,CAAY;CAC5C,gCAAA,gBAAgB,UAAU,SAAS;CACnC,2CAAA,0BAA0B;CAE1B,oCAAA,oBAAoB,WAAW;EAC7B,IAAI,CAAC,iBAAiB;EACtB,MAAM,aAAa;GACjB,OAAO,OAAO;GACd,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,cAAc,OAAO;GACrB,eAAe,OAAO;GACtB,wBAAwB,OAAO;GAC/B,iBAAiB;EACnB;EACA,oBAAA,MAAM,OAAO,MAAM,UAAU;CAC/B,CAAC;CAED,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACG,iBAAiB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,UAAS;GAAY,SAAS;EAAgB,CAAA;EACrE,QACC,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACI;GACP,KAAK,SAAS,+CAA+C;EAC9D,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACO;GACP,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;gCAIU,KAAK,GAAG,YAAY,6BAA6B,GAAG;cAExE;EACD,CAAA,CACD,EAAA,CAAA;EAEH,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;;;2BAiBO,YAAY;uCAE7B;EACD,CAAA;EAEF,iBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;2CAeuB,cAAc;;cAG/C;EACD,CAAA;EAEF,iBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;2BAcO,cAAc;wCAE/B;EACD,CAAA;EAEF,qBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;sCACkB,kBAAkB;;;;;;;;;;;;;;;;;cAkB9C;EACD,CAAA;EAEF,YACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;uBAcG,SAAS;;;cAItB;EACD,CAAA;EAEF,YACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;oCAGgB,SAAS;;;;;;cAOnC;EACD,CAAA;CAEH,EAAA,CAAA;AAEN"}
@@ -10,13 +10,15 @@ interface Props {
10
10
  openaiPixelId?: string;
11
11
  redditPixelId?: PixelId$1;
12
12
  linkedInPartnerId?: `${number}`;
13
+ /** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */
14
+ uetTagId?: `${number}`;
13
15
  hotjarId?: `${number}`;
14
16
  facebookAppId?: string;
15
17
  nonce?: string;
16
18
  debugMode?: boolean;
17
19
  reportWebVitals?: boolean;
18
20
  }
19
- declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, hotjarId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
21
+ declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, uetTagId, hotjarId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
20
22
  //#endregion
21
23
  export { Analytics };
22
24
  //# sourceMappingURL=analytics.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.cts","names":[],"sources":["../../src/tanstack/analytics.tsx"],"mappings":";;;;UASU;EACR,OAAO;EACP;EACA,QAAQ;EACR,cAAc;EACd;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA;;iBAGc,YACd,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,eACA,mBACC,wBAAK,IAAA"}
1
+ {"version":3,"file":"analytics.d.cts","names":[],"sources":["../../src/tanstack/analytics.tsx"],"mappings":";;;;UASU;EACR,OAAO;EACP;EACA,QAAQ;EACR,cAAc;EACd;EACA,gBAAgB;EAChB;;EAEA;EACA;EACA;EACA;EACA;EACA;;iBAGc,YACd,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,UACA,eACA,mBACC,wBAAK,IAAA"}
@@ -10,13 +10,15 @@ interface Props {
10
10
  openaiPixelId?: string;
11
11
  redditPixelId?: PixelId$1;
12
12
  linkedInPartnerId?: `${number}`;
13
+ /** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */
14
+ uetTagId?: `${number}`;
13
15
  hotjarId?: `${number}`;
14
16
  facebookAppId?: string;
15
17
  nonce?: string;
16
18
  debugMode?: boolean;
17
19
  reportWebVitals?: boolean;
18
20
  }
19
- declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, hotjarId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
21
+ declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, uetTagId, hotjarId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
20
22
  //#endregion
21
23
  export { Analytics };
22
24
  //# sourceMappingURL=analytics.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.mts","names":[],"sources":["../../src/tanstack/analytics.tsx"],"mappings":";;;;UASU;EACR,OAAO;EACP;EACA,QAAQ;EACR,cAAc;EACd;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA;;iBAGc,YACd,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,eACA,mBACC,wBAAK,IAAA"}
1
+ {"version":3,"file":"analytics.d.mts","names":[],"sources":["../../src/tanstack/analytics.tsx"],"mappings":";;;;UASU;EACR,OAAO;EACP;EACA,QAAQ;EACR,cAAc;EACd;EACA,gBAAgB;EAChB;;EAEA;EACA;EACA;EACA;EACA;EACA;;iBAGc,YACd,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,UACA,eACA,mBACC,wBAAK,IAAA"}
@@ -5,9 +5,9 @@ import { useWebAnalytics } from "../hooks/use-web-analytics.mjs";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { useLocation } from "@tanstack/react-router";
7
7
  //#region src/tanstack/analytics.tsx
8
- function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, hotjarId, facebookAppId, reportWebVitals = true }) {
9
- const { pathname } = useLocation();
10
- useWebAnalytics(pathname);
8
+ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId, redditPixelId, linkedInPartnerId, uetTagId, hotjarId, facebookAppId, reportWebVitals = true }) {
9
+ const { pathname, searchStr } = useLocation();
10
+ useWebAnalytics(pathname, searchStr);
11
11
  useOutboundClickAnalytics();
12
12
  useReportWebVitals((metric) => {
13
13
  if (!reportWebVitals) return;
@@ -129,6 +129,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, openaiPixelId,
129
129
  b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js";
130
130
  s.parentNode.insertBefore(b, s);
131
131
  })(window.lintrk);
132
+ ` }
133
+ }),
134
+ uetTagId && /* @__PURE__ */ jsx("script", {
135
+ async: true,
136
+ id: "uet-tag",
137
+ dangerouslySetInnerHTML: { __html: `
138
+ (function (w, d, t, u, o) {
139
+ w[u] = w[u] || [], o.ts = (new Date).getTime();
140
+ var n = d.createElement(t);
141
+ n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
142
+ n.async = 1, n.onload = n.onreadystatechange = function() {
143
+ var s = this.readyState;
144
+ s && "loaded" !== s && "complete" !== s ||
145
+ (o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
146
+ n.onload = n.onreadystatechange = null)
147
+ };
148
+ var i = d.getElementsByTagName(t)[0];
149
+ i.parentNode.insertBefore(n, i);
150
+ })(window, document, "script", "uetq", {
151
+ ti: "${uetTagId}",
152
+ enableAutoSpaTracking: true
153
+ });
132
154
  ` }
133
155
  }),
134
156
  hotjarId && /* @__PURE__ */ jsx("script", {
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.mjs","names":[],"sources":["../../src/tanstack/analytics.tsx"],"sourcesContent":["import { useLocation } from '@tanstack/react-router';\nimport { useOutboundClickAnalytics } from '../hooks/use-outbound-click-analytics';\nimport { useReportWebVitals } from '../hooks/use-report-web-vitals';\nimport { useWebAnalytics } from '../hooks/use-web-analytics';\nimport type { PixelId as MetaPixelId } from '../track/fbq';\nimport type { GaId, GtmId } from '../track/gtag';\nimport { track } from '../track/index';\nimport type { PixelId as RedditPixelId } from '../track/rdt';\n\ninterface Props {\n gaId?: GaId;\n gaSrc?: string;\n gtmId?: GtmId;\n metaPixelId?: MetaPixelId;\n openaiPixelId?: string;\n redditPixelId?: RedditPixelId;\n linkedInPartnerId?: `${number}`;\n hotjarId?: `${number}`;\n facebookAppId?: string;\n nonce?: string;\n debugMode?: boolean;\n reportWebVitals?: boolean;\n}\n\nexport function Analytics({\n gaId,\n gaSrc,\n nonce,\n debugMode,\n metaPixelId,\n openaiPixelId,\n redditPixelId,\n linkedInPartnerId,\n hotjarId,\n facebookAppId,\n reportWebVitals = true,\n}: Props) {\n const { pathname } = useLocation();\n useWebAnalytics(pathname);\n useOutboundClickAnalytics();\n\n useReportWebVitals((metric) => {\n if (!reportWebVitals) return;\n const properties = {\n value: metric.delta,\n metric_id: metric.id,\n metric_value: metric.value,\n metric_delta: metric.delta,\n metric_rating: metric.rating,\n metric_navigation_type: metric.navigationType,\n non_interaction: true, // avoids affecting bounce rate.\n };\n track(metric.name, properties);\n });\n\n return (\n <>\n {facebookAppId && <meta property=\"fb:app_id\" content={facebookAppId} />}\n {gaId && (\n <>\n <script\n async\n id=\"gtag\"\n nonce={nonce}\n src={gaSrc ?? `https://www.googletagmanager.com/gtag/js?id=${gaId}`}\n />\n <script\n async\n nonce={nonce}\n id=\"gtag-init\"\n dangerouslySetInnerHTML={{\n __html: `\n window.dataLayer = window.dataLayer || [];\n function gtag(){dataLayer.push(arguments);}\n gtag('js', new Date());\n gtag('config', '${gaId}'${debugMode ? \" ,{ 'debug_mode': true }\" : ''});\n `,\n }}\n />\n </>\n )}\n {metaPixelId && (\n <script\n async\n id=\"meta-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !(function (f, b, e, v, n, t, s) {\n if (f.fbq) return;\n n = f.fbq = function () {\n n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);\n };\n if (!f._fbq) f._fbq = n;\n n.push = n;\n n.loaded = !0;\n n.version = '2.0';\n n.queue = [];\n t = b.createElement(e);\n t.async = !0;\n t.src = v;\n s = b.getElementsByTagName(e)[0];\n s.parentNode.insertBefore(t, s);\n })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');\n fbq('init', '${metaPixelId}');\n fbq('track', 'PageView');`,\n }}\n />\n )}\n {openaiPixelId && (\n <script\n async\n id=\"openai-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, s, u) {\n if (w.oaiq) return;\n var q = function () {\n q.q.push(arguments);\n };\n q.q = [];\n w.oaiq = q;\n var js = d.createElement(s);\n js.async = true;\n js.src = u;\n var f = d.getElementsByTagName(s)[0];\n f.parentNode.insertBefore(js, f);\n })(window, document, \"script\", \"https://bzrcdn.openai.com/sdk/oaiq.min.js\");\n\n oaiq(\"init\", { pixelId: \"${openaiPixelId}\" });\n oaiq(\"measure\", \"page_viewed\", { type: \"contents\" });\n `,\n }}\n />\n )}\n {redditPixelId && (\n <script\n async\n id=\"reddit-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !function(w,d) {\n if(!w.rdt) {\n var p = w.rdt = function() {\n p.sendEvent ? p.sendEvent.apply(p,arguments) : p.callQueue.push(arguments)\n };\n p.callQueue = [];\n var t = d.createElement(\"script\");\n t.src = \"https://www.redditstatic.com/ads/pixel.js\";\n t.async = !0;\n var s = d.getElementsByTagName(\"script\")[0];\n s.parentNode.insertBefore(t,s)\n }\n }(window, document);\n rdt('init', '${redditPixelId}');\n rdt('track', 'PageVisit');`,\n }}\n />\n )}\n {linkedInPartnerId && (\n <script\n async\n id=\"linkedin-insight-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n _linkedin_partner_id = \"${linkedInPartnerId}\";\n window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];\n window._linkedin_data_partner_ids.push(_linkedin_partner_id);\n\n (function(l) {\n if (!l){\n window.lintrk = function(a,b){\n window.lintrk.q.push([a,b])\n };\n window.lintrk.q=[]\n }\n var s = document.getElementsByTagName(\"script\")[0];\n var b = document.createElement(\"script\");\n b.type = \"text/javascript\";b.async = true;\n b.src = \"https://snap.licdn.com/li.lms-analytics/insight.min.js\";\n s.parentNode.insertBefore(b, s);\n })(window.lintrk);\n `,\n }}\n />\n )}\n {hotjarId && (\n <script\n async\n id=\"hotjar\"\n dangerouslySetInnerHTML={{\n __html: `\n (function(h,o,t,j,a,r){\n h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};\n h._hjSettings={hjid:${hotjarId},hjsv:6};\n a=o.getElementsByTagName('head')[0];\n r=o.createElement('script');r.async=1;\n r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;\n a.appendChild(r);\n })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');\n `,\n }}\n />\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;AAwBA,SAAgB,UAAU,EACxB,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,eACA,kBAAkB,QACV;CACR,MAAM,EAAE,aAAa,YAAY;CACjC,gBAAgB,QAAQ;CACxB,0BAA0B;CAE1B,oBAAoB,WAAW;EAC7B,IAAI,CAAC,iBAAiB;EACtB,MAAM,aAAa;GACjB,OAAO,OAAO;GACd,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,cAAc,OAAO;GACrB,eAAe,OAAO;GACtB,wBAAwB,OAAO;GAC/B,iBAAiB;EACnB;EACA,MAAM,OAAO,MAAM,UAAU;CAC/B,CAAC;CAED,OACE,qBAAA,UAAA,EAAA,UAAA;EACG,iBAAiB,oBAAC,QAAD;GAAM,UAAS;GAAY,SAAS;EAAgB,CAAA;EACrE,QACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACI;GACP,KAAK,SAAS,+CAA+C;EAC9D,CAAA,GACD,oBAAC,UAAD;GACE,OAAA;GACO;GACP,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;gCAIU,KAAK,GAAG,YAAY,6BAA6B,GAAG;cAExE;EACD,CAAA,CACD,EAAA,CAAA;EAEH,eACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;;;2BAiBO,YAAY;uCAE7B;EACD,CAAA;EAEF,iBACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;2CAeuB,cAAc;;cAG/C;EACD,CAAA;EAEF,iBACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;2BAcO,cAAc;wCAE/B;EACD,CAAA;EAEF,qBACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;sCACkB,kBAAkB;;;;;;;;;;;;;;;;;cAkB9C;EACD,CAAA;EAEF,YACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;oCAGgB,SAAS;;;;;;cAOnC;EACD,CAAA;CAEH,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"analytics.mjs","names":[],"sources":["../../src/tanstack/analytics.tsx"],"sourcesContent":["import { useLocation } from '@tanstack/react-router';\nimport { useOutboundClickAnalytics } from '../hooks/use-outbound-click-analytics';\nimport { useReportWebVitals } from '../hooks/use-report-web-vitals';\nimport { useWebAnalytics } from '../hooks/use-web-analytics';\nimport type { PixelId as MetaPixelId } from '../track/fbq';\nimport type { GaId, GtmId } from '../track/gtag';\nimport { track } from '../track/index';\nimport type { PixelId as RedditPixelId } from '../track/rdt';\n\ninterface Props {\n gaId?: GaId;\n gaSrc?: string;\n gtmId?: GtmId;\n metaPixelId?: MetaPixelId;\n openaiPixelId?: string;\n redditPixelId?: RedditPixelId;\n linkedInPartnerId?: `${number}`;\n /** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */\n uetTagId?: `${number}`;\n hotjarId?: `${number}`;\n facebookAppId?: string;\n nonce?: string;\n debugMode?: boolean;\n reportWebVitals?: boolean;\n}\n\nexport function Analytics({\n gaId,\n gaSrc,\n nonce,\n debugMode,\n metaPixelId,\n openaiPixelId,\n redditPixelId,\n linkedInPartnerId,\n uetTagId,\n hotjarId,\n facebookAppId,\n reportWebVitals = true,\n}: Props) {\n const { pathname, searchStr } = useLocation();\n useWebAnalytics(pathname, searchStr);\n useOutboundClickAnalytics();\n\n useReportWebVitals((metric) => {\n if (!reportWebVitals) return;\n const properties = {\n value: metric.delta,\n metric_id: metric.id,\n metric_value: metric.value,\n metric_delta: metric.delta,\n metric_rating: metric.rating,\n metric_navigation_type: metric.navigationType,\n non_interaction: true, // avoids affecting bounce rate.\n };\n track(metric.name, properties);\n });\n\n return (\n <>\n {facebookAppId && <meta property=\"fb:app_id\" content={facebookAppId} />}\n {gaId && (\n <>\n <script\n async\n id=\"gtag\"\n nonce={nonce}\n src={gaSrc ?? `https://www.googletagmanager.com/gtag/js?id=${gaId}`}\n />\n <script\n async\n nonce={nonce}\n id=\"gtag-init\"\n dangerouslySetInnerHTML={{\n __html: `\n window.dataLayer = window.dataLayer || [];\n function gtag(){dataLayer.push(arguments);}\n gtag('js', new Date());\n gtag('config', '${gaId}'${debugMode ? \" ,{ 'debug_mode': true }\" : ''});\n `,\n }}\n />\n </>\n )}\n {metaPixelId && (\n <script\n async\n id=\"meta-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !(function (f, b, e, v, n, t, s) {\n if (f.fbq) return;\n n = f.fbq = function () {\n n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);\n };\n if (!f._fbq) f._fbq = n;\n n.push = n;\n n.loaded = !0;\n n.version = '2.0';\n n.queue = [];\n t = b.createElement(e);\n t.async = !0;\n t.src = v;\n s = b.getElementsByTagName(e)[0];\n s.parentNode.insertBefore(t, s);\n })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');\n fbq('init', '${metaPixelId}');\n fbq('track', 'PageView');`,\n }}\n />\n )}\n {openaiPixelId && (\n <script\n async\n id=\"openai-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, s, u) {\n if (w.oaiq) return;\n var q = function () {\n q.q.push(arguments);\n };\n q.q = [];\n w.oaiq = q;\n var js = d.createElement(s);\n js.async = true;\n js.src = u;\n var f = d.getElementsByTagName(s)[0];\n f.parentNode.insertBefore(js, f);\n })(window, document, \"script\", \"https://bzrcdn.openai.com/sdk/oaiq.min.js\");\n\n oaiq(\"init\", { pixelId: \"${openaiPixelId}\" });\n oaiq(\"measure\", \"page_viewed\", { type: \"contents\" });\n `,\n }}\n />\n )}\n {redditPixelId && (\n <script\n async\n id=\"reddit-pixel\"\n dangerouslySetInnerHTML={{\n __html: `\n !function(w,d) {\n if(!w.rdt) {\n var p = w.rdt = function() {\n p.sendEvent ? p.sendEvent.apply(p,arguments) : p.callQueue.push(arguments)\n };\n p.callQueue = [];\n var t = d.createElement(\"script\");\n t.src = \"https://www.redditstatic.com/ads/pixel.js\";\n t.async = !0;\n var s = d.getElementsByTagName(\"script\")[0];\n s.parentNode.insertBefore(t,s)\n }\n }(window, document);\n rdt('init', '${redditPixelId}');\n rdt('track', 'PageVisit');`,\n }}\n />\n )}\n {linkedInPartnerId && (\n <script\n async\n id=\"linkedin-insight-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n _linkedin_partner_id = \"${linkedInPartnerId}\";\n window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];\n window._linkedin_data_partner_ids.push(_linkedin_partner_id);\n\n (function(l) {\n if (!l){\n window.lintrk = function(a,b){\n window.lintrk.q.push([a,b])\n };\n window.lintrk.q=[]\n }\n var s = document.getElementsByTagName(\"script\")[0];\n var b = document.createElement(\"script\");\n b.type = \"text/javascript\";b.async = true;\n b.src = \"https://snap.licdn.com/li.lms-analytics/insight.min.js\";\n s.parentNode.insertBefore(b, s);\n })(window.lintrk);\n `,\n }}\n />\n )}\n {uetTagId && (\n <script\n async\n id=\"uet-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, t, u, o) {\n w[u] = w[u] || [], o.ts = (new Date).getTime();\n var n = d.createElement(t);\n n.src = \"https://bat.bing.net/bat.js?ti=\" + o.ti + (\"uetq\" != u ? \"&q=\" + u : \"\"),\n n.async = 1, n.onload = n.onreadystatechange = function() {\n var s = this.readyState;\n s && \"loaded\" !== s && \"complete\" !== s ||\n (o.q = w[u], w[u] = new UET(o), w[u].push(\"pageLoad\"),\n n.onload = n.onreadystatechange = null)\n };\n var i = d.getElementsByTagName(t)[0];\n i.parentNode.insertBefore(n, i);\n })(window, document, \"script\", \"uetq\", {\n ti: \"${uetTagId}\",\n enableAutoSpaTracking: true\n });\n `,\n }}\n />\n )}\n {hotjarId && (\n <script\n async\n id=\"hotjar\"\n dangerouslySetInnerHTML={{\n __html: `\n (function(h,o,t,j,a,r){\n h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};\n h._hjSettings={hjid:${hotjarId},hjsv:6};\n a=o.getElementsByTagName('head')[0];\n r=o.createElement('script');r.async=1;\n r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;\n a.appendChild(r);\n })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');\n `,\n }}\n />\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;AA0BA,SAAgB,UAAU,EACxB,MACA,OACA,OACA,WACA,aACA,eACA,eACA,mBACA,UACA,UACA,eACA,kBAAkB,QACV;CACR,MAAM,EAAE,UAAU,cAAc,YAAY;CAC5C,gBAAgB,UAAU,SAAS;CACnC,0BAA0B;CAE1B,oBAAoB,WAAW;EAC7B,IAAI,CAAC,iBAAiB;EACtB,MAAM,aAAa;GACjB,OAAO,OAAO;GACd,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,cAAc,OAAO;GACrB,eAAe,OAAO;GACtB,wBAAwB,OAAO;GAC/B,iBAAiB;EACnB;EACA,MAAM,OAAO,MAAM,UAAU;CAC/B,CAAC;CAED,OACE,qBAAA,UAAA,EAAA,UAAA;EACG,iBAAiB,oBAAC,QAAD;GAAM,UAAS;GAAY,SAAS;EAAgB,CAAA;EACrE,QACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACI;GACP,KAAK,SAAS,+CAA+C;EAC9D,CAAA,GACD,oBAAC,UAAD;GACE,OAAA;GACO;GACP,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;gCAIU,KAAK,GAAG,YAAY,6BAA6B,GAAG;cAExE;EACD,CAAA,CACD,EAAA,CAAA;EAEH,eACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;;;2BAiBO,YAAY;uCAE7B;EACD,CAAA;EAEF,iBACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;2CAeuB,cAAc;;cAG/C;EACD,CAAA;EAEF,iBACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;2BAcO,cAAc;wCAE/B;EACD,CAAA;EAEF,qBACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;sCACkB,kBAAkB;;;;;;;;;;;;;;;;;cAkB9C;EACD,CAAA;EAEF,YACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;uBAcG,SAAS;;;cAItB;EACD,CAAA;EAEF,YACC,oBAAC,UAAD;GACE,OAAA;GACA,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;oCAGgB,SAAS;;;;;;cAOnC;EACD,CAAA;CAEH,EAAA,CAAA;AAEN"}
@@ -3,7 +3,8 @@ const require_click_id_index = require("../click-id/index.cjs");
3
3
  let _tanstack_react_start = require("@tanstack/react-start");
4
4
  //#region src/tanstack/middleware.ts
5
5
  /**
6
- * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_rdt_cid`) on the
6
+ * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_gcl_aw`/`_gcl_gb`,
7
+ * `_rdt_cid`, `_uetmsclkid`) on the
7
8
  * document response.
8
9
  *
9
10
  * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.cjs","names":["createMiddleware","resolveClickIdCookies","toSetCookieHeaders"],"sources":["../../src/tanstack/middleware.ts"],"sourcesContent":["import { createMiddleware } from '@tanstack/react-start';\nimport { resolveClickIdCookies, toSetCookieHeaders } from '../click-id/index';\n\nexport interface ClickIdMiddlewareOptions {\n /** `Domain` attribute for the cookies, e.g. `.shware.io`. Omit for a host-only cookie. */\n domain?: string;\n /** `Secure` attribute, default true. Set false only for local http testing. */\n secure?: boolean;\n /** subdomainIndex for a freshly built `_fbc` (com=0, example.com=1, www.example.com=2). Default 1. */\n subdomainIndex?: number;\n /**\n * Re-issue a still-valid `_fbc` on every request as an ITP self-heal (restores the long-lived\n * HTTP cookie if the Meta Pixel's `document.cookie` write re-capped it to 24h in Safari). On by\n * default. Note it attaches a per-user `Set-Cookie` — and thus `no-store` — to every page\n * response carrying an `_fbc`, defeating CDN caching of those pages; set false to strictly follow\n * Meta's conditional-write rule and keep them cacheable. See {@link resolveClickIdCookies}.\n */\n refresh?: boolean;\n /**\n * Override the `Cache-Control` of a response we attach cookies to (default `private, no-store`).\n * A per-user `Set-Cookie` must never end up on a shared-cache entry, or one visitor's `_fbc` would\n * be served to everyone. Only set this false if you guarantee these responses are never cached.\n */\n cacheControl?: string | false;\n /**\n * Consent gate. Return false to skip setting cookies for this request (e.g. before the visitor has\n * granted consent where required). Runs per request with the incoming `Request`.\n */\n shouldPersist?: (request: Request) => boolean;\n}\n\n/**\n * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_rdt_cid`) on the\n * document response.\n *\n * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS\n * runs — is what Meta officially recommends and the only reliable way to keep the cookie alive for\n * 90 days in Safari: ITP caps JavaScript-set cookies on a fbclid-decorated landing page to 24\n * hours, and a document response is never classified as CNAME/IP cloaking (it is the reference the\n * browser measures cloaking against).\n *\n * Register it as a global request middleware:\n * ```ts\n * // start.ts\n * import { createStart } from '@tanstack/react-start'\n * import { clickIdMiddleware } from '@shware/analytics/tanstack'\n * export const startInstance = createStart(() => ({ requestMiddleware: [clickIdMiddleware] }))\n * ```\n *\n * reference: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc\n */\nexport function createClickIdMiddleware(options: ClickIdMiddlewareOptions = {}) {\n const { cacheControl = 'private, no-store' } = options;\n\n return createMiddleware({ type: 'request' }).server(async ({ request, next, handlerType }) => {\n const result = await next();\n\n // Skip serverFn RPC responses. 'router' covers SSR document requests *and* custom server\n // routes (API endpoints) — those also get cookies when the URL carries a click id.\n if (handlerType !== 'router') return result;\n if (options.shouldPersist && !options.shouldPersist(request)) return result;\n\n const { cookies } = resolveClickIdCookies({\n url: request.url,\n cookieHeader: request.headers.get('cookie'),\n domain: options.domain,\n secure: options.secure,\n subdomainIndex: options.subdomainIndex,\n refresh: options.refresh ?? true,\n });\n\n if (cookies.length > 0) {\n for (const header of toSetCookieHeaders(cookies)) {\n result.response.headers.append('set-cookie', header);\n }\n if (cacheControl !== false) {\n result.response.headers.set('cache-control', cacheControl);\n }\n }\n\n return result;\n });\n}\n\n/** Ready-to-register middleware with default options. */\nexport const clickIdMiddleware = createClickIdMiddleware();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,wBAAwB,UAAoC,CAAC,GAAG;CAC9E,MAAM,EAAE,eAAe,wBAAwB;CAE/C,QAAA,GAAOA,sBAAAA,iBAAAA,CAAiB,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,SAAS,MAAM,kBAAkB;EAC5F,MAAM,SAAS,MAAM,KAAK;EAI1B,IAAI,gBAAgB,UAAU,OAAO;EACrC,IAAI,QAAQ,iBAAiB,CAAC,QAAQ,cAAc,OAAO,GAAG,OAAO;EAErE,MAAM,EAAE,YAAYC,uBAAAA,sBAAsB;GACxC,KAAK,QAAQ;GACb,cAAc,QAAQ,QAAQ,IAAI,QAAQ;GAC1C,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;GACxB,SAAS,QAAQ,WAAW;EAC9B,CAAC;EAED,IAAI,QAAQ,SAAS,GAAG;GACtB,KAAK,MAAM,UAAUC,uBAAAA,mBAAmB,OAAO,GAC7C,OAAO,SAAS,QAAQ,OAAO,cAAc,MAAM;GAErD,IAAI,iBAAiB,OACnB,OAAO,SAAS,QAAQ,IAAI,iBAAiB,YAAY;EAE7D;EAEA,OAAO;CACT,CAAC;AACH;;AAGA,MAAa,oBAAoB,wBAAwB"}
1
+ {"version":3,"file":"middleware.cjs","names":["createMiddleware","resolveClickIdCookies","toSetCookieHeaders"],"sources":["../../src/tanstack/middleware.ts"],"sourcesContent":["import { createMiddleware } from '@tanstack/react-start';\nimport { resolveClickIdCookies, toSetCookieHeaders } from '../click-id/index';\n\nexport interface ClickIdMiddlewareOptions {\n /** `Domain` attribute for the cookies, e.g. `.shware.io`. Omit for a host-only cookie. */\n domain?: string;\n /** `Secure` attribute, default true. Set false only for local http testing. */\n secure?: boolean;\n /** subdomainIndex for a freshly built `_fbc` (com=0, example.com=1, www.example.com=2). Default 1. */\n subdomainIndex?: number;\n /**\n * Re-issue a still-valid `_fbc` on every request as an ITP self-heal (restores the long-lived\n * HTTP cookie if the Meta Pixel's `document.cookie` write re-capped it to 24h in Safari). On by\n * default. Note it attaches a per-user `Set-Cookie` — and thus `no-store` — to every page\n * response carrying an `_fbc`, defeating CDN caching of those pages; set false to strictly follow\n * Meta's conditional-write rule and keep them cacheable. See {@link resolveClickIdCookies}.\n */\n refresh?: boolean;\n /**\n * Override the `Cache-Control` of a response we attach cookies to (default `private, no-store`).\n * A per-user `Set-Cookie` must never end up on a shared-cache entry, or one visitor's `_fbc` would\n * be served to everyone. Only set this false if you guarantee these responses are never cached.\n */\n cacheControl?: string | false;\n /**\n * Consent gate. Return false to skip setting cookies for this request (e.g. before the visitor has\n * granted consent where required). Runs per request with the incoming `Request`.\n */\n shouldPersist?: (request: Request) => boolean;\n}\n\n/**\n * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_gcl_aw`/`_gcl_gb`,\n * `_rdt_cid`, `_uetmsclkid`) on the\n * document response.\n *\n * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS\n * runs — is what Meta officially recommends and the only reliable way to keep the cookie alive for\n * 90 days in Safari: ITP caps JavaScript-set cookies on a fbclid-decorated landing page to 24\n * hours, and a document response is never classified as CNAME/IP cloaking (it is the reference the\n * browser measures cloaking against).\n *\n * Register it as a global request middleware:\n * ```ts\n * // start.ts\n * import { createStart } from '@tanstack/react-start'\n * import { clickIdMiddleware } from '@shware/analytics/tanstack'\n * export const startInstance = createStart(() => ({ requestMiddleware: [clickIdMiddleware] }))\n * ```\n *\n * reference: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc\n */\nexport function createClickIdMiddleware(options: ClickIdMiddlewareOptions = {}) {\n const { cacheControl = 'private, no-store' } = options;\n\n return createMiddleware({ type: 'request' }).server(async ({ request, next, handlerType }) => {\n const result = await next();\n\n // Skip serverFn RPC responses. 'router' covers SSR document requests *and* custom server\n // routes (API endpoints) — those also get cookies when the URL carries a click id.\n if (handlerType !== 'router') return result;\n if (options.shouldPersist && !options.shouldPersist(request)) return result;\n\n const { cookies } = resolveClickIdCookies({\n url: request.url,\n cookieHeader: request.headers.get('cookie'),\n domain: options.domain,\n secure: options.secure,\n subdomainIndex: options.subdomainIndex,\n refresh: options.refresh ?? true,\n });\n\n if (cookies.length > 0) {\n for (const header of toSetCookieHeaders(cookies)) {\n result.response.headers.append('set-cookie', header);\n }\n if (cacheControl !== false) {\n result.response.headers.set('cache-control', cacheControl);\n }\n }\n\n return result;\n });\n}\n\n/** Ready-to-register middleware with default options. */\nexport const clickIdMiddleware = createClickIdMiddleware();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,wBAAwB,UAAoC,CAAC,GAAG;CAC9E,MAAM,EAAE,eAAe,wBAAwB;CAE/C,QAAA,GAAOA,sBAAAA,iBAAAA,CAAiB,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,SAAS,MAAM,kBAAkB;EAC5F,MAAM,SAAS,MAAM,KAAK;EAI1B,IAAI,gBAAgB,UAAU,OAAO;EACrC,IAAI,QAAQ,iBAAiB,CAAC,QAAQ,cAAc,OAAO,GAAG,OAAO;EAErE,MAAM,EAAE,YAAYC,uBAAAA,sBAAsB;GACxC,KAAK,QAAQ;GACb,cAAc,QAAQ,QAAQ,IAAI,QAAQ;GAC1C,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;GACxB,SAAS,QAAQ,WAAW;EAC9B,CAAC;EAED,IAAI,QAAQ,SAAS,GAAG;GACtB,KAAK,MAAM,UAAUC,uBAAAA,mBAAmB,OAAO,GAC7C,OAAO,SAAS,QAAQ,OAAO,cAAc,MAAM;GAErD,IAAI,iBAAiB,OACnB,OAAO,SAAS,QAAQ,IAAI,iBAAiB,YAAY;EAE7D;EAEA,OAAO;CACT,CAAC;AACH;;AAGA,MAAa,oBAAoB,wBAAwB"}
@@ -27,7 +27,8 @@ interface ClickIdMiddlewareOptions {
27
27
  shouldPersist?: (request: Request) => boolean;
28
28
  }
29
29
  /**
30
- * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_rdt_cid`) on the
30
+ * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_gcl_aw`/`_gcl_gb`,
31
+ * `_rdt_cid`, `_uetmsclkid`) on the
31
32
  * document response.
32
33
  *
33
34
  * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.cts","names":[],"sources":["../../src/tanstack/middleware.ts"],"mappings":";UAGiB;;EAEf;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA;;;;;EAKA,iBAAiB,SAAS;;;;;;;;;;;;;;;;;;;;;;iBAuBZ,wBAAwB,UAAS,2DAA6B;;cAkCjE,mDAAiB"}
1
+ {"version":3,"file":"middleware.d.cts","names":[],"sources":["../../src/tanstack/middleware.ts"],"mappings":";UAGiB;;EAEf;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA;;;;;EAKA,iBAAiB,SAAS;;;;;;;;;;;;;;;;;;;;;;;iBAwBZ,wBAAwB,UAAS,2DAA6B;;cAkCjE,mDAAiB"}
@@ -27,7 +27,8 @@ interface ClickIdMiddlewareOptions {
27
27
  shouldPersist?: (request: Request) => boolean;
28
28
  }
29
29
  /**
30
- * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_rdt_cid`) on the
30
+ * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_gcl_aw`/`_gcl_gb`,
31
+ * `_rdt_cid`, `_uetmsclkid`) on the
31
32
  * document response.
32
33
  *
33
34
  * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.mts","names":[],"sources":["../../src/tanstack/middleware.ts"],"mappings":";UAGiB;;EAEf;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA;;;;;EAKA,iBAAiB,SAAS;;;;;;;;;;;;;;;;;;;;;;iBAuBZ,wBAAwB,UAAS,2DAA6B;;cAkCjE,mDAAiB"}
1
+ {"version":3,"file":"middleware.d.mts","names":[],"sources":["../../src/tanstack/middleware.ts"],"mappings":";UAGiB;;EAEf;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA;;;;;EAKA,iBAAiB,SAAS;;;;;;;;;;;;;;;;;;;;;;;iBAwBZ,wBAAwB,UAAS,2DAA6B;;cAkCjE,mDAAiB"}
@@ -2,7 +2,8 @@ import { resolveClickIdCookies, toSetCookieHeaders } from "../click-id/index.mjs
2
2
  import { createMiddleware } from "@tanstack/react-start";
3
3
  //#region src/tanstack/middleware.ts
4
4
  /**
5
- * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_rdt_cid`) on the
5
+ * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_gcl_aw`/`_gcl_gb`,
6
+ * `_rdt_cid`, `_uetmsclkid`) on the
6
7
  * document response.
7
8
  *
8
9
  * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.mjs","names":[],"sources":["../../src/tanstack/middleware.ts"],"sourcesContent":["import { createMiddleware } from '@tanstack/react-start';\nimport { resolveClickIdCookies, toSetCookieHeaders } from '../click-id/index';\n\nexport interface ClickIdMiddlewareOptions {\n /** `Domain` attribute for the cookies, e.g. `.shware.io`. Omit for a host-only cookie. */\n domain?: string;\n /** `Secure` attribute, default true. Set false only for local http testing. */\n secure?: boolean;\n /** subdomainIndex for a freshly built `_fbc` (com=0, example.com=1, www.example.com=2). Default 1. */\n subdomainIndex?: number;\n /**\n * Re-issue a still-valid `_fbc` on every request as an ITP self-heal (restores the long-lived\n * HTTP cookie if the Meta Pixel's `document.cookie` write re-capped it to 24h in Safari). On by\n * default. Note it attaches a per-user `Set-Cookie` — and thus `no-store` — to every page\n * response carrying an `_fbc`, defeating CDN caching of those pages; set false to strictly follow\n * Meta's conditional-write rule and keep them cacheable. See {@link resolveClickIdCookies}.\n */\n refresh?: boolean;\n /**\n * Override the `Cache-Control` of a response we attach cookies to (default `private, no-store`).\n * A per-user `Set-Cookie` must never end up on a shared-cache entry, or one visitor's `_fbc` would\n * be served to everyone. Only set this false if you guarantee these responses are never cached.\n */\n cacheControl?: string | false;\n /**\n * Consent gate. Return false to skip setting cookies for this request (e.g. before the visitor has\n * granted consent where required). Runs per request with the incoming `Request`.\n */\n shouldPersist?: (request: Request) => boolean;\n}\n\n/**\n * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_rdt_cid`) on the\n * document response.\n *\n * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS\n * runs — is what Meta officially recommends and the only reliable way to keep the cookie alive for\n * 90 days in Safari: ITP caps JavaScript-set cookies on a fbclid-decorated landing page to 24\n * hours, and a document response is never classified as CNAME/IP cloaking (it is the reference the\n * browser measures cloaking against).\n *\n * Register it as a global request middleware:\n * ```ts\n * // start.ts\n * import { createStart } from '@tanstack/react-start'\n * import { clickIdMiddleware } from '@shware/analytics/tanstack'\n * export const startInstance = createStart(() => ({ requestMiddleware: [clickIdMiddleware] }))\n * ```\n *\n * reference: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc\n */\nexport function createClickIdMiddleware(options: ClickIdMiddlewareOptions = {}) {\n const { cacheControl = 'private, no-store' } = options;\n\n return createMiddleware({ type: 'request' }).server(async ({ request, next, handlerType }) => {\n const result = await next();\n\n // Skip serverFn RPC responses. 'router' covers SSR document requests *and* custom server\n // routes (API endpoints) — those also get cookies when the URL carries a click id.\n if (handlerType !== 'router') return result;\n if (options.shouldPersist && !options.shouldPersist(request)) return result;\n\n const { cookies } = resolveClickIdCookies({\n url: request.url,\n cookieHeader: request.headers.get('cookie'),\n domain: options.domain,\n secure: options.secure,\n subdomainIndex: options.subdomainIndex,\n refresh: options.refresh ?? true,\n });\n\n if (cookies.length > 0) {\n for (const header of toSetCookieHeaders(cookies)) {\n result.response.headers.append('set-cookie', header);\n }\n if (cacheControl !== false) {\n result.response.headers.set('cache-control', cacheControl);\n }\n }\n\n return result;\n });\n}\n\n/** Ready-to-register middleware with default options. */\nexport const clickIdMiddleware = createClickIdMiddleware();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,wBAAwB,UAAoC,CAAC,GAAG;CAC9E,MAAM,EAAE,eAAe,wBAAwB;CAE/C,OAAO,iBAAiB,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,SAAS,MAAM,kBAAkB;EAC5F,MAAM,SAAS,MAAM,KAAK;EAI1B,IAAI,gBAAgB,UAAU,OAAO;EACrC,IAAI,QAAQ,iBAAiB,CAAC,QAAQ,cAAc,OAAO,GAAG,OAAO;EAErE,MAAM,EAAE,YAAY,sBAAsB;GACxC,KAAK,QAAQ;GACb,cAAc,QAAQ,QAAQ,IAAI,QAAQ;GAC1C,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;GACxB,SAAS,QAAQ,WAAW;EAC9B,CAAC;EAED,IAAI,QAAQ,SAAS,GAAG;GACtB,KAAK,MAAM,UAAU,mBAAmB,OAAO,GAC7C,OAAO,SAAS,QAAQ,OAAO,cAAc,MAAM;GAErD,IAAI,iBAAiB,OACnB,OAAO,SAAS,QAAQ,IAAI,iBAAiB,YAAY;EAE7D;EAEA,OAAO;CACT,CAAC;AACH;;AAGA,MAAa,oBAAoB,wBAAwB"}
1
+ {"version":3,"file":"middleware.mjs","names":[],"sources":["../../src/tanstack/middleware.ts"],"sourcesContent":["import { createMiddleware } from '@tanstack/react-start';\nimport { resolveClickIdCookies, toSetCookieHeaders } from '../click-id/index';\n\nexport interface ClickIdMiddlewareOptions {\n /** `Domain` attribute for the cookies, e.g. `.shware.io`. Omit for a host-only cookie. */\n domain?: string;\n /** `Secure` attribute, default true. Set false only for local http testing. */\n secure?: boolean;\n /** subdomainIndex for a freshly built `_fbc` (com=0, example.com=1, www.example.com=2). Default 1. */\n subdomainIndex?: number;\n /**\n * Re-issue a still-valid `_fbc` on every request as an ITP self-heal (restores the long-lived\n * HTTP cookie if the Meta Pixel's `document.cookie` write re-capped it to 24h in Safari). On by\n * default. Note it attaches a per-user `Set-Cookie` — and thus `no-store` — to every page\n * response carrying an `_fbc`, defeating CDN caching of those pages; set false to strictly follow\n * Meta's conditional-write rule and keep them cacheable. See {@link resolveClickIdCookies}.\n */\n refresh?: boolean;\n /**\n * Override the `Cache-Control` of a response we attach cookies to (default `private, no-store`).\n * A per-user `Set-Cookie` must never end up on a shared-cache entry, or one visitor's `_fbc` would\n * be served to everyone. Only set this false if you guarantee these responses are never cached.\n */\n cacheControl?: string | false;\n /**\n * Consent gate. Return false to skip setting cookies for this request (e.g. before the visitor has\n * granted consent where required). Runs per request with the incoming `Request`.\n */\n shouldPersist?: (request: Request) => boolean;\n}\n\n/**\n * TanStack Start request middleware that persists ad click-id cookies (`_fbc`, `_gcl_aw`/`_gcl_gb`,\n * `_rdt_cid`, `_uetmsclkid`) on the\n * document response.\n *\n * Setting `_fbc` here — on the top document via an HTTP `Set-Cookie` header, before any client JS\n * runs — is what Meta officially recommends and the only reliable way to keep the cookie alive for\n * 90 days in Safari: ITP caps JavaScript-set cookies on a fbclid-decorated landing page to 24\n * hours, and a document response is never classified as CNAME/IP cloaking (it is the reference the\n * browser measures cloaking against).\n *\n * Register it as a global request middleware:\n * ```ts\n * // start.ts\n * import { createStart } from '@tanstack/react-start'\n * import { clickIdMiddleware } from '@shware/analytics/tanstack'\n * export const startInstance = createStart(() => ({ requestMiddleware: [clickIdMiddleware] }))\n * ```\n *\n * reference: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc\n */\nexport function createClickIdMiddleware(options: ClickIdMiddlewareOptions = {}) {\n const { cacheControl = 'private, no-store' } = options;\n\n return createMiddleware({ type: 'request' }).server(async ({ request, next, handlerType }) => {\n const result = await next();\n\n // Skip serverFn RPC responses. 'router' covers SSR document requests *and* custom server\n // routes (API endpoints) — those also get cookies when the URL carries a click id.\n if (handlerType !== 'router') return result;\n if (options.shouldPersist && !options.shouldPersist(request)) return result;\n\n const { cookies } = resolveClickIdCookies({\n url: request.url,\n cookieHeader: request.headers.get('cookie'),\n domain: options.domain,\n secure: options.secure,\n subdomainIndex: options.subdomainIndex,\n refresh: options.refresh ?? true,\n });\n\n if (cookies.length > 0) {\n for (const header of toSetCookieHeaders(cookies)) {\n result.response.headers.append('set-cookie', header);\n }\n if (cacheControl !== false) {\n result.response.headers.set('cache-control', cacheControl);\n }\n }\n\n return result;\n });\n}\n\n/** Ready-to-register middleware with default options. */\nexport const clickIdMiddleware = createClickIdMiddleware();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,wBAAwB,UAAoC,CAAC,GAAG;CAC9E,MAAM,EAAE,eAAe,wBAAwB;CAE/C,OAAO,iBAAiB,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,SAAS,MAAM,kBAAkB;EAC5F,MAAM,SAAS,MAAM,KAAK;EAI1B,IAAI,gBAAgB,UAAU,OAAO;EACrC,IAAI,QAAQ,iBAAiB,CAAC,QAAQ,cAAc,OAAO,GAAG,OAAO;EAErE,MAAM,EAAE,YAAY,sBAAsB;GACxC,KAAK,QAAQ;GACb,cAAc,QAAQ,QAAQ,IAAI,QAAQ;GAC1C,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;GACxB,SAAS,QAAQ,WAAW;EAC9B,CAAC;EAED,IAAI,QAAQ,SAAS,GAAG;GACtB,KAAK,MAAM,UAAU,mBAAmB,OAAO,GAC7C,OAAO,SAAS,QAAQ,OAAO,cAAc,MAAM;GAErD,IAAI,iBAAiB,OACnB,OAAO,SAAS,QAAQ,IAAI,iBAAiB,YAAY;EAE7D;EAEA,OAAO;CACT,CAAC;AACH;;AAGA,MAAa,oBAAoB,wBAAwB"}
@@ -4,13 +4,18 @@ const require_third_parties_meta_pixel = require("./meta-pixel.cjs");
4
4
  const require_third_parties_linkedin_insight_tag = require("./linkedin-insight-tag.cjs");
5
5
  const require_third_parties_openai_pixel = require("./openai-pixel.cjs");
6
6
  const require_third_parties_reddit_pixel = require("./reddit-pixel.cjs");
7
+ const require_third_parties_microsoft_uet = require("./microsoft-uet.cjs");
7
8
  exports.sendFBEvent = require_third_parties_meta_pixel.sendFBEvent;
8
9
  exports.sendGAEvent = require_third_parties_google_analytics.sendGAEvent;
9
10
  exports.sendLinkedinEvent = require_third_parties_linkedin_insight_tag.sendLinkedinEvent;
10
11
  exports.sendOpenAIEvent = require_third_parties_openai_pixel.sendOpenAIEvent;
11
12
  exports.sendRedditEvent = require_third_parties_reddit_pixel.sendRedditEvent;
13
+ exports.sendUETEvent = require_third_parties_microsoft_uet.sendUETEvent;
14
+ exports.sendUETIdSync = require_third_parties_microsoft_uet.sendUETIdSync;
12
15
  exports.setFBUser = require_third_parties_meta_pixel.setFBUser;
13
16
  exports.setGAUser = require_third_parties_google_analytics.setGAUser;
14
17
  exports.setLinkedinUser = require_third_parties_linkedin_insight_tag.setLinkedinUser;
15
18
  exports.setOpenAIUser = require_third_parties_openai_pixel.setOpenAIUser;
16
19
  exports.setRedditUser = require_third_parties_reddit_pixel.setRedditUser;
20
+ exports.setUETConsent = require_third_parties_microsoft_uet.setUETConsent;
21
+ exports.setUETUser = require_third_parties_microsoft_uet.setUETUser;
@@ -3,4 +3,6 @@ import { sendFBEvent, setFBUser } from "./meta-pixel.cjs";
3
3
  import { LinkedinConversionConfig, sendLinkedinEvent, setLinkedinUser } from "./linkedin-insight-tag.cjs";
4
4
  import { sendOpenAIEvent, setOpenAIUser } from "./openai-pixel.cjs";
5
5
  import { sendRedditEvent, setRedditUser } from "./reddit-pixel.cjs";
6
- export { type LinkedinConversionConfig, sendFBEvent, sendGAEvent, sendLinkedinEvent, sendOpenAIEvent, sendRedditEvent, setFBUser, setGAUser, setLinkedinUser, setOpenAIUser, setRedditUser };
6
+ import { UETConsent, UETEventParams, UETItem, UETPageType, UETPid } from "../track/uetq.cjs";
7
+ import { sendUETEvent, sendUETIdSync, setUETConsent, setUETUser } from "./microsoft-uet.cjs";
8
+ export { type LinkedinConversionConfig, type UETConsent, type UETEventParams, type UETItem, type UETPageType, type UETPid, sendFBEvent, sendGAEvent, sendLinkedinEvent, sendOpenAIEvent, sendRedditEvent, sendUETEvent, sendUETIdSync, setFBUser, setGAUser, setLinkedinUser, setOpenAIUser, setRedditUser, setUETConsent, setUETUser };
@@ -3,4 +3,6 @@ import { sendFBEvent, setFBUser } from "./meta-pixel.mjs";
3
3
  import { LinkedinConversionConfig, sendLinkedinEvent, setLinkedinUser } from "./linkedin-insight-tag.mjs";
4
4
  import { sendOpenAIEvent, setOpenAIUser } from "./openai-pixel.mjs";
5
5
  import { sendRedditEvent, setRedditUser } from "./reddit-pixel.mjs";
6
- export { type LinkedinConversionConfig, sendFBEvent, sendGAEvent, sendLinkedinEvent, sendOpenAIEvent, sendRedditEvent, setFBUser, setGAUser, setLinkedinUser, setOpenAIUser, setRedditUser };
6
+ import { UETConsent, UETEventParams, UETItem, UETPageType, UETPid } from "../track/uetq.mjs";
7
+ import { sendUETEvent, sendUETIdSync, setUETConsent, setUETUser } from "./microsoft-uet.mjs";
8
+ export { type LinkedinConversionConfig, type UETConsent, type UETEventParams, type UETItem, type UETPageType, type UETPid, sendFBEvent, sendGAEvent, sendLinkedinEvent, sendOpenAIEvent, sendRedditEvent, sendUETEvent, sendUETIdSync, setFBUser, setGAUser, setLinkedinUser, setOpenAIUser, setRedditUser, setUETConsent, setUETUser };
@@ -3,4 +3,5 @@ import { sendFBEvent, setFBUser } from "./meta-pixel.mjs";
3
3
  import { sendLinkedinEvent, setLinkedinUser } from "./linkedin-insight-tag.mjs";
4
4
  import { sendOpenAIEvent, setOpenAIUser } from "./openai-pixel.mjs";
5
5
  import { sendRedditEvent, setRedditUser } from "./reddit-pixel.mjs";
6
- export { sendFBEvent, sendGAEvent, sendLinkedinEvent, sendOpenAIEvent, sendRedditEvent, setFBUser, setGAUser, setLinkedinUser, setOpenAIUser, setRedditUser };
6
+ import { sendUETEvent, sendUETIdSync, setUETConsent, setUETUser } from "./microsoft-uet.mjs";
7
+ export { sendFBEvent, sendGAEvent, sendLinkedinEvent, sendOpenAIEvent, sendRedditEvent, sendUETEvent, sendUETIdSync, setFBUser, setGAUser, setLinkedinUser, setOpenAIUser, setRedditUser, setUETConsent, setUETUser };