@nfcard/validation 0.23.0 → 0.24.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { z } from "zod";
2
+ import { z as z2 } from "zod";
3
3
  import {
4
4
  isTipProviderId,
5
5
  normalizeTipHandle,
@@ -448,6 +448,180 @@ function elementPrintability(el, ctx) {
448
448
  return out;
449
449
  }
450
450
 
451
+ // src/profileLayout.ts
452
+ import { z } from "zod";
453
+ import {
454
+ MAX_DISTINCT_OBJECT_FONTS,
455
+ MAX_FREE_TEXT_LENGTH,
456
+ MAX_LAYOUT_OBJECT_OVERRIDES,
457
+ MAX_OBJECT_ORDER_LENGTH,
458
+ MAX_OWNED_OBJECTS,
459
+ FREE_TEXT_ID_RE,
460
+ PROFILE_SECTIONS,
461
+ PROFILE_SECTION_IDS,
462
+ objectKindOf
463
+ } from "@nfcard/types";
464
+ var HEX_RE = /^#[0-9A-Fa-f]{6}$/;
465
+ var OBJECT_ID_SHAPE_RE = /^[A-Za-z0-9_-]{1,32}$/;
466
+ var textObjectStyleSchema = z.object({
467
+ color: z.string().regex(HEX_RE).optional(),
468
+ sizeStep: z.enum(["sm", "md", "lg"]).optional(),
469
+ align: z.enum(["start", "center"]).optional(),
470
+ weight: z.enum(["normal", "bold"]).optional(),
471
+ italic: z.boolean().optional(),
472
+ underline: z.boolean().optional(),
473
+ // Resolved through the api's font map at render; an unknown key falls
474
+ // back to the template pairing, so membership is not validated here.
475
+ fontKey: z.string().min(1).max(64).regex(/^[A-Za-z0-9-]+$/).optional()
476
+ }).strict();
477
+ var imageObjectStyleSchema = z.object({
478
+ crop: z.object({
479
+ x: z.number().min(0).max(1),
480
+ y: z.number().min(0).max(1),
481
+ zoom: z.number().min(1).max(3)
482
+ }).strict().optional(),
483
+ borderColor: z.string().regex(HEX_RE).optional(),
484
+ borderPx: z.number().int().min(0).max(8).optional(),
485
+ shape: z.enum(["circle", "rounded", "square"]).optional()
486
+ }).strict();
487
+ var buttonObjectStyleSchema = z.object({
488
+ bg: z.string().regex(HEX_RE).optional(),
489
+ variant: z.enum(["solid", "soft", "outline"]).optional()
490
+ }).strict();
491
+ var STYLE_SCHEMA_FOR_KIND = {
492
+ text: textObjectStyleSchema,
493
+ freeText: textObjectStyleSchema,
494
+ image: imageObjectStyleSchema,
495
+ ctaButton: buttonObjectStyleSchema,
496
+ linkOut: buttonObjectStyleSchema,
497
+ action: buttonObjectStyleSchema
498
+ };
499
+ var orderListSchema = z.array(z.string().regex(OBJECT_ID_SHAPE_RE)).max(MAX_OBJECT_ORDER_LENGTH);
500
+ var freeTextObjectSchema = z.object({
501
+ id: z.string().regex(FREE_TEXT_ID_RE),
502
+ kind: z.literal("freeText"),
503
+ section: z.enum(PROFILE_SECTION_IDS),
504
+ text: z.string().max(2e3).transform((v) => sanitizeText(v, MAX_FREE_TEXT_LENGTH))
505
+ }).strict();
506
+ var profileLayoutSchema = z.object({
507
+ v: z.literal(1),
508
+ sectionOrder: z.array(z.enum(PROFILE_SECTION_IDS)).max(PROFILE_SECTION_IDS.length).optional(),
509
+ objectOrder: z.object(
510
+ Object.fromEntries(
511
+ PROFILE_SECTION_IDS.map((id) => [id, orderListSchema.optional()])
512
+ )
513
+ ).strict().partial().optional(),
514
+ objects: z.record(z.string().regex(OBJECT_ID_SHAPE_RE), z.unknown()).optional(),
515
+ ownedObjects: z.array(freeTextObjectSchema).max(MAX_OWNED_OBJECTS).optional()
516
+ }).strict().superRefine((layout, ctx) => {
517
+ if (layout.sectionOrder) {
518
+ const seen = new Set(layout.sectionOrder);
519
+ if (seen.size !== layout.sectionOrder.length) {
520
+ ctx.addIssue({
521
+ code: z.ZodIssueCode.custom,
522
+ path: ["sectionOrder"],
523
+ message: "duplicate section id"
524
+ });
525
+ }
526
+ }
527
+ if (layout.objects) {
528
+ const keys = Object.keys(layout.objects);
529
+ if (keys.length > MAX_LAYOUT_OBJECT_OVERRIDES) {
530
+ ctx.addIssue({
531
+ code: z.ZodIssueCode.custom,
532
+ path: ["objects"],
533
+ message: `more than ${MAX_LAYOUT_OBJECT_OVERRIDES} overrides`
534
+ });
535
+ return;
536
+ }
537
+ const fonts = /* @__PURE__ */ new Set();
538
+ for (const key of keys) {
539
+ const kind = objectKindOf(key);
540
+ if (!kind) {
541
+ ctx.addIssue({
542
+ code: z.ZodIssueCode.custom,
543
+ path: ["objects", key],
544
+ message: "unknown object id"
545
+ });
546
+ continue;
547
+ }
548
+ const styleSchema = STYLE_SCHEMA_FOR_KIND[kind];
549
+ const override = layout.objects[key];
550
+ const overrideParsed = z.object({ style: z.unknown().optional() }).strict().safeParse(override);
551
+ if (!overrideParsed.success) {
552
+ ctx.addIssue({
553
+ code: z.ZodIssueCode.custom,
554
+ path: ["objects", key],
555
+ message: "override must be { style? }"
556
+ });
557
+ continue;
558
+ }
559
+ const style = overrideParsed.data.style;
560
+ if (style === void 0) continue;
561
+ if (!styleSchema) {
562
+ ctx.addIssue({
563
+ code: z.ZodIssueCode.custom,
564
+ path: ["objects", key, "style"],
565
+ message: `kind '${kind}' is not stylable`
566
+ });
567
+ continue;
568
+ }
569
+ const parsed = styleSchema.safeParse(style);
570
+ if (!parsed.success) {
571
+ for (const issue of parsed.error.issues) {
572
+ ctx.addIssue({
573
+ code: z.ZodIssueCode.custom,
574
+ path: ["objects", key, "style", ...issue.path],
575
+ message: issue.message
576
+ });
577
+ }
578
+ continue;
579
+ }
580
+ const fontKey = parsed.data.fontKey;
581
+ if (fontKey) fonts.add(fontKey);
582
+ }
583
+ if (fonts.size > MAX_DISTINCT_OBJECT_FONTS) {
584
+ ctx.addIssue({
585
+ code: z.ZodIssueCode.custom,
586
+ path: ["objects"],
587
+ message: `more than ${MAX_DISTINCT_OBJECT_FONTS} distinct fonts`
588
+ });
589
+ }
590
+ }
591
+ if (layout.ownedObjects) {
592
+ const ids = /* @__PURE__ */ new Set();
593
+ layout.ownedObjects.forEach((obj, i) => {
594
+ if (ids.has(obj.id)) {
595
+ ctx.addIssue({
596
+ code: z.ZodIssueCode.custom,
597
+ path: ["ownedObjects", i, "id"],
598
+ message: "duplicate freeText id"
599
+ });
600
+ }
601
+ ids.add(obj.id);
602
+ if (!PROFILE_SECTIONS[obj.section].hosts.includes("freeText")) {
603
+ ctx.addIssue({
604
+ code: z.ZodIssueCode.custom,
605
+ path: ["ownedObjects", i, "section"],
606
+ message: `section '${obj.section}' does not host freeText`
607
+ });
608
+ }
609
+ if (obj.text.length === 0) {
610
+ ctx.addIssue({
611
+ code: z.ZodIssueCode.custom,
612
+ path: ["ownedObjects", i, "text"],
613
+ message: "empty after sanitisation"
614
+ });
615
+ }
616
+ });
617
+ }
618
+ });
619
+ function safeProfileLayout(value) {
620
+ if (value === void 0 || value === null) return null;
621
+ const parsed = profileLayoutSchema.safeParse(value);
622
+ return parsed.success ? parsed.data : null;
623
+ }
624
+
451
625
  // src/index.ts
452
626
  var WEB_HOSTNAME_RE = /^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/i;
453
627
  function isWebAddress(value) {
@@ -464,7 +638,7 @@ function isWebAddress(value) {
464
638
  const hostPart = value.split(/[/?#]/, 1)[0];
465
639
  return WEB_HOSTNAME_RE.test(hostPart);
466
640
  }
467
- var webAddressSchema = z.string().refine(isWebAddress, "invalidUrl").optional().or(z.literal(""));
641
+ var webAddressSchema = z2.string().refine(isWebAddress, "invalidUrl").optional().or(z2.literal(""));
468
642
  function stripUnsafeChars(s) {
469
643
  return s.replace(/[-<>"'`\\]/g, "").trim();
470
644
  }
@@ -489,8 +663,8 @@ function sanitizeText(raw, max = 50) {
489
663
  const noTags = raw.replace(/<[^>]*>/g, "").replace(/[-]/g, "");
490
664
  return noTags.trim().slice(0, max);
491
665
  }
492
- var secureUrl = z.string().max(2048).optional().or(z.literal("")).transform((v) => sanitizeHttpUrl(v));
493
- var socialLinksSchema = z.object({
666
+ var secureUrl = z2.string().max(2048).optional().or(z2.literal("")).transform((v) => sanitizeHttpUrl(v));
667
+ var socialLinksSchema = z2.object({
494
668
  facebook: secureUrl,
495
669
  linkedin: secureUrl,
496
670
  instagram: secureUrl,
@@ -498,10 +672,10 @@ var socialLinksSchema = z.object({
498
672
  youtube: secureUrl,
499
673
  tiktok: secureUrl,
500
674
  customUrl: secureUrl,
501
- customUrlLabel: z.string().max(80).optional().or(z.literal("")).transform((v) => sanitizeText(v ?? "", 50))
675
+ customUrlLabel: z2.string().max(80).optional().or(z2.literal("")).transform((v) => sanitizeText(v ?? "", 50))
502
676
  });
503
- var overrideUrl = z.string().max(2048).transform((v) => sanitizeHttpUrl(v)).optional();
504
- var socialOverridesSchema = z.object({
677
+ var overrideUrl = z2.string().max(2048).transform((v) => sanitizeHttpUrl(v)).optional();
678
+ var socialOverridesSchema = z2.object({
505
679
  facebook: overrideUrl,
506
680
  linkedin: overrideUrl,
507
681
  instagram: overrideUrl,
@@ -512,47 +686,47 @@ var socialOverridesSchema = z.object({
512
686
  youtube: overrideUrl,
513
687
  tiktok: overrideUrl,
514
688
  customUrl: overrideUrl,
515
- customUrlLabel: z.string().max(100).transform((v) => sanitizeText(v, 80)).optional()
689
+ customUrlLabel: z2.string().max(100).transform((v) => sanitizeText(v, 80)).optional()
516
690
  });
517
- var userDataSchema = z.object({
691
+ var userDataSchema = z2.object({
518
692
  // First/last name became optional — visitors can save with just an
519
693
  // email and fill names in later. Format guards still apply once
520
694
  // filled.
521
- firstName: z.string().max(100).optional().or(z.literal("")),
522
- lastName: z.string().max(100).optional().or(z.literal("")),
523
- position: z.string().max(100).optional().or(z.literal("")),
524
- email: z.string().email("invalidEmail"),
525
- phone: z.string().regex(/^[+]?[\d\s\-().]{6,20}$/, "invalidPhone").optional().or(z.literal("")),
695
+ firstName: z2.string().max(100).optional().or(z2.literal("")),
696
+ lastName: z2.string().max(100).optional().or(z2.literal("")),
697
+ position: z2.string().max(100).optional().or(z2.literal("")),
698
+ email: z2.string().email("invalidEmail"),
699
+ phone: z2.string().regex(/^[+]?[\d\s\-().]{6,20}$/, "invalidPhone").optional().or(z2.literal("")),
526
700
  // ISO 3166-1 alpha-2 country code paired with `phone`. Persisted alongside
527
701
  // the local number so the digital profile renderer (and the VCF generator)
528
702
  // can prepend the correct E.164 calling prefix, e.g. `+36 20 123 4567`.
529
703
  // Must be part of the base schema — Zod strips unknown keys in strict mode,
530
704
  // which would otherwise silently drop the ISO code on wizard submit and
531
705
  // leave every brand-new profile without a country prefix.
532
- phoneCountryCode: z.string().max(5).optional().or(z.literal("")),
533
- location: z.string().max(200).optional().or(z.literal("")),
534
- company: z.string().max(100).optional().or(z.literal("")),
706
+ phoneCountryCode: z2.string().max(5).optional().or(z2.literal("")),
707
+ location: z2.string().max(200).optional().or(z2.literal("")),
708
+ company: z2.string().max(100).optional().or(z2.literal("")),
535
709
  // Stored scheme-less (`www.foxhome.hu`); see webAddressSchema above.
536
710
  website: webAddressSchema,
537
711
  // FOXHOLE — promoted from digitalConfig.contentOverrides.bio when
538
712
  // the digital text-override layer was retired.
539
- bio: z.string().max(500).optional().or(z.literal("")),
540
- profilePhotoUrl: z.string().url().optional().or(z.literal("")),
541
- companyLogoUrl: z.string().url().optional().or(z.literal("")),
713
+ bio: z2.string().max(500).optional().or(z2.literal("")),
714
+ profilePhotoUrl: z2.string().url().optional().or(z2.literal("")),
715
+ companyLogoUrl: z2.string().url().optional().or(z2.literal("")),
542
716
  socialLinks: socialLinksSchema.optional()
543
717
  });
544
- var cardElementsSchema = z.object({
545
- showName: z.boolean(),
546
- showPosition: z.boolean(),
547
- showEmail: z.boolean(),
548
- showPhone: z.boolean(),
549
- showCompany: z.boolean(),
550
- showLogo: z.boolean(),
551
- showQr: z.boolean(),
552
- showPhoto: z.boolean()
718
+ var cardElementsSchema = z2.object({
719
+ showName: z2.boolean(),
720
+ showPosition: z2.boolean(),
721
+ showEmail: z2.boolean(),
722
+ showPhone: z2.boolean(),
723
+ showCompany: z2.boolean(),
724
+ showLogo: z2.boolean(),
725
+ showQr: z2.boolean(),
726
+ showPhoto: z2.boolean()
553
727
  });
554
- var materialSchema = z.enum(["plastic", "3dprint_standard", "metal", "bamboo"]);
555
- var comboIdSchema = z.enum([
728
+ var materialSchema = z2.enum(["plastic", "3dprint_standard", "metal", "bamboo"]);
729
+ var comboIdSchema = z2.enum([
556
730
  "A1",
557
731
  "A2",
558
732
  "B1",
@@ -564,154 +738,154 @@ var comboIdSchema = z.enum([
564
738
  "E1",
565
739
  "E2"
566
740
  ]);
567
- var contactDetailTogglesSchema = z.object({
568
- showEmail: z.boolean().optional(),
569
- showPhone: z.boolean().optional(),
570
- showLocation: z.boolean().optional()
741
+ var contactDetailTogglesSchema = z2.object({
742
+ showEmail: z2.boolean().optional(),
743
+ showPhone: z2.boolean().optional(),
744
+ showLocation: z2.boolean().optional()
571
745
  });
572
- var physicalContentOverridesSchema = z.object({
573
- displayName: z.string().max(100).optional().or(z.literal("")),
574
- company: z.string().max(100).optional().or(z.literal("")),
575
- position: z.string().max(200).optional().or(z.literal("")),
576
- email: z.string().email().optional().or(z.literal("")),
577
- phone: z.string().max(30).optional().or(z.literal("")),
746
+ var physicalContentOverridesSchema = z2.object({
747
+ displayName: z2.string().max(100).optional().or(z2.literal("")),
748
+ company: z2.string().max(100).optional().or(z2.literal("")),
749
+ position: z2.string().max(200).optional().or(z2.literal("")),
750
+ email: z2.string().email().optional().or(z2.literal("")),
751
+ phone: z2.string().max(30).optional().or(z2.literal("")),
578
752
  // Paired ISO code so the override phone can still get a `+XX ` prefix.
579
753
  // See ContentOverrides.phoneCountryCode in @nfcard/types.
580
- phoneCountryCode: z.string().max(5).optional().or(z.literal("")),
581
- location: z.string().max(200).optional().or(z.literal(""))
754
+ phoneCountryCode: z2.string().max(5).optional().or(z2.literal("")),
755
+ location: z2.string().max(200).optional().or(z2.literal(""))
582
756
  });
583
- var hexColorSchema = z.string().regex(/^#[0-9A-Fa-f]{6}$/, "invalidHexColour");
584
- var cellSidesSchema = z.number().int().refine((s) => s === 0 || s >= 3 && s <= 8, { message: "cellSidesRange" });
585
- var chipSidesSchema = z.number().int().refine((s) => s === -1 || s === 0 || s >= 3 && s <= 8, { message: "cellSidesRange" });
586
- var chipAxisSchema = z.union([z.literal(0), z.literal(1), z.literal(2)]);
587
- var cardDesignMaterialSchema = z.enum(["3dprint_standard", "metal", "bamboo"]);
588
- var patternFamilySchema = z.enum(["radial", "gradient", "wave", "spiral"]);
589
- var cardDesignPatternSchema = z.object({
757
+ var hexColorSchema = z2.string().regex(/^#[0-9A-Fa-f]{6}$/, "invalidHexColour");
758
+ var cellSidesSchema = z2.number().int().refine((s) => s === 0 || s >= 3 && s <= 8, { message: "cellSidesRange" });
759
+ var chipSidesSchema = z2.number().int().refine((s) => s === -1 || s === 0 || s >= 3 && s <= 8, { message: "cellSidesRange" });
760
+ var chipAxisSchema = z2.union([z2.literal(0), z2.literal(1), z2.literal(2)]);
761
+ var cardDesignMaterialSchema = z2.enum(["3dprint_standard", "metal", "bamboo"]);
762
+ var patternFamilySchema = z2.enum(["radial", "gradient", "wave", "spiral"]);
763
+ var cardDesignPatternSchema = z2.object({
590
764
  family: patternFamilySchema,
591
- variant: z.enum(["pulse", "bell"]),
592
- density: z.number().min(0).max(1),
593
- holeMinMm: z.number().positive(),
594
- holeMaxMm: z.number().positive(),
595
- intensity: z.number().min(0).max(1),
765
+ variant: z2.enum(["pulse", "bell"]),
766
+ density: z2.number().min(0).max(1),
767
+ holeMinMm: z2.number().positive(),
768
+ holeMaxMm: z2.number().positive(),
769
+ intensity: z2.number().min(0).max(1),
596
770
  cellShape: cellSidesSchema,
597
- cellRotationDeg: z.number(),
771
+ cellRotationDeg: z2.number(),
598
772
  chipShape: chipSidesSchema,
599
- chipRotationDeg: z.number()
773
+ chipRotationDeg: z2.number()
600
774
  }).refine((p) => p.holeMinMm <= p.holeMaxMm, { message: "holeMinGtMax", path: ["holeMinMm"] });
601
- var elementTransformSchema = z.object({
602
- xMm: z.number(),
603
- yMm: z.number(),
604
- rotationDeg: z.number(),
605
- scale: z.number().positive(),
606
- z: z.number()
775
+ var elementTransformSchema = z2.object({
776
+ xMm: z2.number(),
777
+ yMm: z2.number(),
778
+ rotationDeg: z2.number(),
779
+ scale: z2.number().positive(),
780
+ z: z2.number()
607
781
  });
608
- var elementRoleSchema = z.enum(["text", "accent"]);
782
+ var elementRoleSchema = z2.enum(["text", "accent"]);
609
783
  var baseElementFields = {
610
- id: z.string().min(1),
784
+ id: z2.string().min(1),
611
785
  transform: elementTransformSchema,
612
- face: z.enum(["front", "back"]).optional(),
613
- locked: z.boolean().optional(),
614
- colour: z.string().optional()
786
+ face: z2.enum(["front", "back"]).optional(),
787
+ locked: z2.boolean().optional(),
788
+ colour: z2.string().optional()
615
789
  };
616
- var qrModeSchema = z.enum(["redirect", "raw"]).optional();
617
- var cardElementSchema = z.discriminatedUnion("type", [
618
- z.object({ ...baseElementFields, type: z.literal("chip"), shape: chipSidesSchema }),
619
- z.object({ ...baseElementFields, type: z.literal("text"), text: z.string(), sizeMm: z.number().positive(), weight: z.number(), role: elementRoleSchema, fontKey: z.string().optional(), italic: z.boolean().optional(), underline: z.boolean().optional() }),
620
- z.object({ ...baseElementFields, type: z.literal("qr"), url: z.string().url("invalidUrl").or(z.literal("")).optional(), mode: qrModeSchema, sizeMm: z.number().positive(), frameRole: elementRoleSchema, moduleRole: elementRoleSchema }),
621
- z.object({ ...baseElementFields, type: z.literal("image"), assetRef: z.string().min(1), widthMm: z.number().positive(), heightMm: z.number().positive(), role: elementRoleSchema, bgRemoved: z.boolean().optional(), svgOverrides: z.array(z.string().optional()).optional() }),
622
- z.object({ ...baseElementFields, type: z.literal("logo"), assetRef: z.string().min(1), widthMm: z.number().positive(), heightMm: z.number().positive(), role: elementRoleSchema })
790
+ var qrModeSchema = z2.enum(["redirect", "raw"]).optional();
791
+ var cardElementSchema = z2.discriminatedUnion("type", [
792
+ z2.object({ ...baseElementFields, type: z2.literal("chip"), shape: chipSidesSchema }),
793
+ z2.object({ ...baseElementFields, type: z2.literal("text"), text: z2.string(), sizeMm: z2.number().positive(), weight: z2.number(), role: elementRoleSchema, fontKey: z2.string().optional(), italic: z2.boolean().optional(), underline: z2.boolean().optional() }),
794
+ z2.object({ ...baseElementFields, type: z2.literal("qr"), url: z2.string().url("invalidUrl").or(z2.literal("")).optional(), mode: qrModeSchema, sizeMm: z2.number().positive(), frameRole: elementRoleSchema, moduleRole: elementRoleSchema }),
795
+ z2.object({ ...baseElementFields, type: z2.literal("image"), assetRef: z2.string().min(1), widthMm: z2.number().positive(), heightMm: z2.number().positive(), role: elementRoleSchema, bgRemoved: z2.boolean().optional(), svgOverrides: z2.array(z2.string().optional()).optional() }),
796
+ z2.object({ ...baseElementFields, type: z2.literal("logo"), assetRef: z2.string().min(1), widthMm: z2.number().positive(), heightMm: z2.number().positive(), role: elementRoleSchema })
623
797
  ]);
624
- var cardAssetSchema = z.object({
625
- id: z.string().min(1),
626
- kind: z.enum(["vector", "raster"]),
627
- svg: z.string().optional(),
628
- rasterDataUrl: z.string().optional(),
629
- rasterBgDataUrl: z.string().optional(),
630
- rasterAspect: z.number().optional(),
631
- svgColours: z.array(z.string()).optional(),
632
- svgFils: z.array(z.string()).optional()
798
+ var cardAssetSchema = z2.object({
799
+ id: z2.string().min(1),
800
+ kind: z2.enum(["vector", "raster"]),
801
+ svg: z2.string().optional(),
802
+ rasterDataUrl: z2.string().optional(),
803
+ rasterBgDataUrl: z2.string().optional(),
804
+ rasterAspect: z2.number().optional(),
805
+ svgColours: z2.array(z2.string()).optional(),
806
+ svgFils: z2.array(z2.string()).optional()
633
807
  });
634
- var cardDesignSchema = z.object({
808
+ var cardDesignSchema = z2.object({
635
809
  material: cardDesignMaterialSchema,
636
- designType: z.enum(["template", "custom"]),
637
- templateId: z.string().optional(),
638
- thicknessMm: z.number().positive(),
810
+ designType: z2.enum(["template", "custom"]),
811
+ templateId: z2.string().optional(),
812
+ thicknessMm: z2.number().positive(),
639
813
  pattern: cardDesignPatternSchema.nullable(),
640
- layout: z.object({ chipAnchor: z.object({ col: chipAxisSchema, row: chipAxisSchema }) }),
814
+ layout: z2.object({ chipAnchor: z2.object({ col: chipAxisSchema, row: chipAxisSchema }) }),
641
815
  // Exactly three role-keyed bodies (≤3 distinct is therefore guaranteed —
642
816
  // cardgen's max-3-print-bodies rule).
643
- filaments: z.object({ background: hexColorSchema, text: hexColorSchema, accent: hexColorSchema }),
644
- content: z.object({
645
- lines: z.array(
646
- z.object({
647
- text: z.string(),
648
- sizeMm: z.number().positive(),
649
- weight: z.number(),
650
- role: z.enum(["text", "accent"])
817
+ filaments: z2.object({ background: hexColorSchema, text: hexColorSchema, accent: hexColorSchema }),
818
+ content: z2.object({
819
+ lines: z2.array(
820
+ z2.object({
821
+ text: z2.string(),
822
+ sizeMm: z2.number().positive(),
823
+ weight: z2.number(),
824
+ role: z2.enum(["text", "accent"])
651
825
  })
652
826
  )
653
827
  }).optional(),
654
828
  // NFCARD-561 -- `mode` makes "point to profile" DATA, not a string match; when it is
655
829
  // 'redirect' the url is absent and the API substitutes the card's own /r/{code}?src=qr.
656
830
  // Optional throughout: prod holds persisted designs with neither field.
657
- qr: z.object({ enabled: z.boolean(), url: z.string().url("invalidUrl").or(z.literal("")).optional(), mode: qrModeSchema }).optional(),
658
- logo: z.object({ svg: z.string().optional(), svgPath: z.string().optional() }).optional(),
659
- wordmark: z.boolean().optional(),
660
- contentFace: z.enum(["top", "bottom"]).optional(),
831
+ qr: z2.object({ enabled: z2.boolean(), url: z2.string().url("invalidUrl").or(z2.literal("")).optional(), mode: qrModeSchema }).optional(),
832
+ logo: z2.object({ svg: z2.string().optional(), svgPath: z2.string().optional() }).optional(),
833
+ wordmark: z2.boolean().optional(),
834
+ contentFace: z2.enum(["top", "bottom"]).optional(),
661
835
  // NFCARD-197/198 -- additive free-transform layer (optional; absent = legacy grid).
662
- elements: z.array(cardElementSchema).optional(),
663
- assets: z.array(cardAssetSchema).optional(),
664
- contractVersion: z.number().int().positive().optional()
836
+ elements: z2.array(cardElementSchema).optional(),
837
+ assets: z2.array(cardAssetSchema).optional(),
838
+ contractVersion: z2.number().int().positive().optional()
665
839
  }).superRefine((cd, ctx) => {
666
840
  const els = cd.elements;
667
841
  if (!els || els.length === 0) return;
668
842
  if (cd.designType !== "custom") {
669
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "elementsCustomOnly", path: ["elements"] });
843
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: "elementsCustomOnly", path: ["elements"] });
670
844
  }
671
845
  if (cd.contractVersion !== 2) {
672
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "contractVersionRequired", path: ["contractVersion"] });
846
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: "contractVersionRequired", path: ["contractVersion"] });
673
847
  }
674
848
  const seen = /* @__PURE__ */ new Set();
675
849
  for (const el of els) {
676
850
  if (seen.has(el.id)) {
677
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "duplicateElementId", path: ["elements"] });
851
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: "duplicateElementId", path: ["elements"] });
678
852
  }
679
853
  seen.add(el.id);
680
854
  }
681
855
  const chips = els.filter((e) => e.type === "chip");
682
856
  if (chips.length !== 1) {
683
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "exactlyOneChip", path: ["elements"] });
857
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: "exactlyOneChip", path: ["elements"] });
684
858
  }
685
859
  if (chips.length === 1) {
686
860
  const chip = chips[0];
687
861
  const near = mmToNearestChipAnchor({ x: chip.transform.xMm, y: chip.transform.yMm });
688
862
  if (near.col !== cd.layout.chipAnchor.col || near.row !== cd.layout.chipAnchor.row) {
689
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "chipAnchorDualWriteMismatch", path: ["layout", "chipAnchor"] });
863
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: "chipAnchorDualWriteMismatch", path: ["layout", "chipAnchor"] });
690
864
  }
691
865
  }
692
866
  const assetsById = new Map((cd.assets ?? []).map((a) => [a.id, a]));
693
867
  for (const el of els) {
694
868
  for (const viol of elementPrintability(el, { assetsById })) {
695
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: viol.code, path: ["elements"] });
869
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: viol.code, path: ["elements"] });
696
870
  }
697
871
  }
698
872
  });
699
- var physicalConfigSchema = z.object({
873
+ var physicalConfigSchema = z2.object({
700
874
  material: materialSchema,
701
- colour: z.string().regex(/^#[0-9A-Fa-f]{6}$/, "invalidHexColour").optional(),
875
+ colour: z2.string().regex(/^#[0-9A-Fa-f]{6}$/, "invalidHexColour").optional(),
702
876
  comboId: comboIdSchema,
703
877
  frontElements: cardElementsSchema,
704
878
  backElements: cardElementsSchema,
705
- qrTargetUrl: z.string().url("invalidUrl").optional().or(z.literal("")),
706
- customDesignRequest: z.string().max(2e3).optional().or(z.literal("")),
879
+ qrTargetUrl: z2.string().url("invalidUrl").optional().or(z2.literal("")),
880
+ customDesignRequest: z2.string().max(2e3).optional().or(z2.literal("")),
707
881
  frontContactToggles: contactDetailTogglesSchema.optional(),
708
882
  backContactToggles: contactDetailTogglesSchema.optional(),
709
883
  contentOverrides: physicalContentOverridesSchema.optional(),
710
- companyLogoUrl: z.string().url().optional().or(z.literal("")),
884
+ companyLogoUrl: z2.string().url().optional().or(z2.literal("")),
711
885
  // NFCARD PVC 2D pipeline: free-transform elements + image assets ride
712
886
  // top-level (PVC has no cardDesign). Whitelisted so they aren't stripped.
713
- elements: z.array(cardElementSchema).optional(),
714
- assets: z.array(cardAssetSchema).optional(),
887
+ elements: z2.array(cardElementSchema).optional(),
888
+ assets: z2.array(cardAssetSchema).optional(),
715
889
  // Nested so it isn't stripped (this object has no .passthrough()). Optional
716
890
  // → legacy configs + the PVC path persist with no cardDesign block.
717
891
  cardDesign: cardDesignSchema.optional()
@@ -725,27 +899,27 @@ var physicalConfigSchema = z.object({
725
899
  message: "cardDesignMaterialMismatch",
726
900
  path: ["cardDesign", "material"]
727
901
  });
728
- var digitalVisibilitySchema = z.object({
729
- showName: z.boolean().optional(),
730
- showPosition: z.boolean().optional(),
731
- showCompany: z.boolean().optional(),
732
- showEmail: z.boolean().optional(),
733
- showPhone: z.boolean().optional(),
734
- showWebsite: z.boolean().optional(),
735
- showLocation: z.boolean().optional(),
736
- showBio: z.boolean().optional(),
737
- showSocialLinks: z.boolean().optional(),
738
- showLogo: z.boolean().optional()
902
+ var digitalVisibilitySchema = z2.object({
903
+ showName: z2.boolean().optional(),
904
+ showPosition: z2.boolean().optional(),
905
+ showCompany: z2.boolean().optional(),
906
+ showEmail: z2.boolean().optional(),
907
+ showPhone: z2.boolean().optional(),
908
+ showWebsite: z2.boolean().optional(),
909
+ showLocation: z2.boolean().optional(),
910
+ showBio: z2.boolean().optional(),
911
+ showSocialLinks: z2.boolean().optional(),
912
+ showLogo: z2.boolean().optional()
739
913
  });
740
914
  var MAX_CTA_BUTTONS = 5;
741
- var ctaButtonSchema = z.object({
742
- label: z.string().max(2048).optional().or(z.literal("")).transform((v) => sanitizeText(v ?? "", 40)),
743
- url: z.string().max(2048).optional().or(z.literal("")).transform((v) => sanitizeHttpUrl(v))
915
+ var ctaButtonSchema = z2.object({
916
+ label: z2.string().max(2048).optional().or(z2.literal("")).transform((v) => sanitizeText(v ?? "", 40)),
917
+ url: z2.string().max(2048).optional().or(z2.literal("")).transform((v) => sanitizeHttpUrl(v))
744
918
  });
745
- var ctaButtonsSchema = z.array(ctaButtonSchema).max(50).transform(
919
+ var ctaButtonsSchema = z2.array(ctaButtonSchema).max(50).transform(
746
920
  (arr) => arr.filter((b) => b.url !== "" && b.label !== "").slice(0, MAX_CTA_BUTTONS)
747
921
  );
748
- var tipJarSchema = z.unknown().transform((raw) => {
922
+ var tipJarSchema = z2.unknown().transform((raw) => {
749
923
  if (!raw || typeof raw !== "object") return void 0;
750
924
  const rec = raw;
751
925
  if (!isTipProviderId(rec.provider)) return void 0;
@@ -754,7 +928,7 @@ var tipJarSchema = z.unknown().transform((raw) => {
754
928
  const label = typeof rec.label === "string" ? sanitizeText(rec.label, 40) : "";
755
929
  return label ? { provider: rec.provider, handle, label } : { provider: rec.provider, handle };
756
930
  });
757
- var bookingSchema = z.unknown().transform((raw) => {
931
+ var bookingSchema = z2.unknown().transform((raw) => {
758
932
  if (!raw || typeof raw !== "object") return void 0;
759
933
  const rec = raw;
760
934
  if (!isBookingProviderId(rec.provider)) return void 0;
@@ -763,7 +937,7 @@ var bookingSchema = z.unknown().transform((raw) => {
763
937
  const label = typeof rec.label === "string" ? sanitizeText(rec.label, 40) : "";
764
938
  return label ? { provider: rec.provider, handle, label } : { provider: rec.provider, handle };
765
939
  });
766
- var profileBackgroundSchema = z.unknown().transform((raw) => {
940
+ var profileBackgroundSchema = z2.unknown().transform((raw) => {
767
941
  if (!raw || typeof raw !== "object") return void 0;
768
942
  const rec = raw;
769
943
  if (rec.kind === "solid") return { kind: "solid" };
@@ -775,15 +949,15 @@ var profileBackgroundSchema = z.unknown().transform((raw) => {
775
949
  const angleDeg = typeof rec.angleDeg === "number" && Number.isFinite(rec.angleDeg) ? (Math.round(rec.angleDeg) % 360 + 360) % 360 : void 0;
776
950
  return angleDeg === void 0 ? { kind: "gradient", from, to } : { kind: "gradient", from, to, angleDeg };
777
951
  });
778
- var digitalConfigSchema = z.object({
779
- templateId: z.enum(["minimal", "bold", "card", "aurora", "classic", "spotlight", "marquee", "riso"]),
780
- accentColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/),
781
- primaryColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
782
- secondaryColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
783
- textColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
784
- fontKey: z.string().min(1),
785
- showAvatar: z.boolean(),
786
- avatarMode: z.enum(["photo", "monogram", "none"]).optional(),
952
+ var digitalConfigSchema = z2.object({
953
+ templateId: z2.enum(["minimal", "bold", "card", "aurora", "classic", "spotlight", "marquee", "riso"]),
954
+ accentColor: z2.string().regex(/^#[0-9A-Fa-f]{6}$/),
955
+ primaryColor: z2.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
956
+ secondaryColor: z2.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
957
+ textColor: z2.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
958
+ fontKey: z2.string().min(1),
959
+ showAvatar: z2.boolean(),
960
+ avatarMode: z2.enum(["photo", "monogram", "none"]).optional(),
787
961
  visibility: digitalVisibilitySchema.optional(),
788
962
  // FOXHOLE — `contentOverrides` was removed when the wizard started
789
963
  // editing userData directly inside the digital step. The bio that
@@ -798,131 +972,136 @@ var digitalConfigSchema = z.object({
798
972
  booking: bookingSchema.optional(),
799
973
  // NFCARD-641 — absent means solid `secondaryColor`, so every profile that
800
974
  // exists today validates unchanged and no backfill is needed.
801
- background: profileBackgroundSchema.optional()
975
+ background: profileBackgroundSchema.optional(),
976
+ // NFCARD-656 — arrangement + per-object style intent. Absent for every
977
+ // profile that predates the object model; absence and { v: 1 } paint the
978
+ // same page (deriveProfileLayout), so no backfill and nothing existing
979
+ // revalidates differently.
980
+ layout: profileLayoutSchema.optional()
802
981
  });
803
- var entrySourceSchema = z.object({
804
- output: z.enum(["with-card", "digital-only"]).optional(),
805
- entryFlow: z.enum(["order", "try"]).optional(),
806
- ref: z.string().max(64).optional(),
807
- path: z.string().max(256).optional(),
808
- referrer: z.string().max(256).optional(),
982
+ var entrySourceSchema = z2.object({
983
+ output: z2.enum(["with-card", "digital-only"]).optional(),
984
+ entryFlow: z2.enum(["order", "try"]).optional(),
985
+ ref: z2.string().max(64).optional(),
986
+ path: z2.string().max(256).optional(),
987
+ referrer: z2.string().max(256).optional(),
809
988
  // NFCARD-393 - the design deep-link preselects (material implies the
810
989
  // with-card entry; template additionally names a TEMPLATE_DESIGNS id).
811
990
  material: materialSchema.optional(),
812
- template: z.string().max(64).optional(),
813
- utm: z.record(z.string().max(64), z.string().max(256)).refine((o) => Object.keys(o).length <= 10, "too many utm params").optional()
991
+ template: z2.string().max(64).optional(),
992
+ utm: z2.record(z2.string().max(64), z2.string().max(256)).refine((o) => Object.keys(o).length <= 10, "too many utm params").optional()
814
993
  }).strict();
815
- var configurationCreateSchema = z.object({
816
- sessionId: z.string().uuid(),
994
+ var configurationCreateSchema = z2.object({
995
+ sessionId: z2.string().uuid(),
817
996
  userData: userDataSchema,
818
997
  physicalConfig: physicalConfigSchema,
819
998
  digitalConfig: digitalConfigSchema,
820
- quantity: z.number().int().min(1).max(1e3).default(1),
999
+ quantity: z2.number().int().min(1).max(1e3).default(1),
821
1000
  // FOXHOLE-684: free→card upgrade. When set, fulfilment attaches this
822
1001
  // existing (free-tier) profile to the new card instead of creating a
823
1002
  // fresh default one. Optional — the normal order flow omits it.
824
- existingProfileId: z.string().uuid().optional(),
1003
+ existingProfileId: z2.string().uuid().optional(),
825
1004
  // NFCARD-3: on the Template design path, the chosen TEMPLATE_DESIGNS id
826
1005
  // (a slug, e.g. "tpl-pvc-white"). Carries the explicit template reference
827
1006
  // for fulfilment alongside the comboId already baked into physicalConfig.
828
1007
  // Optional — Custom orders omit it.
829
- physicalTemplateId: z.string().min(1).max(64).optional(),
1008
+ physicalTemplateId: z2.string().min(1).max(64).optional(),
830
1009
  // NFCARD-392 - optional entry-attribution blob (see entrySourceSchema).
831
1010
  // Create-only by design: first touch wins, so the update schema does
832
1011
  // not accept it.
833
1012
  entrySource: entrySourceSchema.optional()
834
1013
  });
835
- var configurationUpdateSchema = z.object({
1014
+ var configurationUpdateSchema = z2.object({
836
1015
  userData: userDataSchema.optional(),
837
1016
  physicalConfig: physicalConfigSchema.optional(),
838
1017
  digitalConfig: digitalConfigSchema.optional(),
839
- quantity: z.number().int().min(1).max(1e3).optional(),
840
- existingProfileId: z.string().uuid().optional(),
841
- physicalTemplateId: z.string().min(1).max(64).optional()
1018
+ quantity: z2.number().int().min(1).max(1e3).optional(),
1019
+ existingProfileId: z2.string().uuid().optional(),
1020
+ physicalTemplateId: z2.string().min(1).max(64).optional()
842
1021
  });
843
- var createOrderSchema = z.object({
844
- configurationId: z.string().uuid(),
845
- discountCode: z.string().min(1).max(50).optional(),
1022
+ var createOrderSchema = z2.object({
1023
+ configurationId: z2.string().uuid(),
1024
+ discountCode: z2.string().min(1).max(50).optional(),
846
1025
  // FOXHOLE-714 — Template or Custom design path. Picks which side
847
1026
  // of the Price.templatePriceCents / customPriceCents split applies
848
1027
  // and which Stripe Price ID the Checkout line item references.
849
- designType: z.enum(["template", "custom"])
1028
+ designType: z2.enum(["template", "custom"])
850
1029
  });
851
- var cardUpdateSchema = z.object({
1030
+ var cardUpdateSchema = z2.object({
852
1031
  digitalConfigOverride: digitalConfigSchema.partial().extend({
853
- profilePhotoUrl: z.string().url().optional().or(z.literal("")),
854
- companyLogoUrl: z.string().url().optional().or(z.literal(""))
1032
+ profilePhotoUrl: z2.string().url().optional().or(z2.literal("")),
1033
+ companyLogoUrl: z2.string().url().optional().or(z2.literal(""))
855
1034
  }).partial().optional(),
856
- status: z.enum(["active", "suspended", "archived"]).optional()
1035
+ status: z2.enum(["active", "suspended", "archived"]).optional()
857
1036
  });
858
- var adminCardCreateSchema = z.object({
859
- targetUserId: z.string().min(1),
1037
+ var adminCardCreateSchema = z2.object({
1038
+ targetUserId: z2.string().min(1),
860
1039
  userData: userDataSchema,
861
1040
  digitalConfig: digitalConfigSchema,
862
1041
  physicalConfig: physicalConfigSchema.optional()
863
1042
  });
864
- var profileVisibilitySchema = z.enum(["public", "private", "link_only"]);
1043
+ var profileVisibilitySchema = z2.enum(["public", "private", "link_only"]);
865
1044
  var profileUserDataSchema = userDataSchema.extend({
866
- email: z.string().email("invalidEmail").optional().or(z.literal(""))
1045
+ email: z2.string().email("invalidEmail").optional().or(z2.literal(""))
867
1046
  });
868
- var profileCreateSchema = z.object({
1047
+ var profileCreateSchema = z2.object({
869
1048
  // Optional (FOXHOLE-684). When present, the card gets a CardProfile slot
870
1049
  // pointing to the new profile (subject to the 3-slot cap). When absent,
871
1050
  // a standalone (cardless) free-tier profile is created — used by the
872
1051
  // free signup path; no slot is allocated.
873
- cardId: z.string().uuid("required").optional(),
1052
+ cardId: z2.string().uuid("required").optional(),
874
1053
  // Optional (NFCARD-128): a free profile can be created label-less — the owner
875
1054
  // sets the label later in the editor (label/icon are paid-only). Empty/absent
876
1055
  // is stored as null, so the hub tile falls back to the profile's displayName.
877
- label: z.string().max(50).optional().or(z.literal("")),
878
- icon: z.string().max(10).optional().or(z.literal("")),
1056
+ label: z2.string().max(50).optional().or(z2.literal("")),
1057
+ icon: z2.string().max(10).optional().or(z2.literal("")),
879
1058
  userData: profileUserDataSchema,
880
1059
  digitalConfig: digitalConfigSchema,
881
1060
  visibility: profileVisibilitySchema.default("public"),
882
- accessPin: z.string().min(4, "pinTooShort").max(8, "pinTooLong").regex(/^\d+$/, "pinDigitsOnly").optional().or(z.literal(""))
1061
+ accessPin: z2.string().min(4, "pinTooShort").max(8, "pinTooLong").regex(/^\d+$/, "pinDigitsOnly").optional().or(z2.literal(""))
883
1062
  });
884
- var signupWithProfileSchema = z.object({
885
- email: z.string().email("invalidEmail"),
886
- password: z.string().min(8, "passwordTooShort").max(128, "passwordTooLong"),
887
- name: z.string().min(1).max(120).optional().or(z.literal("")),
1063
+ var signupWithProfileSchema = z2.object({
1064
+ email: z2.string().email("invalidEmail"),
1065
+ password: z2.string().min(8, "passwordTooShort").max(128, "passwordTooLong"),
1066
+ name: z2.string().min(1).max(120).optional().or(z2.literal("")),
888
1067
  // Must be explicitly true — the ÁSZF checkbox is mandatory.
889
- acceptedTerms: z.literal(true),
890
- marketingOptIn: z.boolean().optional().default(false),
891
- captchaToken: z.string().max(4096).optional().or(z.literal("")),
892
- preferredLanguage: z.enum(["hu", "en"]).optional().default("hu"),
893
- label: z.string().min(1, "required").max(50).optional().or(z.literal("")),
1068
+ acceptedTerms: z2.literal(true),
1069
+ marketingOptIn: z2.boolean().optional().default(false),
1070
+ captchaToken: z2.string().max(4096).optional().or(z2.literal("")),
1071
+ preferredLanguage: z2.enum(["hu", "en"]).optional().default("hu"),
1072
+ label: z2.string().min(1, "required").max(50).optional().or(z2.literal("")),
894
1073
  userData: profileUserDataSchema,
895
1074
  digitalConfig: digitalConfigSchema
896
1075
  });
897
- var profileUpdateSchema = z.object({
898
- label: z.string().min(1).max(50).optional(),
899
- icon: z.string().max(10).optional().or(z.literal("")),
1076
+ var profileUpdateSchema = z2.object({
1077
+ label: z2.string().min(1).max(50).optional(),
1078
+ icon: z2.string().max(10).optional().or(z2.literal("")),
900
1079
  userData: profileUserDataSchema.optional(),
901
1080
  digitalConfig: digitalConfigSchema.partial().extend({
902
- profilePhotoUrl: z.string().url().optional().or(z.literal("")),
903
- companyLogoUrl: z.string().url().optional().or(z.literal(""))
1081
+ profilePhotoUrl: z2.string().url().optional().or(z2.literal("")),
1082
+ companyLogoUrl: z2.string().url().optional().or(z2.literal(""))
904
1083
  }).partial().optional(),
905
1084
  visibility: profileVisibilitySchema.optional(),
906
- accessPin: z.string().min(4, "pinTooShort").max(8, "pinTooLong").regex(/^\d+$/, "pinDigitsOnly").optional().or(z.literal(""))
1085
+ accessPin: z2.string().min(4, "pinTooShort").max(8, "pinTooLong").regex(/^\d+$/, "pinDigitsOnly").optional().or(z2.literal(""))
907
1086
  });
908
- var profilePinVerifySchema = z.object({
909
- pin: z.string().min(4).max(8)
1087
+ var profilePinVerifySchema = z2.object({
1088
+ pin: z2.string().min(4).max(8)
910
1089
  });
911
- var profileAccessTokenCreateSchema = z.object({
912
- label: z.string().max(100).optional().or(z.literal("")),
913
- expiresAt: z.string().datetime().optional().or(z.literal(""))
1090
+ var profileAccessTokenCreateSchema = z2.object({
1091
+ label: z2.string().max(100).optional().or(z2.literal("")),
1092
+ expiresAt: z2.string().datetime().optional().or(z2.literal(""))
914
1093
  });
915
- var cardProfileUpdateSchema = z.object({
916
- profiles: z.array(
917
- z.object({
918
- profileId: z.string().uuid(),
919
- sortOrder: z.number().int().min(0),
920
- isDefault: z.boolean()
1094
+ var cardProfileUpdateSchema = z2.object({
1095
+ profiles: z2.array(
1096
+ z2.object({
1097
+ profileId: z2.string().uuid(),
1098
+ sortOrder: z2.number().int().min(0),
1099
+ isDefault: z2.boolean()
921
1100
  })
922
1101
  )
923
1102
  });
924
- var hexColor = z.string().regex(/^#[0-9A-Fa-f]{6}$/, "invalidHexColour");
925
- var httpsOnlyUrl = z.string().url("invalidUrl").refine(
1103
+ var hexColor = z2.string().regex(/^#[0-9A-Fa-f]{6}$/, "invalidHexColour");
1104
+ var httpsOnlyUrl = z2.string().url("invalidUrl").refine(
926
1105
  (v) => {
927
1106
  try {
928
1107
  const u = new URL(v);
@@ -933,35 +1112,35 @@ var httpsOnlyUrl = z.string().url("invalidUrl").refine(
933
1112
  },
934
1113
  { message: "invalidUrl" }
935
1114
  );
936
- var hubConfigSchema = z.object({
937
- displayName: z.string().max(80).optional().or(z.literal("")),
938
- subtitle: z.string().max(120).optional().or(z.literal("")),
939
- avatarMode: z.enum(["auto", "image", "monogram"]).optional(),
940
- avatarUrl: httpsOnlyUrl.optional().or(z.literal("")),
941
- monogramText: z.string().max(2).optional().or(z.literal("")),
1115
+ var hubConfigSchema = z2.object({
1116
+ displayName: z2.string().max(80).optional().or(z2.literal("")),
1117
+ subtitle: z2.string().max(120).optional().or(z2.literal("")),
1118
+ avatarMode: z2.enum(["auto", "image", "monogram"]).optional(),
1119
+ avatarUrl: httpsOnlyUrl.optional().or(z2.literal("")),
1120
+ monogramText: z2.string().max(2).optional().or(z2.literal("")),
942
1121
  accentColor: hexColor.optional(),
943
1122
  backgroundColor: hexColor.optional(),
944
1123
  textColor: hexColor.optional(),
945
1124
  tileBackgroundColor: hexColor.optional(),
946
1125
  tileBorderColor: hexColor.optional(),
947
- fontKey: z.string().min(1).max(50).optional(),
948
- footerText: z.string().max(140).optional().or(z.literal(""))
1126
+ fontKey: z2.string().min(1).max(50).optional(),
1127
+ footerText: z2.string().max(140).optional().or(z2.literal(""))
949
1128
  });
950
- var hubProfilesUpdateSchema = z.object({
951
- profiles: z.array(
952
- z.object({
953
- profileId: z.string().uuid(),
954
- sortOrder: z.number().int().min(0),
955
- isDefault: z.boolean().optional(),
956
- hiddenFromHub: z.boolean().optional()
1129
+ var hubProfilesUpdateSchema = z2.object({
1130
+ profiles: z2.array(
1131
+ z2.object({
1132
+ profileId: z2.string().uuid(),
1133
+ sortOrder: z2.number().int().min(0),
1134
+ isDefault: z2.boolean().optional(),
1135
+ hiddenFromHub: z2.boolean().optional()
957
1136
  })
958
1137
  )
959
1138
  });
960
- var contactExchangeSchema = z.object({
961
- name: z.string().min(1, "required").max(100),
962
- email: z.string().email("invalidEmail").optional().or(z.literal("")),
963
- phone: z.string().regex(/^[+]?[\d\s\-().]{6,20}$/, "invalidPhone").optional().or(z.literal("")),
964
- notes: z.string().max(500).optional().or(z.literal(""))
1139
+ var contactExchangeSchema = z2.object({
1140
+ name: z2.string().min(1, "required").max(100),
1141
+ email: z2.string().email("invalidEmail").optional().or(z2.literal("")),
1142
+ phone: z2.string().regex(/^[+]?[\d\s\-().]{6,20}$/, "invalidPhone").optional().or(z2.literal("")),
1143
+ notes: z2.string().max(500).optional().or(z2.literal(""))
965
1144
  });
966
1145
  function formatZodErrors(error) {
967
1146
  const errors = {};
@@ -1011,6 +1190,7 @@ export {
1011
1190
  TEXT_MIN_EM_MM,
1012
1191
  adminCardCreateSchema,
1013
1192
  bookingSchema,
1193
+ buttonObjectStyleSchema,
1014
1194
  cardAssetSchema,
1015
1195
  cardDesignMaterialSchema,
1016
1196
  cardDesignPatternSchema,
@@ -1038,6 +1218,7 @@ export {
1038
1218
  footprintOf,
1039
1219
  hubConfigSchema,
1040
1220
  hubProfilesUpdateSchema,
1221
+ imageObjectStyleSchema,
1041
1222
  isFiniteTransform,
1042
1223
  materialSchema,
1043
1224
  mmToNearestChipAnchor,
@@ -1048,17 +1229,20 @@ export {
1048
1229
  profileAccessTokenCreateSchema,
1049
1230
  profileBackgroundSchema,
1050
1231
  profileCreateSchema,
1232
+ profileLayoutSchema,
1051
1233
  profilePinVerifySchema,
1052
1234
  profileUpdateSchema,
1053
1235
  profileUserDataSchema,
1054
1236
  profileVisibilitySchema,
1055
1237
  qrModeSchema,
1238
+ safeProfileLayout,
1056
1239
  sanitizeHttpUrl,
1057
1240
  sanitizeText,
1058
1241
  signupWithProfileSchema,
1059
1242
  socialLinksSchema,
1060
1243
  socialOverridesSchema,
1061
1244
  textAdvanceEm,
1245
+ textObjectStyleSchema,
1062
1246
  tipJarSchema,
1063
1247
  userDataSchema,
1064
1248
  validateConfiguration,