@aranova/tracking-react 0.2.3 → 0.4.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.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,596 @@ 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/events/page-view.ts
253
+ import { z } from "zod";
254
+ var pageViewMetadataSchema = z.object({
255
+ page: z.object({
256
+ title: z.string().nullable(),
257
+ path: z.string(),
258
+ search: z.string(),
259
+ hash: z.string()
260
+ }),
261
+ referrer: z.string().nullable(),
262
+ // `.nullable().optional()` — absent (undefined) OR explicit null OR a
263
+ // real viewport object. Mirrors Pydantic's `_Viewport | None = None`
264
+ // on the backend side so the drift test stays clean.
265
+ viewport: z.object({
266
+ w: z.number(),
267
+ h: z.number()
268
+ }).nullable().optional()
269
+ }).strict();
270
+ var pageViewConfigSchema = z.object({}).strict();
271
+
272
+ // ../tracking-core/src/page-view.ts
273
+ var LAST_FIRED_URL_STORAGE_KEY = "aranova_tracking_last_fired_url";
274
+ var lastFiredUrl = null;
275
+ var lastFiredUrlHydrated = false;
276
+ function readSessionStorage(key) {
277
+ try {
278
+ if (typeof window === "undefined") return null;
279
+ return window.sessionStorage.getItem(key);
280
+ } catch {
281
+ return null;
282
+ }
283
+ }
284
+ function writeSessionStorage(key, value) {
285
+ try {
286
+ if (typeof window === "undefined") return;
287
+ window.sessionStorage.setItem(key, value);
288
+ } catch {
289
+ }
290
+ }
291
+ function getLastFiredUrl() {
292
+ if (!lastFiredUrlHydrated) {
293
+ lastFiredUrlHydrated = true;
294
+ const stored = readSessionStorage(LAST_FIRED_URL_STORAGE_KEY);
295
+ if (stored !== null) lastFiredUrl = stored;
296
+ }
297
+ return lastFiredUrl;
298
+ }
299
+ function setLastFiredUrl(url) {
300
+ lastFiredUrl = url;
301
+ lastFiredUrlHydrated = true;
302
+ writeSessionStorage(LAST_FIRED_URL_STORAGE_KEY, url);
303
+ }
304
+ function buildPageViewMetadata(referrerOverride) {
305
+ if (typeof window === "undefined" || typeof document === "undefined")
306
+ return null;
307
+ return pageViewMetadataSchema.parse({
308
+ page: {
309
+ title: document.title || null,
310
+ path: window.location.pathname,
311
+ search: window.location.search,
312
+ hash: window.location.hash
313
+ },
314
+ referrer: referrerOverride !== void 0 ? referrerOverride : document.referrer || null,
315
+ viewport: { w: window.innerWidth, h: window.innerHeight }
316
+ });
317
+ }
318
+ function fireManualPageView(client) {
319
+ if (typeof window === "undefined")
320
+ return;
321
+ const currentHref = window.location.href;
322
+ const previousFiredUrl = getLastFiredUrl();
323
+ const internalReferrer = previousFiredUrl !== null && previousFiredUrl !== currentHref ? previousFiredUrl : null;
324
+ const externalReferrer = typeof document !== "undefined" ? document.referrer || null : null;
325
+ const referrer = internalReferrer ?? externalReferrer;
326
+ const metadata = buildPageViewMetadata(referrer);
327
+ client.trackEvent({
328
+ eventType: "page_view",
329
+ pageUrl: currentHref,
330
+ metadata
331
+ });
332
+ if (currentHref !== previousFiredUrl) {
333
+ setLastFiredUrl(currentHref);
334
+ }
335
+ }
336
+ function attachBfcacheRestore(client) {
337
+ if (typeof window === "undefined") return () => {
338
+ };
339
+ function handlePageShow(event) {
340
+ if (!event.persisted) return;
341
+ fireManualPageView(client);
342
+ }
343
+ window.addEventListener("pageshow", handlePageShow);
344
+ return () => {
345
+ window.removeEventListener("pageshow", handlePageShow);
346
+ };
347
+ }
348
+ function attachAutoPageView(client, options = {}) {
349
+ if (typeof window === "undefined" || typeof history === "undefined") {
350
+ return () => {
351
+ };
352
+ }
353
+ let lastPath = window.location.pathname + window.location.search;
354
+ function maybeFire() {
355
+ const current = window.location.pathname + window.location.search;
356
+ if (current === lastPath)
357
+ return;
358
+ lastPath = current;
359
+ fireManualPageView(client);
360
+ }
361
+ const originalPushState = history.pushState.bind(history);
362
+ const originalReplaceState = history.replaceState.bind(history);
363
+ function patchedPushState(...args) {
364
+ originalPushState(...args);
365
+ setTimeout(maybeFire, 0);
366
+ }
367
+ function patchedReplaceState(...args) {
368
+ originalReplaceState(...args);
369
+ setTimeout(maybeFire, 0);
370
+ }
371
+ function handlePageShow(event) {
372
+ if (!event.persisted) return;
373
+ fireManualPageView(client);
374
+ }
375
+ history.pushState = patchedPushState;
376
+ history.replaceState = patchedReplaceState;
377
+ window.addEventListener("popstate", maybeFire);
378
+ window.addEventListener("pageshow", handlePageShow);
379
+ if (!options.skipInitial)
380
+ fireManualPageView(client);
381
+ return () => {
382
+ history.pushState = originalPushState;
383
+ history.replaceState = originalReplaceState;
384
+ window.removeEventListener("popstate", maybeFire);
385
+ window.removeEventListener("pageshow", handlePageShow);
386
+ };
387
+ }
388
+
389
+ // ../tracking-core/src/ingest.ts
390
+ var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
391
+ var DEFAULT_MAX_QUEUE_SIZE = 10;
392
+ var HARD_MAX_BATCH = 50;
393
+ var API_KEY_HEADER = "X-Aranova-Api-Key";
394
+ function buildContext(surface, sdkVersion, packageName) {
395
+ return {
396
+ surface,
397
+ sdk_version: sdkVersion,
398
+ package_name: packageName,
399
+ site_origin: typeof window === "undefined" ? null : window.location.origin,
400
+ page_title: typeof document === "undefined" ? null : document.title || null,
401
+ referrer: typeof document === "undefined" ? null : document.referrer || null
402
+ };
403
+ }
404
+ function readTrackingParams() {
405
+ if (typeof window === "undefined")
406
+ return createEmptyTrackingParams();
407
+ try {
408
+ captureTrackingParamsFromLocation();
409
+ } catch {
410
+ }
411
+ return getTrackingParamsFromCookieReader(getCookieValueFromDocument);
412
+ }
413
+ function consentSnapshot() {
414
+ try {
415
+ return { state: getConsentState() };
416
+ } catch {
417
+ return null;
418
+ }
419
+ }
420
+ async function postWithFetch(url, body, apiKey, keepalive) {
421
+ if (typeof fetch !== "function")
422
+ return;
423
+ try {
424
+ await fetch(url, {
425
+ method: "POST",
426
+ headers: {
427
+ "Content-Type": "application/json",
428
+ [API_KEY_HEADER]: apiKey
429
+ },
430
+ body,
431
+ keepalive,
432
+ // CORS is open on the tracking endpoint; never send cookies.
433
+ credentials: "omit",
434
+ mode: "cors"
435
+ });
436
+ } catch {
437
+ }
438
+ }
439
+ var globalClient = null;
440
+ var globalClientKey = null;
441
+ function clientConfigKey(config) {
442
+ return `${config.apiKey}@${config.endpoint}#${config.surface}`;
443
+ }
444
+ function getOrCreateTrackingClient(config) {
445
+ const key = clientConfigKey(config);
446
+ if (globalClient !== null && globalClientKey === key) {
447
+ return globalClient;
448
+ }
449
+ if (globalClient !== null) {
450
+ globalClient.destroy();
451
+ }
452
+ globalClient = createTrackingClient(config);
453
+ globalClientKey = key;
454
+ return globalClient;
455
+ }
456
+ function createTrackingClient(config) {
457
+ const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
458
+ const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);
459
+ const sdkVersion = config.sdkVersion ?? null;
460
+ const packageName = config.packageName ?? null;
461
+ const endpointBase = config.endpoint.replace(/\/$/, "");
462
+ const eventsUrl = `${endpointBase}/events`;
463
+ let queue = [];
464
+ let flushTimer = null;
465
+ let firstPage = null;
466
+ let destroyed = false;
467
+ const visitorId = getVisitorId();
468
+ let sessionId = getOrRotateSessionId();
469
+ if (typeof window !== "undefined")
470
+ firstPage = window.location.href;
471
+ function buildSessionPayload() {
472
+ sessionId = getOrRotateSessionId();
473
+ const params = readTrackingParams();
474
+ const context = buildContext(config.surface, sdkVersion, packageName);
475
+ return {
476
+ session_id: sessionId,
477
+ visitor_id: visitorId,
478
+ gclid: params.gclid,
479
+ fbclid: params.fbclid,
480
+ utm_source: params.utm_source,
481
+ utm_medium: params.utm_medium,
482
+ utm_campaign: params.utm_campaign,
483
+ utm_term: params.utm_term,
484
+ utm_content: params.utm_content,
485
+ first_page: firstPage,
486
+ consent_state: consentSnapshot(),
487
+ context
488
+ };
489
+ }
490
+ function scheduleFlush() {
491
+ if (flushTimer !== null || destroyed)
492
+ return;
493
+ flushTimer = setTimeout(() => {
494
+ flushTimer = null;
495
+ void flush();
496
+ }, flushIntervalMs);
497
+ }
498
+ function clearScheduledFlush() {
499
+ if (flushTimer !== null) {
500
+ clearTimeout(flushTimer);
501
+ flushTimer = null;
502
+ }
503
+ }
504
+ async function flush() {
505
+ if (queue.length === 0)
506
+ return;
507
+ const events = queue.slice(0, HARD_MAX_BATCH);
508
+ queue = queue.slice(events.length);
509
+ clearScheduledFlush();
510
+ const body = {
511
+ session: buildSessionPayload(),
512
+ events
513
+ };
514
+ const serialized = JSON.stringify(body);
515
+ await postWithFetch(eventsUrl, serialized, config.apiKey, false);
516
+ }
517
+ function trackEvent(input) {
518
+ if (destroyed)
519
+ return;
520
+ if (!input || typeof input.eventType !== "string" || input.eventType.length === 0)
521
+ return;
522
+ const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : (/* @__PURE__ */ new Date()).toISOString();
523
+ queue.push({
524
+ event_type: input.eventType,
525
+ page_url: input.pageUrl ?? (typeof window === "undefined" ? null : window.location.href),
526
+ metadata: input.metadata ?? null,
527
+ occurred_at: occurredAt
528
+ });
529
+ if (queue.length >= maxQueueSize) {
530
+ void flush();
531
+ } else {
532
+ scheduleFlush();
533
+ }
534
+ }
535
+ function flushOnUnload() {
536
+ if (queue.length === 0)
537
+ return;
538
+ const events = queue.slice(0, HARD_MAX_BATCH);
539
+ queue = queue.slice(events.length);
540
+ clearScheduledFlush();
541
+ const body = {
542
+ session: buildSessionPayload(),
543
+ events
544
+ };
545
+ const serialized = JSON.stringify(body);
546
+ void postWithFetch(eventsUrl, serialized, config.apiKey, true);
547
+ }
548
+ if (typeof window !== "undefined") {
549
+ window.addEventListener("pagehide", flushOnUnload);
550
+ window.addEventListener("visibilitychange", () => {
551
+ if (document.visibilityState === "hidden") flushOnUnload();
552
+ });
553
+ }
554
+ return {
555
+ trackEvent,
556
+ flush,
557
+ getSessionId: () => sessionId,
558
+ getVisitorId: () => visitorId,
559
+ destroy: () => {
560
+ destroyed = true;
561
+ if (queue.length > 0) {
562
+ flushOnUnload();
563
+ }
564
+ clearScheduledFlush();
565
+ queue = [];
566
+ if (typeof window !== "undefined") {
567
+ window.removeEventListener("pagehide", flushOnUnload);
568
+ }
569
+ }
570
+ };
571
+ }
572
+
573
+ // ../tracking-core/src/events/contact-page-visit.ts
574
+ import { z as z2 } from "zod";
575
+ var contactPageVisitMetadataSchema = z2.object({
576
+ page: z2.object({
577
+ path: z2.string()
578
+ })
579
+ }).strict();
580
+ var contactPageVisitConfigSchema = z2.object({
581
+ // RegExp is runtime-only so we wrap it via z.custom. Consumers pass a
582
+ // real regex at createTracking() time; the factory validates with this
583
+ // schema and then keeps the live RegExp reference for runtime matching.
584
+ pathPattern: z2.custom(
585
+ (value) => value instanceof RegExp,
586
+ { message: "pathPattern must be a RegExp" }
587
+ )
588
+ }).strict();
589
+
590
+ // ../tracking-core/src/events/form-submit.ts
591
+ import { z as z3 } from "zod";
592
+ var formSubmitMetadataSchema = z3.object({
593
+ form: z3.object({
594
+ id: z3.string(),
595
+ action: z3.string().nullable()
596
+ }),
597
+ page: z3.object({
598
+ path: z3.string()
599
+ })
600
+ }).strict();
601
+ var formSubmitConfigSchema = z3.object({}).strict();
602
+
603
+ // ../tracking-core/src/events/phone-click.ts
604
+ import { z as z4 } from "zod";
605
+ var phoneClickMetadataSchema = z4.object({
606
+ element: z4.object({
607
+ tag: z4.string(),
608
+ text: z4.string().nullable(),
609
+ href: z4.string()
610
+ }),
611
+ page: z4.object({
612
+ path: z4.string()
613
+ })
614
+ }).strict();
615
+ var phoneClickConfigSchema = z4.object({}).strict();
616
+
617
+ // ../tracking-core/src/events/time-on-site.ts
618
+ import { z as z5 } from "zod";
619
+ var timeOnSiteMetadataSchema = z5.object({
620
+ duration_ms: z5.number().int().nonnegative(),
621
+ page: z5.object({
622
+ path: z5.string()
623
+ })
624
+ }).strict();
625
+ var timeOnSiteConfigSchema = z5.object({
626
+ thresholdSeconds: z5.number().int().positive()
627
+ }).strict();
628
+
629
+ // ../tracking-core/src/events/registry.ts
630
+ var EVENT_REGISTRY = {
631
+ // --- automatic triggers ---
632
+ page_view: {
633
+ kind: "automatic",
634
+ metadataSchema: pageViewMetadataSchema,
635
+ configSchema: pageViewConfigSchema
636
+ },
637
+ time_on_site: {
638
+ kind: "automatic",
639
+ metadataSchema: timeOnSiteMetadataSchema,
640
+ configSchema: timeOnSiteConfigSchema
641
+ },
642
+ contact_page_visit: {
643
+ kind: "automatic",
644
+ metadataSchema: contactPageVisitMetadataSchema,
645
+ configSchema: contactPageVisitConfigSchema
646
+ },
647
+ // --- manual triggers ---
648
+ form_submit: {
649
+ kind: "manual",
650
+ metadataSchema: formSubmitMetadataSchema,
651
+ configSchema: formSubmitConfigSchema
652
+ },
653
+ phone_click: {
654
+ kind: "manual",
655
+ metadataSchema: phoneClickMetadataSchema,
656
+ configSchema: phoneClickConfigSchema
657
+ }
658
+ };
659
+ var ALL_AUTOMATIC_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "automatic").map(([name]) => name);
660
+ var ALL_MANUAL_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "manual").map(([name]) => name);
661
+ function getEventDefinition(name) {
662
+ return EVENT_REGISTRY[name];
663
+ }
664
+
665
+ // ../tracking-core/src/ingest-typed.ts
666
+ function createTypedClient(raw, _registry, options = {}) {
667
+ const debug = options.debug ?? false;
668
+ return {
669
+ trackEvent(eventType, metadata, opts) {
670
+ if (debug) {
671
+ const def = getEventDefinition(eventType);
672
+ def.metadataSchema.parse(metadata);
673
+ }
674
+ raw.trackEvent({
675
+ eventType,
676
+ metadata,
677
+ pageUrl: opts?.pageUrl ?? null,
678
+ occurredAt: opts?.occurredAt ?? null
679
+ });
680
+ },
681
+ flush: raw.flush.bind(raw),
682
+ getSessionId: raw.getSessionId.bind(raw),
683
+ getVisitorId: raw.getVisitorId.bind(raw)
684
+ };
685
+ }
686
+
687
+ // ../tracking-core/src/triggers/time-on-site.ts
688
+ function attachTimeOnSite(client, config) {
689
+ if (typeof window === "undefined" || typeof document === "undefined") {
690
+ return () => {
691
+ };
692
+ }
693
+ const thresholdMs = config.thresholdSeconds * 1e3;
694
+ let accumulatedMs = 0;
695
+ let activeSince = document.visibilityState === "visible" ? Date.now() : null;
696
+ let timer = null;
697
+ let fired = false;
698
+ function fire() {
699
+ if (fired) return;
700
+ fired = true;
701
+ client.trackEvent({
702
+ eventType: "time_on_site",
703
+ metadata: {
704
+ duration_ms: thresholdMs,
705
+ page: { path: window.location.pathname }
706
+ },
707
+ pageUrl: window.location.href,
708
+ occurredAt: null
709
+ });
710
+ }
711
+ function scheduleNext() {
712
+ if (fired || activeSince === null) return;
713
+ const remaining = thresholdMs - accumulatedMs;
714
+ if (remaining <= 0) {
715
+ fire();
716
+ return;
717
+ }
718
+ timer = setTimeout(fire, remaining);
719
+ }
720
+ function clearTimer() {
721
+ if (timer !== null) {
722
+ clearTimeout(timer);
723
+ timer = null;
724
+ }
725
+ }
726
+ function onVisibilityChange() {
727
+ if (fired) return;
728
+ if (document.visibilityState === "hidden") {
729
+ if (activeSince !== null) {
730
+ accumulatedMs += Date.now() - activeSince;
731
+ activeSince = null;
732
+ }
733
+ clearTimer();
734
+ } else {
735
+ activeSince = Date.now();
736
+ scheduleNext();
737
+ }
738
+ }
739
+ document.addEventListener("visibilitychange", onVisibilityChange);
740
+ scheduleNext();
741
+ return () => {
742
+ clearTimer();
743
+ document.removeEventListener("visibilitychange", onVisibilityChange);
744
+ };
745
+ }
746
+
747
+ // ../tracking-core/src/triggers/contact-page-visit.ts
748
+ function attachContactPageVisit(client, config) {
749
+ if (typeof window === "undefined" || typeof history === "undefined") {
750
+ return () => {
751
+ };
752
+ }
753
+ const { pathPattern } = config;
754
+ let lastFiredPath = null;
755
+ function check() {
756
+ const path = window.location.pathname;
757
+ if (!pathPattern.test(path)) return;
758
+ if (lastFiredPath === path) return;
759
+ lastFiredPath = path;
760
+ client.trackEvent({
761
+ eventType: "contact_page_visit",
762
+ metadata: { page: { path } },
763
+ pageUrl: window.location.href,
764
+ occurredAt: null
765
+ });
766
+ }
767
+ const originalPushState = history.pushState.bind(history);
768
+ const originalReplaceState = history.replaceState.bind(history);
769
+ function patchedPushState(...args) {
770
+ originalPushState(...args);
771
+ setTimeout(check, 0);
772
+ }
773
+ function patchedReplaceState(...args) {
774
+ originalReplaceState(...args);
775
+ setTimeout(check, 0);
776
+ }
777
+ history.pushState = patchedPushState;
778
+ history.replaceState = patchedReplaceState;
779
+ window.addEventListener("popstate", check);
780
+ check();
781
+ return () => {
782
+ history.pushState = originalPushState;
783
+ history.replaceState = originalReplaceState;
784
+ window.removeEventListener("popstate", check);
785
+ };
786
+ }
787
+
197
788
  // src/ConsentBanner.tsx
198
789
  import { jsx, jsxs } from "react/jsx-runtime";
199
790
  function ConsentBanner() {
@@ -278,11 +869,83 @@ function useConsentState() {
278
869
  }, []);
279
870
  return consentState;
280
871
  }
872
+
873
+ // src/factory.tsx
874
+ import {
875
+ createContext,
876
+ useContext,
877
+ useEffect as useEffect4,
878
+ useMemo
879
+ } from "react";
880
+ import { jsx as jsx2 } from "react/jsx-runtime";
881
+ function createTracking(options) {
882
+ const { apiKey, endpoint, triggers, debug } = options;
883
+ if (!apiKey) throw new Error("createTracking: apiKey is required");
884
+ if (!endpoint) throw new Error("createTracking: endpoint is required");
885
+ const TrackingContext = createContext(null);
886
+ function TrackingProvider({ gtagId, children }) {
887
+ const client = useMemo(
888
+ () => createTypedClient(
889
+ getOrCreateTrackingClient({
890
+ apiKey,
891
+ endpoint,
892
+ surface: "react",
893
+ packageName: "@aranova/tracking-react",
894
+ debug
895
+ }),
896
+ triggers,
897
+ { debug }
898
+ ),
899
+ []
900
+ );
901
+ useEffect4(() => {
902
+ if (!gtagId) return;
903
+ bootstrapGoogleAdsTracking(gtagId);
904
+ }, [gtagId]);
905
+ useEffect4(() => {
906
+ const detachers = [];
907
+ const rawClient = getOrCreateTrackingClient({
908
+ apiKey,
909
+ endpoint,
910
+ surface: "react",
911
+ packageName: "@aranova/tracking-react",
912
+ debug
913
+ });
914
+ detachers.push(attachAutoPageView(rawClient));
915
+ detachers.push(attachBfcacheRestore(rawClient));
916
+ const timeOnSite = triggers.automatic.time_on_site;
917
+ if (timeOnSite) {
918
+ detachers.push(attachTimeOnSite(rawClient, timeOnSite));
919
+ }
920
+ const contactPageVisit = triggers.automatic.contact_page_visit;
921
+ if (contactPageVisit) {
922
+ detachers.push(attachContactPageVisit(rawClient, contactPageVisit));
923
+ }
924
+ return () => {
925
+ for (let i = detachers.length - 1; i >= 0; i--) {
926
+ detachers[i]();
927
+ }
928
+ };
929
+ }, []);
930
+ return /* @__PURE__ */ jsx2(TrackingContext.Provider, { value: client, children });
931
+ }
932
+ function useTracking() {
933
+ const client = useContext(TrackingContext);
934
+ if (client === null) {
935
+ throw new Error(
936
+ "useTracking must be called inside a <TrackingProvider> returned by createTracking()"
937
+ );
938
+ }
939
+ return client;
940
+ }
941
+ return { TrackingProvider, useTracking };
942
+ }
281
943
  export {
282
944
  ConsentBanner,
283
945
  GoogleAdsTracking,
284
946
  TRACKING_PARAM_KEYS,
285
947
  captureTrackingParamsFromLocation,
948
+ createTracking,
286
949
  createTrackingClientContext,
287
950
  createTrackingEventCreatePayload,
288
951
  createTrackingSessionUpsertPayload,