@temelyos/zod 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/bounded-number.d.ts +8 -0
  2. package/dist/bounded-number.js +36 -0
  3. package/dist/codec-parts.d.ts +8 -0
  4. package/dist/codec-parts.js +21 -0
  5. package/dist/currency-code.d.ts +5 -0
  6. package/dist/currency-code.js +11 -0
  7. package/dist/field-paths.d.ts +54 -0
  8. package/dist/field-paths.js +67 -0
  9. package/dist/field-settings.d.ts +119 -0
  10. package/dist/field-settings.js +219 -0
  11. package/dist/format-template.d.ts +3 -0
  12. package/dist/format-template.js +8 -0
  13. package/dist/hex-string-24.d.ts +45 -0
  14. package/dist/hex-string-24.js +32 -0
  15. package/dist/index.d.ts +25 -501
  16. package/dist/index.js +23 -992
  17. package/dist/iso-date.d.ts +25 -0
  18. package/dist/iso-date.js +108 -0
  19. package/dist/json-text.d.ts +3 -0
  20. package/dist/json-text.js +6 -0
  21. package/dist/labelled.d.ts +17 -0
  22. package/dist/labelled.js +22 -0
  23. package/dist/language-code.d.ts +10 -0
  24. package/dist/language-code.js +15 -0
  25. package/dist/localized-string.d.ts +75 -0
  26. package/dist/localized-string.js +80 -0
  27. package/dist/merge.d.ts +14 -0
  28. package/dist/merge.js +26 -0
  29. package/dist/nullish.d.ts +2 -0
  30. package/dist/nullish.js +7 -0
  31. package/dist/positional-paths.d.ts +22 -0
  32. package/dist/positional-paths.js +99 -0
  33. package/dist/reg-exp-pattern.d.ts +2 -0
  34. package/dist/reg-exp-pattern.js +5 -0
  35. package/dist/slug.d.ts +5 -0
  36. package/dist/slug.js +15 -0
  37. package/dist/strip-empty-fields.d.ts +2 -0
  38. package/dist/strip-empty-fields.js +46 -0
  39. package/dist/strip-to-schema.d.ts +7 -0
  40. package/dist/strip-to-schema.js +35 -0
  41. package/dist/time-zone.d.ts +15 -0
  42. package/dist/time-zone.js +20 -0
  43. package/dist/to-json-schema.d.ts +40 -0
  44. package/dist/to-json-schema.js +44 -0
  45. package/dist/types.d.ts +4 -0
  46. package/dist/utilities.d.ts +39 -0
  47. package/dist/utilities.js +116 -0
  48. package/dist/whole-number.d.ts +2 -0
  49. package/dist/whole-number.js +11 -0
  50. package/package.json +5 -2
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ export interface NumberBounds {
3
+ integer?: boolean;
4
+ max?: number;
5
+ min?: number;
6
+ }
7
+ /** A number, optionally whole and bounded; a refusal names its bounds as the message's `{0}` and `{1}`. */
8
+ export declare function boundedNumber({ integer, max, min }: NumberBounds): z.ZodNumber;
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ // src/bounded-number.ts
3
+ /** A number, optionally whole and bounded; a refusal names its bounds as the message's `{0}` and `{1}`. */
4
+ function boundedNumber({ integer = false, max, min }) {
5
+ return z.number({ error: "Enter a number." }).superRefine((value, ctx) => {
6
+ if (integer && !Number.isInteger(value)) ctx.addIssue({
7
+ code: "custom",
8
+ message: "Enter a whole number."
9
+ });
10
+ else if (min !== void 0 && max !== void 0) {
11
+ if (value < min || value > max) ctx.addIssue({
12
+ code: "custom",
13
+ message: "Enter a number from {0} to {1}.",
14
+ params: {
15
+ 0: min,
16
+ 1: max
17
+ }
18
+ });
19
+ } else if (min !== void 0) {
20
+ if (value < min) ctx.addIssue({
21
+ code: "custom",
22
+ message: "Enter a number of {0} or more.",
23
+ params: { 0: min }
24
+ });
25
+ } else if (max !== void 0 && value > max) ctx.addIssue({
26
+ code: "custom",
27
+ message: "Enter a number of {0} or less.",
28
+ params: { 0: max }
29
+ });
30
+ }).meta({
31
+ ...integer && { type: "integer" },
32
+ ...max !== void 0 && { maximum: max },
33
+ ...min !== void 0 && { minimum: min }
34
+ });
35
+ }
36
+ export { boundedNumber };
@@ -0,0 +1,8 @@
1
+ import { ZodType } from "zod";
2
+ /** The schemas {@link withCodecParts} named for a codec; undefined for any other schema. */
3
+ export declare function codecParts(schema: ZodType): readonly ZodType[] | undefined;
4
+ /**
5
+ * A codec that builds its schemas at run time (from a model's fields, or a result's columns),
6
+ * with those schemas named, so a reader can check what it encodes and decodes its values by.
7
+ */
8
+ export declare function withCodecParts<T extends ZodType>(codec: T, schemas: readonly ZodType[]): T;
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ // src/codec-parts.ts
3
+ /**
4
+ * The schemas a codec built at run time encodes and decodes by, kept in a Zod registry beside it.
5
+ * Typed `unknown[]`: the registry maps schema-typed metadata through `$replace`, which recurses
6
+ * without end on `ZodType`.
7
+ */
8
+ const codecPartsRegistry = z.registry();
9
+ /** The schemas {@link withCodecParts} named for a codec; undefined for any other schema. */
10
+ function codecParts(schema) {
11
+ return codecPartsRegistry.get(schema)?.parts;
12
+ }
13
+ /**
14
+ * A codec that builds its schemas at run time (from a model's fields, or a result's columns),
15
+ * with those schemas named, so a reader can check what it encodes and decodes its values by.
16
+ */
17
+ function withCodecParts(codec, schemas) {
18
+ codecPartsRegistry.add(codec, { parts: schemas });
19
+ return codec;
20
+ }
21
+ export { codecParts, withCodecParts };
@@ -0,0 +1,5 @@
1
+ import { z } from "zod";
2
+ /** A lower-case ISO 4217 code: `usd`, never `USD` or `$`. */
3
+ export declare const CurrencyCodePattern: RegExp;
4
+ /** A field that takes a lower-case ISO 4217 code. `missing` is the caller's, for a field nobody filled in. */
5
+ export declare function currencyCode(missing: string, invalid?: string): z.ZodString;
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ // src/currency-code.ts
3
+ /** A lower-case ISO 4217 code: `usd`, never `USD` or `$`. */
4
+ const CurrencyCodePattern = /^[a-z]{3}$/;
5
+ /** What a value that is not a currency code says, unless the caller says otherwise. An `error` property so the i18n extractor keeps the key. */
6
+ const invalidCurrency = { error: "Invalid currency code." };
7
+ /** A field that takes a lower-case ISO 4217 code. `missing` is the caller's, for a field nobody filled in. */
8
+ function currencyCode(missing, invalid = invalidCurrency.error) {
9
+ return z.string().min(1, missing).regex(CurrencyCodePattern, invalid);
10
+ }
11
+ export { CurrencyCodePattern, currencyCode };
@@ -0,0 +1,54 @@
1
+ import { ZodType } from "zod";
2
+ export interface GetZodFieldPathsOptions {
3
+ /**
4
+ * When `true`, arrays are treated as terminal leaves and NOT descended (no
5
+ * `items.token`-style paths are emitted). Defaults to `false` — arrays are
6
+ * descended via their element type, matching how `getPathValues` fans out
7
+ * across arrays at runtime.
8
+ *
9
+ * Callers whose downstream path-consumer cannot fan out across arrays — e.g.
10
+ * `getSensitivePaths`, whose redact step (`splitSensitive`) walks plain objects
11
+ * only and would silently skip an array-borne path — should set this to `true`.
12
+ */
13
+ skipArrays?: boolean;
14
+ }
15
+ export interface ZodFieldPath {
16
+ /** The field's schema as its object declares it, wrappers kept: what says whether it may be absent. */
17
+ declared: ZodType;
18
+ /** Dotted path to the field within the schema, e.g. `auth.apiKey`. */
19
+ path: string;
20
+ /** The field's schema with wrapper types (see `unwrapField`) removed. A codec is left whole: ask `isDateSchema` what it holds. */
21
+ schema: ZodType;
22
+ }
23
+ /**
24
+ * Enumerates the dotted paths of every field reachable by descending `ZodObject`
25
+ * shapes and the branches of `ZodUnion` / `ZodDiscriminatedUnion` (Zod v4 reports
26
+ * both as `def.type === 'union'`). A field declared in more than one union branch
27
+ * is reported once per branch — callers that need uniqueness should dedupe by
28
+ * `path`.
29
+ *
30
+ * Wrapper types (optional/nullable/default/catch/nonoptional/prefault/readonly/lazy) are always unwrapped.
31
+ * Arrays are descended via their element type by default (emitting non-indexed
32
+ * paths like `items.token`, the way `getPathValues` fans out at runtime); pass
33
+ * `{ skipArrays: true }` to stop at arrays instead. Records and other leaves are
34
+ * never descended. Each field is reported with its unwrapped `schema`, which lets
35
+ * callers read per-field metadata (e.g. `sensitive()` markers) without re-walking
36
+ * wrappers, and as its object `declared` it, wrappers kept, which says whether it
37
+ * may be absent (`isOptionalOutput`).
38
+ *
39
+ * The schema-space analogue of `getPathValues` (which extracts values at a path
40
+ * from a runtime object).
41
+ */
42
+ export declare function getZodFieldPaths(schema: ZodType, options?: GetZodFieldPathsOptions): ZodFieldPath[];
43
+ /**
44
+ * Unwraps optional/nullable/default/catch/nonoptional/prefault/readonly/lazy wrappers. Unlike `getInnerType`,
45
+ * arrays are unwrapped to their element type only when `shouldSkipArrays` is
46
+ * false; when true it stops at arrays, leaving them terminal.
47
+ *
48
+ * A wrapper chain that closes on itself (`const Self = z.lazy(() => Self.array())`
49
+ * unwraps lazy → array → lazy → …) stops at the node that repeats — it has no
50
+ * inner type left to reach. The guard has to live here: `collectFieldPaths`'s own
51
+ * one is only consulted after unwrapping has already returned. `seen` is per
52
+ * top-level call, so it never suppresses an unrelated sibling.
53
+ */
54
+ export declare function unwrapField(schema: ZodType, shouldSkipArrays: boolean, seen?: Set<ZodType>): ZodType;
@@ -0,0 +1,67 @@
1
+ import { isZodObject, isZodUnion } from "./utilities.js";
2
+ import { ZodArray, ZodCatch, ZodDefault, ZodLazy, ZodNonOptional, ZodNullable, ZodOptional, ZodPrefault, ZodReadonly } from "zod";
3
+ // src/field-paths.ts
4
+ function collectFieldPaths(node, prefix, out, shouldSkipArrays, seen) {
5
+ if (seen.has(node)) return;
6
+ seen.add(node);
7
+ if (isZodObject(node)) {
8
+ const shape = node.shape;
9
+ for (const key of Object.keys(shape)) {
10
+ const declared = shape[key];
11
+ const child = unwrapField(declared, shouldSkipArrays);
12
+ const path = [...prefix, key];
13
+ out.push({
14
+ declared,
15
+ path: path.join("."),
16
+ schema: child
17
+ });
18
+ collectFieldPaths(child, path, out, shouldSkipArrays, seen);
19
+ }
20
+ } else if (isZodUnion(node)) for (const option of node.def.options) collectFieldPaths(unwrapField(option, shouldSkipArrays), prefix, out, shouldSkipArrays, seen);
21
+ seen.delete(node);
22
+ }
23
+ /**
24
+ * Enumerates the dotted paths of every field reachable by descending `ZodObject`
25
+ * shapes and the branches of `ZodUnion` / `ZodDiscriminatedUnion` (Zod v4 reports
26
+ * both as `def.type === 'union'`). A field declared in more than one union branch
27
+ * is reported once per branch — callers that need uniqueness should dedupe by
28
+ * `path`.
29
+ *
30
+ * Wrapper types (optional/nullable/default/catch/nonoptional/prefault/readonly/lazy) are always unwrapped.
31
+ * Arrays are descended via their element type by default (emitting non-indexed
32
+ * paths like `items.token`, the way `getPathValues` fans out at runtime); pass
33
+ * `{ skipArrays: true }` to stop at arrays instead. Records and other leaves are
34
+ * never descended. Each field is reported with its unwrapped `schema`, which lets
35
+ * callers read per-field metadata (e.g. `sensitive()` markers) without re-walking
36
+ * wrappers, and as its object `declared` it, wrappers kept, which says whether it
37
+ * may be absent (`isOptionalOutput`).
38
+ *
39
+ * The schema-space analogue of `getPathValues` (which extracts values at a path
40
+ * from a runtime object).
41
+ */
42
+ function getZodFieldPaths(schema, options = {}) {
43
+ const shouldSkipArrays = options.skipArrays ?? false;
44
+ const out = [];
45
+ collectFieldPaths(unwrapField(schema, shouldSkipArrays), [], out, shouldSkipArrays, /* @__PURE__ */ new Set());
46
+ return out;
47
+ }
48
+ /**
49
+ * Unwraps optional/nullable/default/catch/nonoptional/prefault/readonly/lazy wrappers. Unlike `getInnerType`,
50
+ * arrays are unwrapped to their element type only when `shouldSkipArrays` is
51
+ * false; when true it stops at arrays, leaving them terminal.
52
+ *
53
+ * A wrapper chain that closes on itself (`const Self = z.lazy(() => Self.array())`
54
+ * unwraps lazy → array → lazy → …) stops at the node that repeats — it has no
55
+ * inner type left to reach. The guard has to live here: `collectFieldPaths`'s own
56
+ * one is only consulted after unwrapping has already returned. `seen` is per
57
+ * top-level call, so it never suppresses an unrelated sibling.
58
+ */
59
+ function unwrapField(schema, shouldSkipArrays, seen = /* @__PURE__ */ new Set()) {
60
+ if (seen.has(schema)) return schema;
61
+ seen.add(schema);
62
+ if (schema instanceof ZodOptional || schema instanceof ZodNullable || schema instanceof ZodDefault || schema instanceof ZodCatch || schema instanceof ZodNonOptional || schema instanceof ZodPrefault || schema instanceof ZodReadonly) return unwrapField(schema.def.innerType, shouldSkipArrays, seen);
63
+ if (schema instanceof ZodLazy) return unwrapField(schema.def.getter(), shouldSkipArrays, seen);
64
+ if (!shouldSkipArrays && schema instanceof ZodArray) return unwrapField(schema.def.element, shouldSkipArrays, seen);
65
+ return schema;
66
+ }
67
+ export { getZodFieldPaths, unwrapField };
@@ -0,0 +1,119 @@
1
+ import { UnwrapZodType } from "./types.js";
2
+ import { ZodEnum, ZodFile, ZodNumber, ZodObject, ZodRawShape, ZodString, core, z } from "zod";
3
+ export interface BaseFieldSettings<T> {
4
+ default?: T;
5
+ isOptional?: boolean;
6
+ isVisible?: boolean;
7
+ label?: string;
8
+ }
9
+ declare const stringFieldSettingsSchema: ZodObject<{
10
+ default: z.ZodOptional<ZodString>;
11
+ isOptional: z.ZodOptional<z.ZodBoolean>;
12
+ isVisible: z.ZodOptional<z.ZodBoolean>;
13
+ label: z.ZodOptional<ZodString>;
14
+ maxLength: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
15
+ message: ZodString;
16
+ value: ZodNumber;
17
+ }, core.$strip>]>>;
18
+ minLength: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
19
+ message: ZodString;
20
+ value: ZodNumber;
21
+ }, core.$strip>]>>;
22
+ regExp: z.ZodOptional<z.ZodUnion<readonly [z.ZodInstanceOf<RegExp>, ZodObject<{
23
+ message: ZodString;
24
+ value: z.ZodInstanceOf<RegExp>;
25
+ }, core.$strip>]>>;
26
+ }, core.$strip>;
27
+ export type StringFieldSettings = z.infer<typeof stringFieldSettingsSchema>;
28
+ declare const numberFieldSettingsSchema: ZodObject<{
29
+ default: z.ZodOptional<ZodNumber>;
30
+ isOptional: z.ZodOptional<z.ZodBoolean>;
31
+ isVisible: z.ZodOptional<z.ZodBoolean>;
32
+ label: z.ZodOptional<ZodString>;
33
+ max: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
34
+ message: ZodString;
35
+ value: ZodNumber;
36
+ }, core.$strip>]>>;
37
+ min: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
38
+ message: ZodString;
39
+ value: ZodNumber;
40
+ }, core.$strip>]>>;
41
+ }, core.$strip>;
42
+ export type NumberFieldSettings = z.infer<typeof numberFieldSettingsSchema>;
43
+ declare const fileFieldSettingsSchema: ZodObject<{
44
+ default: z.ZodOptional<z.ZodInstanceOf<File>>;
45
+ isOptional: z.ZodOptional<z.ZodBoolean>;
46
+ isVisible: z.ZodOptional<z.ZodBoolean>;
47
+ label: z.ZodOptional<ZodString>;
48
+ maxBytes: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
49
+ message: ZodString;
50
+ value: ZodNumber;
51
+ }, core.$strip>]>>;
52
+ minBytes: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
53
+ message: ZodString;
54
+ value: ZodNumber;
55
+ }, core.$strip>]>>;
56
+ }, core.$strip>;
57
+ export type FileFieldSettings = z.infer<typeof fileFieldSettingsSchema>;
58
+ declare const dropdownFieldSettingsSchema: ZodObject<{
59
+ default: z.ZodOptional<ZodString>;
60
+ isOptional: z.ZodOptional<z.ZodBoolean>;
61
+ isVisible: z.ZodOptional<z.ZodBoolean>;
62
+ label: z.ZodOptional<ZodString>;
63
+ values: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<ZodString>, z.ZodRecord<ZodString, z.ZodUnion<readonly [ZodString, ZodNumber]>>]>>;
64
+ }, core.$strip>;
65
+ export type DropdownFieldSettings = z.infer<typeof dropdownFieldSettingsSchema>;
66
+ export declare const AnyFieldSettingsSchema: z.ZodUnion<readonly [ZodObject<{
67
+ default: z.ZodOptional<ZodString>;
68
+ isOptional: z.ZodOptional<z.ZodBoolean>;
69
+ isVisible: z.ZodOptional<z.ZodBoolean>;
70
+ label: z.ZodOptional<ZodString>;
71
+ maxLength: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
72
+ message: ZodString;
73
+ value: ZodNumber;
74
+ }, core.$strip>]>>;
75
+ minLength: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
76
+ message: ZodString;
77
+ value: ZodNumber;
78
+ }, core.$strip>]>>;
79
+ regExp: z.ZodOptional<z.ZodUnion<readonly [z.ZodInstanceOf<RegExp>, ZodObject<{
80
+ message: ZodString;
81
+ value: z.ZodInstanceOf<RegExp>;
82
+ }, core.$strip>]>>;
83
+ }, core.$strip>, ZodObject<{
84
+ default: z.ZodOptional<ZodNumber>;
85
+ isOptional: z.ZodOptional<z.ZodBoolean>;
86
+ isVisible: z.ZodOptional<z.ZodBoolean>;
87
+ label: z.ZodOptional<ZodString>;
88
+ max: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
89
+ message: ZodString;
90
+ value: ZodNumber;
91
+ }, core.$strip>]>>;
92
+ min: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
93
+ message: ZodString;
94
+ value: ZodNumber;
95
+ }, core.$strip>]>>;
96
+ }, core.$strip>, ZodObject<{
97
+ default: z.ZodOptional<z.ZodInstanceOf<File>>;
98
+ isOptional: z.ZodOptional<z.ZodBoolean>;
99
+ isVisible: z.ZodOptional<z.ZodBoolean>;
100
+ label: z.ZodOptional<ZodString>;
101
+ maxBytes: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
102
+ message: ZodString;
103
+ value: ZodNumber;
104
+ }, core.$strip>]>>;
105
+ minBytes: z.ZodOptional<z.ZodUnion<readonly [ZodNumber, ZodObject<{
106
+ message: ZodString;
107
+ value: ZodNumber;
108
+ }, core.$strip>]>>;
109
+ }, core.$strip>, ZodObject<{
110
+ default: z.ZodOptional<ZodString>;
111
+ isOptional: z.ZodOptional<z.ZodBoolean>;
112
+ isVisible: z.ZodOptional<z.ZodBoolean>;
113
+ label: z.ZodOptional<ZodString>;
114
+ values: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<ZodString>, z.ZodRecord<ZodString, z.ZodUnion<readonly [ZodString, ZodNumber]>>]>>;
115
+ }, core.$strip>]>;
116
+ export type AnyFieldSettings = z.infer<typeof AnyFieldSettingsSchema>;
117
+ export type FieldSettings<T extends ZodObject<ZodRawShape>> = { [K in keyof T['shape']]?: UnwrapZodType<T['shape'][K]> extends ZodString ? StringFieldSettings : UnwrapZodType<T['shape'][K]> extends ZodNumber ? NumberFieldSettings : UnwrapZodType<T['shape'][K]> extends ZodEnum<any> ? DropdownFieldSettings : UnwrapZodType<T['shape'][K]> extends ZodFile ? FileFieldSettings : BaseFieldSettings<z.infer<T['shape'][K]>>; };
118
+ export declare function applyFieldSettings<T extends ZodRawShape>(schema: ZodObject<T>, fields?: FieldSettings<ZodObject<T>>): ZodObject<T>;
119
+ export {};
@@ -0,0 +1,219 @@
1
+ import { isZodCatch, isZodCodec, isZodDefault, isZodEnum, isZodLazy, isZodNullable, isZodNumber, isZodOptional, isZodPipe, isZodString, isZodTransform, isZodWrapper } from "./utilities.js";
2
+ import { z } from "zod";
3
+ import { getLogger } from "@temelyos/core/logging/store";
4
+ // src/field-settings.ts
5
+ const buildFieldSettingsSchema = (defaultType) => z.object({
6
+ default: defaultType.optional(),
7
+ isOptional: z.boolean().optional(),
8
+ isVisible: z.boolean().optional(),
9
+ label: z.string().optional()
10
+ });
11
+ const stringFieldSettingsSchema = buildFieldSettingsSchema(z.string()).extend({
12
+ maxLength: z.union([z.number(), z.object({
13
+ message: z.string(),
14
+ value: z.number()
15
+ })]).optional(),
16
+ minLength: z.union([z.number(), z.object({
17
+ message: z.string(),
18
+ value: z.number()
19
+ })]).optional(),
20
+ regExp: z.union([z.instanceof(RegExp), z.object({
21
+ message: z.string(),
22
+ value: z.instanceof(RegExp)
23
+ })]).optional()
24
+ });
25
+ const numberFieldSettingsSchema = buildFieldSettingsSchema(z.number()).extend({
26
+ max: z.union([z.number(), z.object({
27
+ message: z.string(),
28
+ value: z.number()
29
+ })]).optional(),
30
+ min: z.union([z.number(), z.object({
31
+ message: z.string(),
32
+ value: z.number()
33
+ })]).optional()
34
+ });
35
+ const fileFieldSettingsSchema = buildFieldSettingsSchema(z.instanceof(File)).extend({
36
+ maxBytes: z.union([z.number(), z.object({
37
+ message: z.string(),
38
+ value: z.number()
39
+ })]).optional(),
40
+ minBytes: z.union([z.number(), z.object({
41
+ message: z.string(),
42
+ value: z.number()
43
+ })]).optional()
44
+ });
45
+ const dropdownFieldSettingsSchema = buildFieldSettingsSchema(z.string()).extend({ values: z.union([z.array(z.string()), z.record(z.string(), z.union([z.string(), z.number()]))]).optional() });
46
+ const AnyFieldSettingsSchema = z.union([
47
+ stringFieldSettingsSchema,
48
+ numberFieldSettingsSchema,
49
+ fileFieldSettingsSchema,
50
+ dropdownFieldSettingsSchema
51
+ ]);
52
+ function applyFieldSettings(schema, fields) {
53
+ if (!fields) return schema;
54
+ const updatedShape = Object.assign({}, schema.shape);
55
+ for (const fieldName in fields) {
56
+ const fieldSettings = fields[fieldName];
57
+ if (!(fieldName in schema.shape)) continue;
58
+ const fieldSchema = schema.shape[fieldName];
59
+ if (fieldSettings.isVisible === false) {
60
+ delete updatedShape[fieldName];
61
+ continue;
62
+ }
63
+ const [oldField, wrappers] = getField(fieldSchema);
64
+ let newField = void 0;
65
+ if (isStringFieldSettings(fieldSettings)) {
66
+ if (!isZodString(oldField)) {
67
+ getLogger().warn(`Field ${fieldName} is not a string, but received StringFieldSettings.`);
68
+ continue;
69
+ }
70
+ newField = createStringField(oldField, fieldSettings);
71
+ }
72
+ if (isNumberFieldSettings(fieldSettings)) {
73
+ if (!isZodNumber(oldField)) {
74
+ getLogger().warn(`Field ${fieldName} is not a number, but received NumberFieldSettings.`);
75
+ continue;
76
+ }
77
+ newField = createNumberField(oldField, fieldSettings);
78
+ }
79
+ if (isDropdownFieldSettings(fieldSettings)) {
80
+ if (Array.isArray(fieldSettings.values)) {
81
+ if (!isZodEnum(oldField)) {
82
+ getLogger().warn(`Field ${fieldName} is not a enum, but received DropdownFieldSettings with an Array.`);
83
+ continue;
84
+ }
85
+ } else if (!isZodEnum(oldField)) {
86
+ getLogger().warn(`Field ${fieldName} is not a native enum, but received DropdownFieldSettings.`);
87
+ continue;
88
+ }
89
+ newField = createDropdownField(fieldSettings);
90
+ }
91
+ if (!newField) continue;
92
+ newField = applyWrappers(newField, wrappers, fieldSettings);
93
+ updatedShape[fieldName] = newField;
94
+ }
95
+ return z.object(updatedShape);
96
+ }
97
+ function applyWrappers(base, wrappers, settings = {}) {
98
+ let result = base;
99
+ let isOptionalApplied = false;
100
+ let isDefaultApplied = false;
101
+ for (let i = 0; i < wrappers.length; i++) {
102
+ const wrapper = wrappers[i];
103
+ if (isZodDefault(wrapper)) {
104
+ if (settings.default !== void 0) {
105
+ result = result.default(settings.default);
106
+ isDefaultApplied = true;
107
+ } else result = result.default(wrapper.def.defaultValue);
108
+ } else if (isZodOptional(wrapper) && settings.isOptional !== false) {
109
+ result = result.optional();
110
+ isOptionalApplied = true;
111
+ } else if (isZodNullable(wrapper)) result = result.nullable();
112
+ else if (isZodCatch(wrapper)) result = result.catch(wrapper.def.catchValue);
113
+ else if (isZodLazy(wrapper)) {
114
+ const inner = result;
115
+ result = z.lazy(() => inner);
116
+ } else if (isZodPipe(wrapper)) {
117
+ if (isZodTransform(wrapper.out)) result = result.transform(wrapper.out.def.transform);
118
+ }
119
+ }
120
+ if (settings.isOptional && !isOptionalApplied) result = result.optional();
121
+ if (settings.default !== void 0 && !isDefaultApplied) result = result.default(settings.default);
122
+ return result;
123
+ }
124
+ function createDropdownField(settings) {
125
+ if (Array.isArray(settings.values)) return z.enum(settings.values);
126
+ return z.enum(settings.values ?? {});
127
+ }
128
+ function createNumberField(oldField, settings) {
129
+ let field = z.number();
130
+ if (settings.min !== void 0) {
131
+ if (typeof settings.min === "number") {
132
+ const check = getZodCheck(oldField, "greater_than");
133
+ let params;
134
+ if (check?._zod.def.error) params = { error: check._zod.def.error };
135
+ field = field.min(settings.min, params);
136
+ } else field = field.min(settings.min.value, settings.min.message);
137
+ }
138
+ if (settings.max !== void 0) {
139
+ if (typeof settings.max === "number") {
140
+ const check = getZodCheck(oldField, "less_than");
141
+ let params;
142
+ if (check?._zod.def.error) params = { error: check._zod.def.error };
143
+ field = field.max(settings.max, params);
144
+ } else field = field.max(settings.max.value, settings.max.message);
145
+ }
146
+ const carriedChecks = (oldField.def.checks ?? []).filter((check) => {
147
+ const kind = check._zod.def.check;
148
+ if (kind === "greater_than") return settings.min === void 0;
149
+ if (kind === "less_than") return settings.max === void 0;
150
+ return true;
151
+ });
152
+ return carriedChecks.length ? field.check(...carriedChecks) : field;
153
+ }
154
+ function createStringField(oldField, settings) {
155
+ let field = z.string();
156
+ if (settings.minLength !== void 0) {
157
+ if (typeof settings.minLength === "number") {
158
+ const check = getZodCheck(oldField, "min_length");
159
+ let params;
160
+ if (check?._zod.def.error) params = { error: check._zod.def.error };
161
+ field = field.min(settings.minLength, params);
162
+ } else field = field.min(settings.minLength.value, settings.minLength.message);
163
+ }
164
+ if (settings.maxLength !== void 0) {
165
+ if (typeof settings.maxLength === "number") {
166
+ const mlCheck = getZodCheck(oldField, "max_length");
167
+ let params;
168
+ if (mlCheck?._zod.def.error) params = { error: mlCheck._zod.def.error };
169
+ field = field.max(settings.maxLength, params);
170
+ } else field = field.max(settings.maxLength.value, settings.maxLength.message);
171
+ }
172
+ if (settings.regExp !== void 0) {
173
+ if (settings.regExp instanceof RegExp) {
174
+ const regCheck = getZodCheck(oldField, "string_format", "regex");
175
+ let params;
176
+ if (regCheck?._zod.def.error) params = { error: regCheck._zod.def.error };
177
+ field = field.regex(settings.regExp, params);
178
+ } else field = field.regex(settings.regExp.value, settings.regExp.message);
179
+ }
180
+ const carriedChecks = (oldField.def.checks ?? []).filter((check) => {
181
+ const kind = check._zod.def.check;
182
+ if (kind === "min_length") return settings.minLength === void 0;
183
+ if (kind === "max_length") return settings.maxLength === void 0;
184
+ if (kind === "string_format" && check._zod.def.format === "regex") return settings.regExp === void 0;
185
+ return true;
186
+ });
187
+ return carriedChecks.length ? field.check(...carriedChecks) : field;
188
+ }
189
+ function getField(field) {
190
+ const wrappers = [];
191
+ let type = field;
192
+ while (isZodWrapper(type) || isZodPipe(type) && !isZodCodec(type)) {
193
+ if (isZodPipe(type)) {
194
+ wrappers.push(type);
195
+ type = type.in;
196
+ continue;
197
+ }
198
+ wrappers.push(type);
199
+ type = type.unwrap();
200
+ }
201
+ return [type, wrappers.reverse()];
202
+ }
203
+ function getZodCheck(field, check, format) {
204
+ return field.def.checks?.find((ck) => {
205
+ if (ck._zod.def.check !== check) return false;
206
+ if (check !== "string_format") return true;
207
+ return !format || format === ck._zod.def.format;
208
+ });
209
+ }
210
+ function isDropdownFieldSettings(settings) {
211
+ return "values" in settings && !!settings.values;
212
+ }
213
+ function isNumberFieldSettings(settings) {
214
+ return "min" in settings || "max" in settings;
215
+ }
216
+ function isStringFieldSettings(settings) {
217
+ return "minLength" in settings || "maxLength" in settings || "regExp" in settings;
218
+ }
219
+ export { AnyFieldSettingsSchema, applyFieldSettings, applyWrappers, createDropdownField, createNumberField, createStringField, getField, getZodCheck };
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+ /** Text whose every `{token}` names one of `keys`, with no stray braces. */
3
+ export declare function formatTemplate(keys: readonly string[]): z.ZodString;
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import { isValidFormat } from "@temelyos/core/format";
3
+ // src/format-template.ts
4
+ /** Text whose every `{token}` names one of `keys`, with no stray braces. */
5
+ function formatTemplate(keys) {
6
+ return z.string().refine((value) => isValidFormat(value, keys), "Invalid template.");
7
+ }
8
+ export { formatTemplate };
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Twenty-four lower-case hex characters: the shape of every id on the wire and in a zod model.
4
+ *
5
+ * Named for the shape rather than for Mongo, because the shape is all a model can see — the
6
+ * Mingo driver mints the same ids without Mongo anywhere near them. Lower case only, which is
7
+ * what every driver here produces, so anything else is a value nothing in the platform minted.
8
+ */
9
+ export declare const HexString24Pattern: RegExp;
10
+ /** What an id field says when nothing was chosen, and when what came is not an id. */
11
+ export interface HexString24Messages {
12
+ /** Defaults to "Invalid id.". */
13
+ invalid?: string;
14
+ /** `false` when nothing chosen is an answer: absent, `null` and `''` then all read as unset. */
15
+ missing: false | string;
16
+ }
17
+ /**
18
+ * A field holding one id.
19
+ *
20
+ * The message is the CALLER's, as `timeZone`'s is: "Select an owner." belongs to the form asking
21
+ * for an owner, not here. It answers for nothing chosen: absent, `null`, or the empty string a
22
+ * cleared select sends. A value that is there but is not an id says "Invalid id.", or `invalid`
23
+ * when the caller names one. Left out entirely, zod says what it says about a pattern, which is
24
+ * unreadable, and the reason a request schema should pass one.
25
+ *
26
+ * `{ missing: false }`, or `false` for short, is the field that may be left unset: absent, `null`
27
+ * and `''` pass through as they came, so the repository can tell a clear (`''`) from a field left
28
+ * alone.
29
+ */
30
+ export declare function hexString24(messages?: string | {
31
+ invalid?: string;
32
+ missing: string;
33
+ }): z.ZodString;
34
+ export declare function hexString24(messages: false | {
35
+ invalid?: string;
36
+ missing: false;
37
+ }): z.ZodOptional<z.ZodNullable<z.ZodString>>;
38
+ /**
39
+ * An id with nothing said about whose it is.
40
+ *
41
+ * For a stored model, where the field is a shape rather than a question put to a person: a
42
+ * polymorphic subject's `id`, a join's `_id`. A request that a person fills in wants
43
+ * `hexString24(message)` instead, so a mistyped id reads as something other than a regex.
44
+ */
45
+ export declare const HexString24: z.ZodString;
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ // src/hex-string-24.ts
3
+ /**
4
+ * Twenty-four lower-case hex characters: the shape of every id on the wire and in a zod model.
5
+ *
6
+ * Named for the shape rather than for Mongo, because the shape is all a model can see — the
7
+ * Mingo driver mints the same ids without Mongo anywhere near them. Lower case only, which is
8
+ * what every driver here produces, so anything else is a value nothing in the platform minted.
9
+ */
10
+ const HexString24Pattern = /^[a-f\d]{24}$/;
11
+ /**
12
+ * What a value that is not an id says, unless the caller says otherwise. Generic, because nothing a
13
+ * person picks produces one: only a hand-built request does. An `error` property so the i18n
14
+ * extractor keeps the key.
15
+ */
16
+ const notAnId = { error: "Invalid id." };
17
+ function hexString24(messages) {
18
+ if (messages === void 0) return z.string().regex(HexString24Pattern);
19
+ const { invalid = notAnId.error, missing } = typeof messages === "string" || messages === false ? { missing: messages } : messages;
20
+ if (missing === false) return z.string({ error: invalid }).refine((value) => value === "" || HexString24Pattern.test(value), { error: invalid }).nullish();
21
+ const error = (issue) => issue.input === void 0 || issue.input === null || issue.input === "" ? missing : invalid;
22
+ return z.string({ error }).regex(HexString24Pattern, { error });
23
+ }
24
+ /**
25
+ * An id with nothing said about whose it is.
26
+ *
27
+ * For a stored model, where the field is a shape rather than a question put to a person: a
28
+ * polymorphic subject's `id`, a join's `_id`. A request that a person fills in wants
29
+ * `hexString24(message)` instead, so a mistyped id reads as something other than a regex.
30
+ */
31
+ const HexString24 = hexString24();
32
+ export { HexString24, HexString24Pattern, hexString24 };