@aranova/tracking-next 0.27.0 → 1.1.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.
Files changed (73) hide show
  1. package/dist/blog-server.d.mts +2 -1
  2. package/dist/blog-server.d.ts +2 -1
  3. package/dist/blog-server.js +121 -98
  4. package/dist/blog-server.js.map +1 -1
  5. package/dist/blog-server.mjs +86 -63
  6. package/dist/blog-server.mjs.map +1 -1
  7. package/dist/blog.d.mts +651 -1755
  8. package/dist/blog.d.ts +651 -1755
  9. package/dist/blog.js +121 -98
  10. package/dist/blog.js.map +1 -1
  11. package/dist/blog.mjs +86 -63
  12. package/dist/blog.mjs.map +1 -1
  13. package/dist/calendar-server.d.mts +2 -2
  14. package/dist/calendar-server.d.ts +2 -2
  15. package/dist/calendar-server.js +0 -26
  16. package/dist/calendar-server.js.map +1 -1
  17. package/dist/calendar-server.mjs +0 -26
  18. package/dist/calendar-server.mjs.map +1 -1
  19. package/dist/calendar.d.mts +3 -3
  20. package/dist/calendar.d.ts +3 -3
  21. package/dist/calendar.js +227 -126
  22. package/dist/calendar.js.map +1 -1
  23. package/dist/calendar.mjs +216 -125
  24. package/dist/calendar.mjs.map +1 -1
  25. package/dist/client.d.mts +11 -4
  26. package/dist/client.d.ts +11 -4
  27. package/dist/client.js +611 -347
  28. package/dist/client.js.map +1 -1
  29. package/dist/client.mjs +611 -347
  30. package/dist/client.mjs.map +1 -1
  31. package/dist/index.d.mts +9 -8
  32. package/dist/index.d.ts +9 -8
  33. package/dist/index.js +550 -487
  34. package/dist/index.js.map +1 -1
  35. package/dist/index.mjs +539 -487
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/ingest-typed-MBuMe9Pn.d.ts +883 -0
  38. package/dist/ingest-typed-UA13xTfI.d.mts +883 -0
  39. package/dist/middleware.js +0 -467
  40. package/dist/middleware.js.map +1 -1
  41. package/dist/middleware.mjs +0 -467
  42. package/dist/middleware.mjs.map +1 -1
  43. package/dist/{phone-utils-BVzSNBf1.d.mts → phone-utils-C0ds6cqZ.d.mts} +24 -78
  44. package/dist/{phone-utils-BVzSNBf1.d.ts → phone-utils-C0ds6cqZ.d.ts} +24 -78
  45. package/dist/phone.d.mts +2 -2
  46. package/dist/phone.d.ts +2 -2
  47. package/dist/phone.js +0 -464
  48. package/dist/phone.js.map +1 -1
  49. package/dist/phone.mjs +0 -464
  50. package/dist/phone.mjs.map +1 -1
  51. package/dist/{sales-IGC2tq4J.d.ts → sales-DQC815f3.d.ts} +90 -329
  52. package/dist/{sales-DQGzXSgL.d.mts → sales-w1reW_Y3.d.mts} +90 -329
  53. package/dist/sales.d.mts +3 -3
  54. package/dist/sales.d.ts +3 -3
  55. package/dist/sales.js +270 -127
  56. package/dist/sales.js.map +1 -1
  57. package/dist/sales.mjs +259 -127
  58. package/dist/sales.mjs.map +1 -1
  59. package/dist/server.d.mts +2 -2
  60. package/dist/server.d.ts +2 -2
  61. package/dist/server.js +0 -467
  62. package/dist/server.js.map +1 -1
  63. package/dist/server.mjs +0 -467
  64. package/dist/server.mjs.map +1 -1
  65. package/dist/{tracking-config-runtime-D7prdTE1.d.mts → tracking-config-runtime-C6H7ofmo.d.mts} +6 -0
  66. package/dist/{tracking-config-runtime-D7prdTE1.d.ts → tracking-config-runtime-C6H7ofmo.d.ts} +6 -0
  67. package/dist/transport-CDmGOiCq.d.ts +253 -0
  68. package/dist/transport-I_cMSFQ6.d.mts +253 -0
  69. package/package.json +2 -2
  70. package/dist/ingest-typed-BxLFlZ7N.d.mts +0 -1371
  71. package/dist/ingest-typed-ByQVRMvP.d.ts +0 -1371
  72. package/dist/transport-C-_rFSR4.d.ts +0 -721
  73. package/dist/transport-yUF5Wize.d.mts +0 -721
package/dist/sales.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/sales.ts
@@ -25,6 +35,7 @@ __export(sales_exports, {
25
35
  SUPPORTED_CURRENCIES: () => SUPPORTED_CURRENCIES,
26
36
  TRACKING_RANGES: () => TRACKING_RANGES,
27
37
  createSalesClient: () => createSalesClient,
38
+ currencySchema: () => currencySchema,
28
39
  fetchServices: () => fetchServices,
29
40
  formatDateInTz: () => formatDateInTz,
30
41
  formatMoney: () => formatMoney,
@@ -77,7 +88,7 @@ function normalizeEmail(raw) {
77
88
  return EMAIL_SHAPE.test(cleaned) ? cleaned : null;
78
89
  }
79
90
  function stashUserData(data, country) {
80
- const email = normalizeEmail(data.email);
91
+ const email3 = normalizeEmail(data.email);
81
92
  let phoneNumber = null;
82
93
  if (typeof data.phone === "string" && data.phone.length > 0) {
83
94
  try {
@@ -87,17 +98,17 @@ function stashUserData(data, country) {
87
98
  }
88
99
  }
89
100
  stash = {
90
- email: email ?? stash.email,
101
+ email: email3 ?? stash.email,
91
102
  phoneNumber: phoneNumber ?? stash.phoneNumber
92
103
  };
93
104
  }
94
105
  function applyUserDataForConversion(explicit, country) {
95
106
  if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
96
- let email = stash.email;
107
+ let email3 = stash.email;
97
108
  let phoneNumber = stash.phoneNumber;
98
109
  if (explicit) {
99
110
  const normalizedEmail = normalizeEmail(explicit.email);
100
- if (normalizedEmail) email = normalizedEmail;
111
+ if (normalizedEmail) email3 = normalizedEmail;
101
112
  if (typeof explicit.phone === "string" && explicit.phone.length > 0) {
102
113
  try {
103
114
  phoneNumber = toE164(explicit.phone, country) ?? phoneNumber;
@@ -105,10 +116,10 @@ function applyUserDataForConversion(explicit, country) {
105
116
  }
106
117
  }
107
118
  }
108
- if (email === null && phoneNumber === null) return false;
119
+ if (email3 === null && phoneNumber === null) return false;
109
120
  try {
110
121
  window.gtag("set", "user_data", {
111
- ...email !== null ? { email } : {},
122
+ ...email3 !== null ? { email: email3 } : {},
112
123
  ...phoneNumber !== null ? { phone_number: phoneNumber } : {}
113
124
  });
114
125
  return true;
@@ -210,29 +221,6 @@ function fireConversionWithConsent(input, options) {
210
221
  }
211
222
  }
212
223
 
213
- // ../tracking-core/src/events/page-view.ts
214
- var import_zod = require("zod");
215
- var pageViewMetadataSchema = import_zod.z.object({
216
- page: import_zod.z.object({
217
- title: import_zod.z.string().nullable(),
218
- path: import_zod.z.string(),
219
- search: import_zod.z.string(),
220
- hash: import_zod.z.string()
221
- }).strict(),
222
- referrer: import_zod.z.string().nullable(),
223
- // `.nullable().optional()` — absent (undefined) OR explicit null OR a
224
- // real viewport object. Mirrors Pydantic's `_Viewport | None = None`
225
- // on the backend side so the drift test stays clean.
226
- viewport: import_zod.z.object({
227
- w: import_zod.z.number(),
228
- h: import_zod.z.number()
229
- }).strict().nullable().optional()
230
- }).strict();
231
- var pageViewConfigSchema = import_zod.z.object({}).strict();
232
-
233
- // ../tracking-core/src/session.ts
234
- var SESSION_IDLE_MS = 30 * 60 * 1e3;
235
-
236
224
  // ../tracking-core/src/ingest.ts
237
225
  var API_KEY_HEADER = "X-Aranova-Api-Key";
238
226
  var SDK_VERSION_HEADER = "X-Aranova-Sdk-Version";
@@ -277,9 +265,9 @@ async function apiRequest(config, method, path, body, extraHeaders) {
277
265
  try {
278
266
  const parsed = await response.json();
279
267
  if (parsed && typeof parsed === "object") {
280
- const record = parsed;
281
- if (typeof record.detail === "string") detail = record.detail;
282
- if (typeof record.code === "string") code = record.code;
268
+ const record3 = parsed;
269
+ if (typeof record3.detail === "string") detail = record3.detail;
270
+ if (typeof record3.code === "string") code = record3.code;
283
271
  }
284
272
  } catch {
285
273
  }
@@ -297,9 +285,9 @@ async function apiRequest(config, method, path, body, extraHeaders) {
297
285
  var salesRequest = apiRequest;
298
286
 
299
287
  // ../tracking-core/src/resources/intl/date.ts
300
- function formatDateInTz(iso, timeZone, opts, locale) {
301
- const date = new Date(iso);
302
- if (Number.isNaN(date.getTime())) return iso;
288
+ function formatDateInTz(iso3, timeZone, opts, locale) {
289
+ const date = new Date(iso3);
290
+ if (Number.isNaN(date.getTime())) return iso3;
303
291
  return new Intl.DateTimeFormat(locale, {
304
292
  year: "numeric",
305
293
  month: "short",
@@ -363,7 +351,7 @@ function fireRecordedConversions(firing, input, recorded, sale, currency) {
363
351
  }
364
352
  function createSalesClient(config) {
365
353
  if (config.firing) registerCapability("conversion_goals_manual");
366
- async function record(input) {
354
+ async function record3(input) {
367
355
  const currency = input.currency ?? config.defaultCurrency;
368
356
  if (!currency) {
369
357
  throw new Error(
@@ -389,9 +377,9 @@ function createSalesClient(config) {
389
377
  return sale;
390
378
  }
391
379
  return {
392
- record,
380
+ record: record3,
393
381
  // recordSale is the intent-revealing alias — same behavior, clearer call site.
394
- recordSale: record,
382
+ recordSale: record3,
395
383
  trackConversion(key, options) {
396
384
  if (config.firing && "fireConversion" in config.firing) {
397
385
  if (options?.userData && getConsentState() !== "denied") stashUserData(options.userData);
@@ -510,115 +498,269 @@ function createSalesClient(config) {
510
498
  }
511
499
 
512
500
  // ../tracking-core/src/resources/sales/schema.ts
513
- var import_zod2 = require("zod");
514
- var SUPPORTED_CURRENCIES = ["USD", "CAD"];
515
- var TRACKING_ENVIRONMENTS = ["production", "development"];
516
- var currencySchema = import_zod2.z.enum(SUPPORTED_CURRENCIES);
517
- var centsSchema = import_zod2.z.number().int().nonnegative();
518
- var quantitySchema = import_zod2.z.string().regex(/^\d+(\.\d{1,3})?$/);
519
- var metadataSchema = import_zod2.z.record(import_zod2.z.unknown());
520
- var saleItemSchema = import_zod2.z.object({
521
- external_item_id: import_zod2.z.string().nullable().optional(),
522
- name: import_zod2.z.string().nullable().optional(),
523
- category: import_zod2.z.string().nullable().optional(),
524
- quantity: quantitySchema,
525
- unit_price_cents: centsSchema,
526
- // Non-negativity validated on the wire — same contract as the other cents
527
- // fields — and backstopped by the DB CHECK.
528
- unit_cost_cents: centsSchema.nullable().optional()
529
- }).strict();
530
- var saleServiceSchema = import_zod2.z.object({
531
- service: import_zod2.z.string(),
532
- amount_cents: centsSchema
533
- }).strict();
534
- var customerNameSchema = import_zod2.z.string().max(200);
535
- var customerPhoneSchema = import_zod2.z.string().max(64);
536
- var customerEmailSchema = import_zod2.z.string().max(320).email();
501
+ var z2 = __toESM(require("zod/mini"));
502
+
503
+ // ../tracking-core/src/resources/sales/generated/zod.gen.ts
504
+ var z = __toESM(require("zod/mini"));
505
+ var zErrorCode = z.enum([
506
+ "missing_credentials",
507
+ "invalid_api_key",
508
+ "not_authenticated",
509
+ "wrong_key_type",
510
+ "origin_missing",
511
+ "origin_not_allowed",
512
+ "capability_denied",
513
+ "session_required",
514
+ "operator_only",
515
+ "cross_tenant",
516
+ "business_id_required",
517
+ "bad_request",
518
+ "not_found",
519
+ "conflict",
520
+ "incompatible_payload",
521
+ "repeat_confirmation_required",
522
+ "idempotency_conflict",
523
+ "validation_error",
524
+ "unknown_service",
525
+ "invalid_sale_properties",
526
+ "invalid_cursor",
527
+ "rate_limited",
528
+ "internal_error"
529
+ ]);
530
+ var zSaleItemEntitySchema = z.object({
531
+ category: z.nullable(z.string()),
532
+ external_item_id: z.nullable(z.string()),
533
+ id: z.uuid(),
534
+ name: z.nullable(z.string()),
535
+ quantity: z.string().check(z.regex(/^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$/)),
536
+ unit_cost_cents: z.nullable(z.int()),
537
+ unit_price_cents: z.int()
538
+ });
539
+ var zSaleItemInputSchema = z.strictObject({
540
+ category: z.nullish(z.string()),
541
+ external_item_id: z.nullish(z.string()),
542
+ name: z.nullish(z.string()),
543
+ quantity: z.string().check(z.regex(/^\d+(\.\d{1,3})?$/)),
544
+ unit_cost_cents: z.nullish(z.int().check(z.gte(0))),
545
+ unit_price_cents: z.int().check(z.gte(0))
546
+ });
547
+ var zSaleServiceEntitySchema = z.object({
548
+ amount_cents: z.int(),
549
+ id: z.uuid(),
550
+ mapped: z.nullable(z.boolean()),
551
+ service_id: z.uuid(),
552
+ service_key: z.string(),
553
+ service_label: z.string()
554
+ });
555
+ var zSaleServiceInputSchema = z.strictObject({
556
+ amount_cents: z.int().check(z.gte(0)),
557
+ service: z.string()
558
+ });
559
+ var zSmsAutomationState = z.enum([
560
+ "needs_check",
561
+ "failed",
562
+ "undelivered",
563
+ "blocked_opted_out",
564
+ "withheld",
565
+ "queued",
566
+ "sent",
567
+ "delivered",
568
+ "eligible",
569
+ "not_applicable"
570
+ ]);
571
+ var zSmsWithheldReason = z.enum([
572
+ "automation_disabled",
573
+ "sms_disabled",
574
+ "no_phone",
575
+ "no_consent",
576
+ "no_customer_identity",
577
+ "missing_customer_name",
578
+ "placeholder_customer_name",
579
+ "opted_out",
580
+ "already_messaged",
581
+ "sender_not_ready",
582
+ "missing_review_url",
583
+ "no_handler",
584
+ "dispatch_already_recorded",
585
+ "text_back_disabled",
586
+ "number_not_active",
587
+ "a2p_registration_required",
588
+ "alert_recipient_cannot_receive_sms",
589
+ "line_type_unresolved"
590
+ ]);
591
+ var zSaleSmsStatusSchema = z.object({
592
+ can_send_now: z._default(z.boolean(), false),
593
+ last_message_at: z.nullable(z.iso.datetime({ offset: true })),
594
+ reason_detail: z.nullable(z.string()),
595
+ state: zSmsAutomationState,
596
+ suppressed_by_other_sale: z._default(z.boolean(), false),
597
+ withheld_reason: z.nullable(zSmsWithheldReason)
598
+ });
599
+ var zSupportedCurrency = z.enum(["USD", "CAD"]);
600
+ var zTrackingEnvironment = z.enum(["production", "development"]);
601
+ var zSaleCreateSchema = z.strictObject({
602
+ amount_total_cents: z.nullish(z.int().check(z.gte(0))),
603
+ currency: zSupportedCurrency,
604
+ customer_email: z.nullish(z.email().check(z.maxLength(320))),
605
+ customer_name: z.nullish(z.string().check(z.maxLength(200))),
606
+ customer_phone: z.nullish(z.string().check(z.maxLength(64))),
607
+ description: z.nullish(z.string()),
608
+ environment: z._default(z.optional(zTrackingEnvironment), "production"),
609
+ external_id: z.nullish(z.string()),
610
+ items: z.optional(z.array(zSaleItemInputSchema)),
611
+ metadata: z.nullish(z.record(z.string(), z.unknown())),
612
+ occurred_at: z.iso.datetime({ offset: true }),
613
+ service: z.nullish(z.string()),
614
+ services: z.nullish(z.array(zSaleServiceInputSchema)),
615
+ sms_consent: z.nullish(z.boolean())
616
+ });
617
+ var zValidationError = z.object({
618
+ ctx: z.optional(z.record(z.string(), z.unknown())),
619
+ input: z.optional(z.unknown()),
620
+ loc: z.array(z.union([z.string(), z.int()])),
621
+ msg: z.string(),
622
+ type: z.string()
623
+ });
624
+ var zHttpValidationError = z.object({
625
+ detail: z.optional(z.array(zValidationError))
626
+ });
627
+ var zValidationErrorItem = z.object({
628
+ loc: z.array(z.union([z.string(), z.int()])),
629
+ msg: z.string(),
630
+ type: z.string()
631
+ });
632
+ var zApiErrorSchema = z.object({
633
+ code: zErrorCode,
634
+ context: z._default(z.nullish(z.record(z.string(), z.unknown())), null),
635
+ detail: z.string(),
636
+ errors: z._default(z.nullish(z.array(zValidationErrorItem)), null),
637
+ request_id: z._default(z.nullish(z.string()), null)
638
+ });
639
+ var zWritebackEmailSource = z.enum(["NONE", "SALE", "CUSTOMER", "HARVESTED"]);
640
+ var zWritebackEnrichmentStatus = z.enum(["NONE", "MATCHED", "AMBIGUOUS"]);
641
+ var zWritebackSaleUploadCounts = z.object({
642
+ failed: z._default(z.int(), 0),
643
+ pending: z._default(z.int(), 0),
644
+ skipped: z._default(z.int(), 0),
645
+ submitted: z._default(z.int(), 0),
646
+ total: z._default(z.int(), 0),
647
+ uploaded: z._default(z.int(), 0)
648
+ });
649
+ var zSaleWritebackStatusSchema = z.object({
650
+ blocked_reason: z.nullable(z.string()),
651
+ can_dispatch: z._default(z.boolean(), false),
652
+ email_source: z.nullable(zWritebackEmailSource),
653
+ enrichment_status: z.nullable(zWritebackEnrichmentStatus),
654
+ gclid_attached: z._default(z.boolean(), false),
655
+ identifiers_sent: z.nullable(z.string()),
656
+ mapped_service_ids: z.array(z.uuid()),
657
+ matched_submit_count: z.nullable(z.int()),
658
+ needs_force: z._default(z.boolean(), false),
659
+ rollup: z.enum([
660
+ "failed",
661
+ "skipped",
662
+ "pending",
663
+ "submitted",
664
+ "uploaded",
665
+ "not_uploaded",
666
+ "out_of_window",
667
+ "blocked"
668
+ ]),
669
+ stale: z._default(z.boolean(), false),
670
+ uncovered_service_ids: z.array(z.uuid()),
671
+ uploads: zWritebackSaleUploadCounts
672
+ });
673
+ var zSaleEntitySchema = z.object({
674
+ amount_total_cents: z.int(),
675
+ business_id: z.uuid(),
676
+ business_name: z.nullable(z.string()),
677
+ created_at: z.iso.datetime({ offset: true }),
678
+ currency: z.string(),
679
+ customer_email: z.nullable(z.string()),
680
+ customer_name: z.nullable(z.string()),
681
+ customer_name_placeholder_confirmed: z._default(z.boolean(), false),
682
+ customer_name_placeholder_confirmed_at: z.nullable(z.iso.datetime({ offset: true })),
683
+ customer_phone: z.nullable(z.string()),
684
+ description: z.nullable(z.string()),
685
+ environment: zTrackingEnvironment,
686
+ external_id: z.nullable(z.string()),
687
+ id: z.uuid(),
688
+ items: z.array(zSaleItemEntitySchema),
689
+ metadata: z.nullable(z.record(z.string(), z.unknown())),
690
+ occurred_at: z.iso.datetime({ offset: true }),
691
+ service_id: z.nullable(z.uuid()),
692
+ service_key: z.nullable(z.string()),
693
+ service_label: z.nullable(z.string()),
694
+ services: z.array(zSaleServiceEntitySchema),
695
+ sms_automation: z.nullable(zSaleSmsStatusSchema),
696
+ sms_consent: z._default(z.boolean(), false),
697
+ sms_consent_granted_at: z.nullable(z.iso.datetime({ offset: true })),
698
+ updated_at: z.iso.datetime({ offset: true }),
699
+ writeback: z.nullable(zSaleWritebackStatusSchema)
700
+ });
701
+
702
+ // ../tracking-core/src/resources/sales/schema.ts
703
+ var saleItemSchema = zSaleItemInputSchema;
704
+ var saleServiceSchema = zSaleServiceInputSchema;
705
+ var currencySchema = zSupportedCurrency;
706
+ var SUPPORTED_CURRENCIES = currencySchema.options;
707
+ var centsSchema = z2.int().check(z2.gte(0));
708
+ var metadataSchema = z2.record(z2.string(), z2.unknown());
709
+ var customerNameSchema = z2.string().check(z2.maxLength(200));
710
+ var customerPhoneSchema = z2.string().check(z2.maxLength(64));
711
+ var customerEmailSchema = z2.email().check(z2.maxLength(320));
712
+ function addIssue(ctx, path, message) {
713
+ ctx.issues.push({ code: "custom", input: ctx.value, path, message });
714
+ }
537
715
  function refineServiceXor(val, ctx, { requireAmount }) {
538
716
  if (val.services != null) {
539
717
  if (val.service != null) {
540
- ctx.addIssue({
541
- code: import_zod2.z.ZodIssueCode.custom,
542
- message: "pass either `service` or `services`, not both",
543
- path: ["services"]
544
- });
718
+ addIssue(ctx, ["services"], "pass either `service` or `services`, not both");
545
719
  }
546
720
  if (val.services.length === 0) {
547
- ctx.addIssue({
548
- code: import_zod2.z.ZodIssueCode.custom,
549
- message: "`services` must not be empty",
550
- path: ["services"]
551
- });
721
+ addIssue(ctx, ["services"], "`services` must not be empty");
552
722
  }
553
723
  const keys = val.services.map((s) => s.service);
554
724
  if (new Set(keys).size !== keys.length) {
555
- ctx.addIssue({
556
- code: import_zod2.z.ZodIssueCode.custom,
557
- message: "`services` must not list the same service more than once",
558
- path: ["services"]
559
- });
725
+ addIssue(ctx, ["services"], "`services` must not list the same service more than once");
560
726
  }
561
727
  if (val.amount_total_cents != null) {
562
728
  const sum = val.services.reduce((acc, s) => acc + s.amount_cents, 0);
563
729
  if (val.amount_total_cents !== sum) {
564
- ctx.addIssue({
565
- code: import_zod2.z.ZodIssueCode.custom,
566
- message: "amount_total_cents must equal the sum of the services amounts (omit it to derive it automatically)",
567
- path: ["amount_total_cents"]
568
- });
730
+ addIssue(
731
+ ctx,
732
+ ["amount_total_cents"],
733
+ "amount_total_cents must equal the sum of the services amounts (omit it to derive it automatically)"
734
+ );
569
735
  }
570
736
  }
571
737
  } else if (requireAmount && val.amount_total_cents == null) {
572
- ctx.addIssue({
573
- code: import_zod2.z.ZodIssueCode.custom,
574
- message: "amount_total_cents is required unless `services` is provided",
575
- path: ["amount_total_cents"]
576
- });
738
+ addIssue(
739
+ ctx,
740
+ ["amount_total_cents"],
741
+ "amount_total_cents is required unless `services` is provided"
742
+ );
577
743
  }
578
744
  }
579
- var saleCreateSchema = import_zod2.z.object({
580
- external_id: import_zod2.z.string().nullable().optional(),
581
- description: import_zod2.z.string().nullable().optional(),
582
- service: import_zod2.z.string().nullable().optional(),
583
- services: import_zod2.z.array(saleServiceSchema).nullable().optional(),
584
- currency: currencySchema,
585
- // Optional only because the plural `services` form derives it from the sum
586
- // (see refineServiceXor); the singular/serviceless path still requires it.
587
- amount_total_cents: centsSchema.nullable().optional(),
588
- occurred_at: import_zod2.z.string().datetime(),
589
- environment: import_zod2.z.enum(TRACKING_ENVIRONMENTS).default("production"),
590
- items: import_zod2.z.array(saleItemSchema).default([]),
591
- metadata: metadataSchema.nullable().optional(),
592
- customer_name: customerNameSchema.nullable().optional(),
593
- customer_phone: customerPhoneSchema.nullable().optional(),
594
- customer_email: customerEmailSchema.nullable().optional(),
595
- // CASL consent attestation: the customer agreed to receive SMS. Recorded
596
- // with a timestamp server-side; every SMS send path gates on it.
597
- //
598
- // Tri-state: omit it (or send null) to let the server apply the business's
599
- // configured default-opt-in policy, honouring a returning customer's
600
- // remembered preference. Send an explicit boolean to assert consent state
601
- // yourself — `false` records a deliberate opt-out.
602
- sms_consent: import_zod2.z.boolean().nullable().optional()
603
- }).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: true }));
604
- var saleUpdateSchema = import_zod2.z.object({
605
- description: import_zod2.z.string().nullable().optional(),
606
- service: import_zod2.z.string().nullable().optional(),
607
- services: import_zod2.z.array(saleServiceSchema).nullable().optional(),
608
- currency: currencySchema.optional(),
609
- amount_total_cents: centsSchema.optional(),
610
- occurred_at: import_zod2.z.string().datetime().optional(),
611
- items: import_zod2.z.array(saleItemSchema).optional(),
612
- metadata: metadataSchema.nullable().optional(),
613
- customer_name: customerNameSchema.nullable().optional(),
745
+ var saleCreateSchema = z2.extend(zSaleCreateSchema, { items: z2._default(z2.array(saleItemSchema), []) }).check((ctx) => refineServiceXor(ctx.value, ctx, { requireAmount: true }));
746
+ var saleUpdateSchema = z2.strictObject({
747
+ description: z2.nullish(z2.string()),
748
+ service: z2.nullish(z2.string()),
749
+ services: z2.nullish(z2.array(saleServiceSchema)),
750
+ currency: z2.optional(currencySchema),
751
+ amount_total_cents: z2.optional(centsSchema),
752
+ occurred_at: z2.optional(z2.iso.datetime()),
753
+ items: z2.optional(z2.array(saleItemSchema)),
754
+ metadata: z2.nullish(metadataSchema),
755
+ customer_name: z2.nullish(customerNameSchema),
614
756
  // Re-attest when changing a filler-looking name; the server clears the
615
757
  // prior attestation whenever `customer_name` changes.
616
- customer_name_placeholder_confirmed: import_zod2.z.boolean().optional(),
617
- customer_phone: customerPhoneSchema.nullable().optional(),
618
- customer_email: customerEmailSchema.nullable().optional(),
758
+ customer_name_placeholder_confirmed: z2.optional(z2.boolean()),
759
+ customer_phone: z2.nullish(customerPhoneSchema),
760
+ customer_email: z2.nullish(customerEmailSchema),
619
761
  // NOT NULL server-side: omit to leave unchanged (explicit null is rejected).
620
- sms_consent: import_zod2.z.boolean().optional()
621
- }).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: false }));
762
+ sms_consent: z2.optional(z2.boolean())
763
+ }).check((ctx) => refineServiceXor(ctx.value, ctx, { requireAmount: false }));
622
764
  var TRACKING_RANGES = ["24h", "7d", "30d"];
623
765
  var NAMED_RANGES = [
624
766
  "today",
@@ -645,6 +787,7 @@ async function fetchServices(config) {
645
787
  SUPPORTED_CURRENCIES,
646
788
  TRACKING_RANGES,
647
789
  createSalesClient,
790
+ currencySchema,
648
791
  fetchServices,
649
792
  formatDateInTz,
650
793
  formatMoney,