@aranova/tracking-react 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  declare function ConsentBanner(): react_jsx_runtime.JSX.Element | null;
4
5
 
@@ -28,6 +29,7 @@ interface TrackingClientContext {
28
29
  }
29
30
  interface TrackingSessionUpsertPayload {
30
31
  session_id: string;
32
+ visitor_id: string | null;
31
33
  gclid: string | null;
32
34
  fbclid: string | null;
33
35
  utm_source: string | null;
@@ -67,6 +69,12 @@ declare global {
67
69
  getTrackingParams: () => TrackingParams;
68
70
  getConsentState: () => ConsentState;
69
71
  setConsentState: (state: 'granted' | 'denied') => void;
72
+ trackEvent: (input: {
73
+ eventType: string;
74
+ pageUrl?: string | null;
75
+ metadata?: Record<string, unknown> | null;
76
+ occurredAt?: Date | string | null;
77
+ }) => void;
70
78
  };
71
79
  }
72
80
  }
@@ -92,6 +100,7 @@ interface TrackingSessionInput {
92
100
  consentState?: Record<string, unknown> | null;
93
101
  firstPage?: string | null;
94
102
  sessionId: string;
103
+ visitorId?: string | null;
95
104
  }
96
105
  declare function createTrackingClientContext(surface: TrackingInstallSurface, input?: TrackingContextInput): TrackingClientContext;
97
106
  declare function createTrackingSessionUpsertPayload(trackingParams: TrackingParams, input: TrackingSessionInput, context: TrackingClientContext): TrackingSessionUpsertPayload;
@@ -100,8 +109,38 @@ declare function createTrackingEventCreatePayload(trackingParams: TrackingParams
100
109
  declare const TRACKING_PARAM_KEYS: readonly ["gclid", "fbclid", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
101
110
  declare function captureTrackingParamsFromLocation(url?: string, maxAgeSeconds?: number): TrackingParams;
102
111
 
112
+ interface TrackEventInput {
113
+ eventType: string;
114
+ pageUrl?: string | null;
115
+ metadata?: Record<string, unknown> | null;
116
+ occurredAt?: Date | string | null;
117
+ }
118
+ interface TrackingClient {
119
+ trackEvent: (input: TrackEventInput) => void;
120
+ flush: () => Promise<void>;
121
+ getSessionId: () => string;
122
+ getVisitorId: () => string;
123
+ destroy: () => void;
124
+ }
125
+
103
126
  declare function useGclid(): string | null;
104
127
  declare function useTrackingParams(): TrackingParams;
105
128
  declare function useConsentState(): ConsentState;
106
129
 
107
- export { ConsentBanner, type ConsentState, GoogleAdsTracking, type GoogleAdsTrackingProps, TRACKING_PARAM_KEYS, type TrackingClientContext, type TrackingEventCreatePayload, type TrackingInitConfig, type TrackingInstallSurface, type TrackingParams, type TrackingSessionUpsertPayload, captureTrackingParamsFromLocation, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, getConsentState, setConsentState, useConsentState, useGclid, useTrackingParams };
130
+ interface TrackingProviderProps {
131
+ /** Public tracking API key issued for this business. Required. */
132
+ apiKey: string;
133
+ /** Full ingest endpoint base URL, e.g. https://api.aranova.io/tracking. Required. */
134
+ endpoint: string;
135
+ /** Optional Google Ads gtag id. If omitted, no gtag script is loaded. */
136
+ gtagId?: string;
137
+ /** Disable automatic page_view emission (manual tracking only). */
138
+ disableAutoPageView?: boolean;
139
+ /** Optional debug flag — currently a no-op, reserved for verbose logging. */
140
+ debug?: boolean;
141
+ children: ReactNode;
142
+ }
143
+ declare function TrackingProvider({ apiKey, endpoint, gtagId, disableAutoPageView, debug, children, }: TrackingProviderProps): react_jsx_runtime.JSX.Element;
144
+ declare function useTracking(): TrackingClient;
145
+
146
+ export { ConsentBanner, type ConsentState, GoogleAdsTracking, type GoogleAdsTrackingProps, TRACKING_PARAM_KEYS, type TrackingClient, type TrackingClientContext, type TrackingEventCreatePayload, type TrackingInitConfig, type TrackingInstallSurface, type TrackingParams, TrackingProvider, type TrackingProviderProps, type TrackingSessionUpsertPayload, captureTrackingParamsFromLocation, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, getConsentState, setConsentState, useConsentState, useGclid, useTracking, useTrackingParams };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  declare function ConsentBanner(): react_jsx_runtime.JSX.Element | null;
4
5
 
@@ -28,6 +29,7 @@ interface TrackingClientContext {
28
29
  }
29
30
  interface TrackingSessionUpsertPayload {
30
31
  session_id: string;
32
+ visitor_id: string | null;
31
33
  gclid: string | null;
32
34
  fbclid: string | null;
33
35
  utm_source: string | null;
@@ -67,6 +69,12 @@ declare global {
67
69
  getTrackingParams: () => TrackingParams;
68
70
  getConsentState: () => ConsentState;
69
71
  setConsentState: (state: 'granted' | 'denied') => void;
72
+ trackEvent: (input: {
73
+ eventType: string;
74
+ pageUrl?: string | null;
75
+ metadata?: Record<string, unknown> | null;
76
+ occurredAt?: Date | string | null;
77
+ }) => void;
70
78
  };
71
79
  }
72
80
  }
@@ -92,6 +100,7 @@ interface TrackingSessionInput {
92
100
  consentState?: Record<string, unknown> | null;
93
101
  firstPage?: string | null;
94
102
  sessionId: string;
103
+ visitorId?: string | null;
95
104
  }
96
105
  declare function createTrackingClientContext(surface: TrackingInstallSurface, input?: TrackingContextInput): TrackingClientContext;
97
106
  declare function createTrackingSessionUpsertPayload(trackingParams: TrackingParams, input: TrackingSessionInput, context: TrackingClientContext): TrackingSessionUpsertPayload;
@@ -100,8 +109,38 @@ declare function createTrackingEventCreatePayload(trackingParams: TrackingParams
100
109
  declare const TRACKING_PARAM_KEYS: readonly ["gclid", "fbclid", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
101
110
  declare function captureTrackingParamsFromLocation(url?: string, maxAgeSeconds?: number): TrackingParams;
102
111
 
112
+ interface TrackEventInput {
113
+ eventType: string;
114
+ pageUrl?: string | null;
115
+ metadata?: Record<string, unknown> | null;
116
+ occurredAt?: Date | string | null;
117
+ }
118
+ interface TrackingClient {
119
+ trackEvent: (input: TrackEventInput) => void;
120
+ flush: () => Promise<void>;
121
+ getSessionId: () => string;
122
+ getVisitorId: () => string;
123
+ destroy: () => void;
124
+ }
125
+
103
126
  declare function useGclid(): string | null;
104
127
  declare function useTrackingParams(): TrackingParams;
105
128
  declare function useConsentState(): ConsentState;
106
129
 
107
- export { ConsentBanner, type ConsentState, GoogleAdsTracking, type GoogleAdsTrackingProps, TRACKING_PARAM_KEYS, type TrackingClientContext, type TrackingEventCreatePayload, type TrackingInitConfig, type TrackingInstallSurface, type TrackingParams, type TrackingSessionUpsertPayload, captureTrackingParamsFromLocation, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, getConsentState, setConsentState, useConsentState, useGclid, useTrackingParams };
130
+ interface TrackingProviderProps {
131
+ /** Public tracking API key issued for this business. Required. */
132
+ apiKey: string;
133
+ /** Full ingest endpoint base URL, e.g. https://api.aranova.io/tracking. Required. */
134
+ endpoint: string;
135
+ /** Optional Google Ads gtag id. If omitted, no gtag script is loaded. */
136
+ gtagId?: string;
137
+ /** Disable automatic page_view emission (manual tracking only). */
138
+ disableAutoPageView?: boolean;
139
+ /** Optional debug flag — currently a no-op, reserved for verbose logging. */
140
+ debug?: boolean;
141
+ children: ReactNode;
142
+ }
143
+ declare function TrackingProvider({ apiKey, endpoint, gtagId, disableAutoPageView, debug, children, }: TrackingProviderProps): react_jsx_runtime.JSX.Element;
144
+ declare function useTracking(): TrackingClient;
145
+
146
+ export { ConsentBanner, type ConsentState, GoogleAdsTracking, type GoogleAdsTrackingProps, TRACKING_PARAM_KEYS, type TrackingClient, type TrackingClientContext, type TrackingEventCreatePayload, type TrackingInitConfig, type TrackingInstallSurface, type TrackingParams, TrackingProvider, type TrackingProviderProps, type TrackingSessionUpsertPayload, captureTrackingParamsFromLocation, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, getConsentState, setConsentState, useConsentState, useGclid, useTracking, useTrackingParams };
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ __export(src_exports, {
23
23
  ConsentBanner: () => ConsentBanner,
24
24
  GoogleAdsTracking: () => GoogleAdsTracking,
25
25
  TRACKING_PARAM_KEYS: () => TRACKING_PARAM_KEYS,
26
+ TrackingProvider: () => TrackingProvider,
26
27
  captureTrackingParamsFromLocation: () => captureTrackingParamsFromLocation,
27
28
  createTrackingClientContext: () => createTrackingClientContext,
28
29
  createTrackingEventCreatePayload: () => createTrackingEventCreatePayload,
@@ -31,6 +32,7 @@ __export(src_exports, {
31
32
  setConsentState: () => setConsentState,
32
33
  useConsentState: () => useConsentState,
33
34
  useGclid: () => useGclid,
35
+ useTracking: () => useTracking,
34
36
  useTrackingParams: () => useTrackingParams
35
37
  });
36
38
  module.exports = __toCommonJS(src_exports);
@@ -86,6 +88,7 @@ function createTrackingClientContext(surface, input = {}) {
86
88
  function createTrackingSessionUpsertPayload(trackingParams, input, context) {
87
89
  return {
88
90
  session_id: input.sessionId,
91
+ visitor_id: input.visitorId ?? null,
89
92
  gclid: trackingParams.gclid,
90
93
  fbclid: trackingParams.fbclid,
91
94
  utm_source: trackingParams.utm_source,
@@ -231,6 +234,282 @@ function captureTrackingParamsFromLocation(url = typeof window === "undefined" ?
231
234
  return getTrackingParamsFromCookieReader(getCookieValueFromDocument);
232
235
  }
233
236
 
237
+ // ../tracking-core/src/session.ts
238
+ var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
239
+ var SESSION_STORAGE_KEY = "aranova_tracking_session";
240
+ var SESSION_IDLE_MS = 30 * 60 * 1e3;
241
+ function safeUuid() {
242
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
243
+ return crypto.randomUUID();
244
+ return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;
245
+ }
246
+ function readLocalStorage(key) {
247
+ try {
248
+ return window.localStorage.getItem(key);
249
+ } catch {
250
+ return null;
251
+ }
252
+ }
253
+ function writeLocalStorage(key, value) {
254
+ try {
255
+ window.localStorage.setItem(key, value);
256
+ } catch {
257
+ }
258
+ }
259
+ function getVisitorId() {
260
+ if (typeof window === "undefined")
261
+ return safeUuid();
262
+ const existing = readLocalStorage(VISITOR_STORAGE_KEY);
263
+ if (existing && existing.length > 0)
264
+ return existing;
265
+ const fresh = safeUuid();
266
+ writeLocalStorage(VISITOR_STORAGE_KEY, fresh);
267
+ return fresh;
268
+ }
269
+ function getOrRotateSessionId(now = Date.now()) {
270
+ if (typeof window === "undefined")
271
+ return safeUuid();
272
+ const raw = readLocalStorage(SESSION_STORAGE_KEY);
273
+ if (raw) {
274
+ try {
275
+ const parsed = JSON.parse(raw);
276
+ if (typeof parsed.id === "string" && typeof parsed.last_event_at === "number") {
277
+ if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
278
+ const refreshed = { id: parsed.id, last_event_at: now };
279
+ writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
280
+ return parsed.id;
281
+ }
282
+ }
283
+ } catch {
284
+ }
285
+ }
286
+ const fresh = { id: safeUuid(), last_event_at: now };
287
+ writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
288
+ return fresh.id;
289
+ }
290
+
291
+ // ../tracking-core/src/ingest.ts
292
+ var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
293
+ var DEFAULT_MAX_QUEUE_SIZE = 10;
294
+ var HARD_MAX_BATCH = 50;
295
+ var API_KEY_HEADER = "X-Aranova-Api-Key";
296
+ function buildContext(surface, sdkVersion, packageName) {
297
+ return {
298
+ surface,
299
+ sdk_version: sdkVersion,
300
+ package_name: packageName,
301
+ site_origin: typeof window === "undefined" ? null : window.location.origin,
302
+ page_title: typeof document === "undefined" ? null : document.title || null,
303
+ referrer: typeof document === "undefined" ? null : document.referrer || null
304
+ };
305
+ }
306
+ function readTrackingParams() {
307
+ if (typeof window === "undefined")
308
+ return createEmptyTrackingParams();
309
+ try {
310
+ captureTrackingParamsFromLocation();
311
+ } catch {
312
+ }
313
+ return getTrackingParamsFromCookieReader(getCookieValueFromDocument);
314
+ }
315
+ function consentSnapshot() {
316
+ try {
317
+ return { state: getConsentState() };
318
+ } catch {
319
+ return null;
320
+ }
321
+ }
322
+ async function postWithFetch(url, body, apiKey, keepalive) {
323
+ if (typeof fetch !== "function")
324
+ return;
325
+ try {
326
+ await fetch(url, {
327
+ method: "POST",
328
+ headers: {
329
+ "Content-Type": "application/json",
330
+ [API_KEY_HEADER]: apiKey
331
+ },
332
+ body,
333
+ keepalive,
334
+ // CORS is open on the tracking endpoint; never send cookies.
335
+ credentials: "omit",
336
+ mode: "cors"
337
+ });
338
+ } catch {
339
+ }
340
+ }
341
+ function createTrackingClient(config) {
342
+ const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
343
+ const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);
344
+ const sdkVersion = config.sdkVersion ?? null;
345
+ const packageName = config.packageName ?? null;
346
+ const endpointBase = config.endpoint.replace(/\/$/, "");
347
+ const eventsUrl = `${endpointBase}/events`;
348
+ let queue = [];
349
+ let flushTimer = null;
350
+ let firstPage = null;
351
+ let destroyed = false;
352
+ const visitorId = getVisitorId();
353
+ let sessionId = getOrRotateSessionId();
354
+ if (typeof window !== "undefined")
355
+ firstPage = window.location.href;
356
+ function buildSessionPayload() {
357
+ sessionId = getOrRotateSessionId();
358
+ const params = readTrackingParams();
359
+ const context = buildContext(config.surface, sdkVersion, packageName);
360
+ return {
361
+ session_id: sessionId,
362
+ visitor_id: visitorId,
363
+ gclid: params.gclid,
364
+ fbclid: params.fbclid,
365
+ utm_source: params.utm_source,
366
+ utm_medium: params.utm_medium,
367
+ utm_campaign: params.utm_campaign,
368
+ utm_term: params.utm_term,
369
+ utm_content: params.utm_content,
370
+ first_page: firstPage,
371
+ consent_state: consentSnapshot(),
372
+ context
373
+ };
374
+ }
375
+ function scheduleFlush() {
376
+ if (flushTimer !== null || destroyed)
377
+ return;
378
+ flushTimer = setTimeout(() => {
379
+ flushTimer = null;
380
+ void flush();
381
+ }, flushIntervalMs);
382
+ }
383
+ function clearScheduledFlush() {
384
+ if (flushTimer !== null) {
385
+ clearTimeout(flushTimer);
386
+ flushTimer = null;
387
+ }
388
+ }
389
+ async function flush() {
390
+ if (queue.length === 0)
391
+ return;
392
+ const events = queue.slice(0, HARD_MAX_BATCH);
393
+ queue = queue.slice(events.length);
394
+ clearScheduledFlush();
395
+ const body = {
396
+ session: buildSessionPayload(),
397
+ events
398
+ };
399
+ const serialized = JSON.stringify(body);
400
+ await postWithFetch(eventsUrl, serialized, config.apiKey, false);
401
+ }
402
+ function trackEvent(input) {
403
+ if (destroyed)
404
+ return;
405
+ if (!input || typeof input.eventType !== "string" || input.eventType.length === 0)
406
+ return;
407
+ const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : (/* @__PURE__ */ new Date()).toISOString();
408
+ queue.push({
409
+ event_type: input.eventType,
410
+ page_url: input.pageUrl ?? (typeof window === "undefined" ? null : window.location.href),
411
+ metadata: input.metadata ?? null,
412
+ occurred_at: occurredAt
413
+ });
414
+ if (queue.length >= maxQueueSize) {
415
+ void flush();
416
+ } else {
417
+ scheduleFlush();
418
+ }
419
+ }
420
+ function flushOnUnload() {
421
+ if (queue.length === 0)
422
+ return;
423
+ const events = queue.slice(0, HARD_MAX_BATCH);
424
+ queue = queue.slice(events.length);
425
+ clearScheduledFlush();
426
+ const body = {
427
+ session: buildSessionPayload(),
428
+ events
429
+ };
430
+ const serialized = JSON.stringify(body);
431
+ void postWithFetch(eventsUrl, serialized, config.apiKey, true);
432
+ }
433
+ if (typeof window !== "undefined") {
434
+ window.addEventListener("pagehide", flushOnUnload);
435
+ window.addEventListener("visibilitychange", () => {
436
+ if (document.visibilityState === "hidden") flushOnUnload();
437
+ });
438
+ }
439
+ return {
440
+ trackEvent,
441
+ flush,
442
+ getSessionId: () => sessionId,
443
+ getVisitorId: () => visitorId,
444
+ destroy: () => {
445
+ destroyed = true;
446
+ clearScheduledFlush();
447
+ queue = [];
448
+ if (typeof window !== "undefined") {
449
+ window.removeEventListener("pagehide", flushOnUnload);
450
+ }
451
+ }
452
+ };
453
+ }
454
+
455
+ // ../tracking-core/src/page-view.ts
456
+ function buildPageViewMetadata() {
457
+ if (typeof window === "undefined" || typeof document === "undefined")
458
+ return null;
459
+ return {
460
+ page: {
461
+ title: document.title || null,
462
+ path: window.location.pathname,
463
+ search: window.location.search,
464
+ hash: window.location.hash
465
+ },
466
+ referrer: document.referrer || null,
467
+ viewport: { w: window.innerWidth, h: window.innerHeight }
468
+ };
469
+ }
470
+ function fireManualPageView(client) {
471
+ const metadata = buildPageViewMetadata();
472
+ client.trackEvent({
473
+ eventType: "page_view",
474
+ pageUrl: typeof window === "undefined" ? null : window.location.href,
475
+ metadata
476
+ });
477
+ }
478
+ function attachAutoPageView(client, options = {}) {
479
+ if (typeof window === "undefined" || typeof history === "undefined") {
480
+ return () => {
481
+ };
482
+ }
483
+ let lastPath = window.location.pathname + window.location.search;
484
+ function maybeFire() {
485
+ const current = window.location.pathname + window.location.search;
486
+ if (current === lastPath)
487
+ return;
488
+ lastPath = current;
489
+ fireManualPageView(client);
490
+ }
491
+ const originalPushState = history.pushState.bind(history);
492
+ const originalReplaceState = history.replaceState.bind(history);
493
+ function patchedPushState(...args) {
494
+ originalPushState(...args);
495
+ setTimeout(maybeFire, 0);
496
+ }
497
+ function patchedReplaceState(...args) {
498
+ originalReplaceState(...args);
499
+ setTimeout(maybeFire, 0);
500
+ }
501
+ history.pushState = patchedPushState;
502
+ history.replaceState = patchedReplaceState;
503
+ window.addEventListener("popstate", maybeFire);
504
+ if (!options.skipInitial)
505
+ fireManualPageView(client);
506
+ return () => {
507
+ history.pushState = originalPushState;
508
+ history.replaceState = originalReplaceState;
509
+ window.removeEventListener("popstate", maybeFire);
510
+ };
511
+ }
512
+
234
513
  // src/ConsentBanner.tsx
235
514
  var import_jsx_runtime = require("react/jsx-runtime");
236
515
  function ConsentBanner() {
@@ -315,11 +594,68 @@ function useConsentState() {
315
594
  }, []);
316
595
  return consentState;
317
596
  }
597
+
598
+ // src/TrackingProvider.tsx
599
+ var import_react4 = require("react");
600
+ var import_jsx_runtime2 = require("react/jsx-runtime");
601
+ var TrackingContext = (0, import_react4.createContext)(null);
602
+ function TrackingProvider({
603
+ apiKey,
604
+ endpoint,
605
+ gtagId,
606
+ disableAutoPageView,
607
+ debug,
608
+ children
609
+ }) {
610
+ if (!apiKey)
611
+ throw new Error("TrackingProvider: apiKey is required");
612
+ if (!endpoint)
613
+ throw new Error("TrackingProvider: endpoint is required");
614
+ const clientRef = (0, import_react4.useRef)(null);
615
+ if (clientRef.current === null) {
616
+ clientRef.current = createTrackingClient({
617
+ apiKey,
618
+ endpoint,
619
+ surface: "react",
620
+ packageName: "@aranova/tracking-react",
621
+ debug
622
+ });
623
+ }
624
+ (0, import_react4.useEffect)(() => {
625
+ if (!gtagId)
626
+ return;
627
+ bootstrapGoogleAdsTracking(gtagId);
628
+ }, [gtagId]);
629
+ (0, import_react4.useEffect)(() => {
630
+ const client = clientRef.current;
631
+ if (client === null || disableAutoPageView)
632
+ return;
633
+ const detach = attachAutoPageView(client);
634
+ return () => {
635
+ detach();
636
+ };
637
+ }, [disableAutoPageView]);
638
+ (0, import_react4.useEffect)(() => {
639
+ return () => {
640
+ clientRef.current?.destroy();
641
+ clientRef.current = null;
642
+ };
643
+ }, []);
644
+ const contextValue = (0, import_react4.useMemo)(() => clientRef.current, []);
645
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TrackingContext.Provider, { value: contextValue, children });
646
+ }
647
+ function useTracking() {
648
+ const client = (0, import_react4.useContext)(TrackingContext);
649
+ if (client === null)
650
+ throw new Error("useTracking must be called inside a <TrackingProvider>");
651
+ return client;
652
+ }
318
653
  // Annotate the CommonJS export names for ESM import in node:
319
654
  0 && (module.exports = {
320
655
  ConsentBanner,
321
656
  GoogleAdsTracking,
322
657
  TRACKING_PARAM_KEYS,
658
+ TrackingProvider,
323
659
  captureTrackingParamsFromLocation,
324
660
  createTrackingClientContext,
325
661
  createTrackingEventCreatePayload,
@@ -328,6 +664,7 @@ function useConsentState() {
328
664
  setConsentState,
329
665
  useConsentState,
330
666
  useGclid,
667
+ useTracking,
331
668
  useTrackingParams
332
669
  });
333
670
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/ConsentBanner.tsx","../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../src/GoogleAdsTracking.tsx","../src/hooks.ts"],"sourcesContent":["export { ConsentBanner } from './ConsentBanner';\nexport { GoogleAdsTracking, type GoogleAdsTrackingProps } from './GoogleAdsTracking';\nexport { useConsentState, useGclid, useTrackingParams } from './hooks';\nexport { getConsentState, setConsentState } from '../../tracking-core/src/index';\nexport {\n captureTrackingParamsFromLocation,\n createTrackingClientContext,\n createTrackingEventCreatePayload,\n createTrackingSessionUpsertPayload,\n TRACKING_PARAM_KEYS,\n} from '../../tracking-core/src/index';\nexport type {\n ConsentState,\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInitConfig,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from '../../tracking-core/src/index';\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","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 } from 'react';\n\nimport { bootstrapGoogleAdsTracking } from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n gtagId: string;\n}\n\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n useEffect(() => {\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n return null;\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"],"mappings":"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","names":["import_react","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/ConsentBanner.tsx","../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/ingest.ts","../../tracking-core/src/page-view.ts","../src/GoogleAdsTracking.tsx","../src/hooks.ts","../src/TrackingProvider.tsx"],"sourcesContent":["// Public entry for @aranova/tracking-react — see docs/tracking-package/install-modes.md\nexport { ConsentBanner } from './ConsentBanner';\nexport { GoogleAdsTracking, type GoogleAdsTrackingProps } from './GoogleAdsTracking';\nexport { useConsentState, useGclid, useTrackingParams } from './hooks';\nexport { TrackingProvider, useTracking, type TrackingProviderProps } from './TrackingProvider';\nexport { getConsentState, setConsentState } from '../../tracking-core/src/index';\nexport {\n captureTrackingParamsFromLocation,\n createTrackingClientContext,\n createTrackingEventCreatePayload,\n createTrackingSessionUpsertPayload,\n TRACKING_PARAM_KEYS,\n} from '../../tracking-core/src/index';\nexport type {\n ConsentState,\n TrackingClient,\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInitConfig,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from '../../tracking-core/src/index';\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","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 visitorId?: string | null;\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 visitor_id: input.visitorId ?? null,\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","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return parsed.id;\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return fresh.id;\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","// Tracking ingest client. Queues events, debounce-flushes them to the\n// /tracking/events endpoint, and degrades silently on errors so a broken\n// network never breaks the host site.\n\nimport { getConsentState } from './consent';\nimport {\n captureTrackingParamsFromLocation,\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n} from './tracking';\nimport { getOrRotateSessionId, getVisitorId } from './session';\nimport type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 10;\nexport const HARD_MAX_BATCH = 50;\nexport const API_KEY_HEADER = 'X-Aranova-Api-Key';\n\nexport interface TrackingClientConfig {\n apiKey: string;\n endpoint: string;\n surface: TrackingInstallSurface;\n sdkVersion?: string;\n packageName?: string;\n /** Override the default 2s debounce window. */\n flushIntervalMs?: number;\n /** Override the default 10-event batch trigger. */\n maxQueueSize?: number;\n /** When true, swallow nothing — useful for tests. */\n debug?: boolean;\n}\n\nexport interface TrackEventInput {\n eventType: string;\n pageUrl?: string | null;\n metadata?: Record<string, unknown> | null;\n occurredAt?: Date | string | null;\n}\n\ninterface QueuedEvent {\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n}\n\nexport interface TrackingClient {\n trackEvent: (input: TrackEventInput) => void;\n flush: () => Promise<void>;\n getSessionId: () => string;\n getVisitorId: () => string;\n destroy: () => void;\n}\n\ninterface IngestRequestBody {\n session: TrackingSessionUpsertPayload;\n events: Array<{\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n }>;\n}\n\nfunction buildContext(\n surface: TrackingInstallSurface,\n sdkVersion: string | null,\n packageName: string | null,\n): TrackingClientContext {\n return {\n surface,\n sdk_version: sdkVersion,\n package_name: packageName,\n site_origin: typeof window === 'undefined' ? null : window.location.origin,\n page_title: typeof document === 'undefined' ? null : document.title || null,\n referrer: typeof document === 'undefined' ? null : document.referrer || null,\n };\n}\n\nfunction readTrackingParams(): TrackingParams {\n if (typeof window === 'undefined')\n return createEmptyTrackingParams();\n // Capture from URL on every read so the first event in a session reflects the\n // landing-page params even if the cookie helper hasn't run yet.\n try {\n captureTrackingParamsFromLocation();\n } catch {\n // ignore\n }\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n\nfunction consentSnapshot(): Record<string, unknown> | null {\n try {\n return { state: getConsentState() };\n } catch {\n return null;\n }\n}\n\nasync function postWithFetch(url: string, body: string, apiKey: string, keepalive: boolean): Promise<void> {\n if (typeof fetch !== 'function')\n return;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [API_KEY_HEADER]: apiKey,\n },\n body,\n keepalive,\n // CORS is open on the tracking endpoint; never send cookies.\n credentials: 'omit',\n mode: 'cors',\n });\n } catch {\n // fire-and-forget — never throw to the host site\n }\n}\n\nexport function createTrackingClient(config: TrackingClientConfig): TrackingClient {\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);\n const sdkVersion = config.sdkVersion ?? null;\n const packageName = config.packageName ?? null;\n const endpointBase = config.endpoint.replace(/\\/$/, '');\n const eventsUrl = `${endpointBase}/events`;\n\n let queue: QueuedEvent[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let firstPage: string | null = null;\n let destroyed = false;\n\n // Initialize identity early so the first POST has stable values.\n const visitorId = getVisitorId();\n let sessionId = getOrRotateSessionId();\n\n if (typeof window !== 'undefined')\n firstPage = window.location.href;\n\n function buildSessionPayload(): TrackingSessionUpsertPayload {\n sessionId = getOrRotateSessionId();\n const params = readTrackingParams();\n const context = buildContext(config.surface, sdkVersion, packageName);\n return {\n session_id: sessionId,\n visitor_id: visitorId,\n gclid: params.gclid,\n fbclid: params.fbclid,\n utm_source: params.utm_source,\n utm_medium: params.utm_medium,\n utm_campaign: params.utm_campaign,\n utm_term: params.utm_term,\n utm_content: params.utm_content,\n first_page: firstPage,\n consent_state: consentSnapshot(),\n context,\n };\n }\n\n function scheduleFlush(): void {\n if (flushTimer !== null || destroyed)\n return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush();\n }, flushIntervalMs);\n }\n\n function clearScheduledFlush(): void {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n }\n\n async function flush(): Promise<void> {\n if (queue.length === 0)\n return;\n\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n\n const serialized = JSON.stringify(body);\n\n // Prefer fetch + keepalive for better error visibility. sendBeacon is the\n // pagehide fallback path because it survives navigation away.\n await postWithFetch(eventsUrl, serialized, config.apiKey, false);\n }\n\n function trackEvent(input: TrackEventInput): void {\n if (destroyed)\n return;\n if (!input || typeof input.eventType !== 'string' || input.eventType.length === 0)\n return;\n\n const occurredAt =\n input.occurredAt instanceof Date\n ? input.occurredAt.toISOString()\n : typeof input.occurredAt === 'string'\n ? input.occurredAt\n : new Date().toISOString();\n\n queue.push({\n event_type: input.eventType,\n page_url:\n input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n occurred_at: occurredAt,\n });\n\n if (queue.length >= maxQueueSize) {\n void flush();\n } else {\n scheduleFlush();\n }\n }\n\n function flushOnUnload(): void {\n if (queue.length === 0)\n return;\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n const serialized = JSON.stringify(body);\n void postWithFetch(eventsUrl, serialized, config.apiKey, true);\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', flushOnUnload);\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') flushOnUnload();\n });\n }\n\n return {\n trackEvent,\n flush,\n getSessionId: () => sessionId,\n getVisitorId: () => visitorId,\n destroy: () => {\n destroyed = true;\n clearScheduledFlush();\n queue = [];\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', flushOnUnload);\n }\n },\n };\n}\n","// Auto page_view emission. Fires once on attach, then again on every SPA\n// navigation. Detects navigation via three signals:\n// - history.pushState (patched)\n// - history.replaceState (patched)\n// - popstate event (back/forward buttons)\n//\n// The Next.js provider can opt out of this and use `usePathname` instead so\n// it picks up App Router transitions reliably without monkey-patching globals.\n\nimport type { TrackingClient } from './ingest';\n\nexport interface PageViewMetadata {\n page: {\n title: string | null;\n path: string;\n search: string;\n hash: string;\n };\n referrer: string | null;\n viewport?: { w: number; h: number };\n}\n\nexport function buildPageViewMetadata(): PageViewMetadata | null {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return null;\n return {\n page: {\n title: document.title || null,\n path: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n },\n referrer: document.referrer || null,\n viewport: { w: window.innerWidth, h: window.innerHeight },\n };\n}\n\nexport function fireManualPageView(client: TrackingClient): void {\n const metadata = buildPageViewMetadata();\n client.trackEvent({\n eventType: 'page_view',\n pageUrl: typeof window === 'undefined' ? null : window.location.href,\n metadata: metadata as unknown as Record<string, unknown> | null,\n });\n}\n\nexport interface AttachAutoPageViewOptions {\n /** Skip the initial fire on attach (useful when the host already fired one). */\n skipInitial?: boolean;\n}\n\nexport function attachAutoPageView(\n client: TrackingClient,\n options: AttachAutoPageViewOptions = {},\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n let lastPath = window.location.pathname + window.location.search;\n\n function maybeFire(): void {\n const current = window.location.pathname + window.location.search;\n if (current === lastPath)\n return;\n lastPath = current;\n fireManualPageView(client);\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n // Defer one tick so the URL is settled before we read it.\n setTimeout(maybeFire, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(maybeFire, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', maybeFire);\n\n if (!options.skipInitial)\n fireManualPageView(client);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', maybeFire);\n };\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport { bootstrapGoogleAdsTracking } from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n gtagId: string;\n}\n\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n useEffect(() => {\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n return null;\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 { createContext, useContext, useEffect, useMemo, useRef, type ReactNode } from 'react';\n\nimport {\n attachAutoPageView,\n bootstrapGoogleAdsTracking,\n createTrackingClient,\n type TrackingClient,\n} from '../../tracking-core/src/index';\n\nexport interface TrackingProviderProps {\n /** Public tracking API key issued for this business. Required. */\n apiKey: string;\n /** Full ingest endpoint base URL, e.g. https://api.aranova.io/tracking. Required. */\n endpoint: string;\n /** Optional Google Ads gtag id. If omitted, no gtag script is loaded. */\n gtagId?: string;\n /** Disable automatic page_view emission (manual tracking only). */\n disableAutoPageView?: boolean;\n /** Optional debug flag — currently a no-op, reserved for verbose logging. */\n debug?: boolean;\n children: ReactNode;\n}\n\nconst TrackingContext = createContext<TrackingClient | null>(null);\n\nexport function TrackingProvider({\n apiKey,\n endpoint,\n gtagId,\n disableAutoPageView,\n debug,\n children,\n}: TrackingProviderProps) {\n if (!apiKey)\n throw new Error('TrackingProvider: apiKey is required');\n if (!endpoint)\n throw new Error('TrackingProvider: endpoint is required');\n\n const clientRef = useRef<TrackingClient | null>(null);\n if (clientRef.current === null) {\n clientRef.current = createTrackingClient({\n apiKey,\n endpoint,\n surface: 'react',\n packageName: '@aranova/tracking-react',\n debug,\n });\n }\n\n useEffect(() => {\n if (!gtagId)\n return;\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n useEffect(() => {\n const client = clientRef.current;\n if (client === null || disableAutoPageView)\n return;\n const detach = attachAutoPageView(client);\n return () => {\n detach();\n };\n }, [disableAutoPageView]);\n\n useEffect(() => {\n return () => {\n clientRef.current?.destroy();\n clientRef.current = null;\n };\n }, []);\n\n const contextValue = useMemo(() => clientRef.current, []);\n\n return (\n <TrackingContext.Provider value={contextValue}>\n {children}\n </TrackingContext.Provider>\n );\n}\n\nexport function useTracking(): TrackingClient {\n const client = useContext(TrackingContext);\n if (client === null)\n throw new Error('useTracking must be called inside a <TrackingProvider>');\n return client;\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -49,6 +49,7 @@ function createTrackingClientContext(surface, input = {}) {
49
49
  function createTrackingSessionUpsertPayload(trackingParams, input, context) {
50
50
  return {
51
51
  session_id: input.sessionId,
52
+ visitor_id: input.visitorId ?? null,
52
53
  gclid: trackingParams.gclid,
53
54
  fbclid: trackingParams.fbclid,
54
55
  utm_source: trackingParams.utm_source,
@@ -194,6 +195,282 @@ function captureTrackingParamsFromLocation(url = typeof window === "undefined" ?
194
195
  return getTrackingParamsFromCookieReader(getCookieValueFromDocument);
195
196
  }
196
197
 
198
+ // ../tracking-core/src/session.ts
199
+ var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
200
+ var SESSION_STORAGE_KEY = "aranova_tracking_session";
201
+ var SESSION_IDLE_MS = 30 * 60 * 1e3;
202
+ function safeUuid() {
203
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
204
+ return crypto.randomUUID();
205
+ return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;
206
+ }
207
+ function readLocalStorage(key) {
208
+ try {
209
+ return window.localStorage.getItem(key);
210
+ } catch {
211
+ return null;
212
+ }
213
+ }
214
+ function writeLocalStorage(key, value) {
215
+ try {
216
+ window.localStorage.setItem(key, value);
217
+ } catch {
218
+ }
219
+ }
220
+ function getVisitorId() {
221
+ if (typeof window === "undefined")
222
+ return safeUuid();
223
+ const existing = readLocalStorage(VISITOR_STORAGE_KEY);
224
+ if (existing && existing.length > 0)
225
+ return existing;
226
+ const fresh = safeUuid();
227
+ writeLocalStorage(VISITOR_STORAGE_KEY, fresh);
228
+ return fresh;
229
+ }
230
+ function getOrRotateSessionId(now = Date.now()) {
231
+ if (typeof window === "undefined")
232
+ return safeUuid();
233
+ const raw = readLocalStorage(SESSION_STORAGE_KEY);
234
+ if (raw) {
235
+ try {
236
+ const parsed = JSON.parse(raw);
237
+ if (typeof parsed.id === "string" && typeof parsed.last_event_at === "number") {
238
+ if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
239
+ const refreshed = { id: parsed.id, last_event_at: now };
240
+ writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
241
+ return parsed.id;
242
+ }
243
+ }
244
+ } catch {
245
+ }
246
+ }
247
+ const fresh = { id: safeUuid(), last_event_at: now };
248
+ writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
249
+ return fresh.id;
250
+ }
251
+
252
+ // ../tracking-core/src/ingest.ts
253
+ var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
254
+ var DEFAULT_MAX_QUEUE_SIZE = 10;
255
+ var HARD_MAX_BATCH = 50;
256
+ var API_KEY_HEADER = "X-Aranova-Api-Key";
257
+ function buildContext(surface, sdkVersion, packageName) {
258
+ return {
259
+ surface,
260
+ sdk_version: sdkVersion,
261
+ package_name: packageName,
262
+ site_origin: typeof window === "undefined" ? null : window.location.origin,
263
+ page_title: typeof document === "undefined" ? null : document.title || null,
264
+ referrer: typeof document === "undefined" ? null : document.referrer || null
265
+ };
266
+ }
267
+ function readTrackingParams() {
268
+ if (typeof window === "undefined")
269
+ return createEmptyTrackingParams();
270
+ try {
271
+ captureTrackingParamsFromLocation();
272
+ } catch {
273
+ }
274
+ return getTrackingParamsFromCookieReader(getCookieValueFromDocument);
275
+ }
276
+ function consentSnapshot() {
277
+ try {
278
+ return { state: getConsentState() };
279
+ } catch {
280
+ return null;
281
+ }
282
+ }
283
+ async function postWithFetch(url, body, apiKey, keepalive) {
284
+ if (typeof fetch !== "function")
285
+ return;
286
+ try {
287
+ await fetch(url, {
288
+ method: "POST",
289
+ headers: {
290
+ "Content-Type": "application/json",
291
+ [API_KEY_HEADER]: apiKey
292
+ },
293
+ body,
294
+ keepalive,
295
+ // CORS is open on the tracking endpoint; never send cookies.
296
+ credentials: "omit",
297
+ mode: "cors"
298
+ });
299
+ } catch {
300
+ }
301
+ }
302
+ function createTrackingClient(config) {
303
+ const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
304
+ const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);
305
+ const sdkVersion = config.sdkVersion ?? null;
306
+ const packageName = config.packageName ?? null;
307
+ const endpointBase = config.endpoint.replace(/\/$/, "");
308
+ const eventsUrl = `${endpointBase}/events`;
309
+ let queue = [];
310
+ let flushTimer = null;
311
+ let firstPage = null;
312
+ let destroyed = false;
313
+ const visitorId = getVisitorId();
314
+ let sessionId = getOrRotateSessionId();
315
+ if (typeof window !== "undefined")
316
+ firstPage = window.location.href;
317
+ function buildSessionPayload() {
318
+ sessionId = getOrRotateSessionId();
319
+ const params = readTrackingParams();
320
+ const context = buildContext(config.surface, sdkVersion, packageName);
321
+ return {
322
+ session_id: sessionId,
323
+ visitor_id: visitorId,
324
+ gclid: params.gclid,
325
+ fbclid: params.fbclid,
326
+ utm_source: params.utm_source,
327
+ utm_medium: params.utm_medium,
328
+ utm_campaign: params.utm_campaign,
329
+ utm_term: params.utm_term,
330
+ utm_content: params.utm_content,
331
+ first_page: firstPage,
332
+ consent_state: consentSnapshot(),
333
+ context
334
+ };
335
+ }
336
+ function scheduleFlush() {
337
+ if (flushTimer !== null || destroyed)
338
+ return;
339
+ flushTimer = setTimeout(() => {
340
+ flushTimer = null;
341
+ void flush();
342
+ }, flushIntervalMs);
343
+ }
344
+ function clearScheduledFlush() {
345
+ if (flushTimer !== null) {
346
+ clearTimeout(flushTimer);
347
+ flushTimer = null;
348
+ }
349
+ }
350
+ async function flush() {
351
+ if (queue.length === 0)
352
+ return;
353
+ const events = queue.slice(0, HARD_MAX_BATCH);
354
+ queue = queue.slice(events.length);
355
+ clearScheduledFlush();
356
+ const body = {
357
+ session: buildSessionPayload(),
358
+ events
359
+ };
360
+ const serialized = JSON.stringify(body);
361
+ await postWithFetch(eventsUrl, serialized, config.apiKey, false);
362
+ }
363
+ function trackEvent(input) {
364
+ if (destroyed)
365
+ return;
366
+ if (!input || typeof input.eventType !== "string" || input.eventType.length === 0)
367
+ return;
368
+ const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : (/* @__PURE__ */ new Date()).toISOString();
369
+ queue.push({
370
+ event_type: input.eventType,
371
+ page_url: input.pageUrl ?? (typeof window === "undefined" ? null : window.location.href),
372
+ metadata: input.metadata ?? null,
373
+ occurred_at: occurredAt
374
+ });
375
+ if (queue.length >= maxQueueSize) {
376
+ void flush();
377
+ } else {
378
+ scheduleFlush();
379
+ }
380
+ }
381
+ function flushOnUnload() {
382
+ if (queue.length === 0)
383
+ return;
384
+ const events = queue.slice(0, HARD_MAX_BATCH);
385
+ queue = queue.slice(events.length);
386
+ clearScheduledFlush();
387
+ const body = {
388
+ session: buildSessionPayload(),
389
+ events
390
+ };
391
+ const serialized = JSON.stringify(body);
392
+ void postWithFetch(eventsUrl, serialized, config.apiKey, true);
393
+ }
394
+ if (typeof window !== "undefined") {
395
+ window.addEventListener("pagehide", flushOnUnload);
396
+ window.addEventListener("visibilitychange", () => {
397
+ if (document.visibilityState === "hidden") flushOnUnload();
398
+ });
399
+ }
400
+ return {
401
+ trackEvent,
402
+ flush,
403
+ getSessionId: () => sessionId,
404
+ getVisitorId: () => visitorId,
405
+ destroy: () => {
406
+ destroyed = true;
407
+ clearScheduledFlush();
408
+ queue = [];
409
+ if (typeof window !== "undefined") {
410
+ window.removeEventListener("pagehide", flushOnUnload);
411
+ }
412
+ }
413
+ };
414
+ }
415
+
416
+ // ../tracking-core/src/page-view.ts
417
+ function buildPageViewMetadata() {
418
+ if (typeof window === "undefined" || typeof document === "undefined")
419
+ return null;
420
+ return {
421
+ page: {
422
+ title: document.title || null,
423
+ path: window.location.pathname,
424
+ search: window.location.search,
425
+ hash: window.location.hash
426
+ },
427
+ referrer: document.referrer || null,
428
+ viewport: { w: window.innerWidth, h: window.innerHeight }
429
+ };
430
+ }
431
+ function fireManualPageView(client) {
432
+ const metadata = buildPageViewMetadata();
433
+ client.trackEvent({
434
+ eventType: "page_view",
435
+ pageUrl: typeof window === "undefined" ? null : window.location.href,
436
+ metadata
437
+ });
438
+ }
439
+ function attachAutoPageView(client, options = {}) {
440
+ if (typeof window === "undefined" || typeof history === "undefined") {
441
+ return () => {
442
+ };
443
+ }
444
+ let lastPath = window.location.pathname + window.location.search;
445
+ function maybeFire() {
446
+ const current = window.location.pathname + window.location.search;
447
+ if (current === lastPath)
448
+ return;
449
+ lastPath = current;
450
+ fireManualPageView(client);
451
+ }
452
+ const originalPushState = history.pushState.bind(history);
453
+ const originalReplaceState = history.replaceState.bind(history);
454
+ function patchedPushState(...args) {
455
+ originalPushState(...args);
456
+ setTimeout(maybeFire, 0);
457
+ }
458
+ function patchedReplaceState(...args) {
459
+ originalReplaceState(...args);
460
+ setTimeout(maybeFire, 0);
461
+ }
462
+ history.pushState = patchedPushState;
463
+ history.replaceState = patchedReplaceState;
464
+ window.addEventListener("popstate", maybeFire);
465
+ if (!options.skipInitial)
466
+ fireManualPageView(client);
467
+ return () => {
468
+ history.pushState = originalPushState;
469
+ history.replaceState = originalReplaceState;
470
+ window.removeEventListener("popstate", maybeFire);
471
+ };
472
+ }
473
+
197
474
  // src/ConsentBanner.tsx
198
475
  import { jsx, jsxs } from "react/jsx-runtime";
199
476
  function ConsentBanner() {
@@ -278,10 +555,67 @@ function useConsentState() {
278
555
  }, []);
279
556
  return consentState;
280
557
  }
558
+
559
+ // src/TrackingProvider.tsx
560
+ import { createContext, useContext, useEffect as useEffect4, useMemo, useRef } from "react";
561
+ import { jsx as jsx2 } from "react/jsx-runtime";
562
+ var TrackingContext = createContext(null);
563
+ function TrackingProvider({
564
+ apiKey,
565
+ endpoint,
566
+ gtagId,
567
+ disableAutoPageView,
568
+ debug,
569
+ children
570
+ }) {
571
+ if (!apiKey)
572
+ throw new Error("TrackingProvider: apiKey is required");
573
+ if (!endpoint)
574
+ throw new Error("TrackingProvider: endpoint is required");
575
+ const clientRef = useRef(null);
576
+ if (clientRef.current === null) {
577
+ clientRef.current = createTrackingClient({
578
+ apiKey,
579
+ endpoint,
580
+ surface: "react",
581
+ packageName: "@aranova/tracking-react",
582
+ debug
583
+ });
584
+ }
585
+ useEffect4(() => {
586
+ if (!gtagId)
587
+ return;
588
+ bootstrapGoogleAdsTracking(gtagId);
589
+ }, [gtagId]);
590
+ useEffect4(() => {
591
+ const client = clientRef.current;
592
+ if (client === null || disableAutoPageView)
593
+ return;
594
+ const detach = attachAutoPageView(client);
595
+ return () => {
596
+ detach();
597
+ };
598
+ }, [disableAutoPageView]);
599
+ useEffect4(() => {
600
+ return () => {
601
+ clientRef.current?.destroy();
602
+ clientRef.current = null;
603
+ };
604
+ }, []);
605
+ const contextValue = useMemo(() => clientRef.current, []);
606
+ return /* @__PURE__ */ jsx2(TrackingContext.Provider, { value: contextValue, children });
607
+ }
608
+ function useTracking() {
609
+ const client = useContext(TrackingContext);
610
+ if (client === null)
611
+ throw new Error("useTracking must be called inside a <TrackingProvider>");
612
+ return client;
613
+ }
281
614
  export {
282
615
  ConsentBanner,
283
616
  GoogleAdsTracking,
284
617
  TRACKING_PARAM_KEYS,
618
+ TrackingProvider,
285
619
  captureTrackingParamsFromLocation,
286
620
  createTrackingClientContext,
287
621
  createTrackingEventCreatePayload,
@@ -290,6 +624,7 @@ export {
290
624
  setConsentState,
291
625
  useConsentState,
292
626
  useGclid,
627
+ useTracking,
293
628
  useTrackingParams
294
629
  };
295
630
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ConsentBanner.tsx","../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../src/GoogleAdsTracking.tsx","../src/hooks.ts"],"sourcesContent":["'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","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 } from 'react';\n\nimport { bootstrapGoogleAdsTracking } from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n gtagId: string;\n}\n\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n useEffect(() => {\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n return null;\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"],"mappings":"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","names":["useEffect","useEffect","useEffect","useState","useState","useEffect"]}
1
+ {"version":3,"sources":["../src/ConsentBanner.tsx","../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/ingest.ts","../../tracking-core/src/page-view.ts","../src/GoogleAdsTracking.tsx","../src/hooks.ts","../src/TrackingProvider.tsx"],"sourcesContent":["'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","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 visitorId?: string | null;\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 visitor_id: input.visitorId ?? null,\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","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return parsed.id;\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return fresh.id;\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","// Tracking ingest client. Queues events, debounce-flushes them to the\n// /tracking/events endpoint, and degrades silently on errors so a broken\n// network never breaks the host site.\n\nimport { getConsentState } from './consent';\nimport {\n captureTrackingParamsFromLocation,\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n} from './tracking';\nimport { getOrRotateSessionId, getVisitorId } from './session';\nimport type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 10;\nexport const HARD_MAX_BATCH = 50;\nexport const API_KEY_HEADER = 'X-Aranova-Api-Key';\n\nexport interface TrackingClientConfig {\n apiKey: string;\n endpoint: string;\n surface: TrackingInstallSurface;\n sdkVersion?: string;\n packageName?: string;\n /** Override the default 2s debounce window. */\n flushIntervalMs?: number;\n /** Override the default 10-event batch trigger. */\n maxQueueSize?: number;\n /** When true, swallow nothing — useful for tests. */\n debug?: boolean;\n}\n\nexport interface TrackEventInput {\n eventType: string;\n pageUrl?: string | null;\n metadata?: Record<string, unknown> | null;\n occurredAt?: Date | string | null;\n}\n\ninterface QueuedEvent {\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n}\n\nexport interface TrackingClient {\n trackEvent: (input: TrackEventInput) => void;\n flush: () => Promise<void>;\n getSessionId: () => string;\n getVisitorId: () => string;\n destroy: () => void;\n}\n\ninterface IngestRequestBody {\n session: TrackingSessionUpsertPayload;\n events: Array<{\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n }>;\n}\n\nfunction buildContext(\n surface: TrackingInstallSurface,\n sdkVersion: string | null,\n packageName: string | null,\n): TrackingClientContext {\n return {\n surface,\n sdk_version: sdkVersion,\n package_name: packageName,\n site_origin: typeof window === 'undefined' ? null : window.location.origin,\n page_title: typeof document === 'undefined' ? null : document.title || null,\n referrer: typeof document === 'undefined' ? null : document.referrer || null,\n };\n}\n\nfunction readTrackingParams(): TrackingParams {\n if (typeof window === 'undefined')\n return createEmptyTrackingParams();\n // Capture from URL on every read so the first event in a session reflects the\n // landing-page params even if the cookie helper hasn't run yet.\n try {\n captureTrackingParamsFromLocation();\n } catch {\n // ignore\n }\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n\nfunction consentSnapshot(): Record<string, unknown> | null {\n try {\n return { state: getConsentState() };\n } catch {\n return null;\n }\n}\n\nasync function postWithFetch(url: string, body: string, apiKey: string, keepalive: boolean): Promise<void> {\n if (typeof fetch !== 'function')\n return;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [API_KEY_HEADER]: apiKey,\n },\n body,\n keepalive,\n // CORS is open on the tracking endpoint; never send cookies.\n credentials: 'omit',\n mode: 'cors',\n });\n } catch {\n // fire-and-forget — never throw to the host site\n }\n}\n\nexport function createTrackingClient(config: TrackingClientConfig): TrackingClient {\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);\n const sdkVersion = config.sdkVersion ?? null;\n const packageName = config.packageName ?? null;\n const endpointBase = config.endpoint.replace(/\\/$/, '');\n const eventsUrl = `${endpointBase}/events`;\n\n let queue: QueuedEvent[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let firstPage: string | null = null;\n let destroyed = false;\n\n // Initialize identity early so the first POST has stable values.\n const visitorId = getVisitorId();\n let sessionId = getOrRotateSessionId();\n\n if (typeof window !== 'undefined')\n firstPage = window.location.href;\n\n function buildSessionPayload(): TrackingSessionUpsertPayload {\n sessionId = getOrRotateSessionId();\n const params = readTrackingParams();\n const context = buildContext(config.surface, sdkVersion, packageName);\n return {\n session_id: sessionId,\n visitor_id: visitorId,\n gclid: params.gclid,\n fbclid: params.fbclid,\n utm_source: params.utm_source,\n utm_medium: params.utm_medium,\n utm_campaign: params.utm_campaign,\n utm_term: params.utm_term,\n utm_content: params.utm_content,\n first_page: firstPage,\n consent_state: consentSnapshot(),\n context,\n };\n }\n\n function scheduleFlush(): void {\n if (flushTimer !== null || destroyed)\n return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush();\n }, flushIntervalMs);\n }\n\n function clearScheduledFlush(): void {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n }\n\n async function flush(): Promise<void> {\n if (queue.length === 0)\n return;\n\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n\n const serialized = JSON.stringify(body);\n\n // Prefer fetch + keepalive for better error visibility. sendBeacon is the\n // pagehide fallback path because it survives navigation away.\n await postWithFetch(eventsUrl, serialized, config.apiKey, false);\n }\n\n function trackEvent(input: TrackEventInput): void {\n if (destroyed)\n return;\n if (!input || typeof input.eventType !== 'string' || input.eventType.length === 0)\n return;\n\n const occurredAt =\n input.occurredAt instanceof Date\n ? input.occurredAt.toISOString()\n : typeof input.occurredAt === 'string'\n ? input.occurredAt\n : new Date().toISOString();\n\n queue.push({\n event_type: input.eventType,\n page_url:\n input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n occurred_at: occurredAt,\n });\n\n if (queue.length >= maxQueueSize) {\n void flush();\n } else {\n scheduleFlush();\n }\n }\n\n function flushOnUnload(): void {\n if (queue.length === 0)\n return;\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n const serialized = JSON.stringify(body);\n void postWithFetch(eventsUrl, serialized, config.apiKey, true);\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', flushOnUnload);\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') flushOnUnload();\n });\n }\n\n return {\n trackEvent,\n flush,\n getSessionId: () => sessionId,\n getVisitorId: () => visitorId,\n destroy: () => {\n destroyed = true;\n clearScheduledFlush();\n queue = [];\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', flushOnUnload);\n }\n },\n };\n}\n","// Auto page_view emission. Fires once on attach, then again on every SPA\n// navigation. Detects navigation via three signals:\n// - history.pushState (patched)\n// - history.replaceState (patched)\n// - popstate event (back/forward buttons)\n//\n// The Next.js provider can opt out of this and use `usePathname` instead so\n// it picks up App Router transitions reliably without monkey-patching globals.\n\nimport type { TrackingClient } from './ingest';\n\nexport interface PageViewMetadata {\n page: {\n title: string | null;\n path: string;\n search: string;\n hash: string;\n };\n referrer: string | null;\n viewport?: { w: number; h: number };\n}\n\nexport function buildPageViewMetadata(): PageViewMetadata | null {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return null;\n return {\n page: {\n title: document.title || null,\n path: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n },\n referrer: document.referrer || null,\n viewport: { w: window.innerWidth, h: window.innerHeight },\n };\n}\n\nexport function fireManualPageView(client: TrackingClient): void {\n const metadata = buildPageViewMetadata();\n client.trackEvent({\n eventType: 'page_view',\n pageUrl: typeof window === 'undefined' ? null : window.location.href,\n metadata: metadata as unknown as Record<string, unknown> | null,\n });\n}\n\nexport interface AttachAutoPageViewOptions {\n /** Skip the initial fire on attach (useful when the host already fired one). */\n skipInitial?: boolean;\n}\n\nexport function attachAutoPageView(\n client: TrackingClient,\n options: AttachAutoPageViewOptions = {},\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n let lastPath = window.location.pathname + window.location.search;\n\n function maybeFire(): void {\n const current = window.location.pathname + window.location.search;\n if (current === lastPath)\n return;\n lastPath = current;\n fireManualPageView(client);\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n // Defer one tick so the URL is settled before we read it.\n setTimeout(maybeFire, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(maybeFire, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', maybeFire);\n\n if (!options.skipInitial)\n fireManualPageView(client);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', maybeFire);\n };\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport { bootstrapGoogleAdsTracking } from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n gtagId: string;\n}\n\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n useEffect(() => {\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n return null;\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 { createContext, useContext, useEffect, useMemo, useRef, type ReactNode } from 'react';\n\nimport {\n attachAutoPageView,\n bootstrapGoogleAdsTracking,\n createTrackingClient,\n type TrackingClient,\n} from '../../tracking-core/src/index';\n\nexport interface TrackingProviderProps {\n /** Public tracking API key issued for this business. Required. */\n apiKey: string;\n /** Full ingest endpoint base URL, e.g. https://api.aranova.io/tracking. Required. */\n endpoint: string;\n /** Optional Google Ads gtag id. If omitted, no gtag script is loaded. */\n gtagId?: string;\n /** Disable automatic page_view emission (manual tracking only). */\n disableAutoPageView?: boolean;\n /** Optional debug flag — currently a no-op, reserved for verbose logging. */\n debug?: boolean;\n children: ReactNode;\n}\n\nconst TrackingContext = createContext<TrackingClient | null>(null);\n\nexport function TrackingProvider({\n apiKey,\n endpoint,\n gtagId,\n disableAutoPageView,\n debug,\n children,\n}: TrackingProviderProps) {\n if (!apiKey)\n throw new Error('TrackingProvider: apiKey is required');\n if (!endpoint)\n throw new Error('TrackingProvider: endpoint is required');\n\n const clientRef = useRef<TrackingClient | null>(null);\n if (clientRef.current === null) {\n clientRef.current = createTrackingClient({\n apiKey,\n endpoint,\n surface: 'react',\n packageName: '@aranova/tracking-react',\n debug,\n });\n }\n\n useEffect(() => {\n if (!gtagId)\n return;\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n useEffect(() => {\n const client = clientRef.current;\n if (client === null || disableAutoPageView)\n return;\n const detach = attachAutoPageView(client);\n return () => {\n detach();\n };\n }, [disableAutoPageView]);\n\n useEffect(() => {\n return () => {\n clientRef.current?.destroy();\n clientRef.current = null;\n };\n }, []);\n\n const contextValue = useMemo(() => clientRef.current, []);\n\n return (\n <TrackingContext.Provider value={contextValue}>\n {children}\n </TrackingContext.Provider>\n );\n}\n\nexport function useTracking(): TrackingClient {\n const client = useContext(TrackingContext);\n if (client === null)\n throw new Error('useTracking must be called inside a <TrackingProvider>');\n return client;\n}\n"],"mappings":"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","names":["useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","jsx","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aranova/tracking-react",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "React tracking and consent utilities for Aranova client sites",
6
6
  "files": [