@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.d.ts +1802 -2
- package/dist/index.js +411 -227
- package/package.json +2 -2
- package/src/index.ts +0 -0
- package/src/profileLayout.test.ts +158 -0
- package/src/profileLayout.ts +265 -0
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 =
|
|
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 =
|
|
493
|
-
var socialLinksSchema =
|
|
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:
|
|
675
|
+
customUrlLabel: z2.string().max(80).optional().or(z2.literal("")).transform((v) => sanitizeText(v ?? "", 50))
|
|
502
676
|
});
|
|
503
|
-
var overrideUrl =
|
|
504
|
-
var socialOverridesSchema =
|
|
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:
|
|
689
|
+
customUrlLabel: z2.string().max(100).transform((v) => sanitizeText(v, 80)).optional()
|
|
516
690
|
});
|
|
517
|
-
var userDataSchema =
|
|
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:
|
|
522
|
-
lastName:
|
|
523
|
-
position:
|
|
524
|
-
email:
|
|
525
|
-
phone:
|
|
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:
|
|
533
|
-
location:
|
|
534
|
-
company:
|
|
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:
|
|
540
|
-
profilePhotoUrl:
|
|
541
|
-
companyLogoUrl:
|
|
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 =
|
|
545
|
-
showName:
|
|
546
|
-
showPosition:
|
|
547
|
-
showEmail:
|
|
548
|
-
showPhone:
|
|
549
|
-
showCompany:
|
|
550
|
-
showLogo:
|
|
551
|
-
showQr:
|
|
552
|
-
showPhoto:
|
|
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 =
|
|
555
|
-
var comboIdSchema =
|
|
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 =
|
|
568
|
-
showEmail:
|
|
569
|
-
showPhone:
|
|
570
|
-
showLocation:
|
|
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 =
|
|
573
|
-
displayName:
|
|
574
|
-
company:
|
|
575
|
-
position:
|
|
576
|
-
email:
|
|
577
|
-
phone:
|
|
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:
|
|
581
|
-
location:
|
|
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 =
|
|
584
|
-
var cellSidesSchema =
|
|
585
|
-
var chipSidesSchema =
|
|
586
|
-
var chipAxisSchema =
|
|
587
|
-
var cardDesignMaterialSchema =
|
|
588
|
-
var patternFamilySchema =
|
|
589
|
-
var cardDesignPatternSchema =
|
|
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:
|
|
592
|
-
density:
|
|
593
|
-
holeMinMm:
|
|
594
|
-
holeMaxMm:
|
|
595
|
-
intensity:
|
|
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:
|
|
771
|
+
cellRotationDeg: z2.number(),
|
|
598
772
|
chipShape: chipSidesSchema,
|
|
599
|
-
chipRotationDeg:
|
|
773
|
+
chipRotationDeg: z2.number()
|
|
600
774
|
}).refine((p) => p.holeMinMm <= p.holeMaxMm, { message: "holeMinGtMax", path: ["holeMinMm"] });
|
|
601
|
-
var elementTransformSchema =
|
|
602
|
-
xMm:
|
|
603
|
-
yMm:
|
|
604
|
-
rotationDeg:
|
|
605
|
-
scale:
|
|
606
|
-
z:
|
|
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 =
|
|
782
|
+
var elementRoleSchema = z2.enum(["text", "accent"]);
|
|
609
783
|
var baseElementFields = {
|
|
610
|
-
id:
|
|
784
|
+
id: z2.string().min(1),
|
|
611
785
|
transform: elementTransformSchema,
|
|
612
|
-
face:
|
|
613
|
-
locked:
|
|
614
|
-
colour:
|
|
786
|
+
face: z2.enum(["front", "back"]).optional(),
|
|
787
|
+
locked: z2.boolean().optional(),
|
|
788
|
+
colour: z2.string().optional()
|
|
615
789
|
};
|
|
616
|
-
var qrModeSchema =
|
|
617
|
-
var cardElementSchema =
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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 =
|
|
625
|
-
id:
|
|
626
|
-
kind:
|
|
627
|
-
svg:
|
|
628
|
-
rasterDataUrl:
|
|
629
|
-
rasterBgDataUrl:
|
|
630
|
-
rasterAspect:
|
|
631
|
-
svgColours:
|
|
632
|
-
svgFils:
|
|
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 =
|
|
808
|
+
var cardDesignSchema = z2.object({
|
|
635
809
|
material: cardDesignMaterialSchema,
|
|
636
|
-
designType:
|
|
637
|
-
templateId:
|
|
638
|
-
thicknessMm:
|
|
810
|
+
designType: z2.enum(["template", "custom"]),
|
|
811
|
+
templateId: z2.string().optional(),
|
|
812
|
+
thicknessMm: z2.number().positive(),
|
|
639
813
|
pattern: cardDesignPatternSchema.nullable(),
|
|
640
|
-
layout:
|
|
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:
|
|
644
|
-
content:
|
|
645
|
-
lines:
|
|
646
|
-
|
|
647
|
-
text:
|
|
648
|
-
sizeMm:
|
|
649
|
-
weight:
|
|
650
|
-
role:
|
|
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:
|
|
658
|
-
logo:
|
|
659
|
-
wordmark:
|
|
660
|
-
contentFace:
|
|
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:
|
|
663
|
-
assets:
|
|
664
|
-
contractVersion:
|
|
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:
|
|
843
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, message: "elementsCustomOnly", path: ["elements"] });
|
|
670
844
|
}
|
|
671
845
|
if (cd.contractVersion !== 2) {
|
|
672
|
-
ctx.addIssue({ code:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
869
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, message: viol.code, path: ["elements"] });
|
|
696
870
|
}
|
|
697
871
|
}
|
|
698
872
|
});
|
|
699
|
-
var physicalConfigSchema =
|
|
873
|
+
var physicalConfigSchema = z2.object({
|
|
700
874
|
material: materialSchema,
|
|
701
|
-
colour:
|
|
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:
|
|
706
|
-
customDesignRequest:
|
|
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:
|
|
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:
|
|
714
|
-
assets:
|
|
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 =
|
|
729
|
-
showName:
|
|
730
|
-
showPosition:
|
|
731
|
-
showCompany:
|
|
732
|
-
showEmail:
|
|
733
|
-
showPhone:
|
|
734
|
-
showWebsite:
|
|
735
|
-
showLocation:
|
|
736
|
-
showBio:
|
|
737
|
-
showSocialLinks:
|
|
738
|
-
showLogo:
|
|
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 =
|
|
742
|
-
label:
|
|
743
|
-
url:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
779
|
-
templateId:
|
|
780
|
-
accentColor:
|
|
781
|
-
primaryColor:
|
|
782
|
-
secondaryColor:
|
|
783
|
-
textColor:
|
|
784
|
-
fontKey:
|
|
785
|
-
showAvatar:
|
|
786
|
-
avatarMode:
|
|
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 =
|
|
804
|
-
output:
|
|
805
|
-
entryFlow:
|
|
806
|
-
ref:
|
|
807
|
-
path:
|
|
808
|
-
referrer:
|
|
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:
|
|
813
|
-
utm:
|
|
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 =
|
|
816
|
-
sessionId:
|
|
994
|
+
var configurationCreateSchema = z2.object({
|
|
995
|
+
sessionId: z2.string().uuid(),
|
|
817
996
|
userData: userDataSchema,
|
|
818
997
|
physicalConfig: physicalConfigSchema,
|
|
819
998
|
digitalConfig: digitalConfigSchema,
|
|
820
|
-
quantity:
|
|
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:
|
|
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:
|
|
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 =
|
|
1014
|
+
var configurationUpdateSchema = z2.object({
|
|
836
1015
|
userData: userDataSchema.optional(),
|
|
837
1016
|
physicalConfig: physicalConfigSchema.optional(),
|
|
838
1017
|
digitalConfig: digitalConfigSchema.optional(),
|
|
839
|
-
quantity:
|
|
840
|
-
existingProfileId:
|
|
841
|
-
physicalTemplateId:
|
|
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 =
|
|
844
|
-
configurationId:
|
|
845
|
-
discountCode:
|
|
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:
|
|
1028
|
+
designType: z2.enum(["template", "custom"])
|
|
850
1029
|
});
|
|
851
|
-
var cardUpdateSchema =
|
|
1030
|
+
var cardUpdateSchema = z2.object({
|
|
852
1031
|
digitalConfigOverride: digitalConfigSchema.partial().extend({
|
|
853
|
-
profilePhotoUrl:
|
|
854
|
-
companyLogoUrl:
|
|
1032
|
+
profilePhotoUrl: z2.string().url().optional().or(z2.literal("")),
|
|
1033
|
+
companyLogoUrl: z2.string().url().optional().or(z2.literal(""))
|
|
855
1034
|
}).partial().optional(),
|
|
856
|
-
status:
|
|
1035
|
+
status: z2.enum(["active", "suspended", "archived"]).optional()
|
|
857
1036
|
});
|
|
858
|
-
var adminCardCreateSchema =
|
|
859
|
-
targetUserId:
|
|
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 =
|
|
1043
|
+
var profileVisibilitySchema = z2.enum(["public", "private", "link_only"]);
|
|
865
1044
|
var profileUserDataSchema = userDataSchema.extend({
|
|
866
|
-
email:
|
|
1045
|
+
email: z2.string().email("invalidEmail").optional().or(z2.literal(""))
|
|
867
1046
|
});
|
|
868
|
-
var profileCreateSchema =
|
|
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:
|
|
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:
|
|
878
|
-
icon:
|
|
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:
|
|
1061
|
+
accessPin: z2.string().min(4, "pinTooShort").max(8, "pinTooLong").regex(/^\d+$/, "pinDigitsOnly").optional().or(z2.literal(""))
|
|
883
1062
|
});
|
|
884
|
-
var signupWithProfileSchema =
|
|
885
|
-
email:
|
|
886
|
-
password:
|
|
887
|
-
name:
|
|
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:
|
|
890
|
-
marketingOptIn:
|
|
891
|
-
captchaToken:
|
|
892
|
-
preferredLanguage:
|
|
893
|
-
label:
|
|
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 =
|
|
898
|
-
label:
|
|
899
|
-
icon:
|
|
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:
|
|
903
|
-
companyLogoUrl:
|
|
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:
|
|
1085
|
+
accessPin: z2.string().min(4, "pinTooShort").max(8, "pinTooLong").regex(/^\d+$/, "pinDigitsOnly").optional().or(z2.literal(""))
|
|
907
1086
|
});
|
|
908
|
-
var profilePinVerifySchema =
|
|
909
|
-
pin:
|
|
1087
|
+
var profilePinVerifySchema = z2.object({
|
|
1088
|
+
pin: z2.string().min(4).max(8)
|
|
910
1089
|
});
|
|
911
|
-
var profileAccessTokenCreateSchema =
|
|
912
|
-
label:
|
|
913
|
-
expiresAt:
|
|
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 =
|
|
916
|
-
profiles:
|
|
917
|
-
|
|
918
|
-
profileId:
|
|
919
|
-
sortOrder:
|
|
920
|
-
isDefault:
|
|
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 =
|
|
925
|
-
var httpsOnlyUrl =
|
|
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 =
|
|
937
|
-
displayName:
|
|
938
|
-
subtitle:
|
|
939
|
-
avatarMode:
|
|
940
|
-
avatarUrl: httpsOnlyUrl.optional().or(
|
|
941
|
-
monogramText:
|
|
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:
|
|
948
|
-
footerText:
|
|
1126
|
+
fontKey: z2.string().min(1).max(50).optional(),
|
|
1127
|
+
footerText: z2.string().max(140).optional().or(z2.literal(""))
|
|
949
1128
|
});
|
|
950
|
-
var hubProfilesUpdateSchema =
|
|
951
|
-
profiles:
|
|
952
|
-
|
|
953
|
-
profileId:
|
|
954
|
-
sortOrder:
|
|
955
|
-
isDefault:
|
|
956
|
-
hiddenFromHub:
|
|
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 =
|
|
961
|
-
name:
|
|
962
|
-
email:
|
|
963
|
-
phone:
|
|
964
|
-
notes:
|
|
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,
|