@aranova/tracking-react 0.5.0 → 0.6.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
@@ -229,7 +229,7 @@ function getVisitorId() {
229
229
  }
230
230
  function getOrRotateSessionId(now = Date.now()) {
231
231
  if (typeof window === "undefined")
232
- return safeUuid();
232
+ return { id: safeUuid(), isNew: true };
233
233
  const raw = readLocalStorage(SESSION_STORAGE_KEY);
234
234
  if (raw) {
235
235
  try {
@@ -238,7 +238,7 @@ function getOrRotateSessionId(now = Date.now()) {
238
238
  if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
239
239
  const refreshed = { id: parsed.id, last_event_at: now };
240
240
  writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
241
- return parsed.id;
241
+ return { id: parsed.id, isNew: false };
242
242
  }
243
243
  }
244
244
  } catch {
@@ -246,7 +246,7 @@ function getOrRotateSessionId(now = Date.now()) {
246
246
  }
247
247
  const fresh = { id: safeUuid(), last_event_at: now };
248
248
  writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
249
- return fresh.id;
249
+ return { id: fresh.id, isNew: true };
250
250
  }
251
251
 
252
252
  // ../tracking-core/src/events/page-view.ts
@@ -257,7 +257,7 @@ var pageViewMetadataSchema = z.object({
257
257
  path: z.string(),
258
258
  search: z.string(),
259
259
  hash: z.string()
260
- }),
260
+ }).strict(),
261
261
  referrer: z.string().nullable(),
262
262
  // `.nullable().optional()` — absent (undefined) OR explicit null OR a
263
263
  // real viewport object. Mirrors Pydantic's `_Viewport | None = None`
@@ -265,7 +265,7 @@ var pageViewMetadataSchema = z.object({
265
265
  viewport: z.object({
266
266
  w: z.number(),
267
267
  h: z.number()
268
- }).nullable().optional()
268
+ }).strict().nullable().optional()
269
269
  }).strict();
270
270
  var pageViewConfigSchema = z.object({}).strict();
271
271
 
@@ -386,6 +386,56 @@ function attachAutoPageView(client, options = {}) {
386
386
  };
387
387
  }
388
388
 
389
+ // ../tracking-core/src/heartbeat.ts
390
+ function serializeValue(value) {
391
+ if (value instanceof RegExp) return value.source;
392
+ if (Array.isArray(value)) return value.map(serializeValue);
393
+ if (value !== null && typeof value === "object") {
394
+ const out = {};
395
+ for (const [k, v] of Object.entries(value)) {
396
+ out[k] = serializeValue(v);
397
+ }
398
+ return out;
399
+ }
400
+ return value;
401
+ }
402
+ function buildHeartbeatMetadata(surface, sdkVersion, packageName, triggers) {
403
+ const automaticNames = triggers ? Object.keys(triggers.automatic) : [];
404
+ const manualNames = triggers?.manual ? Object.keys(triggers.manual) : [];
405
+ let triggerConfig = null;
406
+ if (triggers) {
407
+ const cfg = {};
408
+ for (const [name, config] of Object.entries(triggers.automatic)) {
409
+ const serialized = serializeValue(config);
410
+ if (Object.keys(serialized).length > 0) {
411
+ cfg[name] = serialized;
412
+ }
413
+ }
414
+ if (triggers.manual) {
415
+ for (const [name, config] of Object.entries(triggers.manual)) {
416
+ if (config === void 0) continue;
417
+ const serialized = serializeValue(config);
418
+ if (Object.keys(serialized).length > 0) {
419
+ cfg[name] = serialized;
420
+ }
421
+ }
422
+ }
423
+ if (Object.keys(cfg).length > 0) {
424
+ triggerConfig = cfg;
425
+ }
426
+ }
427
+ return {
428
+ sdk_version: sdkVersion ?? "unknown",
429
+ package_name: packageName,
430
+ surface,
431
+ triggers: {
432
+ automatic: automaticNames,
433
+ manual: manualNames
434
+ },
435
+ trigger_config: triggerConfig
436
+ };
437
+ }
438
+
389
439
  // ../tracking-core/src/ingest.ts
390
440
  var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
391
441
  var DEFAULT_MAX_QUEUE_SIZE = 10;
@@ -465,11 +515,33 @@ function createTrackingClient(config) {
465
515
  let firstPage = null;
466
516
  let destroyed = false;
467
517
  const visitorId = getVisitorId();
468
- let sessionId = getOrRotateSessionId();
518
+ const initialSession = getOrRotateSessionId();
519
+ let sessionId = initialSession.id;
469
520
  if (typeof window !== "undefined")
470
521
  firstPage = window.location.href;
522
+ function enqueueHeartbeat() {
523
+ const metadata = buildHeartbeatMetadata(
524
+ config.surface,
525
+ sdkVersion,
526
+ packageName,
527
+ config.triggers ?? null
528
+ );
529
+ queue.push({
530
+ event_type: "sdk_heartbeat",
531
+ page_url: typeof window === "undefined" ? null : window.location.href,
532
+ metadata,
533
+ occurred_at: (/* @__PURE__ */ new Date()).toISOString()
534
+ });
535
+ }
536
+ if (initialSession.isNew) {
537
+ enqueueHeartbeat();
538
+ }
471
539
  function buildSessionPayload() {
472
- sessionId = getOrRotateSessionId();
540
+ const rotated = getOrRotateSessionId();
541
+ if (rotated.isNew && rotated.id !== sessionId) {
542
+ enqueueHeartbeat();
543
+ }
544
+ sessionId = rotated.id;
473
545
  const params = readTrackingParams();
474
546
  const context = buildContext(config.surface, sdkVersion, packageName);
475
547
  return {
@@ -576,85 +648,110 @@ var ctaClickMetadataSchema = z2.object({
576
648
  cta_name: z2.string(),
577
649
  page: z2.object({
578
650
  path: z2.string()
579
- }),
651
+ }).strict(),
580
652
  section: z2.string().nullable().optional(),
581
653
  destination_url: z2.string().nullable().optional()
582
654
  }).strict();
583
655
  var ctaClickConfigSchema = z2.object({}).strict();
584
656
 
585
- // ../tracking-core/src/events/form-start.ts
657
+ // ../tracking-core/src/events/sdk-heartbeat.ts
586
658
  import { z as z3 } from "zod";
587
- var formStartMetadataSchema = z3.object({
588
- form: z3.object({
589
- id: z3.string(),
590
- action: z3.string().nullable()
591
- }),
592
- page: z3.object({
593
- path: z3.string()
594
- })
659
+ var sdkHeartbeatTriggersSchema = z3.object({
660
+ automatic: z3.array(z3.string()),
661
+ manual: z3.array(z3.string())
595
662
  }).strict();
596
- var formStartConfigSchema = z3.object({
597
- selector: z3.string().optional()
663
+ var sdkHeartbeatMetadataSchema = z3.object({
664
+ sdk_version: z3.string(),
665
+ package_name: z3.string().nullable(),
666
+ surface: z3.enum(["next", "react", "script"]),
667
+ triggers: sdkHeartbeatTriggersSchema,
668
+ trigger_config: z3.record(z3.string(), z3.record(z3.string(), z3.unknown())).nullable().optional()
598
669
  }).strict();
670
+ var sdkHeartbeatConfigSchema = z3.object({}).strict();
599
671
 
600
- // ../tracking-core/src/events/form-submit.ts
672
+ // ../tracking-core/src/events/form-start.ts
601
673
  import { z as z4 } from "zod";
602
- var formSubmitMetadataSchema = z4.object({
674
+ var formStartMetadataSchema = z4.object({
603
675
  form: z4.object({
604
676
  id: z4.string(),
605
- action: z4.string().nullable(),
606
- fields: z4.array(
607
- z4.object({
608
- name: z4.string(),
609
- type: z4.string(),
610
- label: z4.string().nullable(),
611
- has_value: z4.boolean()
612
- })
613
- ).optional()
614
- }),
677
+ action: z4.string().nullable()
678
+ }).strict(),
615
679
  page: z4.object({
616
680
  path: z4.string()
617
- })
681
+ }).strict()
682
+ }).strict();
683
+ var formStartConfigSchema = z4.object({
684
+ selector: z4.string().optional()
618
685
  }).strict();
619
- var formSubmitConfigSchema = z4.object({}).strict();
620
686
 
621
- // ../tracking-core/src/events/multi-page-session.ts
687
+ // ../tracking-core/src/events/form-submit.ts
622
688
  import { z as z5 } from "zod";
623
- var multiPageSessionMetadataSchema = z5.object({
624
- page_count: z5.number().int().min(2),
689
+ var jsonValueSchema = z5.lazy(
690
+ () => z5.union([
691
+ z5.string(),
692
+ z5.number().finite(),
693
+ z5.boolean(),
694
+ z5.null(),
695
+ z5.array(jsonValueSchema),
696
+ z5.record(jsonValueSchema)
697
+ ])
698
+ );
699
+ var formSubmitMetadataSchema = z5.object({
700
+ form: z5.object({
701
+ id: z5.string(),
702
+ action: z5.string().nullable(),
703
+ fields: z5.array(
704
+ z5.object({
705
+ name: z5.string(),
706
+ type: z5.string(),
707
+ label: z5.string().nullable(),
708
+ value: jsonValueSchema
709
+ }).strict()
710
+ ).optional()
711
+ }).strict(),
625
712
  page: z5.object({
626
713
  path: z5.string()
627
- })
628
- }).strict();
629
- var multiPageSessionConfigSchema = z5.object({
630
- pageThreshold: z5.number().int().min(2)
714
+ }).strict()
631
715
  }).strict();
716
+ var formSubmitConfigSchema = z5.object({}).strict();
632
717
 
633
- // ../tracking-core/src/events/phone-click.ts
718
+ // ../tracking-core/src/events/multi-page-session.ts
634
719
  import { z as z6 } from "zod";
635
- var phoneClickMetadataSchema = z6.object({
636
- phone_number: z6.string(),
720
+ var multiPageSessionMetadataSchema = z6.object({
721
+ page_count: z6.number().int().min(2),
637
722
  page: z6.object({
638
723
  path: z6.string()
639
- }),
640
- section: z6.string().nullable().optional()
724
+ }).strict()
725
+ }).strict();
726
+ var multiPageSessionConfigSchema = z6.object({
727
+ pageThreshold: z6.number().int().min(2)
641
728
  }).strict();
642
- var phoneClickConfigSchema = z6.object({}).strict();
643
729
 
644
- // ../tracking-core/src/events/scroll-depth.ts
730
+ // ../tracking-core/src/events/phone-click.ts
645
731
  import { z as z7 } from "zod";
646
- var scrollDepthMetadataSchema = z7.object({
647
- depth_percent: z7.number().int().min(1).max(100),
732
+ var phoneClickMetadataSchema = z7.object({
733
+ phone_number: z7.string(),
648
734
  page: z7.object({
649
735
  path: z7.string()
650
- })
736
+ }).strict(),
737
+ section: z7.string().nullable().optional()
738
+ }).strict();
739
+ var phoneClickConfigSchema = z7.object({}).strict();
740
+
741
+ // ../tracking-core/src/events/scroll-depth.ts
742
+ import { z as z8 } from "zod";
743
+ var scrollDepthMetadataSchema = z8.object({
744
+ depth_percent: z8.number().int().min(1).max(100),
745
+ page: z8.object({
746
+ path: z8.string()
747
+ }).strict()
651
748
  }).strict();
652
- var scrollDepthConfigSchema = z7.object({
653
- thresholds: z7.array(z7.number().int().min(1).max(100)).min(1)
749
+ var scrollDepthConfigSchema = z8.object({
750
+ thresholds: z8.array(z8.number().int().min(1).max(100)).min(1)
654
751
  }).strict();
655
752
 
656
753
  // ../tracking-core/src/events/specific-page-visit.ts
657
- import { z as z8 } from "zod";
754
+ import { z as z9 } from "zod";
658
755
  var SPECIFIC_PAGE_NAMES = [
659
756
  "contact_page",
660
757
  "about_page",
@@ -665,34 +762,34 @@ var SPECIFIC_PAGE_NAMES = [
665
762
  "faq_page",
666
763
  "testimonials_page"
667
764
  ];
668
- var specificPageNameSchema = z8.enum(SPECIFIC_PAGE_NAMES);
669
- var specificPageVisitMetadataSchema = z8.object({
765
+ var specificPageNameSchema = z9.enum(SPECIFIC_PAGE_NAMES);
766
+ var specificPageVisitMetadataSchema = z9.object({
670
767
  page_name: specificPageNameSchema,
671
- page: z8.object({
672
- path: z8.string()
673
- })
768
+ page: z9.object({
769
+ path: z9.string()
770
+ }).strict()
674
771
  }).strict();
675
- var specificPageVisitConfigSchema = z8.object({
676
- pages: z8.array(
677
- z8.object({
772
+ var specificPageVisitConfigSchema = z9.object({
773
+ pages: z9.array(
774
+ z9.object({
678
775
  name: specificPageNameSchema,
679
- pathPattern: z8.custom((value) => value instanceof RegExp, {
776
+ pathPattern: z9.custom((value) => value instanceof RegExp, {
680
777
  message: "pathPattern must be a RegExp"
681
778
  })
682
- })
779
+ }).strict()
683
780
  ).min(1)
684
781
  }).strict();
685
782
 
686
783
  // ../tracking-core/src/events/time-on-site.ts
687
- import { z as z9 } from "zod";
688
- var timeOnSiteMetadataSchema = z9.object({
689
- duration_ms: z9.number().int().nonnegative(),
690
- page: z9.object({
691
- path: z9.string()
692
- })
784
+ import { z as z10 } from "zod";
785
+ var timeOnSiteMetadataSchema = z10.object({
786
+ duration_ms: z10.number().int().nonnegative(),
787
+ page: z10.object({
788
+ path: z10.string()
789
+ }).strict()
693
790
  }).strict();
694
- var timeOnSiteConfigSchema = z9.object({
695
- thresholdSeconds: z9.number().int().positive()
791
+ var timeOnSiteConfigSchema = z10.object({
792
+ thresholdSeconds: z10.number().int().positive()
696
793
  }).strict();
697
794
 
698
795
  // ../tracking-core/src/events/registry.ts
@@ -728,6 +825,12 @@ var EVENT_REGISTRY = {
728
825
  metadataSchema: formStartMetadataSchema,
729
826
  configSchema: formStartConfigSchema
730
827
  },
828
+ // --- SDK-internal automatic (not consumer-configurable) ---
829
+ sdk_heartbeat: {
830
+ kind: "automatic",
831
+ metadataSchema: sdkHeartbeatMetadataSchema,
832
+ configSchema: sdkHeartbeatConfigSchema
833
+ },
731
834
  // --- manual triggers ---
732
835
  form_submit: {
733
836
  kind: "manual",
@@ -987,7 +1090,7 @@ function attachMultiPageSession(client, config) {
987
1090
  }
988
1091
  }
989
1092
  function resetIfSessionChanged() {
990
- const currentSession = getOrRotateSessionId();
1093
+ const currentSession = getOrRotateSessionId().id;
991
1094
  const storedSession = storage.getItem(SESSION_KEY);
992
1095
  if (storedSession !== currentSession) {
993
1096
  storage.setItem(SESSION_KEY, currentSession);
@@ -1200,7 +1303,7 @@ import {
1200
1303
  } from "react";
1201
1304
 
1202
1305
  // package.json
1203
- var version = "0.5.0";
1306
+ var version = "0.6.0";
1204
1307
 
1205
1308
  // src/factory.tsx
1206
1309
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -1236,6 +1339,7 @@ function createTracking(options) {
1236
1339
  surface: "react",
1237
1340
  packageName: "@aranova/tracking-react",
1238
1341
  sdkVersion: version,
1342
+ triggers,
1239
1343
  debug
1240
1344
  }),
1241
1345
  triggers,
@@ -1254,6 +1358,7 @@ function createTracking(options) {
1254
1358
  endpoint,
1255
1359
  surface: "react",
1256
1360
  packageName: "@aranova/tracking-react",
1361
+ triggers,
1257
1362
  debug
1258
1363
  });
1259
1364
  detachers.push(attachAutoPageView(rawClient));