@aranova/tracking-react 1.14.0 → 1.14.1

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/sales.js CHANGED
@@ -142,6 +142,14 @@ var DEFAULT_CHOICE = {
142
142
  updatedAt: null,
143
143
  expiresAt: null
144
144
  };
145
+ function buildConsentPayload(state) {
146
+ return {
147
+ ad_storage: state,
148
+ ad_user_data: state,
149
+ ad_personalization: state,
150
+ analytics_storage: state
151
+ };
152
+ }
145
153
  function getConsentChoice() {
146
154
  if (typeof window === "undefined") return DEFAULT_CHOICE;
147
155
  try {
@@ -170,6 +178,24 @@ function getConsentState() {
170
178
  }
171
179
 
172
180
  // ../tracking-core/src/gtag.ts
181
+ var GTAG_SCRIPT_HOST = "https://www.googletagmanager.com/gtag/js";
182
+ var TRACKING_SCRIPT_ATTRIBUTE = "data-aranova-tracking";
183
+ function getScriptMarker(id) {
184
+ return `aranova-${id}`;
185
+ }
186
+ var GTAG_ID_PATTERN = /^[A-Z]{1,3}-[A-Za-z0-9_-]+$/;
187
+ function isValidGtagId(id) {
188
+ return GTAG_ID_PATTERN.test(id);
189
+ }
190
+ function ensureGtagFunction() {
191
+ window.dataLayer = window.dataLayer || [];
192
+ if (typeof window.gtag === "function") return window.gtag;
193
+ function gtag() {
194
+ window.dataLayer?.push(arguments);
195
+ }
196
+ window.gtag = gtag;
197
+ return window.gtag;
198
+ }
173
199
  var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
174
200
  function isValidSendTo(sendTo) {
175
201
  return SEND_TO_RE.test(sendTo);
@@ -188,6 +214,27 @@ function fireGtagConversion(input) {
188
214
  return false;
189
215
  }
190
216
  }
217
+ function applyDefaultConsentState() {
218
+ const gtag = ensureGtagFunction();
219
+ gtag(
220
+ "consent",
221
+ "default",
222
+ buildConsentPayload(getConsentState() === "denied" ? "denied" : "granted")
223
+ );
224
+ }
225
+ function loadGtagScript(gtagId) {
226
+ if (typeof document === "undefined") return;
227
+ const marker = getScriptMarker("gtag-loader");
228
+ const existingScript = document.querySelector(
229
+ `script[${TRACKING_SCRIPT_ATTRIBUTE}="${marker}"]`
230
+ );
231
+ if (existingScript) return;
232
+ const script = document.createElement("script");
233
+ script.async = true;
234
+ script.src = `${GTAG_SCRIPT_HOST}?id=${encodeURIComponent(gtagId)}`;
235
+ script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);
236
+ document.head.append(script);
237
+ }
191
238
 
192
239
  // ../tracking-core/src/resources/conversion-firing.ts
193
240
  var DEDUP_PREFIX = "_aranova_conv_";
@@ -223,6 +270,77 @@ function fireConversionWithConsent(input, options) {
223
270
  }
224
271
  }
225
272
 
273
+ // ../tracking-core/src/events/form-submit.ts
274
+ var z = __toESM(require("zod/mini"));
275
+ var jsonValueSchema = z.lazy(
276
+ () => z.union([
277
+ z.string(),
278
+ z.number(),
279
+ z.boolean(),
280
+ z.null(),
281
+ z.array(jsonValueSchema),
282
+ z.record(z.string(), jsonValueSchema)
283
+ ])
284
+ ).register(z.globalRegistry, { id: "JsonSerializableValue" });
285
+ var contractNameSchema = z.string().check(z.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));
286
+ var contractName = contractNameSchema;
287
+ var violationRole = z.string().check(
288
+ z.maxLength(200),
289
+ z.regex(
290
+ /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/
291
+ )
292
+ );
293
+ var MAX_CONTRACT_VIOLATIONS = 50;
294
+ var contractViolationSchema = z.strictObject({
295
+ role: z.nullable(violationRole),
296
+ rule: z.string().check(z.regex(/^[a-z][a-z_]{0,47}$/))
297
+ });
298
+ var formSubmitMetadataSchema = z.strictObject({
299
+ form: z.strictObject({
300
+ id: z.string(),
301
+ action: z.nullable(z.string()),
302
+ fields: z.optional(
303
+ z.array(
304
+ z.strictObject({
305
+ name: z.string(),
306
+ type: z.string(),
307
+ label: z.nullable(z.string()),
308
+ value: jsonValueSchema,
309
+ role: z.optional(z.nullable(contractName))
310
+ })
311
+ )
312
+ ),
313
+ kind: z.optional(z.nullable(contractName)),
314
+ contract_version: z.optional(z.nullable(z.int().check(z.gte(1)))),
315
+ contract_hash: z.optional(z.nullable(z.string().check(z.maxLength(64)))),
316
+ extra: z.optional(
317
+ z.nullable(
318
+ z.array(
319
+ z.strictObject({
320
+ key: z.string().check(z.maxLength(64)),
321
+ label: z.string().check(z.maxLength(200)),
322
+ value: jsonValueSchema
323
+ })
324
+ ).check(z.maxLength(20))
325
+ )
326
+ ),
327
+ contract_violations: z.optional(
328
+ z.nullable(z.array(contractViolationSchema).check(z.maxLength(MAX_CONTRACT_VIOLATIONS)))
329
+ )
330
+ }),
331
+ page: z.strictObject({
332
+ path: z.string()
333
+ })
334
+ });
335
+ var formSubmitConfigSchema = z.strictObject({});
336
+
337
+ // ../tracking-core/src/ingest-typed.ts
338
+ var import_mini = require("zod/mini");
339
+ var clientSettings = /* @__PURE__ */ new WeakMap();
340
+ function typedClientSettings(client) {
341
+ return clientSettings.get(client) ?? {};
342
+ }
343
+
226
344
  // ../../contracts/autoleap-booking-links.json
227
345
  var autoleap_booking_links_default = {
228
346
  hosts: ["myalp.io"]
@@ -295,8 +413,108 @@ function nationalDigits(phone) {
295
413
  }
296
414
 
297
415
  // ../tracking-core/src/lead-forms/lead-forms.gen.ts
298
- var z = __toESM(require("zod/mini"));
416
+ var z2 = __toESM(require("zod/mini"));
299
417
  var LEAD_FORM_REGISTRY_VERSION = 2;
418
+ var LEAD_FORM_ROLES = [
419
+ "given_name",
420
+ "family_name",
421
+ "phone",
422
+ "email",
423
+ "service",
424
+ "preferred_date",
425
+ "preferred_time",
426
+ "note",
427
+ "visit",
428
+ "intent",
429
+ "vehicle",
430
+ "drop_off",
431
+ "address",
432
+ "patient_type",
433
+ "matter",
434
+ "language",
435
+ "products",
436
+ "goal",
437
+ "business_type",
438
+ "lead_volume",
439
+ "attribution"
440
+ ];
441
+ var leadFormPrimitiveSchemas = /* @__PURE__ */ (() => ({
442
+ string: z2.string().check(z2.maxLength(120)),
443
+ text: z2.string().check(z2.maxLength(2e3)),
444
+ enum: z2.string().check(z2.regex(/^[a-z0-9-]{1,48}$/)),
445
+ multi_enum: z2.array(z2.string().check(z2.regex(/^[a-z0-9-]{1,48}$/))).check(z2.maxLength(10)),
446
+ bool: z2.boolean(),
447
+ date: z2.string().check(z2.regex(/^\d{4}-\d{2}-\d{2}$/)),
448
+ time_window: z2.strictObject({
449
+ start: z2.string().check(z2.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
450
+ end: z2.nullable(z2.string().check(z2.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
451
+ label: z2.optional(z2.string().check(z2.maxLength(60)))
452
+ }),
453
+ phone: z2.string().check(z2.regex(/^\+[1-9]\d{6,14}$/)),
454
+ email: z2.string().check(z2.maxLength(320), z2.regex(/^[^\s@]+@[^\s@]+$/)),
455
+ address: z2.strictObject({
456
+ line1: z2.string().check(z2.maxLength(120)),
457
+ line2: z2.optional(z2.string().check(z2.maxLength(120))),
458
+ city: z2.optional(z2.string().check(z2.maxLength(120))),
459
+ region: z2.optional(z2.string().check(z2.maxLength(120))),
460
+ postal_code: z2.optional(z2.string().check(z2.maxLength(120))),
461
+ country: z2.optional(z2.string().check(z2.maxLength(120)))
462
+ }),
463
+ number: z2.number()
464
+ }))();
465
+ var LEAD_FORM_ROLE_SPECS = {
466
+ given_name: { primitive: "string", smsLabel: "", contactIdentity: true },
467
+ family_name: { primitive: "string", smsLabel: "", contactIdentity: true },
468
+ phone: { primitive: "phone", smsLabel: "", contactIdentity: true },
469
+ email: { primitive: "email", smsLabel: "", contactIdentity: true },
470
+ service: { primitive: "multi_enum", smsLabel: "Service" },
471
+ preferred_date: { primitive: "date", smsLabel: "Date" },
472
+ preferred_time: { primitive: "time_window", smsLabel: "Time" },
473
+ note: { primitive: "text", smsLabel: "Note" },
474
+ visit: {
475
+ primitive: "enum",
476
+ smsLabel: "Visit",
477
+ options: [
478
+ { key: "in-shop", label: "In shop" },
479
+ { key: "in-home", label: "In home" },
480
+ { key: "remote", label: "Remote" },
481
+ { key: "on-site", label: "On site" }
482
+ ]
483
+ },
484
+ intent: {
485
+ primitive: "enum",
486
+ smsLabel: "Wants",
487
+ options: [
488
+ { key: "book", label: "Book" },
489
+ { key: "quote", label: "Quote" }
490
+ ]
491
+ },
492
+ vehicle: { primitive: "string", smsLabel: "Vehicle" },
493
+ drop_off: {
494
+ primitive: "enum",
495
+ smsLabel: "Drop-off",
496
+ options: [
497
+ { key: "wait-for-it", label: "Waiting" },
498
+ { key: "drop-off", label: "Dropping off" }
499
+ ]
500
+ },
501
+ address: { primitive: "address", smsLabel: "Address" },
502
+ patient_type: { primitive: "enum", smsLabel: "Patient" },
503
+ matter: { primitive: "enum", smsLabel: "Matter" },
504
+ language: { primitive: "enum", smsLabel: "Language" },
505
+ products: { primitive: "multi_enum", smsLabel: "Products" },
506
+ goal: { primitive: "enum", smsLabel: "Goal" },
507
+ business_type: { primitive: "enum", smsLabel: "Business" },
508
+ lead_volume: { primitive: "enum", smsLabel: "Lead volume" },
509
+ add_on: { primitive: "bool", smsLabel: "" },
510
+ attribution: { primitive: "enum", smsLabel: "", uninteresting: true }
511
+ };
512
+
513
+ // ../tracking-core/src/lead-forms/submit.ts
514
+ var import_mini2 = require("zod/mini");
515
+
516
+ // ../tracking-core/src/cross-site-link.ts
517
+ var z3 = __toESM(require("zod/mini"));
300
518
 
301
519
  // ../tracking-core/src/uuid.ts
302
520
  function uuidV4() {
@@ -334,17 +552,348 @@ function peekSessionId(now = Date.now()) {
334
552
  }
335
553
  }
336
554
 
337
- // ../tracking-core/src/resources/http/errors.ts
338
- var AranovaApiError = class extends Error {
339
- constructor(message, options) {
340
- super(message);
341
- this.name = "AranovaApiError";
342
- this.status = options.status;
343
- this.code = options.code;
344
- this.requestId = options.requestId;
345
- this.context = options.context;
346
- }
555
+ // ../tracking-core/src/cross-site-link.ts
556
+ var LINK_PARAM = "_arv";
557
+ var LINK_TOKEN_TTL_MS = 5 * 60 * 1e3;
558
+ var FUTURE_SKEW_MS = 60 * 1e3;
559
+ var CARRIED_KEYS = {
560
+ g: "gclid",
561
+ w: "wbraid",
562
+ gb: "gbraid",
563
+ f: "fbclid",
564
+ y: "ylpcid"
347
565
  };
566
+ var CARRIED_ENTRIES = Object.entries(CARRIED_KEYS);
567
+ var identifier = z3.string().check(z3.minLength(1), z3.maxLength(128));
568
+ var optionalIdentifier = z3.optional(identifier);
569
+ var linkTokenSchema = z3.object({
570
+ v: identifier,
571
+ s: identifier,
572
+ // Epoch ms in base36.
573
+ t: z3.string().check(z3.regex(/^[0-9a-z]{1,12}$/)),
574
+ g: optionalIdentifier,
575
+ w: optionalIdentifier,
576
+ gb: optionalIdentifier,
577
+ f: optionalIdentifier,
578
+ fc: optionalIdentifier,
579
+ y: optionalIdentifier
580
+ });
581
+ function stripLinkParam(url) {
582
+ try {
583
+ const parsed = new URL(url, typeof window === "undefined" ? void 0 : window.location.href);
584
+ if (!parsed.searchParams.has(LINK_PARAM)) return url;
585
+ parsed.searchParams.delete(LINK_PARAM);
586
+ return parsed.href;
587
+ } catch {
588
+ return url;
589
+ }
590
+ }
591
+
592
+ // ../tracking-core/src/resources/conversion-config.ts
593
+ var BOOKING_MODES = ["live", "test", "simulated"];
594
+ function isBookingMode(value) {
595
+ return BOOKING_MODES.includes(value);
596
+ }
597
+ function isStringMap(value) {
598
+ return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
599
+ }
600
+ function parseFiring(value) {
601
+ if (!value || typeof value !== "object") return null;
602
+ const f = value;
603
+ if (typeof f.send_to !== "string") return null;
604
+ return {
605
+ send_to: f.send_to,
606
+ value_cents: typeof f.value_cents === "number" ? f.value_cents : null,
607
+ currency: typeof f.currency === "string" ? f.currency : null
608
+ };
609
+ }
610
+ function isNumberList(value) {
611
+ return Array.isArray(value) && value.every((v) => typeof v === "number");
612
+ }
613
+ function isStringList(value) {
614
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
615
+ }
616
+ function parseWindowsMap(value) {
617
+ const windowsByKey = {};
618
+ if (!value || typeof value !== "object") return windowsByKey;
619
+ for (const [key, windows] of Object.entries(value)) {
620
+ if (!windows || typeof windows !== "object") continue;
621
+ const w = windows;
622
+ windowsByKey[key] = {
623
+ morning: isNumberList(w.morning) ? w.morning : [],
624
+ afternoon: isNumberList(w.afternoon) ? w.afternoon : []
625
+ };
626
+ }
627
+ return windowsByKey;
628
+ }
629
+ function parseBookingRules(value) {
630
+ if (!value || typeof value !== "object") return null;
631
+ const r = value;
632
+ if (typeof r.min_days_ahead !== "number" || typeof r.max_days_ahead !== "number") return null;
633
+ if (!r.hours || typeof r.hours !== "object") return null;
634
+ const hours = parseWindowsMap(r.hours);
635
+ return {
636
+ min_days_ahead: r.min_days_ahead,
637
+ max_days_ahead: r.max_days_ahead,
638
+ hours,
639
+ closed_dates: isStringList(r.closed_dates) ? r.closed_dates : [],
640
+ special_hours: parseWindowsMap(r.special_hours),
641
+ vehicle_required: r.vehicle_required === true,
642
+ vehicle_deferred: r.vehicle_deferred === true,
643
+ drop_off_options: isStringList(r.drop_off_options) ? r.drop_off_options : []
644
+ };
645
+ }
646
+ function parseBookingOption(value) {
647
+ if (!value || typeof value !== "object") return [];
648
+ const o = value;
649
+ if (typeof o.key !== "string" || !o.key || typeof o.label !== "string") return [];
650
+ return [
651
+ {
652
+ key: o.key,
653
+ label: o.label,
654
+ group: typeof o.group === "string" && o.group ? o.group : null,
655
+ description: typeof o.description === "string" && o.description ? o.description : null
656
+ }
657
+ ];
658
+ }
659
+ function parseIntegrations(value) {
660
+ if (!value || typeof value !== "object") return {};
661
+ const autoleap = value.autoleap;
662
+ if (!autoleap || typeof autoleap !== "object") return {};
663
+ const booking = autoleap.booking;
664
+ if (!booking || typeof booking !== "object") return {};
665
+ const b = booking;
666
+ if (typeof b.fallback_url !== "string" || !b.fallback_url) return {};
667
+ return {
668
+ autoleap: {
669
+ booking: {
670
+ enabled: b.enabled === true,
671
+ fallback_url: b.fallback_url,
672
+ timezone: typeof b.timezone === "string" ? b.timezone : "UTC",
673
+ config_version: typeof b.config_version === "number" ? b.config_version : null,
674
+ accepts_bookings: b.accepts_bookings === true,
675
+ rules: parseBookingRules(b.rules),
676
+ options: Array.isArray(b.options) ? b.options.flatMap(parseBookingOption) : [],
677
+ ...isBookingMode(b.mode) ? { mode: b.mode } : {}
678
+ }
679
+ }
680
+ };
681
+ }
682
+ function parseTrigger(value) {
683
+ if (!value || typeof value !== "object") return null;
684
+ const t = value;
685
+ if (typeof t.event_type !== "string") return null;
686
+ const spec = { event_type: t.event_type };
687
+ if (typeof t.threshold_percent === "number") spec.threshold_percent = t.threshold_percent;
688
+ if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
689
+ if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
690
+ if (typeof t.page_name === "string") spec.page_name = t.page_name;
691
+ return spec;
692
+ }
693
+ function parseConversionConfig(raw) {
694
+ if (!raw || typeof raw !== "object") return null;
695
+ const obj = raw;
696
+ const servicesRaw = Array.isArray(obj.services) ? obj.services : [];
697
+ const services = servicesRaw.flatMap((entry) => {
698
+ if (!entry || typeof entry !== "object") return [];
699
+ const s = entry;
700
+ if (typeof s.key !== "string") return [];
701
+ return [
702
+ {
703
+ key: s.key,
704
+ label: typeof s.label === "string" ? s.label : void 0,
705
+ firing: parseFiring(s.firing)
706
+ }
707
+ ];
708
+ });
709
+ const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;
710
+ const goals = goalsRaw ? goalsRaw.flatMap((entry) => {
711
+ if (!entry || typeof entry !== "object") return [];
712
+ const g = entry;
713
+ if (typeof g.key !== "string") return [];
714
+ return [
715
+ {
716
+ key: g.key,
717
+ label: typeof g.label === "string" ? g.label : void 0,
718
+ kind: g.kind === "event" ? "event" : "sale",
719
+ trigger: parseTrigger(g.trigger),
720
+ firing: parseFiring(g.firing)
721
+ }
722
+ ];
723
+ }) : services.map((s) => ({
724
+ key: s.key,
725
+ label: s.label,
726
+ kind: "sale",
727
+ trigger: null,
728
+ firing: s.firing
729
+ }));
730
+ return {
731
+ schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 1,
732
+ config_version: typeof obj.config_version === "number" ? obj.config_version : 0,
733
+ business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
734
+ customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
735
+ environment: typeof obj.environment === "string" ? obj.environment : void 0,
736
+ google_tracking_state: obj.google_tracking_state === "active" ? "active" : "disabled",
737
+ gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
738
+ meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
739
+ services,
740
+ goals,
741
+ linked_hostnames: Array.isArray(obj.linked_hostnames) ? obj.linked_hostnames.filter((h) => typeof h === "string" && h.length > 0) : [],
742
+ integrations: parseIntegrations(obj.integrations),
743
+ lead_forms: parseLeadForms(obj.lead_forms)
744
+ };
745
+ }
746
+ function parseLeadForms(value) {
747
+ if (!isRecord(value) || typeof value.registry_version !== "number") return null;
748
+ return {
749
+ registry_version: value.registry_version,
750
+ contracts: records(value.contracts).flatMap(
751
+ (c) => typeof c.form_kind === "string" && typeof c.contract_hash === "string" ? [
752
+ {
753
+ form_kind: c.form_kind,
754
+ requires: records(c.requires).flatMap(
755
+ (r) => typeof r.role === "string" ? [
756
+ {
757
+ role: r.role,
758
+ required_when: parseCondition(r.required_when),
759
+ waived: r.waived === true
760
+ }
761
+ ] : []
762
+ ),
763
+ requires_any: records(c.requires_any).map((g) => ({
764
+ roles: strings(g.roles),
765
+ waived: g.waived === true
766
+ })),
767
+ options: records(c.options).flatMap(
768
+ (o) => typeof o.role === "string" ? [{ role: o.role, options: records(o.options).flatMap(parseContractOption) }] : []
769
+ ),
770
+ contract_hash: c.contract_hash
771
+ }
772
+ ] : []
773
+ )
774
+ };
775
+ }
776
+ function parseCondition(value) {
777
+ if (!isRecord(value) || typeof value.role !== "string") return null;
778
+ return { role: value.role, any_of: strings(value.any_of) };
779
+ }
780
+ function parseContractOption(value) {
781
+ if (typeof value.key !== "string" || typeof value.label !== "string") return [];
782
+ return [
783
+ {
784
+ key: value.key,
785
+ label: value.label,
786
+ is_non_answer: value.is_non_answer === true,
787
+ deprecated: value.deprecated === true
788
+ }
789
+ ];
790
+ }
791
+ function isRecord(value) {
792
+ return !!value && typeof value === "object" && !Array.isArray(value);
793
+ }
794
+ function records(value) {
795
+ return Array.isArray(value) ? value.filter(isRecord) : [];
796
+ }
797
+ function strings(value) {
798
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
799
+ }
800
+
801
+ // ../tracking-core/src/resources/automatic-transaction.ts
802
+ var STORAGE_KEY = "_aranova_auto_txn_map";
803
+ var transactionIds = /* @__PURE__ */ new Map();
804
+ var legacyCounter = 0;
805
+ function scopeKey(sessionId, goalKey, path) {
806
+ return JSON.stringify([sessionId, goalKey, path]);
807
+ }
808
+ function randomId() {
809
+ const cryptoApi = globalThis.crypto;
810
+ if (typeof cryptoApi?.randomUUID === "function") return cryptoApi.randomUUID();
811
+ if (typeof cryptoApi?.getRandomValues === "function") {
812
+ const bytes = cryptoApi.getRandomValues(new Uint8Array(16));
813
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
814
+ }
815
+ legacyCounter = (legacyCounter + 1) % 4294967296;
816
+ const timestamp = Date.now().toString(16).padStart(12, "0");
817
+ const counter = legacyCounter.toString(16).padStart(8, "0");
818
+ const random = Math.floor(Math.random() * 281474976710655).toString(16).padStart(12, "0");
819
+ return `${timestamp}${counter}${random}`.slice(0, 32);
820
+ }
821
+ function isValidTransactionId(value) {
822
+ return typeof value === "string" && /^auto:(?:[0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/i.test(
823
+ value
824
+ ) && value.length <= 64;
825
+ }
826
+ function readStoredMap(sessionId) {
827
+ if (typeof window === "undefined") return { sessionId, entries: {} };
828
+ try {
829
+ const raw = window.localStorage.getItem(STORAGE_KEY);
830
+ if (!raw) return { sessionId, entries: {} };
831
+ const parsed = JSON.parse(raw);
832
+ if (parsed.sessionId !== sessionId || !parsed.entries || typeof parsed.entries !== "object" || Array.isArray(parsed.entries)) {
833
+ return { sessionId, entries: {} };
834
+ }
835
+ return { sessionId, entries: parsed.entries };
836
+ } catch {
837
+ return { sessionId, entries: {} };
838
+ }
839
+ }
840
+ function writeStoredMap(stored) {
841
+ if (typeof window === "undefined") return;
842
+ try {
843
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
844
+ } catch {
845
+ }
846
+ }
847
+ function getAutomaticTransactionId(sessionId, goalKey, path) {
848
+ const key = scopeKey(sessionId, goalKey, path);
849
+ const existing = transactionIds.get(key);
850
+ if (existing) return existing;
851
+ const stored = readStoredMap(sessionId);
852
+ const storedId = stored.entries[key];
853
+ if (isValidTransactionId(storedId)) {
854
+ transactionIds.set(key, storedId);
855
+ return storedId;
856
+ }
857
+ const transactionId = `auto:${randomId()}`;
858
+ transactionIds.set(key, transactionId);
859
+ const latest = readStoredMap(sessionId);
860
+ const concurrentId = latest.entries[key];
861
+ if (isValidTransactionId(concurrentId)) {
862
+ transactionIds.set(key, concurrentId);
863
+ return concurrentId;
864
+ }
865
+ latest.entries[key] = transactionId;
866
+ writeStoredMap(latest);
867
+ return transactionId;
868
+ }
869
+
870
+ // ../tracking-core/src/resources/automatic-trigger.ts
871
+ function hasNumberField(metadata, key) {
872
+ return typeof metadata[key] === "number";
873
+ }
874
+ function hasStringField(metadata, key) {
875
+ return typeof metadata[key] === "string";
876
+ }
877
+ function automaticThresholdMet(goal, eventType, metadata) {
878
+ const trigger = goal.trigger;
879
+ if (!trigger || trigger.event_type !== eventType) return false;
880
+ switch (eventType) {
881
+ case "scroll_depth":
882
+ return hasNumberField(metadata, "depth_percent") && trigger.threshold_percent != null && metadata.depth_percent >= trigger.threshold_percent;
883
+ case "time_on_site":
884
+ return hasNumberField(metadata, "duration_ms") && trigger.threshold_seconds != null && metadata.duration_ms >= trigger.threshold_seconds * 1e3;
885
+ case "multi_page_session":
886
+ return hasNumberField(metadata, "page_count") && trigger.page_threshold != null && metadata.page_count >= trigger.page_threshold;
887
+ case "specific_page_visit":
888
+ return hasStringField(metadata, "page_name") && metadata.page_name === trigger.page_name;
889
+ case "page_view":
890
+ case "form_start":
891
+ case "phone_click":
892
+ return true;
893
+ default:
894
+ return false;
895
+ }
896
+ }
348
897
 
349
898
  // ../tracking-core/src/resources/intl/date.ts
350
899
  function formatDateInTz(iso3, timeZone, opts, locale) {
@@ -393,7 +942,482 @@ function resolveTrackingConfigUrl(ref) {
393
942
  const base = (configured || DEFAULT_CDN_BASE_URL).replace(/\/+$/, "");
394
943
  return `${base}/${trackingConfigKey(ref)}`;
395
944
  }
945
+ var CACHE_PREFIX = "_aranova_cfg_runtime_";
946
+ var AUTHORITY_TTL_MS = 6e4;
947
+ var MAX_QUEUE_LENGTH = 50;
948
+ var RETRY_BASE_MS = 5e3;
396
949
  var RETRY_MAX_MS = 5 * 6e4;
950
+ var runtimes = /* @__PURE__ */ new Map();
951
+ var configuredIds = /* @__PURE__ */ new Set();
952
+ var scriptLoad = null;
953
+ var jsInitialized = false;
954
+ function cacheKey(url) {
955
+ return `${CACHE_PREFIX}${url}`;
956
+ }
957
+ function pushBounded(queue, item) {
958
+ queue.push(item);
959
+ if (queue.length > MAX_QUEUE_LENGTH) queue.splice(0, queue.length - MAX_QUEUE_LENGTH);
960
+ }
961
+ function readCache(url) {
962
+ if (typeof window === "undefined") return null;
963
+ try {
964
+ const raw = window.sessionStorage.getItem(cacheKey(url));
965
+ if (!raw) return null;
966
+ const parsed = JSON.parse(raw);
967
+ const config = parseConversionConfig(parsed.config);
968
+ if (!config) return null;
969
+ return {
970
+ etag: typeof parsed.etag === "string" ? parsed.etag : null,
971
+ config
972
+ };
973
+ } catch {
974
+ return null;
975
+ }
976
+ }
977
+ function writeCache(url, entry) {
978
+ if (typeof window === "undefined") return;
979
+ try {
980
+ window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));
981
+ } catch {
982
+ }
983
+ }
984
+ function isTombstone(config) {
985
+ return config.google_tracking_state === "disabled" || Object.keys(config.gtag_ids).length === 0;
986
+ }
987
+ function validateConfig(config, ref) {
988
+ return config.business_id === ref.businessId && config.environment === ref.environment && (config.google_tracking_state === "active" || config.google_tracking_state === "disabled");
989
+ }
990
+ function pageViewSnapshot() {
991
+ if (typeof window === "undefined" || typeof document === "undefined") return null;
992
+ return {
993
+ href: stripLinkParam(window.location.href),
994
+ title: document.title || null,
995
+ referrer: document.referrer || null
996
+ };
997
+ }
998
+ function ensureScript(gtagId) {
999
+ if (typeof window === "undefined" || typeof document === "undefined") return Promise.resolve();
1000
+ applyDefaultConsentState();
1001
+ loadGtagScript(gtagId);
1002
+ if (scriptLoad) return scriptLoad;
1003
+ scriptLoad = new Promise((resolve) => {
1004
+ const script = document.querySelector(
1005
+ 'script[data-aranova-tracking="aranova-gtag-loader"]'
1006
+ );
1007
+ if (!script) {
1008
+ resolve();
1009
+ return;
1010
+ }
1011
+ if (script.dataset.loaded === "true") {
1012
+ resolve();
1013
+ return;
1014
+ }
1015
+ window.setTimeout(resolve, 0);
1016
+ script.addEventListener(
1017
+ "load",
1018
+ () => {
1019
+ script.dataset.loaded = "true";
1020
+ resolve();
1021
+ },
1022
+ { once: true }
1023
+ );
1024
+ script.addEventListener("error", () => resolve(), { once: true });
1025
+ });
1026
+ return scriptLoad;
1027
+ }
1028
+ var TrackingConfigRuntime = class {
1029
+ constructor(ref, fetchImpl = globalThis.fetch) {
1030
+ this.ref = ref;
1031
+ this.current = null;
1032
+ this.etag = null;
1033
+ this.stateValue = "unconfirmed";
1034
+ this.confirmedAt = 0;
1035
+ this.authorityGeneration = 0;
1036
+ this.inFlight = null;
1037
+ this.flushInFlight = null;
1038
+ this.flushRequested = false;
1039
+ this.retryTimer = null;
1040
+ this.started = false;
1041
+ /** Consecutive failed authority attempts — drives the revalidate backoff. */
1042
+ this.authorityFailures = 0;
1043
+ /** Epoch ms before which `ensureAuthority` must not issue another request. */
1044
+ this.nextAuthorityAttemptAt = 0;
1045
+ this.conversionQueue = [];
1046
+ this.automaticQueue = [];
1047
+ this.pageQueue = [];
1048
+ this.listeners = /* @__PURE__ */ new Set();
1049
+ this.fetchImpl = fetchImpl.bind(globalThis);
1050
+ this.url = resolveTrackingConfigUrl(ref);
1051
+ const cached = readCache(this.url);
1052
+ this.current = cached?.config ?? null;
1053
+ this.etag = cached?.etag ?? null;
1054
+ this.start();
1055
+ }
1056
+ /** The URL this runtime actually fetches (composed or explicit). */
1057
+ configUrl() {
1058
+ return this.url;
1059
+ }
1060
+ /**
1061
+ * Explicitly start authority resolution and Google-tag bootstrap.
1062
+ *
1063
+ * Idempotent so framework effects can call it after hydration without
1064
+ * depending on constructor timing.
1065
+ */
1066
+ start() {
1067
+ if (this.started) {
1068
+ void this.flush();
1069
+ return;
1070
+ }
1071
+ this.started = true;
1072
+ void this.revalidate();
1073
+ if (typeof window !== "undefined") {
1074
+ window.addEventListener("visibilitychange", () => {
1075
+ if (document.visibilityState === "visible") void this.revalidate();
1076
+ });
1077
+ window.addEventListener("focus", () => void this.revalidate());
1078
+ }
1079
+ }
1080
+ state() {
1081
+ return this.stateValue;
1082
+ }
1083
+ config() {
1084
+ return this.stateValue === "active" || this.stateValue === "tombstone" ? this.current : null;
1085
+ }
1086
+ __unsafeExpireAuthorityForTests() {
1087
+ this.confirmedAt = Number.NEGATIVE_INFINITY;
1088
+ this.nextAuthorityAttemptAt = 0;
1089
+ }
1090
+ /** Queue depths — asserted by tests to pin the bound. */
1091
+ __queueDepthsForTests() {
1092
+ return {
1093
+ conversions: this.conversionQueue.length,
1094
+ automatic: this.automaticQueue.length,
1095
+ pages: this.pageQueue.length
1096
+ };
1097
+ }
1098
+ subscribe(listener) {
1099
+ this.listeners.add(listener);
1100
+ return () => this.listeners.delete(listener);
1101
+ }
1102
+ async ensureAuthority() {
1103
+ if (this.stateValue !== "unconfirmed" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS) {
1104
+ return true;
1105
+ }
1106
+ if (Date.now() < this.nextAuthorityAttemptAt) return false;
1107
+ await this.revalidateAuthority();
1108
+ return this.stateValue !== "unconfirmed" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS;
1109
+ }
1110
+ async revalidate() {
1111
+ this.nextAuthorityAttemptAt = 0;
1112
+ await this.revalidateAuthority();
1113
+ await this.flush();
1114
+ }
1115
+ async revalidateAuthority() {
1116
+ if (typeof window === "undefined" || !this.fetchImpl) return;
1117
+ if (this.inFlight) return this.inFlight;
1118
+ this.inFlight = this.revalidateNow().finally(() => {
1119
+ this.inFlight = null;
1120
+ });
1121
+ return this.inFlight;
1122
+ }
1123
+ queuePageView(snapshot = pageViewSnapshot()) {
1124
+ if (!snapshot) return;
1125
+ pushBounded(this.pageQueue, snapshot);
1126
+ void this.flush();
1127
+ }
1128
+ fireConversion(key, options) {
1129
+ pushBounded(this.conversionQueue, { key, ...options });
1130
+ void this.flush();
1131
+ }
1132
+ queueAutomaticEvent(eventType, metadata, transactionPath, transactionScope) {
1133
+ pushBounded(this.automaticQueue, {
1134
+ eventType,
1135
+ metadata,
1136
+ transactionPath,
1137
+ transactionScope
1138
+ });
1139
+ void this.flush();
1140
+ }
1141
+ listGoals() {
1142
+ return this.current?.goals ?? [];
1143
+ }
1144
+ /** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */
1145
+ linkedHostnames() {
1146
+ return this.current?.linked_hostnames ?? [];
1147
+ }
1148
+ /**
1149
+ * Integration blocks, from the cached object even while unconfirmed: the booking fallback
1150
+ * link is safe to show stale. Same reference until the next adopt, for `useSyncExternalStore`.
1151
+ */
1152
+ integrations() {
1153
+ return this.current?.integrations ?? null;
1154
+ }
1155
+ /** The lead-form contracts, from the cached object like `integrations()`. */
1156
+ leadForms() {
1157
+ return this.current?.lead_forms ?? null;
1158
+ }
1159
+ async revalidateNow() {
1160
+ try {
1161
+ const headers = {};
1162
+ if (this.etag) headers["If-None-Match"] = this.etag;
1163
+ const response = await this.fetchImpl(this.url, {
1164
+ method: "GET",
1165
+ headers,
1166
+ cache: "no-cache"
1167
+ });
1168
+ if (response.status === 304 && this.current && validateConfig(this.current, this.ref)) {
1169
+ this.confirm(this.current, this.etag);
1170
+ return;
1171
+ }
1172
+ if (!response.ok) {
1173
+ this.expireAuthority();
1174
+ return;
1175
+ }
1176
+ const next = parseConversionConfig(await response.json());
1177
+ if (!next || !validateConfig(next, this.ref)) {
1178
+ this.expireAuthority();
1179
+ return;
1180
+ }
1181
+ if (this.current && next.config_version < this.current.config_version) {
1182
+ this.expireAuthority();
1183
+ return;
1184
+ }
1185
+ this.confirm(next, response.headers.get("ETag"));
1186
+ } catch {
1187
+ this.expireAuthority();
1188
+ }
1189
+ }
1190
+ expireAuthority() {
1191
+ this.authorityGeneration += 1;
1192
+ if (this.stateValue !== "unconfirmed") this.stateValue = "unconfirmed";
1193
+ this.authorityFailures += 1;
1194
+ const delay = Math.min(RETRY_MAX_MS, RETRY_BASE_MS * 2 ** (this.authorityFailures - 1));
1195
+ this.nextAuthorityAttemptAt = Date.now() + delay;
1196
+ }
1197
+ confirm(config, etag) {
1198
+ this.authorityGeneration += 1;
1199
+ this.current = config;
1200
+ this.etag = etag;
1201
+ this.confirmedAt = Date.now();
1202
+ this.authorityFailures = 0;
1203
+ this.nextAuthorityAttemptAt = 0;
1204
+ this.stateValue = isTombstone(config) ? "tombstone" : "active";
1205
+ writeCache(this.url, { etag, config });
1206
+ if (this.stateValue === "tombstone") {
1207
+ if (this.retryTimer !== null) {
1208
+ window.clearTimeout(this.retryTimer);
1209
+ this.retryTimer = null;
1210
+ }
1211
+ this.conversionQueue.length = 0;
1212
+ this.automaticQueue.length = 0;
1213
+ this.pageQueue.length = 0;
1214
+ }
1215
+ for (const listener of this.listeners) listener();
1216
+ }
1217
+ async flush() {
1218
+ if (this.flushInFlight) {
1219
+ this.flushRequested = true;
1220
+ return this.flushInFlight;
1221
+ }
1222
+ this.flushRequested = false;
1223
+ this.flushInFlight = this.flushNow().finally(() => {
1224
+ this.flushInFlight = null;
1225
+ if (this.flushRequested && this.stateValue === "active") void this.flush();
1226
+ });
1227
+ return this.flushInFlight;
1228
+ }
1229
+ scheduleRetry() {
1230
+ if (this.retryTimer !== null || typeof window === "undefined") return;
1231
+ this.retryTimer = window.setTimeout(() => {
1232
+ this.retryTimer = null;
1233
+ void this.flush();
1234
+ }, 1e3);
1235
+ }
1236
+ async flushNow() {
1237
+ if (!await this.ensureAuthority()) return;
1238
+ if (this.stateValue !== "active" || !this.current) return;
1239
+ const config = this.current;
1240
+ const generation = this.authorityGeneration;
1241
+ const ids = Object.values(config.gtag_ids).filter(
1242
+ (id) => typeof id === "string" && isValidGtagId(id)
1243
+ );
1244
+ if (ids.length === 0) return;
1245
+ await ensureScript(ids[0]);
1246
+ if (this.authorityGeneration !== generation || this.stateValue !== "active" || this.current !== config) {
1247
+ return;
1248
+ }
1249
+ const gtag = window.gtag;
1250
+ if (typeof gtag !== "function") return;
1251
+ try {
1252
+ if (!jsInitialized) {
1253
+ gtag("js", /* @__PURE__ */ new Date());
1254
+ jsInitialized = true;
1255
+ }
1256
+ for (const id of ids) {
1257
+ if (configuredIds.has(id)) continue;
1258
+ gtag("config", id, { send_page_view: false });
1259
+ configuredIds.add(id);
1260
+ }
1261
+ } catch {
1262
+ this.scheduleRetry();
1263
+ return;
1264
+ }
1265
+ while (this.pageQueue.length > 0) {
1266
+ const page = this.pageQueue[0];
1267
+ try {
1268
+ gtag("event", "page_view", {
1269
+ page_location: page.href,
1270
+ page_title: page.title ?? void 0,
1271
+ page_referrer: page.referrer ?? void 0
1272
+ });
1273
+ this.pageQueue.shift();
1274
+ } catch {
1275
+ this.scheduleRetry();
1276
+ return;
1277
+ }
1278
+ }
1279
+ while (this.automaticQueue.length > 0) {
1280
+ const event = this.automaticQueue[0];
1281
+ for (const goal of config.goals) {
1282
+ if (goal.kind !== "event" || !goal.firing) continue;
1283
+ if (!automaticThresholdMet(goal, event.eventType, event.metadata)) continue;
1284
+ pushBounded(this.conversionQueue, {
1285
+ key: goal.key,
1286
+ transactionId: getAutomaticTransactionId(
1287
+ event.transactionScope,
1288
+ goal.key,
1289
+ event.transactionPath
1290
+ )
1291
+ });
1292
+ }
1293
+ this.automaticQueue.shift();
1294
+ }
1295
+ while (this.conversionQueue.length > 0) {
1296
+ const item = this.conversionQueue[0];
1297
+ const goal = config.goals.find((g) => g.key === item.key);
1298
+ const firing = goal?.firing;
1299
+ if (!firing) {
1300
+ this.conversionQueue.shift();
1301
+ continue;
1302
+ }
1303
+ const currency = item.currency ?? firing.currency ?? null;
1304
+ const value = item.value ?? (firing.value_cents != null && currency ? fromMinor(firing.value_cents, currency) : null);
1305
+ const outcome = fireConversionWithConsent({
1306
+ sendTo: firing.send_to,
1307
+ value,
1308
+ currency,
1309
+ transactionId: item.transactionId ?? null
1310
+ });
1311
+ if (outcome === "retryable") {
1312
+ this.scheduleRetry();
1313
+ return;
1314
+ }
1315
+ this.conversionQueue.shift();
1316
+ }
1317
+ }
1318
+ };
1319
+ function getTrackingConfigRuntime(ref, fetchImpl) {
1320
+ const key = `${resolveTrackingConfigUrl(ref)}|${ref.businessId}|${ref.environment}`;
1321
+ const existing = runtimes.get(key);
1322
+ if (existing) return existing;
1323
+ const runtime = new TrackingConfigRuntime(ref, fetchImpl ?? globalThis.fetch);
1324
+ runtimes.set(key, runtime);
1325
+ return runtime;
1326
+ }
1327
+
1328
+ // ../tracking-core/src/lead-forms/submit.ts
1329
+ var KNOWN_ROLES = new Set(LEAD_FORM_ROLES);
1330
+ var ROLE_ORDER = new Map(LEAD_FORM_ROLES.map((role, index) => [role, index]));
1331
+ var ADD_ON = /^add_on:[a-z0-9_-]{1,48}$/;
1332
+ var CONTRACT_NAME = /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/;
1333
+ function judgeLeadForm(kind, formId, fields, contracts, settings) {
1334
+ const generated = contracts?.contracts[kind];
1335
+ const live = liveContract(settings.trackingConfig, kind);
1336
+ const violations = check(live === void 0 ? generated : fromLive(live), fields);
1337
+ const liveHash = live === void 0 ? void 0 : live?.contract_hash ?? null;
1338
+ if (contracts && (liveHash !== void 0 && (generated?.hash ?? null) !== liveHash || contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION)) {
1339
+ violations.push({ role: null, rule: "contract_behind" });
1340
+ }
1341
+ if (violations.length > 0 && (settings.debug || settings.environment === "development")) {
1342
+ const message = `[AranovaTracking] lead form "${formId}" (${kind}) breaks its contract: ${violations.map((v) => `${v.rule}${v.role ? ` (${v.role})` : ""}`).join(", ")}`;
1343
+ console.error(message);
1344
+ if (settings.debug) throw new Error(message);
1345
+ }
1346
+ return {
1347
+ violations,
1348
+ hash: liveHash === void 0 ? generated?.hash ?? null : liveHash
1349
+ };
1350
+ }
1351
+ function liveContract(ref, kind) {
1352
+ const leadForms = ref ? getTrackingConfigRuntime(ref).leadForms() : null;
1353
+ if (!leadForms) return void 0;
1354
+ return leadForms.contracts.find((c) => c.form_kind === kind) ?? null;
1355
+ }
1356
+ function fromLive(live) {
1357
+ if (!live) return void 0;
1358
+ return {
1359
+ requires: live.requires.map((r) => ({
1360
+ role: r.role,
1361
+ requiredWhen: r.required_when ? { role: r.required_when.role, anyOf: r.required_when.any_of } : null
1362
+ })),
1363
+ requiresAny: live.requires_any.map((g) => g.roles),
1364
+ // Deprecated keys still resolve: a cached form may send them (ADR-0024 §7).
1365
+ options: Object.fromEntries(live.options.map((o) => [o.role, o.options.map((x) => x.key)]))
1366
+ };
1367
+ }
1368
+ function check(contract, fields) {
1369
+ const answered = new Map(fields);
1370
+ const violations = [];
1371
+ for (const [role, value] of fields) {
1372
+ const primitive = primitiveOf(role);
1373
+ if (!primitive) {
1374
+ violations.push({ role: CONTRACT_NAME.test(role) ? role : null, rule: "unknown_role" });
1375
+ continue;
1376
+ }
1377
+ if (!(0, import_mini2.safeParse)(leadFormPrimitiveSchemas[primitive], value).success) {
1378
+ violations.push({ role, rule: "invalid_value" });
1379
+ continue;
1380
+ }
1381
+ const allowed = contract?.options[role] ?? registryOptions(role);
1382
+ if (allowed?.length && optionKeys(value).some((key) => !allowed.includes(key))) {
1383
+ violations.push({ role, rule: "unknown_option" });
1384
+ }
1385
+ }
1386
+ for (const { role, requiredWhen } of contract?.requires ?? []) {
1387
+ const owing = requiredWhen === null || optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));
1388
+ if (owing && !answered.has(role)) violations.push({ role, rule: "missing_required" });
1389
+ }
1390
+ for (const group of contract?.requiresAny ?? []) {
1391
+ if (!group.some((role) => answered.has(role))) {
1392
+ violations.push({ role: group.join("|"), rule: "missing_any_of" });
1393
+ }
1394
+ }
1395
+ return violations;
1396
+ }
1397
+ function primitiveOf(role) {
1398
+ if (ADD_ON.test(role)) return "bool";
1399
+ return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].primitive : null;
1400
+ }
1401
+ function registryOptions(role) {
1402
+ const spec = KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role] : void 0;
1403
+ return spec && "options" in spec ? spec.options.map((o) => o.key) : void 0;
1404
+ }
1405
+ function optionKeys(value) {
1406
+ if (typeof value === "string") return [value];
1407
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
1408
+ }
1409
+
1410
+ // ../tracking-core/src/resources/http/errors.ts
1411
+ var AranovaApiError = class extends Error {
1412
+ constructor(message, options) {
1413
+ super(message);
1414
+ this.name = "AranovaApiError";
1415
+ this.status = options.status;
1416
+ this.code = options.code;
1417
+ this.requestId = options.requestId;
1418
+ this.context = options.context;
1419
+ }
1420
+ };
397
1421
 
398
1422
  // ../tracking-core/src/ingest.ts
399
1423
  var API_KEY_HEADER = "X-Aranova-Api-Key";
@@ -429,11 +1453,11 @@ async function apiRequest(config, method, path, body, extraHeaders) {
429
1453
  try {
430
1454
  const parsed = await response.json();
431
1455
  if (parsed && typeof parsed === "object") {
432
- const record3 = parsed;
433
- if (typeof record3.detail === "string") detail = record3.detail;
434
- if (typeof record3.code === "string") code = record3.code;
435
- if (record3.context && typeof record3.context === "object") {
436
- context = record3.context;
1456
+ const record4 = parsed;
1457
+ if (typeof record4.detail === "string") detail = record4.detail;
1458
+ if (typeof record4.code === "string") code = record4.code;
1459
+ if (record4.context && typeof record4.context === "object") {
1460
+ context = record4.context;
437
1461
  }
438
1462
  }
439
1463
  } catch {
@@ -487,7 +1511,7 @@ var DROP_OFF_KEYS = {
487
1511
  waitForIt: "wait-for-it",
488
1512
  dropOff: "drop-off"
489
1513
  };
490
- function formSubmitPayload(input, formSubmit, contractHash) {
1514
+ function formSubmitPayload(input, formSubmit, runtime) {
491
1515
  const field = (role, name, type, label, value) => ({ name, type, label, value, role });
492
1516
  const text = (value) => value?.trim() || void 0;
493
1517
  const { vehicle, dropOff } = input;
@@ -497,7 +1521,7 @@ function formSubmitPayload(input, formSubmit, contractHash) {
497
1521
  // E.164 on the wire: it keys the customer and enhanced-conversion matching.
498
1522
  field("phone", "phone", "tel", "Phone", toE164(input.phone) ?? input.phone),
499
1523
  field("email", "email", "email", "Email", text(input.email)),
500
- field("service", "service", "select", "Service", input.services),
1524
+ field("service", "service", "select", "Service", [...input.services]),
501
1525
  field(
502
1526
  "vehicle",
503
1527
  "vehicle",
@@ -522,6 +1546,14 @@ function formSubmitPayload(input, formSubmit, contractHash) {
522
1546
  const reported = new Set(entries.map((item) => item.name));
523
1547
  const fields = entries.filter((item) => item.value !== void 0);
524
1548
  const extra = (formSubmit.extraFields ?? []).filter((item) => !reported.has(item.name));
1549
+ const settings = typedClientSettings(formSubmit.tracking);
1550
+ const { violations, hash } = judgeLeadForm(
1551
+ "booking",
1552
+ formSubmit.formId,
1553
+ fields.map((item) => [item.role, item.value]),
1554
+ formSubmit.contracts,
1555
+ { ...settings, trackingConfig: settings.trackingConfig ?? runtime?.ref }
1556
+ );
525
1557
  return {
526
1558
  form: {
527
1559
  id: formSubmit.formId,
@@ -529,7 +1561,8 @@ function formSubmitPayload(input, formSubmit, contractHash) {
529
1561
  fields: [...fields, ...extra],
530
1562
  kind: "booking",
531
1563
  contract_version: LEAD_FORM_REGISTRY_VERSION,
532
- ...contractHash ? { contract_hash: contractHash } : {}
1564
+ ...hash ? { contract_hash: hash } : {},
1565
+ ...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {}
533
1566
  },
534
1567
  page: { path: typeof window === "undefined" ? "/" : window.location.pathname }
535
1568
  };
@@ -628,10 +1661,9 @@ function createSalesBookingsClient(config, firing) {
628
1661
  }
629
1662
  if (formSubmit && !submittedForms.has(attemptKey)) {
630
1663
  submittedForms.add(attemptKey);
631
- const contract = runtime?.leadForms()?.contracts.find((c) => c.form_kind === "booking");
632
1664
  formSubmit.tracking.trackEvent(
633
1665
  "form_submit",
634
- formSubmitPayload(input, formSubmit, contract?.contract_hash ?? null)
1666
+ formSubmitPayload(input, formSubmit, runtime)
635
1667
  );
636
1668
  }
637
1669
  const body = {
@@ -647,7 +1679,7 @@ function createSalesBookingsClient(config, firing) {
647
1679
  email: input.email?.trim() || null,
648
1680
  vehicle: input.vehicle ?? null,
649
1681
  drop_off: input.dropOff ?? null,
650
- services: input.services,
1682
+ services: [...input.services],
651
1683
  notes: input.notes?.trim() || null,
652
1684
  marketing_sms_consent: input.marketingSmsConsent === true,
653
1685
  page_path: typeof window === "undefined" ? null : window.location.pathname
@@ -734,7 +1766,7 @@ function fireRecordedConversions(firing, input, recorded, sale, currency) {
734
1766
  }
735
1767
  function createSalesClient(config) {
736
1768
  if (config.firing) registerCapability("conversion_goals_manual");
737
- async function record3(input) {
1769
+ async function record4(input) {
738
1770
  const currency = input.currency ?? config.defaultCurrency;
739
1771
  if (!currency) {
740
1772
  throw new Error(
@@ -760,9 +1792,9 @@ function createSalesClient(config) {
760
1792
  return sale;
761
1793
  }
762
1794
  return {
763
- record: record3,
1795
+ record: record4,
764
1796
  // recordSale is the intent-revealing alias — same behavior, clearer call site.
765
- recordSale: record3,
1797
+ recordSale: record4,
766
1798
  trackConversion(key, options) {
767
1799
  if (config.firing && "fireConversion" in config.firing) {
768
1800
  if (options?.userData && getConsentState() !== "denied") stashUserData(options.userData);
@@ -882,17 +1914,17 @@ function createSalesClient(config) {
882
1914
  }
883
1915
 
884
1916
  // ../tracking-core/src/resources/sales/schema.ts
885
- var z3 = __toESM(require("zod/mini"));
1917
+ var z5 = __toESM(require("zod/mini"));
886
1918
 
887
1919
  // ../tracking-core/src/resources/sales/generated/zod.gen.ts
888
- var z2 = __toESM(require("zod/mini"));
889
- var zAutoLeapSaleFacts = z2.object({
890
- first_order: z2.boolean(),
891
- fleet_customer: z2.boolean(),
892
- invoice_code: z2.string(),
893
- invoice_id: z2.string()
1920
+ var z4 = __toESM(require("zod/mini"));
1921
+ var zAutoLeapSaleFacts = z4.object({
1922
+ first_order: z4.boolean(),
1923
+ fleet_customer: z4.boolean(),
1924
+ invoice_code: z4.string(),
1925
+ invoice_id: z4.string()
894
1926
  });
895
- var zErrorCode = z2.enum([
1927
+ var zErrorCode = z4.enum([
896
1928
  "missing_credentials",
897
1929
  "invalid_api_key",
898
1930
  "not_authenticated",
@@ -936,37 +1968,37 @@ var zErrorCode = z2.enum([
936
1968
  "rate_limited",
937
1969
  "internal_error"
938
1970
  ]);
939
- var zSaleItemEntitySchema = z2.object({
940
- category: z2.nullable(z2.string()),
941
- external_item_id: z2.nullable(z2.string()),
942
- id: z2.uuid(),
943
- name: z2.nullable(z2.string()),
944
- quantity: z2.string().check(z2.regex(/^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$/)),
945
- unit_cost_cents: z2.nullable(z2.int()),
946
- unit_price_cents: z2.int()
1971
+ var zSaleItemEntitySchema = z4.object({
1972
+ category: z4.nullable(z4.string()),
1973
+ external_item_id: z4.nullable(z4.string()),
1974
+ id: z4.uuid(),
1975
+ name: z4.nullable(z4.string()),
1976
+ quantity: z4.string().check(z4.regex(/^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$/)),
1977
+ unit_cost_cents: z4.nullable(z4.int()),
1978
+ unit_price_cents: z4.int()
947
1979
  });
948
- var zSaleItemInputSchema = z2.strictObject({
949
- category: z2.nullish(z2.string()),
950
- external_item_id: z2.nullish(z2.string()),
951
- name: z2.nullish(z2.string()),
952
- quantity: z2.string().check(z2.regex(/^\d+(\.\d{1,3})?$/)),
953
- unit_cost_cents: z2.nullish(z2.int().check(z2.gte(0))),
954
- unit_price_cents: z2.int().check(z2.gte(0))
1980
+ var zSaleItemInputSchema = z4.strictObject({
1981
+ category: z4.nullish(z4.string()),
1982
+ external_item_id: z4.nullish(z4.string()),
1983
+ name: z4.nullish(z4.string()),
1984
+ quantity: z4.string().check(z4.regex(/^\d+(\.\d{1,3})?$/)),
1985
+ unit_cost_cents: z4.nullish(z4.int().check(z4.gte(0))),
1986
+ unit_price_cents: z4.int().check(z4.gte(0))
955
1987
  });
956
- var zSaleServiceEntitySchema = z2.object({
957
- amount_cents: z2.int(),
958
- id: z2.uuid(),
959
- mapped: z2.nullable(z2.boolean()),
960
- service_id: z2.uuid(),
961
- service_key: z2.string(),
962
- service_label: z2.string()
1988
+ var zSaleServiceEntitySchema = z4.object({
1989
+ amount_cents: z4.int(),
1990
+ id: z4.uuid(),
1991
+ mapped: z4.nullable(z4.boolean()),
1992
+ service_id: z4.uuid(),
1993
+ service_key: z4.string(),
1994
+ service_label: z4.string()
963
1995
  });
964
- var zSaleServiceInputSchema = z2.strictObject({
965
- amount_cents: z2.int().check(z2.gte(0)),
966
- service: z2.string()
1996
+ var zSaleServiceInputSchema = z4.strictObject({
1997
+ amount_cents: z4.int().check(z4.gte(0)),
1998
+ service: z4.string()
967
1999
  });
968
- var zSaleSource = z2.enum(["api", "admin", "autoleap"]);
969
- var zSmsAutomationState = z2.enum([
2000
+ var zSaleSource = z4.enum(["api", "admin", "autoleap"]);
2001
+ var zSmsAutomationState = z4.enum([
970
2002
  "needs_check",
971
2003
  "failed",
972
2004
  "undelivered",
@@ -978,7 +2010,7 @@ var zSmsAutomationState = z2.enum([
978
2010
  "eligible",
979
2011
  "not_applicable"
980
2012
  ]);
981
- var zSmsWithheldReason = z2.enum([
2013
+ var zSmsWithheldReason = z4.enum([
982
2014
  "automation_disabled",
983
2015
  "sms_disabled",
984
2016
  "no_phone",
@@ -1004,77 +2036,79 @@ var zSmsWithheldReason = z2.enum([
1004
2036
  "confirmed_by_integration",
1005
2037
  "test_or_bot_session",
1006
2038
  "invalid_phone",
1007
- "recently_texted"
2039
+ "recently_texted",
2040
+ "do_not_contact",
2041
+ "do_not_text_tag"
1008
2042
  ]);
1009
- var zSaleSmsStatusSchema = z2.object({
1010
- can_send_now: z2._default(z2.boolean(), false),
1011
- last_message_at: z2.nullable(z2.iso.datetime({ offset: true })),
1012
- reason_detail: z2.nullable(z2.string()),
2043
+ var zSaleSmsStatusSchema = z4.object({
2044
+ can_send_now: z4._default(z4.boolean(), false),
2045
+ last_message_at: z4.nullable(z4.iso.datetime({ offset: true })),
2046
+ reason_detail: z4.nullable(z4.string()),
1013
2047
  state: zSmsAutomationState,
1014
- suppressed_by_other_sale: z2._default(z2.boolean(), false),
1015
- withheld_reason: z2.nullable(zSmsWithheldReason)
2048
+ suppressed_by_other_sale: z4._default(z4.boolean(), false),
2049
+ withheld_reason: z4.nullable(zSmsWithheldReason)
1016
2050
  });
1017
- var zSupportedCurrency = z2.enum(["USD", "CAD"]);
1018
- var zTrackingEnvironment = z2.enum(["production", "development"]);
1019
- var zSaleCreateSchema = z2.strictObject({
1020
- amount_total_cents: z2.nullish(z2.int().check(z2.gte(0))),
2051
+ var zSupportedCurrency = z4.enum(["USD", "CAD"]);
2052
+ var zTrackingEnvironment = z4.enum(["production", "development"]);
2053
+ var zSaleCreateSchema = z4.strictObject({
2054
+ amount_total_cents: z4.nullish(z4.int().check(z4.gte(0))),
1021
2055
  currency: zSupportedCurrency,
1022
- customer_email: z2.nullish(z2.email().check(z2.maxLength(320))),
1023
- customer_name: z2.nullish(z2.string().check(z2.maxLength(200))),
1024
- customer_phone: z2.nullish(z2.string().check(z2.maxLength(64))),
1025
- description: z2.nullish(z2.string()),
1026
- environment: z2._default(z2.optional(zTrackingEnvironment), "production"),
1027
- external_id: z2.nullish(z2.string()),
1028
- items: z2.optional(z2.array(zSaleItemInputSchema)),
1029
- metadata: z2.nullish(z2.record(z2.string(), z2.unknown())),
1030
- occurred_at: z2.iso.datetime({ offset: true }),
1031
- service: z2.nullish(z2.string()),
1032
- services: z2.nullish(z2.array(zSaleServiceInputSchema)),
1033
- sms_consent: z2.nullish(z2.boolean())
2056
+ customer_email: z4.nullish(z4.email().check(z4.maxLength(320))),
2057
+ customer_name: z4.nullish(z4.string().check(z4.maxLength(200))),
2058
+ customer_phone: z4.nullish(z4.string().check(z4.maxLength(64))),
2059
+ description: z4.nullish(z4.string()),
2060
+ environment: z4._default(z4.optional(zTrackingEnvironment), "production"),
2061
+ external_id: z4.nullish(z4.string()),
2062
+ items: z4.optional(z4.array(zSaleItemInputSchema)),
2063
+ metadata: z4.nullish(z4.record(z4.string(), z4.unknown())),
2064
+ occurred_at: z4.iso.datetime({ offset: true }),
2065
+ service: z4.nullish(z4.string()),
2066
+ services: z4.nullish(z4.array(zSaleServiceInputSchema)),
2067
+ sms_consent: z4.nullish(z4.boolean())
1034
2068
  });
1035
- var zValidationError = z2.object({
1036
- ctx: z2.optional(z2.record(z2.string(), z2.unknown())),
1037
- input: z2.optional(z2.unknown()),
1038
- loc: z2.array(z2.union([z2.string(), z2.int()])),
1039
- msg: z2.string(),
1040
- type: z2.string()
2069
+ var zValidationError = z4.object({
2070
+ ctx: z4.optional(z4.record(z4.string(), z4.unknown())),
2071
+ input: z4.optional(z4.unknown()),
2072
+ loc: z4.array(z4.union([z4.string(), z4.int()])),
2073
+ msg: z4.string(),
2074
+ type: z4.string()
1041
2075
  });
1042
- var zHttpValidationError = z2.object({
1043
- detail: z2.optional(z2.array(zValidationError))
2076
+ var zHttpValidationError = z4.object({
2077
+ detail: z4.optional(z4.array(zValidationError))
1044
2078
  });
1045
- var zValidationErrorItem = z2.object({
1046
- loc: z2.array(z2.union([z2.string(), z2.int()])),
1047
- msg: z2.string(),
1048
- type: z2.string()
2079
+ var zValidationErrorItem = z4.object({
2080
+ loc: z4.array(z4.union([z4.string(), z4.int()])),
2081
+ msg: z4.string(),
2082
+ type: z4.string()
1049
2083
  });
1050
- var zApiErrorSchema = z2.object({
2084
+ var zApiErrorSchema = z4.object({
1051
2085
  code: zErrorCode,
1052
- context: z2._default(z2.nullish(z2.record(z2.string(), z2.unknown())), null),
1053
- detail: z2.string(),
1054
- errors: z2._default(z2.nullish(z2.array(zValidationErrorItem)), null),
1055
- request_id: z2._default(z2.nullish(z2.string()), null)
2086
+ context: z4._default(z4.nullish(z4.record(z4.string(), z4.unknown())), null),
2087
+ detail: z4.string(),
2088
+ errors: z4._default(z4.nullish(z4.array(zValidationErrorItem)), null),
2089
+ request_id: z4._default(z4.nullish(z4.string()), null)
1056
2090
  });
1057
- var zWritebackEmailSource = z2.enum(["NONE", "SALE", "CUSTOMER", "HARVESTED"]);
1058
- var zWritebackEnrichmentStatus = z2.enum(["NONE", "MATCHED", "AMBIGUOUS"]);
1059
- var zWritebackSaleUploadCounts = z2.object({
1060
- failed: z2._default(z2.int(), 0),
1061
- pending: z2._default(z2.int(), 0),
1062
- skipped: z2._default(z2.int(), 0),
1063
- submitted: z2._default(z2.int(), 0),
1064
- total: z2._default(z2.int(), 0),
1065
- uploaded: z2._default(z2.int(), 0)
2091
+ var zWritebackEmailSource = z4.enum(["NONE", "SALE", "CUSTOMER", "HARVESTED"]);
2092
+ var zWritebackEnrichmentStatus = z4.enum(["NONE", "MATCHED", "AMBIGUOUS"]);
2093
+ var zWritebackSaleUploadCounts = z4.object({
2094
+ failed: z4._default(z4.int(), 0),
2095
+ pending: z4._default(z4.int(), 0),
2096
+ skipped: z4._default(z4.int(), 0),
2097
+ submitted: z4._default(z4.int(), 0),
2098
+ total: z4._default(z4.int(), 0),
2099
+ uploaded: z4._default(z4.int(), 0)
1066
2100
  });
1067
- var zSaleWritebackStatusSchema = z2.object({
1068
- blocked_reason: z2.nullable(z2.string()),
1069
- can_dispatch: z2._default(z2.boolean(), false),
1070
- email_source: z2.nullable(zWritebackEmailSource),
1071
- enrichment_status: z2.nullable(zWritebackEnrichmentStatus),
1072
- gclid_attached: z2._default(z2.boolean(), false),
1073
- identifiers_sent: z2.nullable(z2.string()),
1074
- mapped_service_ids: z2.array(z2.uuid()),
1075
- matched_submit_count: z2.nullable(z2.int()),
1076
- needs_force: z2._default(z2.boolean(), false),
1077
- rollup: z2.enum([
2101
+ var zSaleWritebackStatusSchema = z4.object({
2102
+ blocked_reason: z4.nullable(z4.string()),
2103
+ can_dispatch: z4._default(z4.boolean(), false),
2104
+ email_source: z4.nullable(zWritebackEmailSource),
2105
+ enrichment_status: z4.nullable(zWritebackEnrichmentStatus),
2106
+ gclid_attached: z4._default(z4.boolean(), false),
2107
+ identifiers_sent: z4.nullable(z4.string()),
2108
+ mapped_service_ids: z4.array(z4.uuid()),
2109
+ matched_submit_count: z4.nullable(z4.int()),
2110
+ needs_force: z4._default(z4.boolean(), false),
2111
+ rollup: z4.enum([
1078
2112
  "failed",
1079
2113
  "skipped",
1080
2114
  "pending",
@@ -1084,39 +2118,39 @@ var zSaleWritebackStatusSchema = z2.object({
1084
2118
  "out_of_window",
1085
2119
  "blocked"
1086
2120
  ]),
1087
- stale: z2._default(z2.boolean(), false),
1088
- uncovered_service_ids: z2.array(z2.uuid()),
2121
+ stale: z4._default(z4.boolean(), false),
2122
+ uncovered_service_ids: z4.array(z4.uuid()),
1089
2123
  uploads: zWritebackSaleUploadCounts
1090
2124
  });
1091
- var zSaleEntitySchema = z2.object({
1092
- amount_total_cents: z2.int(),
1093
- business_id: z2.uuid(),
1094
- business_name: z2.nullable(z2.string()),
1095
- created_at: z2.iso.datetime({ offset: true }),
1096
- currency: z2.string(),
1097
- customer_email: z2.nullable(z2.string()),
1098
- customer_name: z2.nullable(z2.string()),
1099
- customer_name_placeholder_confirmed: z2._default(z2.boolean(), false),
1100
- customer_name_placeholder_confirmed_at: z2.nullable(z2.iso.datetime({ offset: true })),
1101
- customer_phone: z2.nullable(z2.string()),
1102
- description: z2.nullable(z2.string()),
2125
+ var zSaleEntitySchema = z4.object({
2126
+ amount_total_cents: z4.int(),
2127
+ business_id: z4.uuid(),
2128
+ business_name: z4.nullable(z4.string()),
2129
+ created_at: z4.iso.datetime({ offset: true }),
2130
+ currency: z4.string(),
2131
+ customer_email: z4.nullable(z4.string()),
2132
+ customer_name: z4.nullable(z4.string()),
2133
+ customer_name_placeholder_confirmed: z4._default(z4.boolean(), false),
2134
+ customer_name_placeholder_confirmed_at: z4.nullable(z4.iso.datetime({ offset: true })),
2135
+ customer_phone: z4.nullable(z4.string()),
2136
+ description: z4.nullable(z4.string()),
1103
2137
  environment: zTrackingEnvironment,
1104
- external_id: z2.nullable(z2.string()),
1105
- id: z2.uuid(),
1106
- items: z2.array(zSaleItemEntitySchema),
1107
- metadata: z2.nullable(z2.record(z2.string(), z2.unknown())),
1108
- occurred_at: z2.iso.datetime({ offset: true }),
1109
- service_id: z2.nullable(z2.uuid()),
1110
- service_key: z2.nullable(z2.string()),
1111
- service_label: z2.nullable(z2.string()),
1112
- services: z2.array(zSaleServiceEntitySchema),
1113
- sms_automation: z2.nullable(zSaleSmsStatusSchema),
1114
- sms_consent: z2._default(z2.boolean(), false),
1115
- sms_consent_granted_at: z2.nullable(z2.iso.datetime({ offset: true })),
2138
+ external_id: z4.nullable(z4.string()),
2139
+ id: z4.uuid(),
2140
+ items: z4.array(zSaleItemEntitySchema),
2141
+ metadata: z4.nullable(z4.record(z4.string(), z4.unknown())),
2142
+ occurred_at: z4.iso.datetime({ offset: true }),
2143
+ service_id: z4.nullable(z4.uuid()),
2144
+ service_key: z4.nullable(z4.string()),
2145
+ service_label: z4.nullable(z4.string()),
2146
+ services: z4.array(zSaleServiceEntitySchema),
2147
+ sms_automation: z4.nullable(zSaleSmsStatusSchema),
2148
+ sms_consent: z4._default(z4.boolean(), false),
2149
+ sms_consent_granted_at: z4.nullable(z4.iso.datetime({ offset: true })),
1116
2150
  source: zSaleSource,
1117
- source_facts: z2.nullable(zAutoLeapSaleFacts),
1118
- updated_at: z2.iso.datetime({ offset: true }),
1119
- writeback: z2.nullable(zSaleWritebackStatusSchema)
2151
+ source_facts: z4.nullable(zAutoLeapSaleFacts),
2152
+ updated_at: z4.iso.datetime({ offset: true }),
2153
+ writeback: z4.nullable(zSaleWritebackStatusSchema)
1120
2154
  });
1121
2155
 
1122
2156
  // ../tracking-core/src/resources/sales/schema.ts
@@ -1125,11 +2159,11 @@ var saleServiceSchema = zSaleServiceInputSchema;
1125
2159
  var currencySchema = zSupportedCurrency;
1126
2160
  var SUPPORTED_CURRENCIES = currencySchema.options;
1127
2161
  var SALE_SOURCES = zSaleSource.options;
1128
- var centsSchema = z3.int().check(z3.gte(0));
1129
- var metadataSchema = z3.record(z3.string(), z3.unknown());
1130
- var customerNameSchema = z3.string().check(z3.maxLength(200));
1131
- var customerPhoneSchema = z3.string().check(z3.maxLength(64));
1132
- var customerEmailSchema = z3.email().check(z3.maxLength(320));
2162
+ var centsSchema = z5.int().check(z5.gte(0));
2163
+ var metadataSchema = z5.record(z5.string(), z5.unknown());
2164
+ var customerNameSchema = z5.string().check(z5.maxLength(200));
2165
+ var customerPhoneSchema = z5.string().check(z5.maxLength(64));
2166
+ var customerEmailSchema = z5.email().check(z5.maxLength(320));
1133
2167
  function addIssue(ctx, path, message) {
1134
2168
  ctx.issues.push({ code: "custom", input: ctx.value, path, message });
1135
2169
  }
@@ -1163,24 +2197,24 @@ function refineServiceXor(val, ctx, { requireAmount }) {
1163
2197
  );
1164
2198
  }
1165
2199
  }
1166
- var saleCreateSchema = z3.extend(zSaleCreateSchema, { items: z3._default(z3.array(saleItemSchema), []) }).check((ctx) => refineServiceXor(ctx.value, ctx, { requireAmount: true }));
1167
- var saleUpdateSchema = z3.strictObject({
1168
- description: z3.nullish(z3.string()),
1169
- service: z3.nullish(z3.string()),
1170
- services: z3.nullish(z3.array(saleServiceSchema)),
1171
- currency: z3.optional(currencySchema),
1172
- amount_total_cents: z3.optional(centsSchema),
1173
- occurred_at: z3.optional(z3.iso.datetime()),
1174
- items: z3.optional(z3.array(saleItemSchema)),
1175
- metadata: z3.nullish(metadataSchema),
1176
- customer_name: z3.nullish(customerNameSchema),
2200
+ var saleCreateSchema = z5.extend(zSaleCreateSchema, { items: z5._default(z5.array(saleItemSchema), []) }).check((ctx) => refineServiceXor(ctx.value, ctx, { requireAmount: true }));
2201
+ var saleUpdateSchema = z5.strictObject({
2202
+ description: z5.nullish(z5.string()),
2203
+ service: z5.nullish(z5.string()),
2204
+ services: z5.nullish(z5.array(saleServiceSchema)),
2205
+ currency: z5.optional(currencySchema),
2206
+ amount_total_cents: z5.optional(centsSchema),
2207
+ occurred_at: z5.optional(z5.iso.datetime()),
2208
+ items: z5.optional(z5.array(saleItemSchema)),
2209
+ metadata: z5.nullish(metadataSchema),
2210
+ customer_name: z5.nullish(customerNameSchema),
1177
2211
  // Re-attest when changing a filler-looking name; the server clears the
1178
2212
  // prior attestation whenever `customer_name` changes.
1179
- customer_name_placeholder_confirmed: z3.optional(z3.boolean()),
1180
- customer_phone: z3.nullish(customerPhoneSchema),
1181
- customer_email: z3.nullish(customerEmailSchema),
2213
+ customer_name_placeholder_confirmed: z5.optional(z5.boolean()),
2214
+ customer_phone: z5.nullish(customerPhoneSchema),
2215
+ customer_email: z5.nullish(customerEmailSchema),
1182
2216
  // NOT NULL server-side: omit to leave unchanged (explicit null is rejected).
1183
- sms_consent: z3.optional(z3.boolean())
2217
+ sms_consent: z5.optional(z5.boolean())
1184
2218
  }).check((ctx) => refineServiceXor(ctx.value, ctx, { requireAmount: false }));
1185
2219
  var TRACKING_RANGES = ["24h", "7d", "30d"];
1186
2220
  var NAMED_RANGES = [