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