consentlayer 0.0.0-stage → 1.0.0-rc.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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -2
  3. package/dist/adapters/google-consent-mode.d.ts +20 -0
  4. package/dist/adapters/google-consent-mode.js +18 -0
  5. package/dist/adapters/google-consent-mode.js.map +1 -0
  6. package/dist/adapters/shopify.d.ts +49 -0
  7. package/dist/adapters/shopify.js +85 -0
  8. package/dist/adapters/shopify.js.map +1 -0
  9. package/dist/adapters/tcf.d.ts +20 -0
  10. package/dist/adapters/tcf.js +64 -0
  11. package/dist/adapters/tcf.js.map +1 -0
  12. package/dist/analytics-provider-07mxUgIK.d.ts +36 -0
  13. package/dist/angular.d.ts +56 -0
  14. package/dist/angular.js +120 -0
  15. package/dist/angular.js.map +1 -0
  16. package/dist/auto-tracking.d.ts +11 -0
  17. package/dist/auto-tracking.js +72 -0
  18. package/dist/auto-tracking.js.map +1 -0
  19. package/dist/chunk-7XLGT6U3.js +34 -0
  20. package/dist/chunk-7XLGT6U3.js.map +1 -0
  21. package/dist/chunk-BWPWDN4G.js +61 -0
  22. package/dist/chunk-BWPWDN4G.js.map +1 -0
  23. package/dist/chunk-DK45XPEY.js +77 -0
  24. package/dist/chunk-DK45XPEY.js.map +1 -0
  25. package/dist/chunk-ETU64ZV3.js +98 -0
  26. package/dist/chunk-ETU64ZV3.js.map +1 -0
  27. package/dist/chunk-FDJMIJFV.js +367 -0
  28. package/dist/chunk-FDJMIJFV.js.map +1 -0
  29. package/dist/chunk-HJGBVDPM.js +47 -0
  30. package/dist/chunk-HJGBVDPM.js.map +1 -0
  31. package/dist/chunk-J3KUPF4R.js +69 -0
  32. package/dist/chunk-J3KUPF4R.js.map +1 -0
  33. package/dist/chunk-LBEIEAJX.js +55 -0
  34. package/dist/chunk-LBEIEAJX.js.map +1 -0
  35. package/dist/chunk-LEAQJXJU.js +582 -0
  36. package/dist/chunk-LEAQJXJU.js.map +1 -0
  37. package/dist/chunk-R6I7OYQB.js +69 -0
  38. package/dist/chunk-R6I7OYQB.js.map +1 -0
  39. package/dist/chunk-RK2M57GW.js +54 -0
  40. package/dist/chunk-RK2M57GW.js.map +1 -0
  41. package/dist/chunk-RZ4TYRNY.js +128 -0
  42. package/dist/chunk-RZ4TYRNY.js.map +1 -0
  43. package/dist/chunk-TVSZJLRC.js +112 -0
  44. package/dist/chunk-TVSZJLRC.js.map +1 -0
  45. package/dist/chunk-TYOQYV45.js +79 -0
  46. package/dist/chunk-TYOQYV45.js.map +1 -0
  47. package/dist/chunk-UAIZU5WD.js +52 -0
  48. package/dist/chunk-UAIZU5WD.js.map +1 -0
  49. package/dist/chunk-VIIGBALD.js +14 -0
  50. package/dist/chunk-VIIGBALD.js.map +1 -0
  51. package/dist/consent-controller-DyHPNfyT.d.ts +47 -0
  52. package/dist/consentlayer-banner.global.js +20 -0
  53. package/dist/consentlayer.global.js +9 -0
  54. package/dist/create-analytics-Bzvm1UpE.d.ts +44 -0
  55. package/dist/ecommerce.d.ts +41 -0
  56. package/dist/ecommerce.js +43 -0
  57. package/dist/ecommerce.js.map +1 -0
  58. package/dist/index.d.ts +58 -0
  59. package/dist/index.js +42 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/next.d.ts +16 -0
  62. package/dist/next.js +27 -0
  63. package/dist/next.js.map +1 -0
  64. package/dist/nuxt/config.d.ts +7 -0
  65. package/dist/nuxt/config.js +6 -0
  66. package/dist/nuxt/config.js.map +1 -0
  67. package/dist/nuxt/index.d.ts +11 -0
  68. package/dist/nuxt/index.js +35 -0
  69. package/dist/nuxt/index.js.map +1 -0
  70. package/dist/nuxt/plugin.d.ts +5 -0
  71. package/dist/nuxt/plugin.js +24 -0
  72. package/dist/nuxt/plugin.js.map +1 -0
  73. package/dist/providers/contentsquare.d.ts +27 -0
  74. package/dist/providers/contentsquare.js +58 -0
  75. package/dist/providers/contentsquare.js.map +1 -0
  76. package/dist/providers/ga4.d.ts +46 -0
  77. package/dist/providers/ga4.js +3 -0
  78. package/dist/providers/ga4.js.map +1 -0
  79. package/dist/providers/gtm.d.ts +28 -0
  80. package/dist/providers/gtm.js +83 -0
  81. package/dist/providers/gtm.js.map +1 -0
  82. package/dist/providers/hotjar.d.ts +38 -0
  83. package/dist/providers/hotjar.js +68 -0
  84. package/dist/providers/hotjar.js.map +1 -0
  85. package/dist/providers/meta.d.ts +42 -0
  86. package/dist/providers/meta.js +65 -0
  87. package/dist/providers/meta.js.map +1 -0
  88. package/dist/providers/posthog.d.ts +42 -0
  89. package/dist/providers/posthog.js +91 -0
  90. package/dist/providers/posthog.js.map +1 -0
  91. package/dist/providers/sentry.d.ts +38 -0
  92. package/dist/providers/sentry.js +82 -0
  93. package/dist/providers/sentry.js.map +1 -0
  94. package/dist/providers/tiktok.d.ts +46 -0
  95. package/dist/providers/tiktok.js +72 -0
  96. package/dist/providers/tiktok.js.map +1 -0
  97. package/dist/providers/vercel.d.ts +14 -0
  98. package/dist/providers/vercel.js +17 -0
  99. package/dist/providers/vercel.js.map +1 -0
  100. package/dist/react/index.d.ts +78 -0
  101. package/dist/react/index.js +79 -0
  102. package/dist/react/index.js.map +1 -0
  103. package/dist/sentry/next.d.ts +1 -0
  104. package/dist/sentry/next.js +3 -0
  105. package/dist/sentry/next.js.map +1 -0
  106. package/dist/sentry/server.d.ts +24 -0
  107. package/dist/sentry/server.js +28 -0
  108. package/dist/sentry/server.js.map +1 -0
  109. package/dist/sentry-replay-HSFVEFCM.js +3 -0
  110. package/dist/sentry-replay-HSFVEFCM.js.map +1 -0
  111. package/dist/sentry-sdk-YQOGKGWD.js +3 -0
  112. package/dist/sentry-sdk-YQOGKGWD.js.map +1 -0
  113. package/dist/sentry-tracing-KWT7ITHF.js +3 -0
  114. package/dist/sentry-tracing-KWT7ITHF.js.map +1 -0
  115. package/dist/server/ga4.d.ts +15 -0
  116. package/dist/server/ga4.js +29 -0
  117. package/dist/server/ga4.js.map +1 -0
  118. package/dist/server/index.d.ts +116 -0
  119. package/dist/server/index.js +206 -0
  120. package/dist/server/index.js.map +1 -0
  121. package/dist/server/meta.d.ts +14 -0
  122. package/dist/server/meta.js +63 -0
  123. package/dist/server/meta.js.map +1 -0
  124. package/dist/server/sgtm.d.ts +18 -0
  125. package/dist/server/sgtm.js +34 -0
  126. package/dist/server/sgtm.js.map +1 -0
  127. package/dist/server/tiktok.d.ts +15 -0
  128. package/dist/server/tiktok.js +64 -0
  129. package/dist/server/tiktok.js.map +1 -0
  130. package/dist/solid.d.ts +59 -0
  131. package/dist/solid.js +3 -0
  132. package/dist/solid.js.map +1 -0
  133. package/dist/solidstart.d.ts +16 -0
  134. package/dist/solidstart.js +22 -0
  135. package/dist/solidstart.js.map +1 -0
  136. package/dist/svelte/GtmNoscript.svelte +18 -0
  137. package/dist/svelte/MetaPixelNoscript.svelte +18 -0
  138. package/dist/svelte.d.ts +55 -0
  139. package/dist/svelte.js +3 -0
  140. package/dist/svelte.js.map +1 -0
  141. package/dist/sveltekit.d.ts +18 -0
  142. package/dist/sveltekit.js +16 -0
  143. package/dist/sveltekit.js.map +1 -0
  144. package/dist/testing.d.ts +11 -0
  145. package/dist/testing.js +50 -0
  146. package/dist/testing.js.map +1 -0
  147. package/dist/types-BV_AFZKQ.d.ts +407 -0
  148. package/dist/vue.d.ts +92 -0
  149. package/dist/vue.js +97 -0
  150. package/dist/vue.js.map +1 -0
  151. package/package.json +281 -4
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/angular.ts"],"names":[],"mappings":"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","file":"angular.js","sourcesContent":["import {\n ApplicationRef,\n computed,\n DestroyRef,\n DOCUMENT,\n ErrorHandler,\n inject,\n InjectionToken,\n makeEnvironmentProviders,\n PLATFORM_ID,\n provideEnvironmentInitializer,\n signal,\n type EnvironmentProviders,\n type Signal,\n} from '@angular/core'\nimport { NavigationEnd, Router, ROUTES } from '@angular/router'\nimport {\n createConsentController,\n type ConsentControllerActions,\n type ConsentControllerOptions,\n type ConsentControllerState,\n} from './core/consent-controller'\nimport { latestAnalytics } from './core/create-analytics'\nimport { gtmNoscriptIframe, metaNoscriptImg } from './core/noscript'\nimport type { Analytics, ConsentApi, ConsentState, PageViewPayload } from './core/types'\n\n/** Options for `provideConsentLayer()`. */\nexport type ConsentLayerOptions = ConsentControllerOptions & {\n analytics: Analytics\n page?: PageViewPayload\n /** Reports errors that reach Angular's `ErrorHandler` through `captureError`. Defaults to `true`. */\n captureErrors?: boolean\n}\n\n/** What `injectConsentController()` returns: the UI state as signals, and the actions. */\nexport type ConsentController = {\n readonly [Key in keyof ConsentControllerState]: Signal<ConsentControllerState[Key]>\n} & ConsentControllerActions\n\nconst ANALYTICS = new InjectionToken<Analytics>('consentlayer analytics')\nconst CONTROLLER = new InjectionToken<ConsentController>('consentlayer controller')\n\nconst isBrowser = () => inject(PLATFORM_ID) === 'browser'\n\n/**\n * Provides the instance and the consent UI state. In the browser, boots\n * analytics once the application is stable, sends a page view per Angular\n * Router navigation, and reports errors from Angular's `ErrorHandler`.\n */\nexport function provideConsentLayer(options: ConsentLayerOptions): EnvironmentProviders {\n const { analytics, page, captureErrors = true, ...controllerOptions } = options\n return makeEnvironmentProviders([\n { provide: ANALYTICS, useValue: analytics },\n {\n provide: CONTROLLER,\n useFactory: (): ConsentController => {\n const controller = createConsentController(analytics, controllerOptions)\n const state = signal(controller.getSnapshot())\n if (isBrowser()) {\n const routes = inject(ROUTES, { optional: true })\n const router = routes && inject(Router)\n const navigations = router?.events.subscribe((event) => {\n // The router sets the page title right after NavigationEnd.\n if (event instanceof NavigationEnd)\n queueMicrotask(() => controller.setRoute(event.urlAfterRedirects.split('#')[0] ?? '/', page))\n })\n if (!router) controller.setRoute('default', page)\n const unsubscribe = controller.subscribe(() => state.set(controller.getSnapshot()))\n let stop: (() => void) | undefined\n let destroyed = false\n void inject(ApplicationRef)\n .whenStable()\n .then(() => {\n if (!destroyed) stop = controller.start()\n })\n inject(DestroyRef).onDestroy(() => {\n destroyed = true\n stop?.()\n unsubscribe()\n navigations?.unsubscribe()\n })\n }\n const field = <Key extends keyof ConsentControllerState>(key: Key) => computed(() => state()[key])\n return {\n ready: field('ready'),\n error: field('error'),\n bannerVisible: field('bannerVisible'),\n dialogVisible: field('dialogVisible'),\n categories: field('categories'),\n gpc: field('gpc'),\n prefs: field('prefs'),\n ...controller.actions,\n }\n },\n },\n provideEnvironmentInitializer(() => void inject(CONTROLLER)),\n captureErrors\n ? {\n provide: ErrorHandler,\n useFactory: (): ErrorHandler => {\n const fallback = new ErrorHandler()\n const browser = isBrowser()\n return {\n handleError(error: unknown) {\n if (browser) void analytics.captureError(error, { source: 'boundary' })\n fallback.handleError(error)\n },\n }\n },\n }\n : [],\n ])\n}\n\n/** The instance from `provideConsentLayer()`, else the one `createAnalytics()` made. */\nexport function injectAnalytics(): Analytics {\n const analytics = inject(ANALYTICS, { optional: true }) ?? latestAnalytics()\n if (!analytics) throw new Error('injectAnalytics needs createAnalytics() to have run, or provideConsentLayer()')\n return analytics\n}\n\n/** Current consent as a signal, `null` before a choice. */\nexport function injectConsent(): {\n consent: Signal<ConsentState | null>\n engine: ConsentApi\n setConsent: (consent: ConsentState) => Promise<void>\n update: (partial: Partial<ConsentState>) => Promise<void>\n} {\n const analytics = injectAnalytics()\n const consent = signal(analytics.consent.getConsent())\n if (isBrowser()) {\n inject(DestroyRef).onDestroy(analytics.consent.subscribe(() => consent.set(analytics.consent.getConsent())))\n }\n return {\n consent: consent.asReadonly(),\n engine: analytics.consent,\n setConsent: (next) => analytics.consent.setConsent(next),\n update: (partial) => analytics.consent.update(partial),\n }\n}\n\n/** State and actions for building your own consent UI. */\nexport function injectConsentController(): ConsentController {\n const controller = inject(CONTROLLER, { optional: true })\n if (!controller) throw new Error('injectConsentController needs provideConsentLayer()')\n return controller\n}\n\n/**\n * Adds Meta's `<noscript>` PageView pixel to the end of `<body>` in HTML\n * rendered on the server. It fires only for visitors without JavaScript, who\n * never see the consent banner, so it tracks them without consent. Add it only\n * where that is lawful for you. Adds nothing without `enabled` or while\n * `pixelId` is unset. Other options become attributes of the `<img>`.\n */\nexport function provideMetaPixelNoscript({\n pixelId,\n enabled,\n ...attrs\n}: { pixelId: string | undefined; enabled?: boolean } & Record<string, unknown>): EnvironmentProviders {\n return provideEnvironmentInitializer(() => {\n if (!enabled || !pixelId || isBrowser()) return\n const document = inject(DOCUMENT)\n const noscript = document.createElement('noscript')\n noscript.innerHTML = metaNoscriptImg(pixelId, attrs)\n document.body.appendChild(noscript)\n })\n}\n\n/**\n * Adds Google Tag Manager's `<noscript>` iframe to the start of `<body>` in\n * HTML rendered on the server. It loads the container only for visitors without\n * JavaScript, who never see the consent banner, so its tags run without\n * consent. Add it only where that is lawful for you. Adds nothing without\n * `enabled` or while `containerId` is unset. Other options become attributes of the `<iframe>`.\n */\nexport function provideGtmNoscript({\n containerId,\n enabled,\n ...attrs\n}: { containerId: string | undefined; enabled?: boolean } & Record<string, unknown>): EnvironmentProviders {\n return provideEnvironmentInitializer(() => {\n if (!enabled || !containerId || isBrowser()) return\n const document = inject(DOCUMENT)\n const noscript = document.createElement('noscript')\n noscript.innerHTML = gtmNoscriptIframe(containerId, attrs)\n document.body.insertBefore(noscript, document.body.firstChild)\n })\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { A as AutoTrackingConfig, a as AutoTracking } from './types-BV_AFZKQ.js';
2
+ export { b as AutoTrackingClickConfig, c as AutoTrackingClickMode, d as AutoTrackingScrollConfig, e as AutoTrackingTimeConfig } from './types-BV_AFZKQ.js';
3
+
4
+ /**
5
+ * Tracks page sessions, clicks, scroll depth and time on page once analytics
6
+ * consent is granted: `createAnalytics({ autoTracking: autoTracking() })`.
7
+ * An object turns on only the kinds it names, unless it names none.
8
+ */
9
+ declare function autoTracking(config?: AutoTrackingConfig): AutoTracking;
10
+
11
+ export { AutoTracking, AutoTrackingConfig, autoTracking };
@@ -0,0 +1,72 @@
1
+ import { getNames } from './chunk-R6I7OYQB.js';
2
+
3
+ // src/auto-tracking.ts
4
+ var DEFAULT_CLICK_SELECTORS = ["button", '[role="button"]', "a", 'input[type="submit"]', 'input[type="button"]'];
5
+ var DEFAULT_SCROLL_THRESHOLDS = [25, 50, 75, 100];
6
+ var DEFAULT_TIME_INTERVALS = [5, 15, 30, 60, 120];
7
+ function autoTracking(config = {}) {
8
+ const { clicks, scroll, timeOnPage, pageViews, only } = config;
9
+ const named = [clicks, scroll, timeOnPage, pageViews].some((kind) => kind != null && kind !== false);
10
+ const on = (kind) => kind == null ? !named : kind !== false;
11
+ const settings = (kind) => typeof kind === "object" ? kind : {};
12
+ return ({ track, signal }) => {
13
+ const send = (name, params) => void track(name, params, { only, requireAnalyticsConsent: true });
14
+ let session = 0;
15
+ let page = new AbortController();
16
+ signal.addEventListener("abort", () => page.abort());
17
+ if (on(clicks)) {
18
+ const { mode = "all", attribute = "data-track", selectors = DEFAULT_CLICK_SELECTORS } = settings(clicks);
19
+ const selector = mode === "attribute" ? `[${attribute}]` : selectors.join(", ");
20
+ const onClick = (event) => {
21
+ const element = selector ? event.target?.closest(selector) : null;
22
+ if (!(element instanceof HTMLElement)) return;
23
+ send(getNames().autoTrackingEvents.uiClick, {
24
+ ui_target: element.getAttribute(attribute) || element.getAttribute("aria-label") || "unknown",
25
+ ui_element: element.tagName.toLowerCase(),
26
+ ui_id: element.id || void 0,
27
+ path: window.location.pathname,
28
+ session_id: session
29
+ });
30
+ };
31
+ window.addEventListener("click", onClick, { signal });
32
+ }
33
+ return () => {
34
+ page.abort();
35
+ page = new AbortController();
36
+ const pageSignal = page.signal;
37
+ const sessionId = ++session;
38
+ if (on(pageViews)) send(getNames().autoTrackingEvents.pageSessionStart, { session_id: sessionId });
39
+ if (on(scroll)) {
40
+ const thresholds = (settings(scroll).thresholds ?? DEFAULT_SCROLL_THRESHOLDS).filter((value) => Number.isFinite(value) && value > 0 && value <= 100).sort((a, b) => a - b);
41
+ const emitted = /* @__PURE__ */ new Set();
42
+ const onScroll = () => {
43
+ const doc = document.documentElement;
44
+ const total = doc.scrollHeight - window.innerHeight;
45
+ if (total <= 0) return;
46
+ const depth = Math.round(Math.min(1, (window.scrollY || doc.scrollTop) / total) * 100);
47
+ for (const threshold of thresholds) {
48
+ if (depth < (threshold === 100 ? 95 : threshold) || emitted.has(threshold)) continue;
49
+ emitted.add(threshold);
50
+ send(getNames().autoTrackingEvents.scrollDepth, { depth: threshold, session_id: sessionId });
51
+ }
52
+ if (emitted.size === thresholds.length) window.removeEventListener("scroll", onScroll);
53
+ };
54
+ window.addEventListener("scroll", onScroll, { passive: true, signal: pageSignal });
55
+ }
56
+ if (on(timeOnPage)) {
57
+ for (const seconds of settings(timeOnPage).intervals ?? DEFAULT_TIME_INTERVALS) {
58
+ if (!Number.isFinite(seconds) || seconds <= 0) continue;
59
+ const handle = window.setTimeout(
60
+ () => send(getNames().autoTrackingEvents.timeOnPage, { seconds, session_id: sessionId }),
61
+ seconds * 1e3
62
+ );
63
+ pageSignal.addEventListener("abort", () => window.clearTimeout(handle));
64
+ }
65
+ }
66
+ };
67
+ };
68
+ }
69
+
70
+ export { autoTracking };
71
+ //# sourceMappingURL=auto-tracking.js.map
72
+ //# sourceMappingURL=auto-tracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auto-tracking.ts"],"names":[],"mappings":"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","file":"auto-tracking.js","sourcesContent":["import { getNames } from './core/names'\nimport type { AutoTracking, AutoTrackingConfig } from './core/types'\n\nexport type {\n AutoTracking,\n AutoTrackingClickConfig,\n AutoTrackingClickMode,\n AutoTrackingConfig,\n AutoTrackingScrollConfig,\n AutoTrackingTimeConfig,\n} from './core/types'\n\nconst DEFAULT_CLICK_SELECTORS = ['button', '[role=\"button\"]', 'a', 'input[type=\"submit\"]', 'input[type=\"button\"]']\nconst DEFAULT_SCROLL_THRESHOLDS = [25, 50, 75, 100]\nconst DEFAULT_TIME_INTERVALS = [5, 15, 30, 60, 120]\n\n/**\n * Tracks page sessions, clicks, scroll depth and time on page once analytics\n * consent is granted: `createAnalytics({ autoTracking: autoTracking() })`.\n * An object turns on only the kinds it names, unless it names none.\n */\nexport function autoTracking(config: AutoTrackingConfig = {}): AutoTracking {\n const { clicks, scroll, timeOnPage, pageViews, only } = config\n const named = [clicks, scroll, timeOnPage, pageViews].some((kind) => kind != null && kind !== false)\n const on = (kind: unknown) => (kind == null ? !named : kind !== false)\n const settings = <T extends object>(kind: boolean | T | undefined): Partial<T> =>\n typeof kind === 'object' ? kind : {}\n\n return ({ track, signal }) => {\n const send = (name: string, params: Record<string, unknown>) =>\n void track(name, params, { only, requireAnalyticsConsent: true })\n let session = 0\n let page = new AbortController()\n signal.addEventListener('abort', () => page.abort())\n\n if (on(clicks)) {\n const { mode = 'all', attribute = 'data-track', selectors = DEFAULT_CLICK_SELECTORS } = settings(clicks)\n const selector = mode === 'attribute' ? `[${attribute}]` : selectors.join(', ')\n const onClick = (event: Event) => {\n const element = selector ? (event.target as Element | null)?.closest(selector) : null\n if (!(element instanceof HTMLElement)) return\n send(getNames().autoTrackingEvents.uiClick, {\n ui_target: element.getAttribute(attribute) || element.getAttribute('aria-label') || 'unknown',\n ui_element: element.tagName.toLowerCase(),\n ui_id: element.id || undefined,\n path: window.location.pathname,\n session_id: session,\n })\n }\n window.addEventListener('click', onClick, { signal })\n }\n\n return () => {\n page.abort()\n page = new AbortController()\n const pageSignal = page.signal\n const sessionId = ++session\n\n if (on(pageViews)) send(getNames().autoTrackingEvents.pageSessionStart, { session_id: sessionId })\n\n if (on(scroll)) {\n const thresholds = (settings(scroll).thresholds ?? DEFAULT_SCROLL_THRESHOLDS)\n .filter((value) => Number.isFinite(value) && value > 0 && value <= 100)\n .sort((a, b) => a - b)\n const emitted = new Set<number>()\n const onScroll = () => {\n const doc = document.documentElement\n const total = doc.scrollHeight - window.innerHeight\n if (total <= 0) return\n const depth = Math.round(Math.min(1, (window.scrollY || doc.scrollTop) / total) * 100)\n for (const threshold of thresholds) {\n // Scroll positions rarely land exactly on the bottom, so 100 fires from 95.\n if (depth < (threshold === 100 ? 95 : threshold) || emitted.has(threshold)) continue\n emitted.add(threshold)\n send(getNames().autoTrackingEvents.scrollDepth, { depth: threshold, session_id: sessionId })\n }\n if (emitted.size === thresholds.length) window.removeEventListener('scroll', onScroll)\n }\n window.addEventListener('scroll', onScroll, { passive: true, signal: pageSignal })\n }\n\n if (on(timeOnPage)) {\n for (const seconds of settings(timeOnPage).intervals ?? DEFAULT_TIME_INTERVALS) {\n if (!Number.isFinite(seconds) || seconds <= 0) continue\n const handle = window.setTimeout(\n () => send(getNames().autoTrackingEvents.timeOnPage, { seconds, session_id: sessionId }),\n seconds * 1000,\n )\n pageSignal.addEventListener('abort', () => window.clearTimeout(handle))\n }\n }\n }\n }\n}\n"]}
@@ -0,0 +1,34 @@
1
+ // src/core/plugin.ts
2
+ function definePlugin(plugin) {
3
+ return plugin;
4
+ }
5
+ function skippedPlugin(id, option) {
6
+ return { id, requiredConsent: "none", setup: (context) => context.log(`${id} is off: ${option} is not set.`) };
7
+ }
8
+ var loading = /* @__PURE__ */ new Map();
9
+ function loadScript(src, attributes = {}) {
10
+ if (typeof document === "undefined") return Promise.resolve();
11
+ const href = new URL(src, document.baseURI).href;
12
+ const pending = loading.get(href);
13
+ if (pending) return pending;
14
+ if ([...document.scripts].some((script) => script.src === href)) return Promise.resolve();
15
+ const promise = new Promise((resolve, reject) => {
16
+ const script = document.createElement("script");
17
+ script.async = true;
18
+ script.src = href;
19
+ for (const [name, value] of Object.entries(attributes)) script.setAttribute(name, value);
20
+ script.onload = () => resolve();
21
+ script.onerror = () => {
22
+ script.remove();
23
+ loading.delete(href);
24
+ reject(new Error(`Failed to load ${src}`));
25
+ };
26
+ document.head.appendChild(script);
27
+ });
28
+ loading.set(href, promise);
29
+ return promise;
30
+ }
31
+
32
+ export { definePlugin, loadScript, skippedPlugin };
33
+ //# sourceMappingURL=chunk-7XLGT6U3.js.map
34
+ //# sourceMappingURL=chunk-7XLGT6U3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/plugin.ts"],"names":[],"mappings":";AAKO,SAAS,aAAwC,MAAA,EAAc;AACpE,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,aAAA,CAAc,IAAY,MAAA,EAAiC;AACzE,EAAA,OAAO,EAAE,EAAA,EAAI,eAAA,EAAiB,MAAA,EAAQ,OAAO,CAAC,OAAA,KAAY,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAG,EAAE,CAAA,SAAA,EAAY,MAAM,cAAc,CAAA,EAAE;AAC/G;AAEA,IAAM,OAAA,uBAAc,GAAA,EAA2B;AAGxC,SAAS,UAAA,CAAW,GAAA,EAAa,UAAA,GAAqC,EAAC,EAAkB;AAC9F,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,QAAQ,OAAA,EAAQ;AAC5D,EAAA,MAAM,OAAO,IAAI,GAAA,CAAI,GAAA,EAAK,QAAA,CAAS,OAAO,CAAA,CAAE,IAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAChC,EAAA,IAAI,SAAS,OAAO,OAAA;AACpB,EAAA,IAAI,CAAC,GAAG,QAAA,CAAS,OAAO,EAAE,IAAA,CAAK,CAAC,MAAA,KAAW,MAAA,CAAO,GAAA,KAAQ,IAAI,CAAA,EAAG,OAAO,QAAQ,OAAA,EAAQ;AACxF,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAc,CAAC,SAAS,MAAA,KAAW;AACrD,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,IAAA,MAAA,CAAO,GAAA,GAAM,IAAA;AACb,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG,MAAA,CAAO,YAAA,CAAa,IAAA,EAAM,KAAK,CAAA;AACvF,IAAA,MAAA,CAAO,MAAA,GAAS,MAAM,OAAA,EAAQ;AAC9B,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,MAAA,CAAO,MAAA,EAAO;AACd,MAAA,OAAA,CAAQ,OAAO,IAAI,CAAA;AACnB,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,GAAG,EAAE,CAAC,CAAA;AAAA,IAC3C,CAAA;AACA,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,EAClC,CAAC,CAAA;AACD,EAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,OAAO,CAAA;AACzB,EAAA,OAAO,OAAA;AACT","file":"chunk-7XLGT6U3.js","sourcesContent":["import type { AnalyticsPlugin } from './types'\n\nexport { getFlag, setFlag } from './names'\n\n/** Types a plugin; the manager runs its hooks. */\nexport function definePlugin<T extends AnalyticsPlugin>(plugin: T): T {\n return plugin\n}\n\n/** Stands in for a provider whose required option is unset, e.g. an env var missing locally. */\nexport function skippedPlugin(id: string, option: string): AnalyticsPlugin {\n return { id, requiredConsent: 'none', setup: (context) => context.log(`${id} is off: ${option} is not set.`) }\n}\n\nconst loading = new Map<string, Promise<void>>()\n\n/** Appends `<script async src>` once per `src`. Resolves on load, rejects on error. */\nexport function loadScript(src: string, attributes: Record<string, string> = {}): Promise<void> {\n if (typeof document === 'undefined') return Promise.resolve()\n const href = new URL(src, document.baseURI).href\n const pending = loading.get(href)\n if (pending) return pending\n if ([...document.scripts].some((script) => script.src === href)) return Promise.resolve()\n const promise = new Promise<void>((resolve, reject) => {\n const script = document.createElement('script')\n script.async = true\n script.src = href\n for (const [name, value] of Object.entries(attributes)) script.setAttribute(name, value)\n script.onload = () => resolve()\n script.onerror = () => {\n script.remove()\n loading.delete(href)\n reject(new Error(`Failed to load ${src}`))\n }\n document.head.appendChild(script)\n })\n loading.set(href, promise)\n return promise\n}\n"]}
@@ -0,0 +1,61 @@
1
+ import { getFlag, setFlag } from './chunk-R6I7OYQB.js';
2
+
3
+ // src/core/consent-mode.ts
4
+ var signals = (consent) => ({
5
+ ad_user_data: consent?.marketing ? "granted" : "denied",
6
+ ad_personalization: consent?.marketing ? "granted" : "denied",
7
+ ad_storage: consent?.marketing ? "granted" : "denied",
8
+ analytics_storage: consent?.analytics ? "granted" : "denied",
9
+ functionality_storage: consent?.preferences ? "granted" : "denied",
10
+ security_storage: "granted"
11
+ });
12
+ var REGION = /^[A-Z]{2}(-[A-Z0-9]{1,3})?$/;
13
+ function ensureGtag() {
14
+ window.dataLayer = window.dataLayer ?? [];
15
+ window.gtag = window.gtag ?? function gtag() {
16
+ window.dataLayer?.push(arguments);
17
+ };
18
+ }
19
+ function consentModeSignals(options = {}) {
20
+ const { regions = [], waitForUpdate, urlPassthrough, adsDataRedaction } = options;
21
+ for (const { region } of regions) {
22
+ if (!region.length || !region.every((code) => REGION.test(code))) {
23
+ throw new Error(
24
+ `[consentlayer] googleConsentMode regions: ${JSON.stringify(region)} is not a list of ISO 3166-2 codes such as 'US' or 'US-CA'`
25
+ );
26
+ }
27
+ }
28
+ if (waitForUpdate !== void 0 && !(Number.isInteger(waitForUpdate) && waitForUpdate >= 0)) {
29
+ throw new Error(
30
+ `[consentlayer] googleConsentMode waitForUpdate must be a whole number of milliseconds, received ${waitForUpdate}`
31
+ );
32
+ }
33
+ const wait = waitForUpdate === void 0 ? {} : { wait_for_update: waitForUpdate };
34
+ let defaulted = false;
35
+ let sent;
36
+ return (consent) => {
37
+ if (typeof window === "undefined") return;
38
+ ensureGtag();
39
+ const gtag = window.gtag;
40
+ if (!defaulted) {
41
+ defaulted = true;
42
+ gtag("consent", "default", { ...signals(null), ...wait });
43
+ for (const { region, ...granted } of regions) gtag("consent", "default", { ...signals(granted), ...wait, region });
44
+ if (urlPassthrough) gtag("set", "url_passthrough", true);
45
+ if (adsDataRedaction) gtag("set", "ads_data_redaction", true);
46
+ }
47
+ const update = signals(consent);
48
+ if ((consent || sent) && JSON.stringify(update) !== sent) {
49
+ gtag("consent", "update", update);
50
+ sent = JSON.stringify(update);
51
+ }
52
+ };
53
+ }
54
+ function builtInConsentMode(consent) {
55
+ const observe = getFlag("BuiltInConsentMode") ?? setFlag("BuiltInConsentMode", consentModeSignals());
56
+ observe(consent);
57
+ }
58
+
59
+ export { builtInConsentMode, consentModeSignals, ensureGtag };
60
+ //# sourceMappingURL=chunk-BWPWDN4G.js.map
61
+ //# sourceMappingURL=chunk-BWPWDN4G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/consent-mode.ts"],"names":[],"mappings":"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","file":"chunk-BWPWDN4G.js","sourcesContent":["import { getFlag, setFlag } from './plugin'\nimport type { ConsentState } from './types'\n\nexport type GoogleConsentModeOptions = {\n /** Categories that count as granted before a choice in the listed ISO 3166-2 regions, e.g. `'US'` or `'US-CA'`. */\n regions?: GoogleConsentModeRegion[]\n /** Milliseconds Google tags wait for a consent update before sending. */\n waitForUpdate?: number\n /** Pass ad click IDs through URLs while `ad_storage` is denied. */\n urlPassthrough?: boolean\n /** Redact ad click data while `ad_storage` is denied. */\n adsDataRedaction?: boolean\n}\n\nexport type GoogleConsentModeRegion = { region: string[] } & Partial<\n Record<'preferences' | 'analytics' | 'marketing', boolean>\n>\n\nconst signals = (consent: Partial<Pick<ConsentState, 'preferences' | 'analytics' | 'marketing'>> | null) => ({\n ad_user_data: consent?.marketing ? 'granted' : 'denied',\n ad_personalization: consent?.marketing ? 'granted' : 'denied',\n ad_storage: consent?.marketing ? 'granted' : 'denied',\n analytics_storage: consent?.analytics ? 'granted' : 'denied',\n functionality_storage: consent?.preferences ? 'granted' : 'denied',\n security_storage: 'granted',\n})\n\nconst REGION = /^[A-Z]{2}(-[A-Z0-9]{1,3})?$/\n\nexport function ensureGtag() {\n window.dataLayer = window.dataLayer ?? []\n // gtag.js only executes dataLayer entries that are real `arguments` objects.\n window.gtag =\n window.gtag ??\n function gtag() {\n // eslint-disable-next-line prefer-rest-params\n window.dataLayer?.push(arguments)\n }\n}\n\n/** Sets the Consent Mode defaults on the first call, then sends an update for each choice. */\nexport function consentModeSignals(options: GoogleConsentModeOptions = {}) {\n const { regions = [], waitForUpdate, urlPassthrough, adsDataRedaction } = options\n for (const { region } of regions) {\n if (!region.length || !region.every((code) => REGION.test(code))) {\n throw new Error(\n `[consentlayer] googleConsentMode regions: ${JSON.stringify(region)} is not a list of ISO 3166-2 codes such as 'US' or 'US-CA'`,\n )\n }\n }\n if (waitForUpdate !== undefined && !(Number.isInteger(waitForUpdate) && waitForUpdate >= 0)) {\n throw new Error(\n `[consentlayer] googleConsentMode waitForUpdate must be a whole number of milliseconds, received ${waitForUpdate}`,\n )\n }\n const wait = waitForUpdate === undefined ? {} : { wait_for_update: waitForUpdate }\n let defaulted = false\n let sent: string | undefined\n return (consent: ConsentState | null) => {\n if (typeof window === 'undefined') return\n ensureGtag()\n const gtag = window.gtag!\n if (!defaulted) {\n defaulted = true\n gtag('consent', 'default', { ...signals(null), ...wait })\n for (const { region, ...granted } of regions) gtag('consent', 'default', { ...signals(granted), ...wait, region })\n if (urlPassthrough) gtag('set', 'url_passthrough', true)\n if (adsDataRedaction) gtag('set', 'ads_data_redaction', true)\n }\n // Before any choice, the defaults (and their regions) stay in force.\n const update = signals(consent)\n if ((consent || sent) && JSON.stringify(update) !== sent) {\n gtag('consent', 'update', update)\n sent = JSON.stringify(update)\n }\n }\n}\n\n/** The signals `ga4()` and `gtm()` send without `googleConsentMode()`; shared, so they go out once. */\nexport function builtInConsentMode(consent: ConsentState | null) {\n const observe =\n getFlag<ReturnType<typeof consentModeSignals>>('BuiltInConsentMode') ??\n setFlag('BuiltInConsentMode', consentModeSignals())\n observe(consent)\n}\n"]}
@@ -0,0 +1,77 @@
1
+ // src/server/http.ts
2
+ var ANALYTICS_MAX_BODY = 16 * 1024;
3
+ async function sha256(input) {
4
+ if (!input) return void 0;
5
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
6
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
7
+ }
8
+ function assertSameOrigin(request, options) {
9
+ const requestUrl = new URL(request.url);
10
+ const origin = request.headers.get("origin");
11
+ const referer = request.headers.get("referer");
12
+ let headerOrigin = origin;
13
+ if (!headerOrigin && referer) {
14
+ try {
15
+ headerOrigin = new URL(referer).origin;
16
+ } catch {
17
+ throw Object.assign(new Error("Forbidden: malformed referer"), { status: 403 });
18
+ }
19
+ }
20
+ if (options?.requireHeader && !headerOrigin) {
21
+ throw Object.assign(new Error("Forbidden: missing origin header"), {
22
+ status: 403
23
+ });
24
+ }
25
+ if (headerOrigin && headerOrigin !== requestUrl.origin) {
26
+ throw Object.assign(new Error("Forbidden: cross-origin request"), {
27
+ status: 403
28
+ });
29
+ }
30
+ }
31
+ async function parseJsonWithLimit(request, maxBytes = 10 * 1024) {
32
+ const contentType = request.headers.get("content-type") || "";
33
+ if (!contentType.toLowerCase().includes("application/json")) {
34
+ throw Object.assign(new Error("Unsupported Media Type"), { status: 415 });
35
+ }
36
+ const reader = request.body?.getReader();
37
+ if (!reader) throw Object.assign(new Error("Empty body"), { status: 400 });
38
+ let received = 0;
39
+ const chunks = [];
40
+ while (true) {
41
+ const { done, value } = await reader.read();
42
+ if (done) break;
43
+ if (!value) continue;
44
+ received += value.byteLength;
45
+ if (received > maxBytes) {
46
+ throw Object.assign(new Error("Payload Too Large"), { status: 413 });
47
+ }
48
+ chunks.push(value);
49
+ }
50
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(concatUint8Arrays(chunks));
51
+ try {
52
+ return JSON.parse(text);
53
+ } catch {
54
+ throw Object.assign(new Error("Invalid JSON"), { status: 400 });
55
+ }
56
+ }
57
+ function parseCookieHeader(cookieHeader) {
58
+ return cookieHeader.split(";").map((chunk) => chunk.trim()).filter(Boolean).reduce((accumulator, chunk) => {
59
+ const [key, ...rest] = chunk.split("=");
60
+ accumulator[key] = rest.join("=");
61
+ return accumulator;
62
+ }, {});
63
+ }
64
+ function concatUint8Arrays(chunks) {
65
+ const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
66
+ const output = new Uint8Array(total);
67
+ let offset = 0;
68
+ for (const chunk of chunks) {
69
+ output.set(chunk, offset);
70
+ offset += chunk.byteLength;
71
+ }
72
+ return output;
73
+ }
74
+
75
+ export { ANALYTICS_MAX_BODY, assertSameOrigin, parseCookieHeader, parseJsonWithLimit, sha256 };
76
+ //# sourceMappingURL=chunk-DK45XPEY.js.map
77
+ //# sourceMappingURL=chunk-DK45XPEY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/http.ts"],"names":[],"mappings":"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","file":"chunk-DK45XPEY.js","sourcesContent":["export const ANALYTICS_MAX_BODY = 16 * 1024\n\nexport async function sha256(input: string | null | undefined): Promise<string | undefined> {\n if (!input) return undefined\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\nexport function assertSameOrigin(request: Request, options?: { requireHeader?: boolean }): void {\n const requestUrl = new URL(request.url)\n const origin = request.headers.get('origin')\n const referer = request.headers.get('referer')\n let headerOrigin = origin\n if (!headerOrigin && referer) {\n try {\n headerOrigin = new URL(referer).origin\n } catch {\n throw Object.assign(new Error('Forbidden: malformed referer'), { status: 403 })\n }\n }\n\n if (options?.requireHeader && !headerOrigin) {\n throw Object.assign(new Error('Forbidden: missing origin header'), {\n status: 403,\n })\n }\n\n if (headerOrigin && headerOrigin !== requestUrl.origin) {\n throw Object.assign(new Error('Forbidden: cross-origin request'), {\n status: 403,\n })\n }\n}\n\nexport async function parseJsonWithLimit<T = unknown>(request: Request, maxBytes = 10 * 1024): Promise<T> {\n const contentType = request.headers.get('content-type') || ''\n if (!contentType.toLowerCase().includes('application/json')) {\n throw Object.assign(new Error('Unsupported Media Type'), { status: 415 })\n }\n const reader = request.body?.getReader()\n if (!reader) throw Object.assign(new Error('Empty body'), { status: 400 })\n let received = 0\n const chunks: Uint8Array[] = []\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value) continue\n received += value.byteLength\n if (received > maxBytes) {\n throw Object.assign(new Error('Payload Too Large'), { status: 413 })\n }\n chunks.push(value)\n }\n const text = new TextDecoder('utf-8', { fatal: false }).decode(concatUint8Arrays(chunks))\n try {\n return JSON.parse(text) as T\n } catch {\n throw Object.assign(new Error('Invalid JSON'), { status: 400 })\n }\n}\n\nexport function parseCookieHeader(cookieHeader: string): Record<string, string> {\n return cookieHeader\n .split(';')\n .map((chunk) => chunk.trim())\n .filter(Boolean)\n .reduce<Record<string, string>>((accumulator, chunk) => {\n const [key, ...rest] = chunk.split('=')\n accumulator[key] = rest.join('=')\n return accumulator\n }, {})\n}\n\nfunction concatUint8Arrays(chunks: Uint8Array[]): Uint8Array {\n const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0)\n const output = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n output.set(chunk, offset)\n offset += chunk.byteLength\n }\n return output\n}\n"]}
@@ -0,0 +1,98 @@
1
+ import { expireCookies } from './chunk-TVSZJLRC.js';
2
+ import { builtInConsentMode, ensureGtag } from './chunk-BWPWDN4G.js';
3
+ import { assertValidGa4MeasurementId, assertHttpsOrRelativeUrl } from './chunk-J3KUPF4R.js';
4
+ import { skippedPlugin, definePlugin, loadScript } from './chunk-7XLGT6U3.js';
5
+ import { getFlag, setFlag } from './chunk-R6I7OYQB.js';
6
+
7
+ // src/providers/ga4.ts
8
+ var GA4_EVENT_NAMES = {
9
+ lead: "generate_lead"
10
+ };
11
+ function ga4EventName(name) {
12
+ return GA4_EVENT_NAMES[name] ?? name.replace(/[^A-Za-z0-9_]/g, "_");
13
+ }
14
+ function ga4UserData(identity) {
15
+ const data = {};
16
+ if (identity.email) data.email = identity.email;
17
+ if (identity.phone) data.phone_number = identity.phone.startsWith("+") ? identity.phone : `+${identity.phone}`;
18
+ return data;
19
+ }
20
+ function ga4(options) {
21
+ if (!options.measurementId?.trim()) return skippedPlugin("ga4", "measurementId");
22
+ const measurementId = assertValidGa4MeasurementId(options.measurementId.trim());
23
+ const advanced = options.consentMode === "advanced";
24
+ const serverContainerUrl = options.serverContainerUrl && assertHttpsOrRelativeUrl(options.serverContainerUrl, "ga4 serverContainerUrl");
25
+ let ownConsentMode = false;
26
+ const gtag = (...args) => window.gtag?.(...args);
27
+ const configure = () => {
28
+ const configured = setFlag("Ga4Configured", getFlag("Ga4Configured") ?? {});
29
+ if (configured[measurementId]) return;
30
+ gtag("config", measurementId, {
31
+ send_page_view: false,
32
+ ...serverContainerUrl && { server_container_url: serverContainerUrl }
33
+ });
34
+ configured[measurementId] = true;
35
+ };
36
+ const applyIdentity = (identity) => {
37
+ gtag("set", { user_id: identity.userId ?? null });
38
+ gtag("set", "user_data", ga4UserData(identity));
39
+ };
40
+ return definePlugin({
41
+ id: "ga4",
42
+ requiredConsent: advanced ? "none" : "analytics",
43
+ loadBeforeConsent: advanced || (options.loadBeforeConsent ?? false),
44
+ setup() {
45
+ if (typeof window === "undefined") return;
46
+ ownConsentMode = !getFlag("GoogleConsentMode");
47
+ if (ownConsentMode) builtInConsentMode(null);
48
+ else ensureGtag();
49
+ gtag("js", /* @__PURE__ */ new Date());
50
+ if (advanced) configure();
51
+ },
52
+ consent({ consent, identity }) {
53
+ if (typeof window === "undefined") return;
54
+ if (ownConsentMode) builtInConsentMode(consent);
55
+ if (consent && !consent.analytics)
56
+ expireCookies((name) => name === "_ga" || name === "_gid" || name.startsWith("_ga_"));
57
+ if (advanced) applyIdentity(consent?.analytics ? identity : {});
58
+ else if (consent?.analytics) configure();
59
+ },
60
+ load({ emitError }) {
61
+ if (typeof window === "undefined") return;
62
+ loadScript(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(measurementId)}`).catch(
63
+ () => emitError({
64
+ scope: "provider-script-load",
65
+ message: `Failed to load GA4 gtag.js for measurement ID ${measurementId}`
66
+ })
67
+ );
68
+ },
69
+ page(page) {
70
+ if (typeof window === "undefined") return;
71
+ gtag("event", "page_view", {
72
+ page_location: page.url ?? window.location.href,
73
+ page_path: page.path ?? window.location.pathname,
74
+ page_title: page.title ?? document.title
75
+ });
76
+ },
77
+ track(event) {
78
+ if (typeof window === "undefined") return;
79
+ gtag("event", ga4EventName(event.name), {
80
+ ...event.params,
81
+ ...event.options.providers?.ga4
82
+ });
83
+ },
84
+ identify(identity, _traits, { consent }) {
85
+ if (typeof window === "undefined" || advanced && !consent?.analytics) return;
86
+ if (identity.userId) gtag("set", { user_id: identity.userId });
87
+ gtag("set", "user_data", ga4UserData(identity));
88
+ },
89
+ reset() {
90
+ if (typeof window === "undefined") return;
91
+ applyIdentity({});
92
+ }
93
+ });
94
+ }
95
+
96
+ export { ga4, ga4EventName, ga4UserData };
97
+ //# sourceMappingURL=chunk-ETU64ZV3.js.map
98
+ //# sourceMappingURL=chunk-ETU64ZV3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/ga4.ts"],"names":[],"mappings":"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","file":"chunk-ETU64ZV3.js","sourcesContent":["import { builtInConsentMode, ensureGtag } from '../core/consent-mode'\nimport type { UserIdentity } from '../core/types'\nimport { definePlugin, getFlag, loadScript, setFlag, skippedPlugin } from '../core/plugin'\nimport { expireCookies } from '../core/utils'\nimport { assertHttpsOrRelativeUrl, assertValidGa4MeasurementId } from '../core/validation'\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[]\n gtag?: (...args: unknown[]) => void\n }\n}\n\n/** Options for `ga4`. */\nexport type Ga4Options = {\n /** `undefined` or `''` turns the provider off. */\n measurementId: string | undefined\n /**\n * `true`: gtag.js loads at boot with Consent Mode set to denied.\n * `false` (default): nothing loads until analytics consent is granted.\n */\n loadBeforeConsent?: boolean\n consentMode?: Ga4ConsentMode\n /** Your tagging server, e.g. `https://sgtm.example.com`: hits go to it instead of Google. */\n serverContainerUrl?: string\n}\n\n/**\n * `'basic'` (default): the tag is configured once analytics is granted.\n * `'advanced'`: the tag loads and is configured at boot, so Google receives\n * cookieless pings while consent is denied.\n */\nexport type Ga4ConsentMode = 'basic' | 'advanced'\n\nconst GA4_EVENT_NAMES: Record<string, string> = {\n lead: 'generate_lead',\n}\n\n/** GA4 event names allow only letters, digits and underscores. */\nexport function ga4EventName(name: string): string {\n return GA4_EVENT_NAMES[name] ?? name.replace(/[^A-Za-z0-9_]/g, '_')\n}\n\n/** User-provided data; empty when there is no email or phone to share. */\nexport function ga4UserData(identity: UserIdentity): Record<string, string> {\n const data: Record<string, string> = {}\n if (identity.email) data.email = identity.email\n if (identity.phone) data.phone_number = identity.phone.startsWith('+') ? identity.phone : `+${identity.phone}`\n return data\n}\n\n/** Google Analytics 4 via gtag.js, with Consent Mode v2. Needs analytics consent; ad signals follow marketing consent. */\nexport function ga4(options: Ga4Options) {\n if (!options.measurementId?.trim()) return skippedPlugin('ga4', 'measurementId')\n const measurementId = assertValidGa4MeasurementId(options.measurementId.trim())\n const advanced = options.consentMode === 'advanced'\n const serverContainerUrl =\n options.serverContainerUrl && assertHttpsOrRelativeUrl(options.serverContainerUrl, 'ga4 serverContainerUrl')\n let ownConsentMode = false\n const gtag = (...args: unknown[]) => window.gtag?.(...args)\n const configure = () => {\n const configured = setFlag('Ga4Configured', getFlag<Record<string, boolean>>('Ga4Configured') ?? {})\n if (configured[measurementId]) return\n gtag('config', measurementId, {\n send_page_view: false,\n ...(serverContainerUrl && { server_container_url: serverContainerUrl }),\n })\n configured[measurementId] = true\n }\n const applyIdentity = (identity: UserIdentity) => {\n gtag('set', { user_id: identity.userId ?? null })\n gtag('set', 'user_data', ga4UserData(identity))\n }\n return definePlugin({\n id: 'ga4',\n requiredConsent: advanced ? 'none' : 'analytics',\n loadBeforeConsent: advanced || (options.loadBeforeConsent ?? false),\n setup() {\n if (typeof window === 'undefined') return\n ownConsentMode = !getFlag('GoogleConsentMode')\n if (ownConsentMode) builtInConsentMode(null)\n else ensureGtag()\n gtag('js', new Date())\n if (advanced) configure()\n },\n consent({ consent, identity }) {\n if (typeof window === 'undefined') return\n if (ownConsentMode) builtInConsentMode(consent)\n if (consent && !consent.analytics)\n expireCookies((name) => name === '_ga' || name === '_gid' || name.startsWith('_ga_'))\n if (advanced) applyIdentity(consent?.analytics ? identity : {})\n // In basic mode, configured only after consent, so no cookieless pings are sent before it.\n else if (consent?.analytics) configure()\n },\n load({ emitError }) {\n if (typeof window === 'undefined') return\n loadScript(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(measurementId)}`).catch(() =>\n emitError({\n scope: 'provider-script-load',\n message: `Failed to load GA4 gtag.js for measurement ID ${measurementId}`,\n }),\n )\n },\n page(page) {\n if (typeof window === 'undefined') return\n gtag('event', 'page_view', {\n page_location: page.url ?? window.location.href,\n page_path: page.path ?? window.location.pathname,\n page_title: page.title ?? document.title,\n })\n },\n track(event) {\n if (typeof window === 'undefined') return\n gtag('event', ga4EventName(event.name), {\n ...event.params,\n ...event.options.providers?.ga4,\n })\n },\n identify(identity, _traits, { consent }) {\n if (typeof window === 'undefined' || (advanced && !consent?.analytics)) return\n if (identity.userId) gtag('set', { user_id: identity.userId })\n gtag('set', 'user_data', ga4UserData(identity))\n },\n reset() {\n if (typeof window === 'undefined') return\n applyIdentity({})\n },\n })\n}\n"]}