@cleverbrush/schema 1.1.10 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +1314 -101
  2. package/dist/builders/AnySchemaBuilder.d.ts +55 -15
  3. package/dist/builders/AnySchemaBuilder.js +2 -112
  4. package/dist/builders/AnySchemaBuilder.js.map +1 -0
  5. package/dist/builders/ArraySchemaBuilder.d.ts +124 -23
  6. package/dist/builders/ArraySchemaBuilder.js +2 -284
  7. package/dist/builders/ArraySchemaBuilder.js.map +1 -0
  8. package/dist/builders/BooleanSchemaBuilder.d.ts +95 -20
  9. package/dist/builders/BooleanSchemaBuilder.js +2 -150
  10. package/dist/builders/BooleanSchemaBuilder.js.map +1 -0
  11. package/dist/builders/DateSchemaBuilder.d.ts +135 -37
  12. package/dist/builders/DateSchemaBuilder.js +2 -433
  13. package/dist/builders/DateSchemaBuilder.js.map +1 -0
  14. package/dist/builders/ExternSchemaBuilder.d.ts +200 -0
  15. package/dist/builders/ExternSchemaBuilder.js +2 -0
  16. package/dist/builders/ExternSchemaBuilder.js.map +1 -0
  17. package/dist/builders/FunctionSchemaBuilder.d.ts +58 -15
  18. package/dist/builders/FunctionSchemaBuilder.js +2 -113
  19. package/dist/builders/FunctionSchemaBuilder.js.map +1 -0
  20. package/dist/builders/LazySchemaBuilder.d.ts +167 -0
  21. package/dist/builders/NullSchemaBuilder.d.ts +162 -0
  22. package/dist/builders/NumberSchemaBuilder.d.ts +143 -31
  23. package/dist/builders/NumberSchemaBuilder.js +2 -386
  24. package/dist/builders/NumberSchemaBuilder.js.map +1 -0
  25. package/dist/builders/ObjectSchemaBuilder.d.ts +266 -61
  26. package/dist/builders/ObjectSchemaBuilder.js +2 -589
  27. package/dist/builders/ObjectSchemaBuilder.js.map +1 -0
  28. package/dist/builders/PropertyValidationResult.d.ts +68 -0
  29. package/dist/builders/RecordSchemaBuilder.d.ts +341 -0
  30. package/dist/builders/RecordSchemaBuilder.js +2 -0
  31. package/dist/builders/RecordSchemaBuilder.js.map +1 -0
  32. package/dist/builders/SchemaBuilder.d.ts +818 -30
  33. package/dist/builders/StringSchemaBuilder.d.ts +152 -37
  34. package/dist/builders/StringSchemaBuilder.js +2 -414
  35. package/dist/builders/StringSchemaBuilder.js.map +1 -0
  36. package/dist/builders/TupleSchemaBuilder.d.ts +248 -0
  37. package/dist/builders/TupleSchemaBuilder.js +2 -0
  38. package/dist/builders/TupleSchemaBuilder.js.map +1 -0
  39. package/dist/builders/UnionSchemaBuilder.d.ts +133 -39
  40. package/dist/builders/UnionSchemaBuilder.js +2 -216
  41. package/dist/builders/UnionSchemaBuilder.js.map +1 -0
  42. package/dist/chunk-BFCMDDTX.js +2 -0
  43. package/dist/chunk-BFCMDDTX.js.map +1 -0
  44. package/dist/chunk-CTP4RHDG.js +2 -0
  45. package/dist/chunk-CTP4RHDG.js.map +1 -0
  46. package/dist/chunk-EUQ5AE3H.js +2 -0
  47. package/dist/chunk-EUQ5AE3H.js.map +1 -0
  48. package/dist/chunk-FWUEUW2N.js +2 -0
  49. package/dist/chunk-FWUEUW2N.js.map +1 -0
  50. package/dist/chunk-IJ6FO37G.js +2 -0
  51. package/dist/chunk-IJ6FO37G.js.map +1 -0
  52. package/dist/chunk-KESLT5EE.js +2 -0
  53. package/dist/chunk-KESLT5EE.js.map +1 -0
  54. package/dist/chunk-KFTO7TMB.js +2 -0
  55. package/dist/chunk-KFTO7TMB.js.map +1 -0
  56. package/dist/chunk-MKAYFPAR.js +2 -0
  57. package/dist/chunk-MKAYFPAR.js.map +1 -0
  58. package/dist/chunk-NXPH3ZUW.js +2 -0
  59. package/dist/chunk-NXPH3ZUW.js.map +1 -0
  60. package/dist/chunk-S5TC6NSU.js +2 -0
  61. package/dist/chunk-S5TC6NSU.js.map +1 -0
  62. package/dist/chunk-YWBNVHR6.js +2 -0
  63. package/dist/chunk-YWBNVHR6.js.map +1 -0
  64. package/dist/chunk-Z7QZ7IXT.js +2 -0
  65. package/dist/chunk-Z7QZ7IXT.js.map +1 -0
  66. package/dist/chunk-ZAN4ZHCJ.js +2 -0
  67. package/dist/chunk-ZAN4ZHCJ.js.map +1 -0
  68. package/dist/chunk-ZUPYV5TI.js +2 -0
  69. package/dist/chunk-ZUPYV5TI.js.map +1 -0
  70. package/dist/core.d.ts +23 -0
  71. package/dist/core.js +2 -0
  72. package/dist/core.js.map +1 -0
  73. package/dist/extension.d.ts +413 -0
  74. package/dist/extensions/array.d.ts +112 -0
  75. package/dist/extensions/enum.d.ts +190 -0
  76. package/dist/extensions/index.d.ts +108 -0
  77. package/dist/extensions/nullable.d.ts +26 -0
  78. package/dist/extensions/number.d.ts +228 -0
  79. package/dist/extensions/string.d.ts +332 -0
  80. package/dist/extensions/util.d.ts +45 -0
  81. package/dist/index.d.ts +7 -20
  82. package/dist/index.js +2 -19
  83. package/dist/index.js.map +1 -0
  84. package/dist/utils/transaction.d.ts +27 -4
  85. package/package.json +74 -6
  86. package/dist/builders/SchemaBuilder.js +0 -275
  87. package/dist/utils/transaction.js +0 -178
@@ -1,18 +1,44 @@
1
- import { SchemaBuilder, ValidationResult, ValidationContext, InferType } from './SchemaBuilder.js';
2
- type UnionSchemaBuilderCreateProps<T extends readonly SchemaBuilder<any, any>[], R extends boolean = true> = Partial<ReturnType<UnionSchemaBuilder<T, R>['introspect']>>;
3
- type SchemaArrayToUnion<TArr extends readonly SchemaBuilder<any, any>[]> = TArr['length'] extends 1 ? InferType<TArr[0]> : TArr extends readonly [
4
- infer TFirst extends SchemaBuilder<any, any>,
5
- ...infer TRest extends SchemaBuilder<any, any>[]
1
+ import type { ObjectSchemaBuilder, ObjectSchemaValidationResult } from './ObjectSchemaBuilder.js';
2
+ import { type BRAND, type InferType, type NestedValidationResult, SchemaBuilder, type ValidationContext, type ValidationErrorMessageProvider, type ValidationResult } from './SchemaBuilder.js';
3
+ type UnionSchemaBuilderCreateProps<T extends readonly SchemaBuilder<any, any, any, any, any>[], R extends boolean = true> = Partial<ReturnType<UnionSchemaBuilder<T, R>['introspect']>>;
4
+ /**
5
+ * Mapped tuple type that converts a tuple of SchemaBuilder options into
6
+ * a tuple of their corresponding validation results.
7
+ * Union schema options get `UnionSchemaValidationResult` with recursive
8
+ * `getNestedErrors` navigation; object schema options get
9
+ * `ObjectSchemaValidationResult`; other types get `ValidationResult`.
10
+ */
11
+ export type OptionValidationResults<TOptions extends readonly SchemaBuilder<any, any, any, any, any>[]> = {
12
+ [K in keyof TOptions]: TOptions[K] extends UnionSchemaBuilder<infer UOptions extends readonly SchemaBuilder<any, any, any, any, any>[], any, any> ? UnionSchemaValidationResult<InferType<TOptions[K]>, UOptions> : TOptions[K] extends ObjectSchemaBuilder<any, any, any, any> ? ObjectSchemaValidationResult<InferType<TOptions[K]>, TOptions[K]> : ValidationResult<InferType<TOptions[K]>>;
13
+ };
14
+ /**
15
+ * Validation result type returned by `UnionSchemaBuilder.validate()`.
16
+ * Extends `ValidationResult` with:
17
+ * - `getNestedErrors` for root-level union errors and per-branch validation results
18
+ */
19
+ export type UnionSchemaValidationResult<T, TOptions extends readonly SchemaBuilder<any, any, any, any, any>[]> = ValidationResult<T> & {
20
+ /**
21
+ * Returns root-level union validation errors combined with
22
+ * per-branch validation results.
23
+ * The returned value has both `NestedValidationResult` properties
24
+ * (`errors`, `isValid`, `descriptor`, `seenValue`) and tuple-indexed
25
+ * branch results (`[0]`, `[1]`, etc.).
26
+ */
27
+ getNestedErrors(): OptionValidationResults<TOptions> & NestedValidationResult<any, any, any>;
28
+ };
29
+ type SchemaArrayToUnion<TArr extends readonly SchemaBuilder<any, any, any, any, any>[]> = TArr['length'] extends 1 ? InferType<TArr[0]> : TArr extends readonly [
30
+ infer TFirst extends SchemaBuilder<any, any, any, any, any>,
31
+ ...infer TRest extends SchemaBuilder<any, any, any, any, any>[]
6
32
  ] ? InferType<TFirst> | SchemaArrayToUnion<[...TRest]> : never;
7
- type TakeBeforeIndex<TArr extends readonly SchemaBuilder<any, any>[], TIndex extends number> = TArr extends [
8
- ...infer TRest extends SchemaBuilder<any, any>[],
9
- infer TLast extends SchemaBuilder<any, any>
33
+ type TakeBeforeIndex<TArr extends readonly SchemaBuilder<any, any, any, any, any>[], TIndex extends number> = TArr extends [
34
+ ...infer TRest extends SchemaBuilder<any, any, any, any, any>[],
35
+ infer _ extends SchemaBuilder<any, any, any, any, any>
10
36
  ] ? TRest['length'] extends TIndex ? TRest : TakeBeforeIndex<TRest, TIndex> : never;
11
- type TakeAfterIndex<TArr extends readonly SchemaBuilder<any, any>[], TIndex extends number, TAcc extends readonly SchemaBuilder<any, any>[] = []> = TArr extends [
12
- ...infer TRest extends SchemaBuilder<any, any>[],
13
- infer TLast extends SchemaBuilder<any, any>
37
+ type TakeAfterIndex<TArr extends readonly SchemaBuilder<any, any, any, any, any>[], TIndex extends number, TAcc extends readonly SchemaBuilder<any, any, any, any, any>[] = []> = TArr extends [
38
+ ...infer TRest extends SchemaBuilder<any, any, any, any, any>[],
39
+ infer TLast extends SchemaBuilder<any, any, any, any, any>
14
40
  ] ? TRest['length'] extends TIndex ? TAcc : TakeAfterIndex<TRest, TIndex, [TLast, ...TAcc]> : never;
15
- type TakeExceptIndex<TArr extends readonly SchemaBuilder<any, any>[], TIndex extends number> = [...TakeBeforeIndex<TArr, TIndex>, ...TakeAfterIndex<TArr, TIndex>];
41
+ type TakeExceptIndex<TArr extends readonly SchemaBuilder<any, any, any, any, any>[], TIndex extends number> = [...TakeBeforeIndex<TArr, TIndex>, ...TakeAfterIndex<TArr, TIndex>];
16
42
  /**
17
43
  * Union schema builder class. Allows to create schemas
18
44
  * containing alternatives. E.g. string | number | Date.
@@ -30,7 +56,7 @@ type TakeExceptIndex<TArr extends readonly SchemaBuilder<any, any>[], TIndex ext
30
56
  * @example
31
57
  * ```ts
32
58
  * const schema = union(string('foo')).or(string('bar'));
33
- * const result = await schema.validate('foo');
59
+ * const result = schema.validate('foo');
34
60
  * // result.valid === true
35
61
  * // result.object === 'foo'
36
62
  * ```
@@ -38,7 +64,7 @@ type TakeExceptIndex<TArr extends readonly SchemaBuilder<any, any>[], TIndex ext
38
64
  * @example
39
65
  * ```ts
40
66
  * const schema = union(string('foo')).or(string('bar'));
41
- * const result = await schema.validate('baz');
67
+ * const result = schema.validate('baz');
42
68
  * // result.valid === false
43
69
  * ```
44
70
  *
@@ -46,27 +72,30 @@ type TakeExceptIndex<TArr extends readonly SchemaBuilder<any, any>[], TIndex ext
46
72
  * ```ts
47
73
  * const schema = union(string('yes')).or(string('no')).or(number(0)).or(number(1));
48
74
  * // equals to 'yes' | 'no' | 0 | 1 in TS
49
- * const result = await schema.validate('yes');
75
+ * const result = schema.validate('yes');
50
76
  * // result.valid === true
51
77
  * // result.object === 'yes'
52
78
  *
53
- * const result2 = await schema.validate(0);
79
+ * const result2 = schema.validate(0);
54
80
  * // result2.valid === true
55
81
  * // result2.object === 0
56
82
  *
57
- * const result3 = await schema.validate('baz');
83
+ * const result3 = schema.validate('baz');
58
84
  * // result3.valid === false
59
85
  *
60
- * const result4 = await schema.validate(2);
86
+ * const result4 = schema.validate(2);
61
87
  * // result4.valid === false
62
88
  * ```
63
89
  *
64
90
  * @see {@link union}
65
91
  */
66
- export declare class UnionSchemaBuilder<TOptions extends readonly SchemaBuilder<any, any>[], TRequired extends boolean = true, TExplicitType = undefined> extends SchemaBuilder<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, TRequired> {
92
+ export declare class UnionSchemaBuilder<TOptions extends readonly SchemaBuilder<any, any, any, any, any>[], TRequired extends boolean = true, TNullable extends boolean = false, TExplicitType = undefined, THasDefault extends boolean = false, TExtensions = {}> extends SchemaBuilder<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, TRequired, TNullable, THasDefault, TExtensions> {
67
93
  #private;
68
- static create(props: UnionSchemaBuilderCreateProps<any>): UnionSchemaBuilder<any, true, undefined>;
69
- private constructor();
94
+ /**
95
+ * @hidden
96
+ */
97
+ static create(props: UnionSchemaBuilderCreateProps<any>): UnionSchemaBuilder<any, true, false, undefined, false, {}>;
98
+ protected constructor(props: UnionSchemaBuilderCreateProps<TOptions, TRequired>);
70
99
  introspect(): {
71
100
  /**
72
101
  * Array of schemas participating in the union.
@@ -74,60 +103,125 @@ export declare class UnionSchemaBuilder<TOptions extends readonly SchemaBuilder<
74
103
  options: TOptions;
75
104
  type: string;
76
105
  isRequired: boolean;
77
- preprocessors: readonly import("./SchemaBuilder.js").Preprocessor<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType>[];
78
- validators: readonly import("./SchemaBuilder.js").Validator<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType>[];
106
+ isNullable: boolean;
107
+ isReadonly: boolean;
108
+ preprocessors: readonly import("./SchemaBuilder.js").PreprocessorEntry<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType>[];
109
+ validators: readonly import("./SchemaBuilder.js").ValidatorEntry<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType>[];
110
+ requiredValidationErrorMessageProvider: ValidationErrorMessageProvider<SchemaBuilder<any, any, any, any, any>>;
111
+ extensions: {
112
+ [x: string]: unknown;
113
+ };
114
+ hasDefault: boolean;
115
+ defaultValue: (TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType) | (() => TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType) | undefined;
116
+ description: string | undefined;
117
+ hasCatch: boolean;
118
+ catchValue: (TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType) | (() => TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType) | undefined;
79
119
  };
120
+ /**
121
+ * Null is a legitimate JavaScript value that a union option (e.g.
122
+ * `NullSchemaBuilder`) may accept. Override the base-class behaviour so
123
+ * that a required union does **not** reject `null` during pre-validation;
124
+ * instead, each option gets the opportunity to validate it.
125
+ * @override
126
+ */
127
+ protected get isNullRequiredViolation(): boolean;
128
+ /**
129
+ * @inheritdoc
130
+ */
131
+ hasType<T>(_notUsed?: T): UnionSchemaBuilder<TOptions, true, TNullable, T, THasDefault, TExtensions> & TExtensions;
132
+ /**
133
+ * @inheritdoc
134
+ */
135
+ clearHasType(): UnionSchemaBuilder<TOptions, TRequired, TNullable, undefined, THasDefault, TExtensions> & TExtensions;
136
+ /**
137
+ * Performs synchronous validation of the union schema over `object`. {@inheritDoc SchemaBuilder.validate}
138
+ */
139
+ validate(object: TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, context?: ValidationContext): UnionSchemaValidationResult<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, TOptions>;
140
+ /**
141
+ * Performs async validation of the union schema over `object`. {@inheritDoc SchemaBuilder.validateAsync}
142
+ */
143
+ validateAsync(object: TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, context?: ValidationContext): Promise<UnionSchemaValidationResult<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, TOptions>>;
144
+ /**
145
+ * Performs synchronous validation of the union schema over `object`.
146
+ * Throws if any preprocessor, validator, or error message provider returns a Promise.
147
+ * @param context Optional `ValidationContext` settings.
148
+ */
149
+ protected _validate(object: TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, context?: ValidationContext): UnionSchemaValidationResult<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, TOptions>;
150
+ /**
151
+ * Performs async validation of the union schema over `object`.
152
+ * Supports async preprocessors, validators, and error message providers.
153
+ * @param context Optional `ValidationContext` settings.
154
+ */
155
+ protected _validateAsync(object: TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, context?: ValidationContext): Promise<UnionSchemaValidationResult<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, TOptions>>;
156
+ protected createFromProps<T extends readonly SchemaBuilder<any, any, any, any, any>[], TReq extends boolean>(props: UnionSchemaBuilderCreateProps<T, TReq>): this;
80
157
  /**
81
158
  * @hidden
82
159
  */
83
- hasType<T>(notUsed?: T): UnionSchemaBuilder<TOptions, true, T>;
160
+ required(errorMessage?: ValidationErrorMessageProvider): UnionSchemaBuilder<TOptions, true, TNullable, TExplicitType, THasDefault, TExtensions> & TExtensions;
84
161
  /**
85
162
  * @hidden
86
163
  */
87
- clearHasType(): UnionSchemaBuilder<TOptions, TRequired, undefined>;
164
+ optional(): UnionSchemaBuilder<TOptions, false, TNullable, TExplicitType, THasDefault, TExtensions> & TExtensions;
88
165
  /**
89
- * Performs validion of the union schema over `object`.
90
- * @param context Optional `ValidationContext` settings.
166
+ * @hidden
91
167
  */
92
- validate(object: TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType, context?: ValidationContext): Promise<ValidationResult<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType>>;
93
- protected createFromProps<T extends readonly SchemaBuilder<any, any>[], TReq extends boolean>(props: UnionSchemaBuilderCreateProps<T, TReq>): this;
168
+ default(value: (TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType) | (() => TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType)): UnionSchemaBuilder<TOptions, true, TNullable, TExplicitType, true, TExtensions> & TExtensions;
94
169
  /**
95
170
  * @hidden
96
171
  */
97
- required(): UnionSchemaBuilder<TOptions, true, TExplicitType>;
172
+ clearDefault(): UnionSchemaBuilder<TOptions, TRequired, TNullable, TExplicitType, false, TExtensions> & TExtensions;
98
173
  /**
99
174
  * @hidden
100
175
  */
101
- optional(): UnionSchemaBuilder<TOptions, false, TExplicitType>;
176
+ brand<TBrand extends string | symbol>(_name?: TBrand): UnionSchemaBuilder<TOptions, TRequired, TNullable, (TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType) & {
177
+ readonly [K in BRAND]: TBrand;
178
+ }, THasDefault, TExtensions> & TExtensions;
179
+ /**
180
+ * @hidden
181
+ */
182
+ readonly(): UnionSchemaBuilder<TOptions, TRequired, TNullable, Readonly<TExplicitType extends undefined ? SchemaArrayToUnion<TOptions> : TExplicitType>, THasDefault, TExtensions> & TExtensions;
102
183
  /**
103
184
  * Adds a new schema option described by `schema`.
104
185
  * schema must be an instance of `SchemaBuilder` class ancestor.
105
186
  * @param schema schema to be added as an option.
106
187
  */
107
- or<T extends SchemaBuilder<any, any>>(schema: T): UnionSchemaBuilder<[...TOptions, T], TRequired, TExplicitType>;
188
+ or<T extends SchemaBuilder<any, any, any, any, any>>(schema: T): UnionSchemaBuilder<[
189
+ ...TOptions,
190
+ T
191
+ ], TRequired, TNullable, TExplicitType, THasDefault, TExtensions> & TExtensions;
108
192
  /**
109
193
  * Removes option by its `index`. If `index` is out of bounds,
110
194
  * an error is thrown.
111
195
  * @param index index of the option, starting from `0`.
112
196
  */
113
- removeOption<T extends number>(index: T): UnionSchemaBuilder<TakeExceptIndex<TOptions, T>, TRequired, TExplicitType>;
197
+ removeOption<T extends number>(index: T): UnionSchemaBuilder<TakeExceptIndex<TOptions, T>, TRequired, TNullable, TExplicitType, THasDefault, TExtensions> & TExtensions;
114
198
  /**
115
199
  * Removes first option from the union schema.
116
200
  */
117
201
  removeFirstOption(): TOptions extends [
118
- infer TFirst,
119
- ...infer TRest extends SchemaBuilder<any, any>[]
120
- ] ? UnionSchemaBuilder<TRest, TRequired, TExplicitType> : never;
202
+ infer _,
203
+ ...infer TRest extends SchemaBuilder<any, any, any, any, any>[]
204
+ ] ? UnionSchemaBuilder<TRest, TRequired, TNullable, TExplicitType, THasDefault, TExtensions> & TExtensions : never;
121
205
  /**
122
206
  * Removes all options and replaces them by single `schema` option.
123
207
  * Equivalent to `union(schema)` function, but could be useful in some cases.
124
208
  * @param schema schema to be added as a single option to the new schema.
125
209
  */
126
- reset<T extends SchemaBuilder<any, any>>(schema: T): UnionSchemaBuilder<[T], TRequired, TExplicitType>;
210
+ reset<T extends SchemaBuilder<any, any, any, any, any>>(schema: T): UnionSchemaBuilder<[
211
+ T
212
+ ], TRequired, TNullable, TExplicitType, THasDefault, TExtensions> & TExtensions;
213
+ /**
214
+ * @hidden
215
+ */
216
+ nullable(): UnionSchemaBuilder<TOptions, TRequired, true, TExplicitType, THasDefault, TExtensions> & TExtensions;
217
+ /**
218
+ * @hidden
219
+ */
220
+ notNullable(): UnionSchemaBuilder<TOptions, TRequired, false, TExplicitType, THasDefault, TExtensions> & TExtensions;
127
221
  }
128
222
  /**
129
223
  * Creates a union schema.
130
- * @param schema required and will be considered as a first option for the union shchema.
224
+ * @param schema required and will be considered as a first option for the union schema.
131
225
  */
132
- export declare const union: <T extends SchemaBuilder<any, any>>(schema: T) => UnionSchemaBuilder<[T], true, undefined>;
226
+ export declare const union: <T extends SchemaBuilder<any, any, any, any, any>>(schema: T) => UnionSchemaBuilder<[T]>;
133
227
  export {};
@@ -1,216 +1,2 @@
1
- import { SchemaBuilder } from './SchemaBuilder.js';
2
- /**
3
- * Union schema builder class. Allows to create schemas
4
- * containing alternatives. E.g. string | number | Date.
5
- * Use it when you want to define a schema for a value
6
- * that can be of different types. The type of the value
7
- * will be determined by the first schema that succeeds
8
- * validation. Any schema type can be supplied as variant.
9
- * Which means that you are not limited to primitive types and
10
- * can construct complex types as well, e.g. object | array.
11
- *
12
- * **NOTE** this class is exported only to give opportunity to extend it
13
- * by inheriting. It is not recommended to create an instance of this class
14
- * directly. Use {@link union | union()} function instead.
15
- *
16
- * @example
17
- * ```ts
18
- * const schema = union(string('foo')).or(string('bar'));
19
- * const result = await schema.validate('foo');
20
- * // result.valid === true
21
- * // result.object === 'foo'
22
- * ```
23
- *
24
- * @example
25
- * ```ts
26
- * const schema = union(string('foo')).or(string('bar'));
27
- * const result = await schema.validate('baz');
28
- * // result.valid === false
29
- * ```
30
- *
31
- * @example
32
- * ```ts
33
- * const schema = union(string('yes')).or(string('no')).or(number(0)).or(number(1));
34
- * // equals to 'yes' | 'no' | 0 | 1 in TS
35
- * const result = await schema.validate('yes');
36
- * // result.valid === true
37
- * // result.object === 'yes'
38
- *
39
- * const result2 = await schema.validate(0);
40
- * // result2.valid === true
41
- * // result2.object === 0
42
- *
43
- * const result3 = await schema.validate('baz');
44
- * // result3.valid === false
45
- *
46
- * const result4 = await schema.validate(2);
47
- * // result4.valid === false
48
- * ```
49
- *
50
- * @see {@link union}
51
- */
52
- export class UnionSchemaBuilder extends SchemaBuilder {
53
- #options;
54
- static create(props) {
55
- return new UnionSchemaBuilder({
56
- type: 'union',
57
- ...props
58
- });
59
- }
60
- constructor(props) {
61
- super(props);
62
- if (Array.isArray(props.options)) {
63
- this.#options = props.options;
64
- }
65
- }
66
- introspect() {
67
- return {
68
- ...super.introspect(),
69
- /**
70
- * Array of schemas participating in the union.
71
- */
72
- options: this.#options
73
- };
74
- }
75
- /**
76
- * @hidden
77
- */
78
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
79
- hasType(notUsed) {
80
- return this.createFromProps({
81
- ...this.introspect()
82
- });
83
- }
84
- /**
85
- * @hidden
86
- */
87
- clearHasType() {
88
- return this.createFromProps({
89
- ...this.introspect()
90
- });
91
- }
92
- /**
93
- * Performs validion of the union schema over `object`.
94
- * @param context Optional `ValidationContext` settings.
95
- */
96
- async validate(object, context) {
97
- const superResult = await super.preValidate(object, context);
98
- const { valid, transaction: preValidationTransaction, context: prevalidationContext, errors } = superResult;
99
- const { path } = prevalidationContext;
100
- if (!valid) {
101
- return {
102
- valid,
103
- errors
104
- };
105
- }
106
- let { object: { validatedObject: objToValidate } } = preValidationTransaction;
107
- if (!this.isRequired &&
108
- (typeof objToValidate === 'undefined' || objToValidate === null)) {
109
- return {
110
- valid: true,
111
- object: objToValidate
112
- };
113
- }
114
- let minErrorsCount = Number.MAX_SAFE_INTEGER;
115
- let resultingErrors = [];
116
- for (let i = 0; i < this.#options.length; i++) {
117
- const { valid, errors, object: validatedOption } = await this.#options[i].validate(objToValidate, {
118
- ...prevalidationContext,
119
- path: `${path}[option ${i}]`
120
- });
121
- if (valid) {
122
- return {
123
- valid: true,
124
- object: validatedOption
125
- };
126
- }
127
- else {
128
- if (Array.isArray(errors) &&
129
- errors.length > 0 &&
130
- errors.length < minErrorsCount) {
131
- resultingErrors = errors;
132
- minErrorsCount = errors.length;
133
- }
134
- objToValidate =
135
- preValidationTransaction.rollback().validatedObject;
136
- }
137
- }
138
- return {
139
- valid: false,
140
- errors: resultingErrors
141
- };
142
- }
143
- createFromProps(props) {
144
- return UnionSchemaBuilder.create(props);
145
- }
146
- /**
147
- * @hidden
148
- */
149
- required() {
150
- return super.required();
151
- }
152
- /**
153
- * @hidden
154
- */
155
- optional() {
156
- return super.optional();
157
- }
158
- /**
159
- * Adds a new schema option described by `schema`.
160
- * schema must be an instance of `SchemaBuilder` class ancestor.
161
- * @param schema schema to be added as an option.
162
- */
163
- or(schema) {
164
- if (!(schema instanceof SchemaBuilder)) {
165
- throw new Error('schema must be an instance of the SchemaBuilder class');
166
- }
167
- return this.createFromProps({
168
- ...this.introspect(),
169
- options: [...this.#options, schema]
170
- });
171
- }
172
- /**
173
- * Removes option by its `index`. If `index` is out of bounds,
174
- * an error is thrown.
175
- * @param index index of the option, starting from `0`.
176
- */
177
- removeOption(index) {
178
- if (typeof index !== 'number' ||
179
- index < 0 ||
180
- index > this.#options.length) {
181
- throw new Error('index must be >= 0 and <= count of the options');
182
- }
183
- return this.createFromProps({
184
- ...this.introspect(),
185
- options: this.#options.filter((v, i) => i !== index)
186
- });
187
- }
188
- /**
189
- * Removes first option from the union schema.
190
- */
191
- removeFirstOption() {
192
- return this.removeOption(0);
193
- }
194
- /**
195
- * Removes all options and replaces them by single `schema` option.
196
- * Equivalent to `union(schema)` function, but could be useful in some cases.
197
- * @param schema schema to be added as a single option to the new schema.
198
- */
199
- reset(schema) {
200
- if (!(schema instanceof SchemaBuilder)) {
201
- throw new Error('schema must be an instance of the SchemaBuilder class');
202
- }
203
- return this.createFromProps({
204
- ...this.introspect(),
205
- options: [schema]
206
- });
207
- }
208
- }
209
- /**
210
- * Creates a union schema.
211
- * @param schema required and will be considered as a first option for the union shchema.
212
- */
213
- export const union = (schema) => UnionSchemaBuilder.create({
214
- isRequired: true,
215
- options: [schema]
216
- });
1
+ import{a,b}from"../chunk-ZUPYV5TI.js";import"../chunk-CTP4RHDG.js";export{a as UnionSchemaBuilder,b as union};
2
+ //# sourceMappingURL=UnionSchemaBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ import{a as R,b as g}from"./chunk-KESLT5EE.js";import{a as V,b as N}from"./chunk-NXPH3ZUW.js";import{a as M,b as D}from"./chunk-IJ6FO37G.js";import{a as P,b as C}from"./chunk-FWUEUW2N.js";import{a as v,b as F}from"./chunk-S5TC6NSU.js";import{a as j,b as A}from"./chunk-ZUPYV5TI.js";import{a as T,b as p}from"./chunk-ZAN4ZHCJ.js";import{a as x,b as m}from"./chunk-Z7QZ7IXT.js";import{a as E,b as h}from"./chunk-KFTO7TMB.js";import{a as f,b}from"./chunk-YWBNVHR6.js";import{a as S,b as B}from"./chunk-EUQ5AE3H.js";import{f as o}from"./chunk-CTP4RHDG.js";var d=class l extends o{#e;#t=null;static create(e){return new l({type:"lazy",...e})}constructor(e){if(super(e),typeof e.getter!="function")throw new Error("LazySchemaBuilder: getter must be a function");this.#e=e.getter}resolve(){return this.#t===null&&(this.#t=this.#e()),this.#t}introspect(){return{...super.introspect(),getter:this.#e}}#n(e,t){let{valid:r,transaction:a,errors:i}=e;if(!r)return{valid:r,errors:i};let{object:{validatedObject:s}}=a;return s==null?{valid:!0,object:s}:this.resolve().validate(s,t)}validate(e,t){return super.validate(e,t)}async validateAsync(e,t){return super.validateAsync(e,t)}_validate(e,t){return this.#n(this.preValidateSync(e,t),t)}async _validateAsync(e,t){let r=await super.preValidateAsync(e,t),{valid:a,transaction:i,errors:s}=r;if(!a)return{valid:a,errors:s};let{object:{validatedObject:n}}=i;return n==null?{valid:!0,object:n}:this.resolve().validateAsync(n,t)}createFromProps(e){return l.create(e)}hasType(e){return this.createFromProps({...this.introspect()})}clearHasType(){return this.createFromProps({...this.introspect()})}required(e){return super.required(e)}optional(){return super.optional()}default(e){return super.default(e)}clearDefault(){return super.clearDefault()}brand(e){return super.brand(e)}readonly(){return super.readonly()}nullable(){return super.nullable()}notNullable(){return super.notNullable()}};function w(l){return d.create({type:"lazy",isRequired:!0,preprocessors:[],validators:[],getter:l})}var c=class l extends o{static create(e){return new l({type:"null",...e})}constructor(e){super(e)}hasType(e){return this.createFromProps({...this.introspect()})}clearHasType(){return this.createFromProps({...this.introspect()})}#e(e){return e===null?{valid:!0,object:null}:e===void 0&&this.hasDefault?this.resolveDefaultValue()===null?{valid:!0,object:null}:{valid:!1,errors:[{message:"must be null"}]}:e===void 0&&!this.isRequired?{valid:!0,object:void 0}:{valid:!1,errors:[{message:"must be null"}]}}validate(e,t){return super.validate(e,t)}async validateAsync(e,t){return super.validateAsync(e,t)}_validate(e,t){return this.#e(e)}async _validateAsync(e,t){return this.#e(e)}createFromProps(e){return l.create(e)}required(e){return super.required(e)}optional(){return super.optional()}default(e){return super.default(e)}clearDefault(){return super.clearDefault()}brand(e){return super.brand(e)}readonly(){return super.readonly()}nullable(){return super.nullable()}notNullable(){return super.notNullable()}},H=()=>c.create({isRequired:!0});var y={string:P,number:R,boolean:E,date:f,object:V,array:x,tuple:v,record:M,union:j,func:S,any:T},q={string:C,number:g,boolean:h,date:b,object:N,array:m,tuple:F,record:D,union:A,func:B,any:p},O=new Set(["validate","validateAsync","parse","parseAsync","safeParse","safeParseAsync","introspect","optional","required","addPreprocessor","clearPreprocessors","addValidator","clearValidators","hasType","clearHasType","createFromProps","preValidate","preValidateSync","preValidateAsync","getValidationErrorMessage","getValidationErrorMessageSync","assureValidationErrorMessageProvider","withExtension","getExtension"]);function z(l){let e={};for(let t of Object.keys(l)){if(!(t in y))throw new Error(`Unknown builder type "${t}". Valid types: ${Object.keys(y).join(", ")}`);let r=l[t];if(!r||typeof r!="object")throw new Error(`Extension config for "${t}" must be an object of methods`);e[t]={};for(let a of Object.keys(r)){if(O.has(a))throw new Error(`Cannot override reserved method "${a}" on "${t}"`);let i=r[a];if(typeof i!="function")throw new Error(`Extension method "${t}.${a}" must be a function`);e[t][a]=function(...s){let n=i.apply(this,s);return n&&typeof n=="object"&&typeof n.withExtension=="function"&&(typeof n.getExtension!="function"||n.getExtension(a)===void 0)?n.withExtension(a,s.length===1?s[0]:s.length===0?!0:s):n}}}return{config:e}}function L(...l){let e=new Map;for(let r of l)for(let a of Object.keys(r.config)){e.has(a)||e.set(a,new Map);let i=e.get(a),s=r.config[a];for(let n of Object.keys(s)){if(i.has(n))throw new Error(`Extension method collision: "${n}" is defined by multiple extensions for "${a}"`);i.set(n,s[n])}}let t={};for(let r of Object.keys(y)){let a=e.get(r);if(!a||a.size===0){t[r]=q[r];continue}let i=y[r],s=class extends i{constructor(...n){super(...n)}static create(n){return new s({...n})}createFromProps(n){return s.create(n)}};for(let[n,u]of a)Object.defineProperty(s.prototype,n,{value:u,writable:!0,configurable:!0,enumerable:!1});t[r]=(...n)=>{let u=q[r](...n);return Object.setPrototypeOf(u,s.prototype),u}}return t}export{d as a,w as b,c,H as d,z as e,L as f};
2
+ //# sourceMappingURL=chunk-BFCMDDTX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/builders/LazySchemaBuilder.ts","../src/builders/NullSchemaBuilder.ts","../src/extension.ts"],"sourcesContent":["import {\n type BRAND,\n SchemaBuilder,\n type ValidationContext,\n type ValidationErrorMessageProvider,\n type ValidationResult\n} from './SchemaBuilder.js';\n\ntype LazySchemaBuilderCreateProps<R extends boolean = true> = Partial<\n ReturnType<LazySchemaBuilder<any, R>['introspect']>\n>;\n\n/**\n * Lazy schema builder class. Allows defining recursive/self-referential schemas\n * by wrapping a getter function that returns the target schema. The getter is\n * called once on first validation and the result is cached.\n *\n * This is the primary mechanism for building recursive data structures such as\n * tree nodes, nested menus, and threaded comments.\n *\n * **NOTE** TypeScript cannot infer recursive types automatically, so you must\n * provide an explicit type annotation on the variable holding the schema:\n *\n * @example\n * ```ts\n * type TreeNode = { value: number; children: TreeNode[] };\n *\n * const treeNode: SchemaBuilder<TreeNode, true> = object({\n * value: number(),\n * children: array(lazy(() => treeNode))\n * });\n *\n * treeNode.validate({ value: 1, children: [{ value: 2, children: [] }] });\n * // { valid: true, object: { value: 1, children: [{ value: 2, children: [] }] } }\n * ```\n *\n * @example\n * ```ts\n * type Comment = { text: string; replies: Comment[] };\n *\n * const commentSchema: SchemaBuilder<Comment, true> = object({\n * text: string(),\n * replies: array(lazy(() => commentSchema))\n * });\n * ```\n */\nexport class LazySchemaBuilder<\n TResult = any,\n TRequired extends boolean = true,\n TNullable extends boolean = false,\n THasDefault extends boolean = false,\n TExtensions = {}\n> extends SchemaBuilder<\n TResult,\n TRequired,\n TNullable,\n THasDefault,\n TExtensions\n> {\n #getter: () => SchemaBuilder<TResult, any, any>;\n #resolvedSchema: SchemaBuilder<TResult, any, any> | null = null;\n\n /**\n * @hidden\n */\n public static create(props: LazySchemaBuilderCreateProps<any>) {\n return new LazySchemaBuilder({\n type: 'lazy',\n ...props\n });\n }\n\n protected constructor(props: LazySchemaBuilderCreateProps<TRequired>) {\n super(props as any);\n if (typeof (props as any).getter !== 'function') {\n throw new Error('LazySchemaBuilder: getter must be a function');\n }\n this.#getter = (props as any).getter;\n }\n\n /**\n * Resolves the lazy schema by calling the getter (once; result is cached).\n * After the first call subsequent calls return the cached schema instance.\n */\n public resolve(): SchemaBuilder<TResult, any, any> {\n if (this.#resolvedSchema === null) {\n this.#resolvedSchema = this.#getter();\n }\n return this.#resolvedSchema;\n }\n\n /**\n * @inheritdoc\n */\n public introspect() {\n return {\n ...super.introspect(),\n /**\n * The getter function that returns the lazily-resolved schema.\n * Call {@link LazySchemaBuilder.resolve} to obtain the schema instance.\n */\n getter: this.#getter\n };\n }\n\n #buildResult(\n superResult: ReturnType<LazySchemaBuilder['preValidateSync']>,\n context?: ValidationContext\n ): ValidationResult<TResult> {\n const {\n valid,\n transaction: preValidationTransaction,\n errors\n } = superResult;\n\n if (!valid) {\n return { valid, errors };\n }\n\n const {\n object: { validatedObject: objToValidate }\n } = preValidationTransaction!;\n\n // Value is null/undefined and the schema is optional — skip delegation.\n if (objToValidate == null) {\n return { valid: true, object: objToValidate };\n }\n\n return this.resolve().validate(\n objToValidate,\n context\n ) as ValidationResult<TResult>;\n }\n\n /** {@inheritDoc SchemaBuilder.validate} */\n public validate(\n object: TResult,\n context?: ValidationContext\n ): ValidationResult<TResult> {\n return super.validate(object, context) as ValidationResult<TResult>;\n }\n\n /** {@inheritDoc SchemaBuilder.validateAsync} */\n public async validateAsync(\n object: TResult,\n context?: ValidationContext\n ): Promise<ValidationResult<TResult>> {\n return super.validateAsync(object, context) as Promise<\n ValidationResult<TResult>\n >;\n }\n\n /**\n * Performs synchronous validation of the schema over `object`.\n * Throws if any preprocessor, validator, or error message provider returns a Promise.\n * @param context Optional `ValidationContext` settings.\n */\n protected _validate(\n object: TResult,\n context?: ValidationContext\n ): ValidationResult<TResult> {\n return this.#buildResult(\n this.preValidateSync(object, context),\n context\n );\n }\n\n /**\n * Performs async validation of the schema over `object`.\n * Supports async preprocessors, validators, and error message providers.\n * @param context Optional `ValidationContext` settings.\n */\n protected async _validateAsync(\n object: TResult,\n context?: ValidationContext\n ): Promise<ValidationResult<TResult>> {\n const superResult = await super.preValidateAsync(object, context);\n\n const {\n valid,\n transaction: preValidationTransaction,\n errors\n } = superResult;\n\n if (!valid) {\n return { valid, errors };\n }\n\n const {\n object: { validatedObject: objToValidate }\n } = preValidationTransaction!;\n\n if (objToValidate == null) {\n return { valid: true, object: objToValidate };\n }\n\n return this.resolve().validateAsync(objToValidate, context) as Promise<\n ValidationResult<TResult>\n >;\n }\n\n protected createFromProps<TReq extends boolean>(\n props: LazySchemaBuilderCreateProps<TReq>\n ): this {\n return LazySchemaBuilder.create(props as any) as any;\n }\n\n /**\n * @inheritdoc\n */\n public hasType<T>(\n _notUsed?: T\n ): LazySchemaBuilder<T, true, TNullable, THasDefault, TExtensions> &\n TExtensions {\n return this.createFromProps({\n ...this.introspect()\n } as any) as any;\n }\n\n /**\n * @inheritdoc\n */\n public clearHasType(): LazySchemaBuilder<\n TResult,\n TRequired,\n TNullable,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return this.createFromProps({\n ...this.introspect()\n } as any) as any;\n }\n\n /**\n * @hidden\n */\n public required(\n errorMessage?: ValidationErrorMessageProvider\n ): LazySchemaBuilder<TResult, true, TNullable, THasDefault, TExtensions> &\n TExtensions {\n return super.required(errorMessage);\n }\n\n /**\n * @hidden\n */\n public optional(): LazySchemaBuilder<\n TResult,\n false,\n TNullable,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.optional();\n }\n\n /**\n * @hidden\n */\n public default(\n value: TResult | (() => TResult)\n ): LazySchemaBuilder<TResult, true, TNullable, true, TExtensions> &\n TExtensions {\n return super.default(value) as any;\n }\n\n /**\n * @hidden\n */\n public clearDefault(): LazySchemaBuilder<\n TResult,\n TRequired,\n TNullable,\n false,\n TExtensions\n > &\n TExtensions {\n return super.clearDefault() as any;\n }\n\n /**\n * @hidden\n */\n public brand<TBrand extends string | symbol>(\n _name?: TBrand\n ): LazySchemaBuilder<\n TResult & { readonly [K in BRAND]: TBrand },\n TRequired,\n TNullable,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.brand(_name);\n }\n\n /**\n * @hidden\n */\n public readonly(): LazySchemaBuilder<\n Readonly<TResult>,\n TRequired,\n TNullable,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.readonly();\n }\n\n /**\n * @hidden\n */\n public nullable(): LazySchemaBuilder<\n TResult,\n TRequired,\n true,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.nullable() as any;\n }\n\n /**\n * @hidden\n */\n public notNullable(): LazySchemaBuilder<\n TResult,\n TRequired,\n false,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.notNullable() as any;\n }\n}\n\n/**\n * Creates a lazy schema that defers the schema definition until first validation.\n * Use this to define recursive/self-referential schemas.\n *\n * The getter function is called **once** on first use and the result is cached.\n * You **must** provide an explicit TypeScript type annotation on the variable\n * holding the outer schema — TypeScript cannot infer recursive types automatically.\n *\n * @param getter - A function that returns the schema to use for validation.\n *\n * @example\n * ```ts\n * // Tree structure\n * type TreeNode = { value: number; children: TreeNode[] };\n *\n * const treeNode: SchemaBuilder<TreeNode, true> = object({\n * value: number(),\n * children: array(lazy(() => treeNode))\n * });\n * ```\n *\n * @example\n * ```ts\n * // Optional recursive field (submenu)\n * type MenuItem = { label: string; submenu?: MenuItem[] };\n *\n * const menuItem: SchemaBuilder<MenuItem, true> = object({\n * label: string(),\n * submenu: array(lazy(() => menuItem)).optional()\n * });\n * ```\n */\nexport function lazy<TResult>(\n getter: () => SchemaBuilder<TResult, any, any>\n): LazySchemaBuilder<TResult, true, false, false, {}> {\n return LazySchemaBuilder.create({\n type: 'lazy',\n isRequired: true,\n preprocessors: [],\n validators: [],\n getter\n } as any);\n}\n","import {\n type BRAND,\n SchemaBuilder,\n type ValidationContext,\n type ValidationErrorMessageProvider,\n type ValidationResult\n} from './SchemaBuilder.js';\n\ntype NullSchemaBuilderCreateProps<R extends boolean = true> = Partial<\n ReturnType<NullSchemaBuilder<R>['introspect']>\n>;\n\n/**\n * Schema builder for `null` values. Validates that the input is exactly `null`.\n *\n * When required (the default), only `null` is accepted. When optional (via\n * `.optional()`), both `null` and `undefined` are accepted; any other value\n * is rejected.\n *\n * This builder is useful when you need to represent an explicitly-null field\n * in a typed schema, for example in discriminated-union branches or when\n * modelling a JSON payload that may carry a JSON `null` value.\n *\n * **NOTE** this class is exported only to give opportunity to extend it\n * by inheriting. It is not recommended to create an instance of this class\n * directly. Use {@link nul | nul()} function instead.\n *\n * @example\n * ```ts\n * import { nul } from '@cleverbrush/schema';\n *\n * const schema = nul();\n *\n * schema.validate(null); // { valid: true, object: null }\n * schema.validate(undefined); // { valid: false }\n * schema.validate(0); // { valid: false }\n * schema.validate(''); // { valid: false }\n * ```\n *\n * @example\n * ```ts\n * // Optional — accepts null or undefined\n * const schema = nul().optional();\n *\n * schema.validate(null); // { valid: true, object: null }\n * schema.validate(undefined); // { valid: true, object: undefined }\n * schema.validate(false); // { valid: false }\n * ```\n *\n * @example\n * ```ts\n * // Use inside a union to model a nullable string field\n * import { union, string, nul, InferType } from '@cleverbrush/schema';\n *\n * const NullableString = union(string()).or(nul());\n * type NullableString = InferType<typeof NullableString>;\n * // string | null\n *\n * NullableString.validate('hello'); // valid\n * NullableString.validate(null); // valid\n * NullableString.validate(42); // invalid\n * ```\n *\n * @see {@link nul}\n */\nexport class NullSchemaBuilder<\n TRequired extends boolean = true,\n TNullable extends boolean = false,\n TExplicitType = undefined,\n THasDefault extends boolean = false,\n TExtensions = {}\n> extends SchemaBuilder<null, TRequired, TNullable, THasDefault, TExtensions> {\n /**\n * @hidden\n */\n public static create(props: NullSchemaBuilderCreateProps<any>) {\n return new NullSchemaBuilder({\n type: 'null',\n ...props\n });\n }\n\n protected constructor(props: NullSchemaBuilderCreateProps<TRequired>) {\n super(props as any);\n }\n\n /**\n * @hidden\n */\n public hasType<T>(\n _notUsed?: T\n ): NullSchemaBuilder<true, TNullable, T, THasDefault, TExtensions> &\n TExtensions {\n return this.createFromProps({\n ...this.introspect()\n } as any) as any;\n }\n\n /**\n * @hidden\n */\n public clearHasType(): NullSchemaBuilder<\n TRequired,\n TNullable,\n undefined,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return this.createFromProps({\n ...this.introspect()\n } as any) as any;\n }\n\n // The SchemaBuilder base-class preValidateSync/preValidateAsync treats\n // null as an invalid value for required schemas, which would prevent null\n // from ever passing validation here. We therefore bypass preValidateSync\n // entirely and implement the full (and simple) validation inline.\n #buildResult(object: any): ValidationResult<null> {\n if (object === null) return { valid: true, object: null };\n\n if (object === undefined && this.hasDefault) {\n const defaultVal = this.resolveDefaultValue();\n if (defaultVal === null) return { valid: true, object: null };\n return { valid: false, errors: [{ message: 'must be null' }] };\n }\n\n if (object === undefined && !this.isRequired) {\n return { valid: true, object: undefined as any };\n }\n\n return {\n valid: false,\n errors: [{ message: 'must be null' }]\n };\n }\n\n /** {@inheritDoc SchemaBuilder.validate} */\n public validate(\n object: null,\n context?: ValidationContext\n ): ValidationResult<null> {\n return super.validate(object, context) as ValidationResult<null>;\n }\n\n /** {@inheritDoc SchemaBuilder.validateAsync} */\n public async validateAsync(\n object: null,\n context?: ValidationContext\n ): Promise<ValidationResult<null>> {\n return super.validateAsync(object, context) as Promise<\n ValidationResult<null>\n >;\n }\n\n /**\n * Performs synchronous validation of the schema over `object`.\n * @param context Optional `ValidationContext` settings.\n */\n protected _validate(\n object: null,\n _context?: ValidationContext\n ): ValidationResult<null> {\n return this.#buildResult(object);\n }\n\n /**\n * Performs async validation of the schema over `object`.\n * @param context Optional `ValidationContext` settings.\n */\n protected async _validateAsync(\n object: null,\n _context?: ValidationContext\n ): Promise<ValidationResult<null>> {\n return this.#buildResult(object);\n }\n\n protected createFromProps<TReq extends boolean>(\n props: NullSchemaBuilderCreateProps<TReq>\n ): this {\n return NullSchemaBuilder.create(props as any) as any;\n }\n\n /**\n * @hidden\n */\n public required(\n errorMessage?: ValidationErrorMessageProvider\n ): NullSchemaBuilder<\n true,\n TNullable,\n TExplicitType,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.required(errorMessage);\n }\n\n /**\n * @hidden\n */\n public optional(): NullSchemaBuilder<\n false,\n TNullable,\n TExplicitType,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.optional();\n }\n\n /**\n * @hidden\n */\n public default(\n value: null | (() => null)\n ): NullSchemaBuilder<true, TNullable, TExplicitType, true, TExtensions> &\n TExtensions {\n return super.default(value) as any;\n }\n\n /**\n * @hidden\n */\n public clearDefault(): NullSchemaBuilder<\n TRequired,\n TNullable,\n TExplicitType,\n false,\n TExtensions\n > &\n TExtensions {\n return super.clearDefault() as any;\n }\n\n /**\n * @hidden\n */\n public brand<TBrand extends string | symbol>(\n _name?: TBrand\n ): NullSchemaBuilder<\n TRequired,\n TNullable,\n null & { readonly [K in BRAND]: TBrand },\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.brand(_name);\n }\n\n /**\n * Marks the inferred type as `Readonly<null>`. Since `null` is already\n * immutable this is an identity operation, but it sets the `isReadonly`\n * introspection flag for tooling consistency.\n *\n * @see {@link SchemaBuilder.readonly}\n */\n public readonly(): NullSchemaBuilder<\n TRequired,\n TNullable,\n Readonly<null>,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.readonly();\n }\n\n /**\n * @hidden\n */\n public nullable(): NullSchemaBuilder<\n TRequired,\n true,\n TExplicitType,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.nullable() as any;\n }\n\n /**\n * @hidden\n */\n public notNullable(): NullSchemaBuilder<\n TRequired,\n false,\n TExplicitType,\n THasDefault,\n TExtensions\n > &\n TExtensions {\n return super.notNullable() as any;\n }\n}\n\n/**\n * Creates a schema that validates the value is exactly `null`.\n *\n * By default the schema is **required** — only `null` is accepted.\n * Call `.optional()` to also allow `undefined`.\n *\n * @example\n * ```ts\n * import { nul } from '@cleverbrush/schema';\n *\n * nul().validate(null); // { valid: true, object: null }\n * nul().validate(undefined); // { valid: false }\n * nul().validate(0); // { valid: false }\n * ```\n *\n * @example\n * ```ts\n * nul().optional().validate(null); // { valid: true, object: null }\n * nul().optional().validate(undefined); // { valid: true, object: undefined }\n * nul().optional().validate(false); // { valid: false }\n * ```\n *\n * @example\n * ```ts\n * // Nullable field in an object schema\n * import { object, string, nul, union, InferType } from '@cleverbrush/schema';\n *\n * const Schema = object({\n * name: string(),\n * deleted: union(nul()).or(string()), // null | string\n * });\n *\n * type T = InferType<typeof Schema>;\n * // { name: string; deleted: null | string }\n * ```\n */\nexport const nul = () =>\n NullSchemaBuilder.create({\n isRequired: true\n }) as NullSchemaBuilder<true>;\n","/**\n * @module extension\n *\n * The **extension system** for `@cleverbrush/schema` allows third-party and\n * first-party code to add custom methods to any schema builder type\n * (`string`, `number`, `date`, `object`, …) without modifying the core\n * library.\n *\n * ## Overview\n *\n * Extensions follow a two-step workflow:\n *\n * 1. **Define** an extension with {@link defineExtension} — declare which\n * builder types it targets and what methods it adds.\n * 2. **Apply** one or more extensions with {@link withExtensions} — get back\n * augmented factory functions (`string()`, `number()`, …) whose return\n * types include the new methods.\n *\n * ## Ergonomic authoring\n *\n * Extension methods do **not** need to call `withExtension()` manually.\n * The system automatically attaches metadata using the method name as the\n * extension key and the method arguments as the value. This keeps extension\n * definitions concise:\n *\n * ```ts\n * const slugExt = defineExtension({\n * string: {\n * slug(this: StringSchemaBuilder) {\n * return this.addValidator((val) => {\n * const valid = /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(val);\n * return { valid, errors: valid ? [] : [{ message: 'must be a valid URL slug' }] };\n * });\n * }\n * }\n * });\n * ```\n *\n * If you need **custom metadata** (e.g. a different key or a transformed\n * value), call `this.withExtension(key, value)` explicitly — the auto-infer\n * logic will detect the existing key and skip the automatic attachment.\n *\n * ## Stacking and composition\n *\n * Multiple extensions can target the same builder type. Pass them all to\n * `withExtensions()` and the methods are merged. A runtime error is thrown\n * if two extensions define the same method name on the same builder type.\n *\n * ```ts\n * const s = withExtensions(emailExt, slugExt, rangeExt);\n * const schema = s.string().email().slug(); // both methods available\n * ```\n *\n * ## Introspection\n *\n * Extension metadata is accessible via `schema.introspect().extensions`.\n * Each key corresponds to an extension method name and its value is whatever\n * was passed (or auto-inferred) as the extension data.\n *\n * @see {@link defineExtension} — define an extension\n * @see {@link withExtensions} — apply extensions to builder factories\n * @see {@link ExtensionConfig} — shape of the configuration object\n * @see {@link ExtensionDescriptor} — branded descriptor returned by `defineExtension`\n */\nimport { AnySchemaBuilder, any } from './builders/AnySchemaBuilder.js';\nimport { ArraySchemaBuilder, array } from './builders/ArraySchemaBuilder.js';\nimport {\n BooleanSchemaBuilder,\n boolean\n} from './builders/BooleanSchemaBuilder.js';\nimport { DateSchemaBuilder, date } from './builders/DateSchemaBuilder.js';\nimport {\n FunctionSchemaBuilder,\n func\n} from './builders/FunctionSchemaBuilder.js';\nimport { NumberSchemaBuilder, number } from './builders/NumberSchemaBuilder.js';\nimport { ObjectSchemaBuilder, object } from './builders/ObjectSchemaBuilder.js';\nimport { RecordSchemaBuilder, record } from './builders/RecordSchemaBuilder.js';\nimport type { SchemaBuilder } from './builders/SchemaBuilder.js';\nimport { StringSchemaBuilder, string } from './builders/StringSchemaBuilder.js';\nimport { TupleSchemaBuilder, tuple } from './builders/TupleSchemaBuilder.js';\nimport { UnionSchemaBuilder, union } from './builders/UnionSchemaBuilder.js';\n\n// ---------------------------------------------------------------------------\n// Builder type name mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Maps each builder type name to the corresponding generic builder class.\n *\n * Used internally to type-check extension method `this` bindings — for\n * example, an extension targeting `\"string\"` receives `this: StringSchemaBuilder`.\n *\n * @internal Not exported — used only by the extension type machinery.\n */\ntype BuilderMap = {\n string: StringSchemaBuilder<any, any, any, any, any>;\n number: NumberSchemaBuilder<any, any, any, any, any>;\n boolean: BooleanSchemaBuilder<any, any, any, any, any, any, any>;\n date: DateSchemaBuilder<any, any, any, any, any>;\n object: ObjectSchemaBuilder<any, any, any, any, any, any>;\n array: ArraySchemaBuilder<any, any, any, any, any, any, any>;\n tuple: TupleSchemaBuilder<any, any, any, any, any, any, any>;\n record: RecordSchemaBuilder<any, any, any, any, any, any, any>;\n union: UnionSchemaBuilder<any, any, any, any, any, any>;\n func: FunctionSchemaBuilder<any, any, any, any, any>;\n any: AnySchemaBuilder<any, any, any, any, any, any>;\n};\n\ntype BuilderTypeName = keyof BuilderMap;\n\n// Runtime mapping from type name to the actual class constructor\nconst builderClasses: Record<BuilderTypeName, typeof SchemaBuilder> = {\n string: StringSchemaBuilder as any,\n number: NumberSchemaBuilder as any,\n boolean: BooleanSchemaBuilder as any,\n date: DateSchemaBuilder as any,\n object: ObjectSchemaBuilder as any,\n array: ArraySchemaBuilder as any,\n tuple: TupleSchemaBuilder as any,\n record: RecordSchemaBuilder as any,\n union: UnionSchemaBuilder as any,\n func: FunctionSchemaBuilder as any,\n any: AnySchemaBuilder as any\n};\n\n// Runtime mapping from type name to factory function\nconst builderFactories: Record<BuilderTypeName, (...args: any[]) => any> = {\n string,\n number,\n boolean,\n date,\n object,\n array,\n tuple,\n record,\n union,\n func,\n any\n};\n\n// ---------------------------------------------------------------------------\n// Extension configuration types\n// ---------------------------------------------------------------------------\n\n/**\n * Defines the shape of an extension configuration object passed to\n * {@link defineExtension}.\n *\n * Each key is a **builder type name** — one of `\"string\"`, `\"number\"`,\n * `\"boolean\"`, `\"date\"`, `\"object\"`, `\"array\"`, `\"union\"`, `\"func\"`, or\n * `\"any\"`. The value is a record of **method implementations** to add to\n * that builder type.\n *\n * Method implementations receive `this` bound to the target builder instance\n * (e.g. `StringSchemaBuilder` for the `\"string\"` key) and **must** return a\n * builder of the same type to support fluent chaining.\n *\n * @remarks\n * Extension methods that only add validators/preprocessors do not need to\n * call `this.withExtension()` — the system will auto-attach metadata using\n * the method name as the key and the arguments as the value. Call\n * `this.withExtension(key, value)` explicitly only when you need custom\n * metadata (e.g. a transformed value or a different key).\n *\n * @example\n * ```ts\n * // Minimal extension config — auto-inferred metadata\n * const config: ExtensionConfig = {\n * string: {\n * slug(this: StringSchemaBuilder) {\n * return this.addValidator((v) => ({ valid: /^[a-z0-9-]+$/.test(v), errors: [] }));\n * }\n * },\n * number: {\n * port(this: NumberSchemaBuilder) {\n * return this.isInteger().min(1).max(65535);\n * }\n * }\n * };\n * ```\n *\n * @see {@link defineExtension}\n */\nexport type ExtensionConfig = {\n [K in BuilderTypeName]?: Record<\n string,\n (this: BuilderMap[K], ...args: any[]) => any\n >;\n};\n\n/**\n * A branded descriptor returned by {@link defineExtension}.\n *\n * The descriptor captures the extension's method signatures at the **type\n * level** so that {@link withExtensions} can produce correctly-typed factory\n * functions. At runtime it holds the (possibly wrapped) configuration object.\n *\n * Extension descriptors are intentionally **opaque** — consumers should not\n * access `config` directly. Instead, pass descriptors to\n * {@link withExtensions} to obtain augmented builder factories.\n *\n * @typeParam T - The concrete {@link ExtensionConfig} shape. Inferred\n * automatically by `defineExtension`; you rarely need to specify it.\n *\n * @example\n * ```ts\n * // The type is inferred — no need to annotate\n * const myExt: ExtensionDescriptor<{ string: { slug: ... } }> = defineExtension({ ... });\n * ```\n *\n * @see {@link defineExtension}\n * @see {@link withExtensions}\n */\nexport type ExtensionDescriptor<T extends ExtensionConfig = ExtensionConfig> = {\n readonly __brand: unique symbol;\n readonly config: T;\n};\n\n// ---------------------------------------------------------------------------\n// Type-level extraction of extension methods per builder type\n// ---------------------------------------------------------------------------\n\n/** Extracts the method signatures an extension adds to a given builder type. */\ntype ExtractMethods<\n TExt extends ExtensionConfig,\n TType extends BuilderTypeName\n> =\n TExt[TType] extends Record<string, (...args: any[]) => any>\n ? TExt[TType]\n : {};\n\n/** Merges the methods from multiple extensions for a given builder type. */\ntype MergeExtensionMethods<\n TExts extends readonly ExtensionDescriptor<any>[],\n TType extends BuilderTypeName\n> = TExts extends readonly [\n ExtensionDescriptor<infer TFirst>,\n ...infer TRest extends readonly ExtensionDescriptor<any>[]\n]\n ? ExtractMethods<TFirst, TType> & MergeExtensionMethods<TRest, TType>\n : {};\n\n// ---------------------------------------------------------------------------\n// Return types for withExtensions()\n// ---------------------------------------------------------------------------\n\n/**\n * Intersected onto consumer-facing builder types to make `withExtension`\n * and `getExtension` uncallable (`never`). Using an intersection instead\n * of `Omit` preserves the class identity so extended builders remain\n * assignable to `SchemaBuilder<any, any, any, any, any>`.\n */\nexport type HiddenExtensionMethods = {\n /** @internal Extension-author only — use inside `defineExtension()`. */\n withExtension: never;\n /** @internal Extension-author only — use inside `defineExtension()`. */\n getExtension: never;\n};\n\n/**\n * Overrides extension method return types so they always return the full\n * extended builder type. This ensures extension methods preserve all other\n * extension methods through chaining (e.g. `s.string().email().slug()`).\n *\n * The self-reference (`FixedMethods` appears in its own mapped return\n * types) is resolved lazily by TypeScript because the recursion sits\n * inside a function-return position within a conditional mapped type.\n */\nexport type FixedMethods<TRawMethods, TBase> = {\n [K in keyof TRawMethods]: TRawMethods[K] extends (\n this: any,\n ...args: infer A\n ) => any\n ? (\n ...args: A\n ) => TBase & FixedMethods<TRawMethods, TBase> & HiddenExtensionMethods\n : TRawMethods[K];\n};\n\n/**\n * Produces the consumer-facing type for an extended builder: the base\n * builder intersected with its fixed extension methods, with\n * `withExtension` / `getExtension` overridden to `never` so they\n * don't appear as callable in consumer code.\n */\nexport type CleanExtended<TBuilder, TExt> = TBuilder &\n FixedMethods<TExt, TBuilder> &\n HiddenExtensionMethods;\n\n// -- Factory types that return builders with corrected extension methods ------\n\ntype ExtendedStringFactory<TExt> = {\n (): CleanExtended<\n StringSchemaBuilder<string, true, false, false, TExt>,\n TExt\n >;\n <T extends string>(\n equals: T\n ): CleanExtended<StringSchemaBuilder<T, true, false, false, TExt>, TExt>;\n};\n\ntype ExtendedNumberFactory<TExt> = {\n (): CleanExtended<\n NumberSchemaBuilder<number, true, false, false, TExt>,\n TExt\n >;\n <T extends number>(\n equals: T\n ): CleanExtended<NumberSchemaBuilder<T, true, false, false, TExt>, TExt>;\n};\n\ntype ExtendedBooleanFactory<TExt> = () => CleanExtended<\n BooleanSchemaBuilder<boolean, true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedDateFactory<TExt> = () => CleanExtended<\n DateSchemaBuilder<Date, true, false, false, TExt>,\n TExt\n>;\n\ntype ExtendedObjectFactory<TExt> = <\n P extends Record<string, SchemaBuilder<any, any, any, any, any>>\n>(\n properties?: P\n) => CleanExtended<\n ObjectSchemaBuilder<P, true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedArrayFactory<TExt> = <\n TElementSchema extends SchemaBuilder<any, any, any, any, any>\n>(\n elementSchema?: TElementSchema\n) => CleanExtended<\n ArraySchemaBuilder<TElementSchema, true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedUnionFactory<TExt> = <\n T extends SchemaBuilder<any, any, any, any, any>\n>(\n schema: T\n) => CleanExtended<\n UnionSchemaBuilder<[T], true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedFuncFactory<TExt> = () => CleanExtended<\n FunctionSchemaBuilder<true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedAnyFactory<TExt> = () => CleanExtended<\n AnySchemaBuilder<true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedTupleFactory<TExt> = <\n const TElements extends readonly SchemaBuilder<any, any, any, any, any>[]\n>(\n elements: [...TElements]\n) => CleanExtended<\n TupleSchemaBuilder<TElements, true, false, undefined, false, TExt>,\n TExt\n>;\n\ntype ExtendedRecordFactory<TExt> = <\n TKeySchema extends StringSchemaBuilder<any, any, any, any>,\n TValueSchema extends SchemaBuilder<any, any, any, any, any>\n>(\n keySchema: TKeySchema,\n valueSchema: TValueSchema\n) => CleanExtended<\n RecordSchemaBuilder<\n TKeySchema,\n TValueSchema,\n true,\n false,\n undefined,\n false,\n TExt\n >,\n TExt\n>;\n\n/**\n * The return type of {@link withExtensions}.\n *\n * Contains a factory function for every builder type (`string`, `number`,\n * `boolean`, `date`, `object`, `array`, `union`, `func`, `any`). Each\n * factory returns a builder whose type includes the methods contributed\n * by all provided extension descriptors.\n *\n * @typeParam TExts - Tuple of extension descriptors passed to `withExtensions`.\n *\n * @see {@link withExtensions}\n */\ntype WithExtensionsResult<TExts extends readonly ExtensionDescriptor<any>[]> = {\n string: ExtendedStringFactory<MergeExtensionMethods<TExts, 'string'>>;\n number: ExtendedNumberFactory<MergeExtensionMethods<TExts, 'number'>>;\n boolean: ExtendedBooleanFactory<MergeExtensionMethods<TExts, 'boolean'>>;\n date: ExtendedDateFactory<MergeExtensionMethods<TExts, 'date'>>;\n object: ExtendedObjectFactory<MergeExtensionMethods<TExts, 'object'>>;\n array: ExtendedArrayFactory<MergeExtensionMethods<TExts, 'array'>>;\n tuple: ExtendedTupleFactory<MergeExtensionMethods<TExts, 'tuple'>>;\n record: ExtendedRecordFactory<MergeExtensionMethods<TExts, 'record'>>;\n union: ExtendedUnionFactory<MergeExtensionMethods<TExts, 'union'>>;\n func: ExtendedFuncFactory<MergeExtensionMethods<TExts, 'func'>>;\n any: ExtendedAnyFactory<MergeExtensionMethods<TExts, 'any'>>;\n};\n\n// ---------------------------------------------------------------------------\n// Reserved method names — cannot be overridden by extensions\n// ---------------------------------------------------------------------------\n\n/**\n * Method names on `SchemaBuilder` that extensions are **not** allowed to\n * override. An error is thrown at definition time if an extension tries\n * to use any of these names.\n *\n * @internal\n */\nconst RESERVED_METHODS = new Set([\n 'validate',\n 'validateAsync',\n 'parse',\n 'parseAsync',\n 'safeParse',\n 'safeParseAsync',\n 'introspect',\n 'optional',\n 'required',\n 'addPreprocessor',\n 'clearPreprocessors',\n 'addValidator',\n 'clearValidators',\n 'hasType',\n 'clearHasType',\n 'createFromProps',\n 'preValidate',\n 'preValidateSync',\n 'preValidateAsync',\n 'getValidationErrorMessage',\n 'getValidationErrorMessageSync',\n 'assureValidationErrorMessageProvider',\n 'withExtension',\n 'getExtension'\n]);\n\n// ---------------------------------------------------------------------------\n// defineExtension()\n// ---------------------------------------------------------------------------\n\n/**\n * Defines an extension targeting one or more schema builder types.\n *\n * Each extension is a plain object keyed by builder type name (`\"string\"`,\n * `\"number\"`, `\"date\"`, …) whose values are method implementations.\n * Methods receive `this` bound to the builder instance and must return a\n * builder to support fluent chaining.\n *\n * ## Ergonomic metadata (auto-infer)\n *\n * Extension methods **do not** have to call `this.withExtension()`. The\n * system wraps each method and automatically attaches\n * `withExtension(methodName, args)` to the returned builder when the key\n * is not already present. This eliminates the most common source of\n * duplication in extension code.\n *\n * - **Zero-arg methods** → metadata value is `true`\n * - **Single-arg methods** → metadata value is the argument itself\n * - **Multi-arg methods** → metadata value is the arguments array\n *\n * If you need **custom metadata** (e.g. a different key, a transformed\n * value, or a structured object), call `this.withExtension(key, value)`\n * explicitly inside the method — the auto-infer logic detects the existing\n * key and skips automatic attachment.\n *\n * ## Validation\n *\n * `defineExtension` validates the configuration eagerly:\n * - Unknown builder type names throw immediately.\n * - {@link RESERVED_METHODS | Reserved method names} (e.g. `validate`,\n * `introspect`) cannot be overridden.\n * - Non-function values in the method record are rejected.\n *\n * @param config - An {@link ExtensionConfig} object mapping builder type\n * names to method records.\n * @returns A branded {@link ExtensionDescriptor} ready to pass to\n * {@link withExtensions}.\n *\n * @example Simple extension (auto-inferred metadata)\n * ```ts\n * const slugExt = defineExtension({\n * string: {\n * slug(this: StringSchemaBuilder) {\n * return this.addValidator((val) => {\n * const valid = /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(val);\n * return { valid, errors: valid ? [] : [{ message: 'invalid slug' }] };\n * });\n * }\n * }\n * });\n *\n * // Usage:\n * const s = withExtensions(slugExt);\n * const schema = s.string().slug();\n * schema.introspect().extensions.slug; // true\n * ```\n *\n * @example Extension with custom metadata\n * ```ts\n * const currencyExt = defineExtension({\n * number: {\n * currency(this: NumberSchemaBuilder, opts?: { maxDecimals?: number }) {\n * const maxDec = opts?.maxDecimals ?? 2;\n * return this.withExtension('currency', { maxDecimals: maxDec })\n * .min(0)\n * .addValidator((val) => {\n * const decimals = (String(val).split('.')[1] ?? '').length;\n * const valid = decimals <= maxDec;\n * return { valid, errors: valid ? [] : [{ message: `max ${maxDec} decimals` }] };\n * });\n * }\n * }\n * });\n * ```\n *\n * @example Multi-builder extension\n * ```ts\n * const myExt = defineExtension({\n * string: {\n * email(this: StringSchemaBuilder) { return this.addValidator(...); }\n * },\n * number: {\n * port(this: NumberSchemaBuilder) { return this.isInteger().min(1).max(65535); }\n * }\n * });\n * ```\n *\n * @throws {Error} If a builder type name is unknown.\n * @throws {Error} If a method name is reserved.\n * @throws {Error} If a method value is not a function.\n *\n * @see {@link withExtensions} — apply the defined extension\n * @see {@link ExtensionConfig} — configuration shape\n */\nexport function defineExtension<T extends ExtensionConfig>(\n config: T\n): ExtensionDescriptor<T> {\n // Validate at definition time and wrap methods for auto-infer extension key\n const wrappedConfig: any = {};\n for (const builderName of Object.keys(config) as BuilderTypeName[]) {\n if (!(builderName in builderClasses)) {\n throw new Error(\n `Unknown builder type \"${builderName}\". Valid types: ${Object.keys(builderClasses).join(', ')}`\n );\n }\n\n const methods = config[builderName];\n if (!methods || typeof methods !== 'object') {\n throw new Error(\n `Extension config for \"${builderName}\" must be an object of methods`\n );\n }\n\n wrappedConfig[builderName] = {};\n for (const methodName of Object.keys(methods)) {\n if (RESERVED_METHODS.has(methodName)) {\n throw new Error(\n `Cannot override reserved method \"${methodName}\" on \"${builderName}\"`\n );\n }\n const origMethod = methods[methodName];\n if (typeof origMethod !== 'function') {\n throw new Error(\n `Extension method \"${builderName}.${methodName}\" must be a function`\n );\n }\n // Wrap the method to auto-infer extension key if not already set\n wrappedConfig[builderName][methodName] = function (\n this: any,\n ...args: any[]\n ) {\n const result = (origMethod as any).apply(this, args);\n // If result is a builder and does not have the extension key, auto-apply withExtension\n if (\n result &&\n typeof result === 'object' &&\n typeof result.withExtension === 'function' &&\n // Only auto-apply if the extension key is not already present\n (typeof result.getExtension !== 'function' ||\n result.getExtension(methodName) === undefined)\n ) {\n // Only auto-apply if the original method did not call withExtension\n return result.withExtension(\n methodName,\n args.length === 1\n ? args[0]\n : args.length === 0\n ? true\n : args\n );\n }\n return result;\n };\n }\n }\n\n return { config: wrappedConfig } as ExtensionDescriptor<T>;\n}\n\n// ---------------------------------------------------------------------------\n// withExtensions()\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a set of schema factory functions with the provided extensions\n * applied.\n *\n * Each factory function (`string()`, `number()`, `date()`, …) returned by\n * `withExtensions` produces builder instances whose prototypes include the\n * extension methods. All built-in builder methods remain available and\n * fully chainable alongside the new ones.\n *\n * ## Stacking multiple extensions\n *\n * Pass any number of {@link ExtensionDescriptor}s — their methods are\n * merged per builder type. If two extensions define the **same** method\n * name on the same builder type, a runtime error is thrown to prevent\n * silent conflicts.\n *\n * ## Type safety\n *\n * The return type is fully inferred: TypeScript knows exactly which\n * extension methods are available on each builder factory. Extension\n * methods return the full extended builder type, so chaining like\n * `s.string().email().slug().minLength(3)` is fully typed.\n *\n * ## Builder types without extensions\n *\n * Builders that have no methods from any of the provided extensions\n * use the standard (unextended) factory, so there is zero overhead.\n *\n * @param extensions - One or more {@link ExtensionDescriptor}s created\n * by {@link defineExtension}.\n * @returns An object with factory functions for all builder types\n * (`string`, `number`, `boolean`, `date`, `object`, `array`, `union`,\n * `func`, `any`), each returning augmented builders.\n *\n * @example Basic usage\n * ```ts\n * const s = withExtensions(emailExt, rangeExt);\n *\n * // string() now has .email()\n * const emailSchema = s.string().email().minLength(5);\n *\n * // number() now has .range()\n * const rangeSchema = s.number().range(0, 100);\n *\n * // builders without targeted extensions work as normal\n * const dateSchema = s.date();\n * ```\n *\n * @example Stacking extensions on the same builder\n * ```ts\n * const s = withExtensions(emailExt, slugExt, trimmedExt);\n * const schema = s.string().email().slug().trimmed();\n * ```\n *\n * @example Using extensions in object schemas\n * ```ts\n * const s = withExtensions(emailExt, portExt);\n * const ServerConfig = s.object({\n * host: s.string().email(),\n * port: s.number().port()\n * });\n * ```\n *\n * @throws {Error} If two extensions define the same method name on the\n * same builder type.\n *\n * @see {@link defineExtension} — create extension descriptors\n * @see {@link ExtensionDescriptor}\n */\nexport function withExtensions<\n const TExts extends readonly ExtensionDescriptor<any>[]\n>(...extensions: TExts): WithExtensionsResult<TExts> {\n // Collect all methods per builder type and check for collisions\n const methodsByBuilder = new Map<BuilderTypeName, Map<string, Function>>();\n\n for (const ext of extensions) {\n for (const builderName of Object.keys(\n ext.config\n ) as BuilderTypeName[]) {\n if (!methodsByBuilder.has(builderName)) {\n methodsByBuilder.set(builderName, new Map());\n }\n const methods = methodsByBuilder.get(builderName)!;\n const extMethods = ext.config[builderName]!;\n\n for (const methodName of Object.keys(extMethods)) {\n if (methods.has(methodName)) {\n throw new Error(\n `Extension method collision: \"${methodName}\" is defined by multiple extensions for \"${builderName}\"`\n );\n }\n methods.set(methodName, extMethods[methodName]);\n }\n }\n }\n\n // For each builder type, create a dynamic subclass if there are extension methods\n const factories: Record<string, (...args: any[]) => any> = {};\n\n for (const builderName of Object.keys(\n builderClasses\n ) as BuilderTypeName[]) {\n const methods = methodsByBuilder.get(builderName);\n\n if (!methods || methods.size === 0) {\n // No extensions for this builder type — use the standard factory\n factories[builderName] = builderFactories[builderName];\n continue;\n }\n\n const BaseClass = builderClasses[builderName] as any;\n\n // Create a dynamic subclass\n const ExtendedClass = class extends BaseClass {\n // biome-ignore lint/complexity/noUselessConstructor: required\n constructor(...args: any[]) {\n super(...args);\n }\n\n static create(props: any) {\n return new ExtendedClass({\n ...props\n });\n }\n\n protected createFromProps(props: any): any {\n return ExtendedClass.create(props);\n }\n };\n\n // Add extension methods to the subclass prototype\n for (const [methodName, methodFn] of methods) {\n Object.defineProperty(ExtendedClass.prototype, methodName, {\n value: methodFn,\n writable: true,\n configurable: true,\n enumerable: false\n });\n }\n\n // Create a factory that uses the extended class\n factories[builderName] = (...args: any[]) => {\n // Delegate to the original factory to get an initialized instance,\n // then upgrade its prototype so it gains the extension methods.\n // Note: Object.setPrototypeOf can cause V8 hidden-class deoptimization\n // on the mutated object, but avoids the double allocation and\n // introspect() round-trip of the previous approach.\n const original = builderFactories[builderName](...args);\n Object.setPrototypeOf(original, ExtendedClass.prototype);\n return original;\n };\n }\n\n return factories as WithExtensionsResult<TExts>;\n}\n"],"mappings":"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","names":["LazySchemaBuilder","_LazySchemaBuilder","SchemaBuilder","#getter","#resolvedSchema","props","#buildResult","superResult","context","valid","preValidationTransaction","errors","objToValidate","object","_notUsed","errorMessage","value","_name","lazy","getter","NullSchemaBuilder","_NullSchemaBuilder","SchemaBuilder","props","_notUsed","#buildResult","object","context","_context","errorMessage","value","_name","nul","builderClasses","StringSchemaBuilder","NumberSchemaBuilder","BooleanSchemaBuilder","DateSchemaBuilder","ObjectSchemaBuilder","ArraySchemaBuilder","TupleSchemaBuilder","RecordSchemaBuilder","UnionSchemaBuilder","FunctionSchemaBuilder","AnySchemaBuilder","builderFactories","string","number","boolean","date","object","array","tuple","record","union","func","any","RESERVED_METHODS","defineExtension","config","wrappedConfig","builderName","methods","methodName","origMethod","args","result","withExtensions","extensions","methodsByBuilder","ext","extMethods","factories","BaseClass","ExtendedClass","props","methodFn","original"]}