@aranova/tracking-next 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
@@ -206,7 +206,7 @@ function getVisitorId() {
206
206
  }
207
207
  function getOrRotateSessionId(now = Date.now()) {
208
208
  if (typeof window === "undefined")
209
- return safeUuid();
209
+ return { id: safeUuid(), isNew: true };
210
210
  const raw = readLocalStorage(SESSION_STORAGE_KEY);
211
211
  if (raw) {
212
212
  try {
@@ -215,7 +215,7 @@ function getOrRotateSessionId(now = Date.now()) {
215
215
  if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
216
216
  const refreshed = { id: parsed.id, last_event_at: now };
217
217
  writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
218
- return parsed.id;
218
+ return { id: parsed.id, isNew: false };
219
219
  }
220
220
  }
221
221
  } catch {
@@ -223,7 +223,7 @@ function getOrRotateSessionId(now = Date.now()) {
223
223
  }
224
224
  const fresh = { id: safeUuid(), last_event_at: now };
225
225
  writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
226
- return fresh.id;
226
+ return { id: fresh.id, isNew: true };
227
227
  }
228
228
 
229
229
  // ../tracking-core/src/events/page-view.ts
@@ -234,7 +234,7 @@ var pageViewMetadataSchema = z.object({
234
234
  path: z.string(),
235
235
  search: z.string(),
236
236
  hash: z.string()
237
- }),
237
+ }).strict(),
238
238
  referrer: z.string().nullable(),
239
239
  // `.nullable().optional()` — absent (undefined) OR explicit null OR a
240
240
  // real viewport object. Mirrors Pydantic's `_Viewport | None = None`
@@ -242,7 +242,7 @@ var pageViewMetadataSchema = z.object({
242
242
  viewport: z.object({
243
243
  w: z.number(),
244
244
  h: z.number()
245
- }).nullable().optional()
245
+ }).strict().nullable().optional()
246
246
  }).strict();
247
247
  var pageViewConfigSchema = z.object({}).strict();
248
248
 
@@ -323,6 +323,56 @@ function attachBfcacheRestore(client) {
323
323
  };
324
324
  }
325
325
 
326
+ // ../tracking-core/src/heartbeat.ts
327
+ function serializeValue(value) {
328
+ if (value instanceof RegExp) return value.source;
329
+ if (Array.isArray(value)) return value.map(serializeValue);
330
+ if (value !== null && typeof value === "object") {
331
+ const out = {};
332
+ for (const [k, v] of Object.entries(value)) {
333
+ out[k] = serializeValue(v);
334
+ }
335
+ return out;
336
+ }
337
+ return value;
338
+ }
339
+ function buildHeartbeatMetadata(surface, sdkVersion, packageName, triggers) {
340
+ const automaticNames = triggers ? Object.keys(triggers.automatic) : [];
341
+ const manualNames = triggers?.manual ? Object.keys(triggers.manual) : [];
342
+ let triggerConfig = null;
343
+ if (triggers) {
344
+ const cfg = {};
345
+ for (const [name, config] of Object.entries(triggers.automatic)) {
346
+ const serialized = serializeValue(config);
347
+ if (Object.keys(serialized).length > 0) {
348
+ cfg[name] = serialized;
349
+ }
350
+ }
351
+ if (triggers.manual) {
352
+ for (const [name, config] of Object.entries(triggers.manual)) {
353
+ if (config === void 0) continue;
354
+ const serialized = serializeValue(config);
355
+ if (Object.keys(serialized).length > 0) {
356
+ cfg[name] = serialized;
357
+ }
358
+ }
359
+ }
360
+ if (Object.keys(cfg).length > 0) {
361
+ triggerConfig = cfg;
362
+ }
363
+ }
364
+ return {
365
+ sdk_version: sdkVersion ?? "unknown",
366
+ package_name: packageName,
367
+ surface,
368
+ triggers: {
369
+ automatic: automaticNames,
370
+ manual: manualNames
371
+ },
372
+ trigger_config: triggerConfig
373
+ };
374
+ }
375
+
326
376
  // ../tracking-core/src/ingest.ts
327
377
  var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
328
378
  var DEFAULT_MAX_QUEUE_SIZE = 10;
@@ -402,11 +452,33 @@ function createTrackingClient(config) {
402
452
  let firstPage = null;
403
453
  let destroyed = false;
404
454
  const visitorId = getVisitorId();
405
- let sessionId = getOrRotateSessionId();
455
+ const initialSession = getOrRotateSessionId();
456
+ let sessionId = initialSession.id;
406
457
  if (typeof window !== "undefined")
407
458
  firstPage = window.location.href;
459
+ function enqueueHeartbeat() {
460
+ const metadata = buildHeartbeatMetadata(
461
+ config.surface,
462
+ sdkVersion,
463
+ packageName,
464
+ config.triggers ?? null
465
+ );
466
+ queue.push({
467
+ event_type: "sdk_heartbeat",
468
+ page_url: typeof window === "undefined" ? null : window.location.href,
469
+ metadata,
470
+ occurred_at: (/* @__PURE__ */ new Date()).toISOString()
471
+ });
472
+ }
473
+ if (initialSession.isNew) {
474
+ enqueueHeartbeat();
475
+ }
408
476
  function buildSessionPayload() {
409
- sessionId = getOrRotateSessionId();
477
+ const rotated = getOrRotateSessionId();
478
+ if (rotated.isNew && rotated.id !== sessionId) {
479
+ enqueueHeartbeat();
480
+ }
481
+ sessionId = rotated.id;
410
482
  const params = readTrackingParams();
411
483
  const context = buildContext(config.surface, sdkVersion, packageName);
412
484
  return {
@@ -513,85 +585,110 @@ var ctaClickMetadataSchema = z2.object({
513
585
  cta_name: z2.string(),
514
586
  page: z2.object({
515
587
  path: z2.string()
516
- }),
588
+ }).strict(),
517
589
  section: z2.string().nullable().optional(),
518
590
  destination_url: z2.string().nullable().optional()
519
591
  }).strict();
520
592
  var ctaClickConfigSchema = z2.object({}).strict();
521
593
 
522
- // ../tracking-core/src/events/form-start.ts
594
+ // ../tracking-core/src/events/sdk-heartbeat.ts
523
595
  import { z as z3 } from "zod";
524
- var formStartMetadataSchema = z3.object({
525
- form: z3.object({
526
- id: z3.string(),
527
- action: z3.string().nullable()
528
- }),
529
- page: z3.object({
530
- path: z3.string()
531
- })
596
+ var sdkHeartbeatTriggersSchema = z3.object({
597
+ automatic: z3.array(z3.string()),
598
+ manual: z3.array(z3.string())
532
599
  }).strict();
533
- var formStartConfigSchema = z3.object({
534
- selector: z3.string().optional()
600
+ var sdkHeartbeatMetadataSchema = z3.object({
601
+ sdk_version: z3.string(),
602
+ package_name: z3.string().nullable(),
603
+ surface: z3.enum(["next", "react", "script"]),
604
+ triggers: sdkHeartbeatTriggersSchema,
605
+ trigger_config: z3.record(z3.string(), z3.record(z3.string(), z3.unknown())).nullable().optional()
535
606
  }).strict();
607
+ var sdkHeartbeatConfigSchema = z3.object({}).strict();
536
608
 
537
- // ../tracking-core/src/events/form-submit.ts
609
+ // ../tracking-core/src/events/form-start.ts
538
610
  import { z as z4 } from "zod";
539
- var formSubmitMetadataSchema = z4.object({
611
+ var formStartMetadataSchema = z4.object({
540
612
  form: z4.object({
541
613
  id: z4.string(),
542
- action: z4.string().nullable(),
543
- fields: z4.array(
544
- z4.object({
545
- name: z4.string(),
546
- type: z4.string(),
547
- label: z4.string().nullable(),
548
- has_value: z4.boolean()
549
- })
550
- ).optional()
551
- }),
614
+ action: z4.string().nullable()
615
+ }).strict(),
552
616
  page: z4.object({
553
617
  path: z4.string()
554
- })
618
+ }).strict()
619
+ }).strict();
620
+ var formStartConfigSchema = z4.object({
621
+ selector: z4.string().optional()
555
622
  }).strict();
556
- var formSubmitConfigSchema = z4.object({}).strict();
557
623
 
558
- // ../tracking-core/src/events/multi-page-session.ts
624
+ // ../tracking-core/src/events/form-submit.ts
559
625
  import { z as z5 } from "zod";
560
- var multiPageSessionMetadataSchema = z5.object({
561
- page_count: z5.number().int().min(2),
626
+ var jsonValueSchema = z5.lazy(
627
+ () => z5.union([
628
+ z5.string(),
629
+ z5.number().finite(),
630
+ z5.boolean(),
631
+ z5.null(),
632
+ z5.array(jsonValueSchema),
633
+ z5.record(jsonValueSchema)
634
+ ])
635
+ );
636
+ var formSubmitMetadataSchema = z5.object({
637
+ form: z5.object({
638
+ id: z5.string(),
639
+ action: z5.string().nullable(),
640
+ fields: z5.array(
641
+ z5.object({
642
+ name: z5.string(),
643
+ type: z5.string(),
644
+ label: z5.string().nullable(),
645
+ value: jsonValueSchema
646
+ }).strict()
647
+ ).optional()
648
+ }).strict(),
562
649
  page: z5.object({
563
650
  path: z5.string()
564
- })
565
- }).strict();
566
- var multiPageSessionConfigSchema = z5.object({
567
- pageThreshold: z5.number().int().min(2)
651
+ }).strict()
568
652
  }).strict();
653
+ var formSubmitConfigSchema = z5.object({}).strict();
569
654
 
570
- // ../tracking-core/src/events/phone-click.ts
655
+ // ../tracking-core/src/events/multi-page-session.ts
571
656
  import { z as z6 } from "zod";
572
- var phoneClickMetadataSchema = z6.object({
573
- phone_number: z6.string(),
657
+ var multiPageSessionMetadataSchema = z6.object({
658
+ page_count: z6.number().int().min(2),
574
659
  page: z6.object({
575
660
  path: z6.string()
576
- }),
577
- section: z6.string().nullable().optional()
661
+ }).strict()
662
+ }).strict();
663
+ var multiPageSessionConfigSchema = z6.object({
664
+ pageThreshold: z6.number().int().min(2)
578
665
  }).strict();
579
- var phoneClickConfigSchema = z6.object({}).strict();
580
666
 
581
- // ../tracking-core/src/events/scroll-depth.ts
667
+ // ../tracking-core/src/events/phone-click.ts
582
668
  import { z as z7 } from "zod";
583
- var scrollDepthMetadataSchema = z7.object({
584
- depth_percent: z7.number().int().min(1).max(100),
669
+ var phoneClickMetadataSchema = z7.object({
670
+ phone_number: z7.string(),
585
671
  page: z7.object({
586
672
  path: z7.string()
587
- })
673
+ }).strict(),
674
+ section: z7.string().nullable().optional()
588
675
  }).strict();
589
- var scrollDepthConfigSchema = z7.object({
590
- thresholds: z7.array(z7.number().int().min(1).max(100)).min(1)
676
+ var phoneClickConfigSchema = z7.object({}).strict();
677
+
678
+ // ../tracking-core/src/events/scroll-depth.ts
679
+ import { z as z8 } from "zod";
680
+ var scrollDepthMetadataSchema = z8.object({
681
+ depth_percent: z8.number().int().min(1).max(100),
682
+ page: z8.object({
683
+ path: z8.string()
684
+ }).strict()
685
+ }).strict();
686
+ var scrollDepthConfigSchema = z8.object({
687
+ thresholds: z8.array(z8.number().int().min(1).max(100)).min(1)
591
688
  }).strict();
592
689
 
593
690
  // ../tracking-core/src/events/specific-page-visit.ts
594
- import { z as z8 } from "zod";
691
+ import { z as z9 } from "zod";
595
692
  var SPECIFIC_PAGE_NAMES = [
596
693
  "contact_page",
597
694
  "about_page",
@@ -602,34 +699,34 @@ var SPECIFIC_PAGE_NAMES = [
602
699
  "faq_page",
603
700
  "testimonials_page"
604
701
  ];
605
- var specificPageNameSchema = z8.enum(SPECIFIC_PAGE_NAMES);
606
- var specificPageVisitMetadataSchema = z8.object({
702
+ var specificPageNameSchema = z9.enum(SPECIFIC_PAGE_NAMES);
703
+ var specificPageVisitMetadataSchema = z9.object({
607
704
  page_name: specificPageNameSchema,
608
- page: z8.object({
609
- path: z8.string()
610
- })
705
+ page: z9.object({
706
+ path: z9.string()
707
+ }).strict()
611
708
  }).strict();
612
- var specificPageVisitConfigSchema = z8.object({
613
- pages: z8.array(
614
- z8.object({
709
+ var specificPageVisitConfigSchema = z9.object({
710
+ pages: z9.array(
711
+ z9.object({
615
712
  name: specificPageNameSchema,
616
- pathPattern: z8.custom((value) => value instanceof RegExp, {
713
+ pathPattern: z9.custom((value) => value instanceof RegExp, {
617
714
  message: "pathPattern must be a RegExp"
618
715
  })
619
- })
716
+ }).strict()
620
717
  ).min(1)
621
718
  }).strict();
622
719
 
623
720
  // ../tracking-core/src/events/time-on-site.ts
624
- import { z as z9 } from "zod";
625
- var timeOnSiteMetadataSchema = z9.object({
626
- duration_ms: z9.number().int().nonnegative(),
627
- page: z9.object({
628
- path: z9.string()
629
- })
721
+ import { z as z10 } from "zod";
722
+ var timeOnSiteMetadataSchema = z10.object({
723
+ duration_ms: z10.number().int().nonnegative(),
724
+ page: z10.object({
725
+ path: z10.string()
726
+ }).strict()
630
727
  }).strict();
631
- var timeOnSiteConfigSchema = z9.object({
632
- thresholdSeconds: z9.number().int().positive()
728
+ var timeOnSiteConfigSchema = z10.object({
729
+ thresholdSeconds: z10.number().int().positive()
633
730
  }).strict();
634
731
 
635
732
  // ../tracking-core/src/events/registry.ts
@@ -665,6 +762,12 @@ var EVENT_REGISTRY = {
665
762
  metadataSchema: formStartMetadataSchema,
666
763
  configSchema: formStartConfigSchema
667
764
  },
765
+ // --- SDK-internal automatic (not consumer-configurable) ---
766
+ sdk_heartbeat: {
767
+ kind: "automatic",
768
+ metadataSchema: sdkHeartbeatMetadataSchema,
769
+ configSchema: sdkHeartbeatConfigSchema
770
+ },
668
771
  // --- manual triggers ---
669
772
  form_submit: {
670
773
  kind: "manual",
@@ -1007,7 +1110,7 @@ function GoogleAdsTracking({ gtagId }) {
1007
1110
  import { usePathname, useSearchParams } from "next/navigation";
1008
1111
 
1009
1112
  // package.json
1010
- var version = "0.5.0";
1113
+ var version = "0.6.0";
1011
1114
 
1012
1115
  // src/factory.tsx
1013
1116
  import {
@@ -1053,6 +1156,7 @@ function createTracking(options) {
1053
1156
  surface: "next",
1054
1157
  packageName: "@aranova/tracking-next",
1055
1158
  sdkVersion: version,
1159
+ triggers,
1056
1160
  debug
1057
1161
  }),
1058
1162
  triggers,
@@ -1066,6 +1170,7 @@ function createTracking(options) {
1066
1170
  endpoint,
1067
1171
  surface: "next",
1068
1172
  packageName: "@aranova/tracking-next",
1173
+ triggers,
1069
1174
  debug
1070
1175
  });
1071
1176
  fireManualPageView(rawClient);
@@ -1078,6 +1183,7 @@ function createTracking(options) {
1078
1183
  endpoint,
1079
1184
  surface: "next",
1080
1185
  packageName: "@aranova/tracking-next",
1186
+ triggers,
1081
1187
  debug
1082
1188
  });
1083
1189
  for (const { name, pathPattern } of specificPageVisitConfig.pages) {
@@ -1099,6 +1205,7 @@ function createTracking(options) {
1099
1205
  endpoint,
1100
1206
  surface: "next",
1101
1207
  packageName: "@aranova/tracking-next",
1208
+ triggers,
1102
1209
  debug
1103
1210
  });
1104
1211
  return attachBfcacheRestore(rawClient);
@@ -1110,6 +1217,7 @@ function createTracking(options) {
1110
1217
  endpoint,
1111
1218
  surface: "next",
1112
1219
  packageName: "@aranova/tracking-next",
1220
+ triggers,
1113
1221
  debug
1114
1222
  });
1115
1223
  return attachTimeOnSite(rawClient, timeOnSiteConfig);
@@ -1125,7 +1233,7 @@ function createTracking(options) {
1125
1233
  const MPS_FIRED_KEY = "aranova_tracking_mps_fired";
1126
1234
  try {
1127
1235
  const store = window.sessionStorage;
1128
- const currentSession = getOrRotateSessionId();
1236
+ const currentSession = getOrRotateSessionId().id;
1129
1237
  const storedSession = store.getItem(MPS_SESSION_KEY);
1130
1238
  if (storedSession !== currentSession) {
1131
1239
  store.setItem(MPS_SESSION_KEY, currentSession);
@@ -1144,6 +1252,7 @@ function createTracking(options) {
1144
1252
  endpoint,
1145
1253
  surface: "next",
1146
1254
  packageName: "@aranova/tracking-next",
1255
+ triggers,
1147
1256
  debug
1148
1257
  });
1149
1258
  rawClient.trackEvent({
@@ -1166,6 +1275,7 @@ function createTracking(options) {
1166
1275
  endpoint,
1167
1276
  surface: "next",
1168
1277
  packageName: "@aranova/tracking-next",
1278
+ triggers,
1169
1279
  debug
1170
1280
  });
1171
1281
  const scrollDepth = triggers.automatic.scroll_depth;