@aranova/tracking-next 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,313 @@
1
+ // ../tracking-core/src/consent.ts
2
+ var CONSENT_STATE_KEY = "consent_state";
3
+ var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
4
+ function buildConsentPayload(state) {
5
+ return {
6
+ ad_storage: state,
7
+ ad_user_data: state,
8
+ ad_personalization: state,
9
+ analytics_storage: state
10
+ };
11
+ }
12
+ function getConsentState() {
13
+ if (typeof window === "undefined")
14
+ return "pending";
15
+ const storedState = window.localStorage.getItem(CONSENT_STATE_KEY);
16
+ if (storedState === "granted" || storedState === "denied")
17
+ return storedState;
18
+ return "pending";
19
+ }
20
+ function setConsentState(state) {
21
+ if (typeof window === "undefined")
22
+ return;
23
+ window.localStorage.setItem(CONSENT_STATE_KEY, state);
24
+ window.localStorage.setItem(CONSENT_TIMESTAMP_KEY, (/* @__PURE__ */ new Date()).toISOString());
25
+ if (typeof window.gtag === "function")
26
+ window.gtag("consent", "update", buildConsentPayload(state));
27
+ }
28
+
29
+ // ../tracking-core/src/payloads.ts
30
+ function createTrackingClientContext(surface, input = {}) {
31
+ return {
32
+ surface,
33
+ sdk_version: input.sdkVersion ?? null,
34
+ package_name: input.packageName ?? null,
35
+ site_origin: input.siteOrigin ?? (typeof window === "undefined" ? null : window.location.origin),
36
+ page_title: input.pageTitle ?? (typeof document === "undefined" ? null : document.title || null),
37
+ referrer: input.referrer ?? (typeof document === "undefined" ? null : document.referrer || null)
38
+ };
39
+ }
40
+ function createTrackingSessionUpsertPayload(trackingParams, input, context) {
41
+ return {
42
+ session_id: input.sessionId,
43
+ gclid: trackingParams.gclid,
44
+ fbclid: trackingParams.fbclid,
45
+ utm_source: trackingParams.utm_source,
46
+ utm_medium: trackingParams.utm_medium,
47
+ utm_campaign: trackingParams.utm_campaign,
48
+ utm_term: trackingParams.utm_term,
49
+ utm_content: trackingParams.utm_content,
50
+ first_page: input.firstPage ?? null,
51
+ consent_state: input.consentState ?? null,
52
+ context
53
+ };
54
+ }
55
+ function createTrackingEventCreatePayload(trackingParams, input, context) {
56
+ return {
57
+ session_id: input.sessionId,
58
+ event_type: input.eventType,
59
+ gclid: trackingParams.gclid,
60
+ fbclid: trackingParams.fbclid,
61
+ page_url: input.pageUrl ?? (typeof window === "undefined" ? null : window.location.href),
62
+ metadata: input.metadata ?? null,
63
+ context
64
+ };
65
+ }
66
+
67
+ // ../tracking-core/src/gtag.ts
68
+ function createConsentDefaultScript() {
69
+ return `
70
+ window.dataLayer = window.dataLayer || [];
71
+ function gtag(){dataLayer.push(arguments);}
72
+ window.gtag = gtag;
73
+ gtag('consent', 'default', {
74
+ ad_storage: 'denied',
75
+ ad_user_data: 'denied',
76
+ ad_personalization: 'denied',
77
+ analytics_storage: 'denied',
78
+ wait_for_update: 500,
79
+ });
80
+ `.trim();
81
+ }
82
+ function createGtagInitScript(gtagId) {
83
+ return `
84
+ window.dataLayer = window.dataLayer || [];
85
+ function gtag(){dataLayer.push(arguments);}
86
+ window.gtag = gtag;
87
+ gtag('js', new Date());
88
+ gtag('config', '${gtagId}');
89
+ `.trim();
90
+ }
91
+ function createConsentRestoreScript() {
92
+ return `
93
+ const consentState = window.localStorage.getItem('consent_state');
94
+ if (window.gtag && (consentState === 'granted' || consentState === 'denied')) {
95
+ window.gtag('consent', 'update', {
96
+ ad_storage: consentState,
97
+ ad_user_data: consentState,
98
+ ad_personalization: consentState,
99
+ analytics_storage: consentState,
100
+ });
101
+ }
102
+ `.trim();
103
+ }
104
+
105
+ // ../tracking-core/src/tracking.ts
106
+ var TRACKING_COOKIE_MAX_AGE_SECONDS = 7776e3;
107
+ var TRACKING_PARAM_KEYS = [
108
+ "gclid",
109
+ "fbclid",
110
+ "utm_source",
111
+ "utm_medium",
112
+ "utm_campaign",
113
+ "utm_term",
114
+ "utm_content"
115
+ ];
116
+ function createEmptyTrackingParams() {
117
+ return {
118
+ gclid: null,
119
+ fbclid: null,
120
+ utm_source: null,
121
+ utm_medium: null,
122
+ utm_campaign: null,
123
+ utm_term: null,
124
+ utm_content: null
125
+ };
126
+ }
127
+ function normalizeTrackingCookieValue(value) {
128
+ return typeof value === "string" && value.length > 0 ? value : null;
129
+ }
130
+ function getTrackingParamsFromCookieReader(readCookie) {
131
+ return TRACKING_PARAM_KEYS.reduce((params, key) => {
132
+ params[key] = normalizeTrackingCookieValue(readCookie(key));
133
+ return params;
134
+ }, createEmptyTrackingParams());
135
+ }
136
+ function getTrackingQueryValues(searchParams) {
137
+ return TRACKING_PARAM_KEYS.reduce((params, key) => {
138
+ const value = searchParams.get(key);
139
+ if (typeof value === "string" && value.trim().length > 0) {
140
+ params[key] = value;
141
+ }
142
+ return params;
143
+ }, {});
144
+ }
145
+ function getCookieValueFromDocument(key) {
146
+ if (typeof document === "undefined")
147
+ return null;
148
+ const cookies2 = document.cookie ? document.cookie.split("; ") : [];
149
+ const match = cookies2.find((cookie) => cookie.startsWith(`${key}=`));
150
+ if (!match)
151
+ return null;
152
+ const [, rawValue = ""] = match.split("=");
153
+ return normalizeTrackingCookieValue(decodeURIComponent(rawValue));
154
+ }
155
+ function setTrackingCookie(key, value, maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS) {
156
+ if (typeof document === "undefined")
157
+ return;
158
+ const encodedValue = encodeURIComponent(value);
159
+ document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;
160
+ }
161
+ function persistTrackingParamsFromSearchParams(searchParams, maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS) {
162
+ const trackingValues = getTrackingQueryValues(searchParams);
163
+ Object.entries(trackingValues).forEach(([key, value]) => {
164
+ setTrackingCookie(key, value, maxAgeSeconds);
165
+ });
166
+ return trackingValues;
167
+ }
168
+ function captureTrackingParamsFromLocation(url = typeof window === "undefined" ? "" : window.location.href, maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS) {
169
+ const resolvedUrl = typeof window === "undefined" ? new URL(url || "https://example.invalid") : new URL(url, window.location.origin);
170
+ persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);
171
+ return getTrackingParamsFromCookieReader(getCookieValueFromDocument);
172
+ }
173
+
174
+ // src/ConsentBanner.tsx
175
+ import { useEffect, useState } from "react";
176
+ import { jsx, jsxs } from "react/jsx-runtime";
177
+ function ConsentBanner() {
178
+ const [consentState, setBannerConsentState] = useState("pending");
179
+ const [isReady, setIsReady] = useState(false);
180
+ useEffect(() => {
181
+ setBannerConsentState(getConsentState());
182
+ setIsReady(true);
183
+ }, []);
184
+ if (!isReady || consentState !== "pending")
185
+ return null;
186
+ const handleAccept = () => {
187
+ setConsentState("granted");
188
+ setBannerConsentState("granted");
189
+ };
190
+ const handleDecline = () => {
191
+ setConsentState("denied");
192
+ setBannerConsentState("denied");
193
+ };
194
+ return /* @__PURE__ */ jsx("div", { className: "fixed bottom-0 left-0 right-0 z-50 border-t border-gray-200 bg-white p-4 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-5xl flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
195
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-700", children: "We use cookies to understand ad performance and improve how our marketing works across visits. You can accept or decline this tracking." }),
196
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [
197
+ /* @__PURE__ */ jsx(
198
+ "button",
199
+ {
200
+ type: "button",
201
+ onClick: handleDecline,
202
+ className: "rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100",
203
+ children: "Decline"
204
+ }
205
+ ),
206
+ /* @__PURE__ */ jsx(
207
+ "button",
208
+ {
209
+ type: "button",
210
+ onClick: handleAccept,
211
+ className: "rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-800",
212
+ children: "Accept"
213
+ }
214
+ )
215
+ ] })
216
+ ] }) });
217
+ }
218
+
219
+ // src/hooks.ts
220
+ import { useEffect as useEffect2, useState as useState2 } from "react";
221
+ function useGclid() {
222
+ const [gclid, setGclid] = useState2(null);
223
+ useEffect2(() => {
224
+ setGclid(getCookieValueFromDocument("gclid"));
225
+ }, []);
226
+ return gclid;
227
+ }
228
+ function useTrackingParams() {
229
+ const [trackingParams, setTrackingParams] = useState2(createEmptyTrackingParams());
230
+ useEffect2(() => {
231
+ setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));
232
+ }, []);
233
+ return trackingParams;
234
+ }
235
+ function useConsentState() {
236
+ const [consentState, setConsentStateValue] = useState2("pending");
237
+ useEffect2(() => {
238
+ setConsentStateValue(getConsentState());
239
+ const handleStorage = (event) => {
240
+ if (event.key === "consent_state")
241
+ setConsentStateValue(getConsentState());
242
+ };
243
+ window.addEventListener("storage", handleStorage);
244
+ return () => {
245
+ window.removeEventListener("storage", handleStorage);
246
+ };
247
+ }, []);
248
+ return consentState;
249
+ }
250
+
251
+ // src/GoogleAdsTracking.tsx
252
+ import Script from "next/script";
253
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
254
+ function GoogleAdsTracking({ gtagId }) {
255
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
256
+ /* @__PURE__ */ jsx2(Script, { id: "consent-default", strategy: "beforeInteractive", children: createConsentDefaultScript() }),
257
+ /* @__PURE__ */ jsx2(
258
+ Script,
259
+ {
260
+ src: `https://www.googletagmanager.com/gtag/js?id=${gtagId}`,
261
+ strategy: "afterInteractive"
262
+ }
263
+ ),
264
+ /* @__PURE__ */ jsx2(Script, { id: "gtag-init", strategy: "afterInteractive", children: createGtagInitScript(gtagId) }),
265
+ /* @__PURE__ */ jsx2(Script, { id: "consent-restore", strategy: "afterInteractive", children: createConsentRestoreScript() })
266
+ ] });
267
+ }
268
+
269
+ // src/middleware.ts
270
+ import "server-only";
271
+ import { NextResponse } from "next/server";
272
+ function createTrackingMiddleware() {
273
+ return (request) => {
274
+ const response = NextResponse.next();
275
+ const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);
276
+ Object.entries(trackingValues).forEach(([param, value]) => {
277
+ response.cookies.set(param, value, {
278
+ maxAge: 7776e3,
279
+ path: "/",
280
+ httpOnly: false
281
+ });
282
+ });
283
+ return response;
284
+ };
285
+ }
286
+
287
+ // src/server.ts
288
+ import "server-only";
289
+ import { cookies } from "next/headers";
290
+ function getCookieStore() {
291
+ return cookies();
292
+ }
293
+ function getTrackingParamsServer() {
294
+ const cookieStore = getCookieStore();
295
+ return getTrackingParamsFromCookieReader((key) => cookieStore.get(key)?.value);
296
+ }
297
+ export {
298
+ ConsentBanner,
299
+ GoogleAdsTracking,
300
+ TRACKING_PARAM_KEYS,
301
+ captureTrackingParamsFromLocation,
302
+ createTrackingClientContext,
303
+ createTrackingEventCreatePayload,
304
+ createTrackingMiddleware,
305
+ createTrackingSessionUpsertPayload,
306
+ getConsentState,
307
+ getTrackingParamsServer,
308
+ setConsentState,
309
+ useConsentState,
310
+ useGclid,
311
+ useTrackingParams
312
+ };
313
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../src/ConsentBanner.tsx","../src/hooks.ts","../src/GoogleAdsTracking.tsx","../src/middleware.ts","../src/server.ts"],"sourcesContent":["import type { ConsentState } from './types';\n\nexport const CONSENT_STATE_KEY = 'consent_state';\nexport const CONSENT_TIMESTAMP_KEY = 'consent_timestamp';\n\nexport type GtagConsentValue = 'granted' | 'denied';\n\nexport interface ConsentUpdatePayload {\n ad_storage: GtagConsentValue;\n ad_user_data: GtagConsentValue;\n ad_personalization: GtagConsentValue;\n analytics_storage: GtagConsentValue;\n}\n\nexport function buildConsentPayload(state: GtagConsentValue): ConsentUpdatePayload {\n return {\n ad_storage: state,\n ad_user_data: state,\n ad_personalization: state,\n analytics_storage: state,\n };\n}\n\nexport function getConsentState(): ConsentState {\n if (typeof window === 'undefined')\n return 'pending';\n\n const storedState = window.localStorage.getItem(CONSENT_STATE_KEY);\n\n if (storedState === 'granted' || storedState === 'denied')\n return storedState;\n\n return 'pending';\n}\n\nexport function setConsentState(state: GtagConsentValue): void {\n if (typeof window === 'undefined')\n return;\n\n window.localStorage.setItem(CONSENT_STATE_KEY, state);\n window.localStorage.setItem(CONSENT_TIMESTAMP_KEY, new Date().toISOString());\n\n if (typeof window.gtag === 'function')\n window.gtag('consent', 'update', buildConsentPayload(state));\n}\n\nexport function restoreStoredConsent(): ConsentState {\n const consentState = getConsentState();\n\n if (consentState === 'granted' || consentState === 'denied')\n setConsentState(consentState);\n\n return consentState;\n}\n","import type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\ninterface TrackingContextInput {\n packageName?: string | null;\n pageTitle?: string | null;\n referrer?: string | null;\n sdkVersion?: string | null;\n siteOrigin?: string | null;\n}\n\ninterface TrackingEventInput {\n eventType: string;\n metadata?: Record<string, unknown> | null;\n pageUrl?: string | null;\n sessionId: string;\n}\n\ninterface TrackingSessionInput {\n consentState?: Record<string, unknown> | null;\n firstPage?: string | null;\n sessionId: string;\n}\n\nexport function createTrackingClientContext(\n surface: TrackingInstallSurface,\n input: TrackingContextInput = {},\n): TrackingClientContext {\n return {\n surface,\n sdk_version: input.sdkVersion ?? null,\n package_name: input.packageName ?? null,\n site_origin: input.siteOrigin ?? (typeof window === 'undefined' ? null : window.location.origin),\n page_title: input.pageTitle ?? (typeof document === 'undefined' ? null : document.title || null),\n referrer: input.referrer ?? (typeof document === 'undefined' ? null : document.referrer || null),\n };\n}\n\nexport function createTrackingSessionUpsertPayload(\n trackingParams: TrackingParams,\n input: TrackingSessionInput,\n context: TrackingClientContext,\n): TrackingSessionUpsertPayload {\n return {\n session_id: input.sessionId,\n gclid: trackingParams.gclid,\n fbclid: trackingParams.fbclid,\n utm_source: trackingParams.utm_source,\n utm_medium: trackingParams.utm_medium,\n utm_campaign: trackingParams.utm_campaign,\n utm_term: trackingParams.utm_term,\n utm_content: trackingParams.utm_content,\n first_page: input.firstPage ?? null,\n consent_state: input.consentState ?? null,\n context,\n };\n}\n\nexport function createTrackingEventCreatePayload(\n trackingParams: TrackingParams,\n input: TrackingEventInput,\n context: TrackingClientContext,\n): TrackingEventCreatePayload {\n return {\n session_id: input.sessionId,\n event_type: input.eventType,\n gclid: trackingParams.gclid,\n fbclid: trackingParams.fbclid,\n page_url: input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n context,\n };\n}\n","import { buildConsentPayload, getConsentState, restoreStoredConsent } from './consent';\n\nexport const GTAG_SCRIPT_HOST = 'https://www.googletagmanager.com/gtag/js';\nexport const TRACKING_SCRIPT_ATTRIBUTE = 'data-aranova-tracking';\n\nfunction getScriptMarker(id: string): string {\n return `aranova-${id}`;\n}\n\nexport function createConsentDefaultScript(): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('consent', 'default', {\n ad_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied',\n analytics_storage: 'denied',\n wait_for_update: 500,\n});\n`.trim();\n}\n\nexport function createGtagInitScript(gtagId: string): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('js', new Date());\ngtag('config', '${gtagId}');\n`.trim();\n}\n\nexport function createConsentRestoreScript(): string {\n return `\nconst consentState = window.localStorage.getItem('consent_state');\nif (window.gtag && (consentState === 'granted' || consentState === 'denied')) {\n window.gtag('consent', 'update', {\n ad_storage: consentState,\n ad_user_data: consentState,\n ad_personalization: consentState,\n analytics_storage: consentState,\n });\n}\n`.trim();\n}\n\nexport function ensureGtagFunction(): NonNullable<Window['gtag']> {\n window.dataLayer = window.dataLayer || [];\n\n if (typeof window.gtag === 'function')\n return window.gtag;\n\n window.gtag = (...args: unknown[]) => {\n window.dataLayer?.push(args);\n };\n\n return window.gtag;\n}\n\nexport function applyDefaultConsentState(): void {\n const gtag = ensureGtagFunction();\n gtag('consent', 'default', {\n ad_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied',\n analytics_storage: 'denied',\n wait_for_update: 500,\n });\n}\n\nexport function loadGtagScript(gtagId: string): void {\n if (typeof document === 'undefined')\n return;\n\n const marker = getScriptMarker('gtag-loader');\n const existingScript = document.querySelector<HTMLScriptElement>(`script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`);\n if (existingScript)\n return;\n\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_HOST}?id=${encodeURIComponent(gtagId)}`;\n script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);\n document.head.appendChild(script);\n}\n\nexport function initializeGtag(gtagId: string): void {\n const gtag = ensureGtagFunction();\n gtag('js', new Date());\n gtag('config', gtagId);\n}\n\nexport function restoreConsentState(): void {\n if (typeof window === 'undefined')\n return;\n\n const consentState = getConsentState();\n if (consentState === 'granted' || consentState === 'denied')\n window.gtag?.('consent', 'update', buildConsentPayload(consentState));\n}\n\nexport function bootstrapGoogleAdsTracking(gtagId: string): void {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\n restoreStoredConsent();\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { getConsentState, setConsentState, type ConsentState } from '../../tracking-core/src/index';\n\nexport function ConsentBanner() {\n const [consentState, setBannerConsentState] = useState<ConsentState>('pending');\n const [isReady, setIsReady] = useState(false);\n\n useEffect(() => {\n setBannerConsentState(getConsentState());\n setIsReady(true);\n }, []);\n\n if (!isReady || consentState !== 'pending')\n return null;\n\n const handleAccept = (): void => {\n setConsentState('granted');\n setBannerConsentState('granted');\n };\n\n const handleDecline = (): void => {\n setConsentState('denied');\n setBannerConsentState('denied');\n };\n\n return (\n <div className=\"fixed bottom-0 left-0 right-0 z-50 border-t border-gray-200 bg-white p-4 shadow-lg\">\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <p className=\"text-sm text-gray-700\">\n We use cookies to understand ad performance and improve how our marketing works across visits.\n You can accept or decline this tracking.\n </p>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <button\n type=\"button\"\n onClick={handleDecline}\n className=\"rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100\"\n >\n Decline\n </button>\n <button\n type=\"button\"\n onClick={handleAccept}\n className=\"rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-800\"\n >\n Accept\n </button>\n </div>\n </div>\n </div>\n );\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { getConsentState } from '../../tracking-core/src/index';\nimport {\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n type ConsentState,\n type TrackingParams,\n} from '../../tracking-core/src/index';\n\nexport function useGclid(): string | null {\n const [gclid, setGclid] = useState<string | null>(null);\n\n useEffect(() => {\n setGclid(getCookieValueFromDocument('gclid'));\n }, []);\n\n return gclid;\n}\n\nexport function useTrackingParams(): TrackingParams {\n const [trackingParams, setTrackingParams] = useState<TrackingParams>(createEmptyTrackingParams());\n\n useEffect(() => {\n setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));\n }, []);\n\n return trackingParams;\n}\n\nexport function useConsentState(): ConsentState {\n const [consentState, setConsentStateValue] = useState<ConsentState>('pending');\n\n useEffect(() => {\n setConsentStateValue(getConsentState());\n\n const handleStorage = (event: StorageEvent): void => {\n if (event.key === 'consent_state')\n setConsentStateValue(getConsentState());\n };\n\n window.addEventListener('storage', handleStorage);\n\n return () => {\n window.removeEventListener('storage', handleStorage);\n };\n }, []);\n\n return consentState;\n}\n","'use client';\n\nimport Script from 'next/script';\n\nimport {\n createConsentDefaultScript,\n createConsentRestoreScript,\n createGtagInitScript,\n} from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n gtagId: string;\n}\n\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n return (\n <>\n <Script id=\"consent-default\" strategy=\"beforeInteractive\">\n {createConsentDefaultScript()}\n </Script>\n <Script\n src={`https://www.googletagmanager.com/gtag/js?id=${gtagId}`}\n strategy=\"afterInteractive\"\n />\n <Script id=\"gtag-init\" strategy=\"afterInteractive\">\n {createGtagInitScript(gtagId)}\n </Script>\n <Script id=\"consent-restore\" strategy=\"afterInteractive\">\n {createConsentRestoreScript()}\n </Script>\n </>\n );\n}\n","import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import 'server-only';\n\nimport { cookies } from 'next/headers';\n\nimport { getTrackingParamsFromCookieReader, type TrackingParams } from '../../tracking-core/src/index';\n\ninterface CookieValue {\n value: string;\n}\n\ninterface CookieStore {\n get(name: string): CookieValue | undefined;\n}\n\nfunction getCookieStore(): CookieStore {\n return cookies() as unknown as CookieStore;\n}\n\nexport function getTrackingParamsServer(): TrackingParams {\n const cookieStore = getCookieStore();\n return getTrackingParamsFromCookieReader((key) => cookieStore.get(key)?.value);\n}\n"],"mappings":"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","names":["cookies","useEffect","useState","useState","useEffect","jsx","jsxs"]}
@@ -0,0 +1,5 @@
1
+ import { NextMiddleware } from 'next/server';
2
+
3
+ declare function createTrackingMiddleware(): NextMiddleware;
4
+
5
+ export { createTrackingMiddleware };
@@ -0,0 +1,5 @@
1
+ import { NextMiddleware } from 'next/server';
2
+
3
+ declare function createTrackingMiddleware(): NextMiddleware;
4
+
5
+ export { createTrackingMiddleware };
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/middleware.ts
21
+ var middleware_exports = {};
22
+ __export(middleware_exports, {
23
+ createTrackingMiddleware: () => createTrackingMiddleware
24
+ });
25
+ module.exports = __toCommonJS(middleware_exports);
26
+ var import_server_only = require("server-only");
27
+ var import_server = require("next/server");
28
+
29
+ // ../tracking-core/src/tracking.ts
30
+ var TRACKING_PARAM_KEYS = [
31
+ "gclid",
32
+ "fbclid",
33
+ "utm_source",
34
+ "utm_medium",
35
+ "utm_campaign",
36
+ "utm_term",
37
+ "utm_content"
38
+ ];
39
+ function getTrackingQueryValues(searchParams) {
40
+ return TRACKING_PARAM_KEYS.reduce((params, key) => {
41
+ const value = searchParams.get(key);
42
+ if (typeof value === "string" && value.trim().length > 0) {
43
+ params[key] = value;
44
+ }
45
+ return params;
46
+ }, {});
47
+ }
48
+
49
+ // src/middleware.ts
50
+ function createTrackingMiddleware() {
51
+ return (request) => {
52
+ const response = import_server.NextResponse.next();
53
+ const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);
54
+ Object.entries(trackingValues).forEach(([param, value]) => {
55
+ response.cookies.set(param, value, {
56
+ maxAge: 7776e3,
57
+ path: "/",
58
+ httpOnly: false
59
+ });
60
+ });
61
+ return response;
62
+ };
63
+ }
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ createTrackingMiddleware
67
+ });
68
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts","../../tracking-core/src/tracking.ts"],"sourcesContent":["import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAO;AAEP,oBAA6B;;;ACEtB,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA6BO,SAAS,uBACd,cAC2C;AAC3C,SAAO,oBAAoB,OAAkD,CAAC,QAAQ,QAAQ;AAC5F,UAAM,QAAQ,aAAa,IAAI,GAAG;AAElC,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,aAAO,GAAG,IAAI;AAAA,IAChB;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;;;AD9CO,SAAS,2BAA2C;AACzD,SAAO,CAAC,YAAyB;AAC/B,UAAM,WAAW,2BAAa,KAAK;AACnC,UAAM,iBAAiB,uBAAuB,QAAQ,QAAQ,YAAY;AAE1E,WAAO,QAAQ,cAAc,EAAE,QAAQ,CAAC,CAAC,OAAO,KAAK,MAAM;AACzD,eAAS,QAAQ,IAAI,OAAO,OAAO;AAAA,QACjC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,43 @@
1
+ // src/middleware.ts
2
+ import "server-only";
3
+ import { NextResponse } from "next/server";
4
+
5
+ // ../tracking-core/src/tracking.ts
6
+ var TRACKING_PARAM_KEYS = [
7
+ "gclid",
8
+ "fbclid",
9
+ "utm_source",
10
+ "utm_medium",
11
+ "utm_campaign",
12
+ "utm_term",
13
+ "utm_content"
14
+ ];
15
+ function getTrackingQueryValues(searchParams) {
16
+ return TRACKING_PARAM_KEYS.reduce((params, key) => {
17
+ const value = searchParams.get(key);
18
+ if (typeof value === "string" && value.trim().length > 0) {
19
+ params[key] = value;
20
+ }
21
+ return params;
22
+ }, {});
23
+ }
24
+
25
+ // src/middleware.ts
26
+ function createTrackingMiddleware() {
27
+ return (request) => {
28
+ const response = NextResponse.next();
29
+ const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);
30
+ Object.entries(trackingValues).forEach(([param, value]) => {
31
+ response.cookies.set(param, value, {
32
+ maxAge: 7776e3,
33
+ path: "/",
34
+ httpOnly: false
35
+ });
36
+ });
37
+ return response;
38
+ };
39
+ }
40
+ export {
41
+ createTrackingMiddleware
42
+ };
43
+ //# sourceMappingURL=middleware.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts","../../tracking-core/src/tracking.ts"],"sourcesContent":["import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n"],"mappings":";AAAA,OAAO;AAEP,SAAS,oBAAoB;;;ACEtB,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA6BO,SAAS,uBACd,cAC2C;AAC3C,SAAO,oBAAoB,OAAkD,CAAC,QAAQ,QAAQ;AAC5F,UAAM,QAAQ,aAAa,IAAI,GAAG;AAElC,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,aAAO,GAAG,IAAI;AAAA,IAChB;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;;;AD9CO,SAAS,2BAA2C;AACzD,SAAO,CAAC,YAAyB;AAC/B,UAAM,WAAW,aAAa,KAAK;AACnC,UAAM,iBAAiB,uBAAuB,QAAQ,QAAQ,YAAY;AAE1E,WAAO,QAAQ,cAAc,EAAE,QAAQ,CAAC,CAAC,OAAO,KAAK,MAAM;AACzD,eAAS,QAAQ,IAAI,OAAO,OAAO;AAAA,QACjC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,67 @@
1
+ type ConsentState = 'granted' | 'denied' | 'pending';
2
+ interface TrackingParams {
3
+ gclid: string | null;
4
+ fbclid: string | null;
5
+ utm_source: string | null;
6
+ utm_medium: string | null;
7
+ utm_campaign: string | null;
8
+ utm_term: string | null;
9
+ utm_content: string | null;
10
+ }
11
+ type TrackingInstallSurface = 'next' | 'react' | 'script';
12
+ interface TrackingClientContext {
13
+ surface: TrackingInstallSurface;
14
+ sdk_version: string | null;
15
+ package_name: string | null;
16
+ site_origin: string | null;
17
+ page_title: string | null;
18
+ referrer: string | null;
19
+ }
20
+ interface TrackingSessionUpsertPayload {
21
+ session_id: string;
22
+ gclid: string | null;
23
+ fbclid: string | null;
24
+ utm_source: string | null;
25
+ utm_medium: string | null;
26
+ utm_campaign: string | null;
27
+ utm_term: string | null;
28
+ utm_content: string | null;
29
+ first_page: string | null;
30
+ consent_state: Record<string, unknown> | null;
31
+ context: TrackingClientContext;
32
+ }
33
+ interface TrackingEventCreatePayload {
34
+ session_id: string;
35
+ event_type: string;
36
+ gclid: string | null;
37
+ fbclid: string | null;
38
+ page_url: string | null;
39
+ metadata: Record<string, unknown> | null;
40
+ context: TrackingClientContext;
41
+ }
42
+ interface TrackingInitConfig {
43
+ apiKey?: string;
44
+ endpoint?: string;
45
+ gtagId?: string;
46
+ autoCaptureTrackingParams?: boolean;
47
+ renderConsentBanner?: boolean;
48
+ cookieMaxAgeSeconds?: number;
49
+ surface?: TrackingInstallSurface;
50
+ }
51
+ declare global {
52
+ interface Window {
53
+ gtag?: (...args: unknown[]) => void;
54
+ dataLayer?: unknown[];
55
+ AranovaTracking?: {
56
+ init: (config: TrackingInitConfig) => void;
57
+ captureTrackingParams: () => TrackingParams;
58
+ getTrackingParams: () => TrackingParams;
59
+ getConsentState: () => ConsentState;
60
+ setConsentState: (state: 'granted' | 'denied') => void;
61
+ };
62
+ }
63
+ }
64
+
65
+ declare function getTrackingParamsServer(): TrackingParams;
66
+
67
+ export { type ConsentState as C, type TrackingInstallSurface as T, type TrackingClientContext as a, type TrackingParams as b, type TrackingEventCreatePayload as c, type TrackingSessionUpsertPayload as d, type TrackingInitConfig as e, getTrackingParamsServer as g };
@@ -0,0 +1,67 @@
1
+ type ConsentState = 'granted' | 'denied' | 'pending';
2
+ interface TrackingParams {
3
+ gclid: string | null;
4
+ fbclid: string | null;
5
+ utm_source: string | null;
6
+ utm_medium: string | null;
7
+ utm_campaign: string | null;
8
+ utm_term: string | null;
9
+ utm_content: string | null;
10
+ }
11
+ type TrackingInstallSurface = 'next' | 'react' | 'script';
12
+ interface TrackingClientContext {
13
+ surface: TrackingInstallSurface;
14
+ sdk_version: string | null;
15
+ package_name: string | null;
16
+ site_origin: string | null;
17
+ page_title: string | null;
18
+ referrer: string | null;
19
+ }
20
+ interface TrackingSessionUpsertPayload {
21
+ session_id: string;
22
+ gclid: string | null;
23
+ fbclid: string | null;
24
+ utm_source: string | null;
25
+ utm_medium: string | null;
26
+ utm_campaign: string | null;
27
+ utm_term: string | null;
28
+ utm_content: string | null;
29
+ first_page: string | null;
30
+ consent_state: Record<string, unknown> | null;
31
+ context: TrackingClientContext;
32
+ }
33
+ interface TrackingEventCreatePayload {
34
+ session_id: string;
35
+ event_type: string;
36
+ gclid: string | null;
37
+ fbclid: string | null;
38
+ page_url: string | null;
39
+ metadata: Record<string, unknown> | null;
40
+ context: TrackingClientContext;
41
+ }
42
+ interface TrackingInitConfig {
43
+ apiKey?: string;
44
+ endpoint?: string;
45
+ gtagId?: string;
46
+ autoCaptureTrackingParams?: boolean;
47
+ renderConsentBanner?: boolean;
48
+ cookieMaxAgeSeconds?: number;
49
+ surface?: TrackingInstallSurface;
50
+ }
51
+ declare global {
52
+ interface Window {
53
+ gtag?: (...args: unknown[]) => void;
54
+ dataLayer?: unknown[];
55
+ AranovaTracking?: {
56
+ init: (config: TrackingInitConfig) => void;
57
+ captureTrackingParams: () => TrackingParams;
58
+ getTrackingParams: () => TrackingParams;
59
+ getConsentState: () => ConsentState;
60
+ setConsentState: (state: 'granted' | 'denied') => void;
61
+ };
62
+ }
63
+ }
64
+
65
+ declare function getTrackingParamsServer(): TrackingParams;
66
+
67
+ export { type ConsentState as C, type TrackingInstallSurface as T, type TrackingClientContext as a, type TrackingParams as b, type TrackingEventCreatePayload as c, type TrackingSessionUpsertPayload as d, type TrackingInitConfig as e, getTrackingParamsServer as g };
@@ -0,0 +1 @@
1
+ export { g as getTrackingParamsServer } from './server-BMTa7njW.mjs';
@@ -0,0 +1 @@
1
+ export { g as getTrackingParamsServer } from './server-BMTa7njW.js';