@modulify/validator 0.1.0 → 0.2.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 (61) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +324 -107
  3. package/dist/assert.cjs +66 -0
  4. package/dist/assert.d.ts +16 -0
  5. package/dist/assert.mjs +66 -0
  6. package/dist/assertions.cjs +190 -92
  7. package/dist/assertions.d.ts +58 -2
  8. package/dist/assertions.mjs +191 -93
  9. package/dist/checkers.d.ts +8 -0
  10. package/dist/combinators.cjs +341 -0
  11. package/dist/combinators.d.ts +17 -0
  12. package/dist/combinators.mjs +341 -0
  13. package/dist/constraints.d.ts +4 -0
  14. package/dist/extractors.d.ts +2 -0
  15. package/dist/index.cjs +172 -61
  16. package/dist/index.d.ts +10 -4
  17. package/dist/index.mjs +176 -64
  18. package/dist/json-schema.cjs +514 -0
  19. package/dist/json-schema.d.ts +14 -0
  20. package/dist/json-schema.mjs +514 -0
  21. package/dist/metadata.cjs +8 -0
  22. package/dist/metadata.cjs.js +130 -0
  23. package/dist/metadata.d.ts +8 -0
  24. package/dist/metadata.es.js +131 -0
  25. package/dist/metadata.mjs +8 -0
  26. package/dist/predicates.cjs +40 -5
  27. package/dist/predicates.d.ts +25 -3
  28. package/dist/predicates.mjs +40 -5
  29. package/dist/violations.d.ts +29 -0
  30. package/docs/en/00-index.md +14 -0
  31. package/docs/en/01-shape-api.md +348 -0
  32. package/docs/en/02-metadata-and-introspection.md +276 -0
  33. package/docs/en/03-violations.md +267 -0
  34. package/docs/en/04-json-schema-export.md +264 -0
  35. package/docs/en/05-public-api.md +123 -0
  36. package/docs/en/06-common-recipes.md +273 -0
  37. package/docs/en/07-ai-reference.md +215 -0
  38. package/docs/en/08-violation-code-types.md +241 -0
  39. package/docs/ru/00-index.md +15 -0
  40. package/docs/ru/01-shape-api.md +348 -0
  41. package/docs/ru/02-metadata-and-introspection.md +276 -0
  42. package/docs/ru/03-violations.md +267 -0
  43. package/docs/ru/04-json-schema-export.md +264 -0
  44. package/docs/ru/05-public-api.md +123 -0
  45. package/docs/ru/06-common-recipes.md +273 -0
  46. package/docs/ru/07-ai-reference.md +215 -0
  47. package/docs/ru/08-violation-code-types.md +241 -0
  48. package/docs/ru/README.md +371 -0
  49. package/package.json +51 -33
  50. package/types/index.d.ts +789 -30
  51. package/types/json-schema.d.ts +75 -0
  52. package/dist/assertions/Assert.d.ts +0 -2
  53. package/dist/assertions/HasLength.d.ts +0 -7
  54. package/dist/assertions/check.d.ts +0 -3
  55. package/dist/assertions/index.d.ts +0 -16
  56. package/dist/runners/Each.d.ts +0 -3
  57. package/dist/runners/HasProperties.d.ts +0 -6
  58. package/dist/runners/index.d.ts +0 -2
  59. package/dist/runners.cjs +0 -32
  60. package/dist/runners.d.ts +0 -2
  61. package/dist/runners.mjs +0 -32
package/types/index.d.ts CHANGED
@@ -1,65 +1,824 @@
1
- export type Intersect<T extends unknown[]> =
2
- T extends [infer First, ...infer Rest extends unknown[]]
1
+ /** Internal utility that intersects all members of a tuple into a single type. */
2
+ export type Intersect<T extends readonly unknown[]> =
3
+ T extends readonly [infer First, ...infer Rest extends readonly unknown[]]
3
4
  ? First & Intersect<Rest>
4
5
  : unknown;
5
6
 
7
+ /** Internal utility that converts a union like `A | B` into `A & B`. */
8
+ export type UnionToIntersection<T> =
9
+ (T extends unknown ? (value: T) => void : never) extends (value: infer I) => void
10
+ ? I
11
+ : never;
12
+
13
+ /** Internal helper for nested validation results. */
6
14
  export type Recursive<T> = T | Recursive<T>[]
7
15
 
8
- export type MaybeMany<V> = V | V[]
16
+ /** Accepts either a single value or an array of values. */
17
+ export type MaybeMany<V> = V | readonly V[]
18
+ /** Accepts either a plain value or a promise of that value. */
9
19
  export type MaybePromise<V> = V | Promise<V>
10
20
 
21
+ /** Runtime predicate that also acts as a TypeScript type guard. */
11
22
  export type Predicate<T = unknown> = (value: unknown) => value is T
12
- export type Meta<T> = {
13
- fqn: string | symbol,
14
- bail: boolean;
15
- reason?: string | symbol;
16
- meta?: T;
23
+
24
+ /** Checker used by assertion constraints after a value is extracted. */
25
+ export type Checker<T, A extends readonly unknown[] = readonly unknown[]> = (value: T, ...args: A) => boolean
26
+
27
+ /** Extracts a derived value from the original input before a checker runs. */
28
+ export type Extractor<T, V> = (value: T) => V
29
+
30
+ /** Origin layer that produced a violation. */
31
+ export type ViolationKind = 'assertion' | 'validator' | 'runtime'
32
+
33
+ /** Contract stored in `ViolationCodeRegistry` for a known machine-readable code. */
34
+ export type ViolationCodeEntry<
35
+ K extends ViolationKind = ViolationKind,
36
+ N extends string = string,
37
+ A extends readonly unknown[] = readonly unknown[],
38
+ > = {
39
+ kind: K;
40
+ name: N;
41
+ args: A;
42
+ }
43
+
44
+ type ResolveViolationEntry<E> =
45
+ [E] extends [never]
46
+ ? ViolationCodeEntry
47
+ : E extends ViolationCodeEntry
48
+ ? E
49
+ : ViolationCodeEntry
50
+
51
+ type ResolveViolationSubjectCode<COrT extends string | readonly unknown[]> = COrT extends string ? COrT : string
52
+ type ResolveViolationSubjectArgs<COrT extends string | readonly unknown[], C extends string> = [COrT] extends [readonly unknown[]]
53
+ ? COrT
54
+ : ViolationArgs<C>
55
+
56
+ /**
57
+ * Extensible registry of machine-readable violation codes.
58
+ *
59
+ * Consumers can augment this interface in their app code:
60
+ * `declare module '@modulify/validator' { interface ViolationCodeRegistry { 'app.user.conflict': ViolationCodeEntry<'validator', 'user', readonly []> } }`
61
+ *
62
+ * Legacy `never` markers remain supported as a fallback for gradual migration:
63
+ * `declare module '@modulify/validator' { interface ViolationCodeRegistry { 'legacy.code': never } }`
64
+ */
65
+ export interface ViolationCodeRegistry {
66
+ 'length.exact': ViolationCodeEntry<'assertion', 'hasLength', readonly [exact: number]>;
67
+ 'length.max': ViolationCodeEntry<'assertion', 'hasLength', readonly [max: number]>;
68
+ 'length.min': ViolationCodeEntry<'assertion', 'hasLength', readonly [min: number]>;
69
+ 'length.range': ViolationCodeEntry<'assertion', 'hasLength', readonly [range: readonly [number, number]]>;
70
+ 'length.unsupported-type': ViolationCodeEntry<'assertion', 'hasLength', readonly []>;
71
+ 'number.exact': ViolationCodeEntry<'assertion', 'hasValue', readonly [exact: number]>;
72
+ 'number.max': ViolationCodeEntry<'assertion', 'hasValue', readonly [max: number]>;
73
+ 'number.min': ViolationCodeEntry<'assertion', 'hasValue', readonly [min: number]>;
74
+ 'number.multiple-of': ViolationCodeEntry<'assertion', 'multipleOf', readonly [step: number]>;
75
+ 'number.nan': ViolationCodeEntry<'assertion', 'isNaN', readonly []>;
76
+ 'number.range': ViolationCodeEntry<'assertion', 'hasValue', readonly [range: readonly [number, number]]>;
77
+ 'number.unsupported-type': ViolationCodeEntry<'assertion', 'hasValue' | 'multipleOf', readonly []>;
78
+ 'runtime.rejection': ViolationCodeEntry<'runtime', 'validate', readonly [reason: unknown]>;
79
+ 'shape.fields.mismatch': ViolationCodeEntry<
80
+ 'validator',
81
+ 'shape',
82
+ readonly [selectors: readonly [ObjectShapeFieldSelector, ObjectShapeFieldSelector]]
83
+ >;
84
+ 'shape.unknown-key': ViolationCodeEntry<'validator', 'shape', readonly []>;
85
+ 'size.exact': ViolationCodeEntry<'assertion', 'hasSize', readonly [exact: number]>;
86
+ 'size.max': ViolationCodeEntry<'assertion', 'hasSize', readonly [max: number]>;
87
+ 'size.min': ViolationCodeEntry<'assertion', 'hasSize', readonly [min: number]>;
88
+ 'size.range': ViolationCodeEntry<'assertion', 'hasSize', readonly [range: readonly [number, number]]>;
89
+ 'size.unsupported-type': ViolationCodeEntry<'assertion', 'hasSize', readonly []>;
90
+ 'string.email': ViolationCodeEntry<'assertion', 'isEmail', readonly []>;
91
+ 'string.ends-with': ViolationCodeEntry<'assertion', 'endsWith', readonly [suffix: string]>;
92
+ 'string.pattern': ViolationCodeEntry<'assertion', 'hasPattern', readonly [pattern: RegExp]>;
93
+ 'string.starts-with': ViolationCodeEntry<'assertion', 'startsWith', readonly [prefix: string]>;
94
+ 'string.unsupported-type': ViolationCodeEntry<
95
+ 'assertion',
96
+ 'hasPattern' | 'startsWith' | 'endsWith',
97
+ readonly []
98
+ >;
99
+ 'tuple.length': ViolationCodeEntry<'validator', 'tuple', readonly [length: number]>;
100
+ 'type.array': ViolationCodeEntry<'validator', 'each' | 'tuple', readonly []>;
101
+ 'type.bigint': ViolationCodeEntry<'assertion', 'isBigInt', readonly []>;
102
+ 'type.blob': ViolationCodeEntry<'assertion', 'isBlob', readonly []>;
103
+ 'type.boolean': ViolationCodeEntry<'assertion', 'isBoolean', readonly []>;
104
+ 'type.date': ViolationCodeEntry<'assertion', 'isDate', readonly []>;
105
+ 'type.file': ViolationCodeEntry<'assertion', 'isFile', readonly []>;
106
+ 'type.function': ViolationCodeEntry<'assertion', 'isFunction', readonly []>;
107
+ 'type.map': ViolationCodeEntry<'assertion', 'isMap', readonly []>;
108
+ 'type.null': ViolationCodeEntry<'assertion', 'isNull', readonly []>;
109
+ 'type.number': ViolationCodeEntry<'assertion', 'isNumber', readonly []>;
110
+ 'type.record': ViolationCodeEntry<'validator', 'shape' | 'discriminatedUnion' | 'record', readonly []>;
111
+ 'type.set': ViolationCodeEntry<'assertion', 'isSet', readonly []>;
112
+ 'type.string': ViolationCodeEntry<'assertion', 'isString', readonly []>;
113
+ 'type.symbol': ViolationCodeEntry<'assertion', 'isSymbol', readonly []>;
114
+ 'union.invalid-discriminator': ViolationCodeEntry<
115
+ 'validator',
116
+ 'discriminatedUnion',
117
+ readonly [variants: readonly PropertyKey[]]
118
+ >;
119
+ 'union.no-match': ViolationCodeEntry<'validator', 'union', readonly [branches: number]>;
120
+ 'value.defined': ViolationCodeEntry<'assertion', 'isDefined', readonly []>;
121
+ 'value.exact': ViolationCodeEntry<'assertion', 'exact', readonly [expected: unknown]>;
122
+ 'value.one-of': ViolationCodeEntry<'assertion', 'oneOf', readonly [values: readonly unknown[]]>;
17
123
  }
18
124
 
19
- export type Assertion<T = unknown, M = unknown> = Predicate<T> & Meta<M> & {
20
- readonly also: Assertion[]
21
- That(...asserts: Assertion[]): Assertion<T, M>;
125
+ /** Union of all registered machine-readable violation codes. */
126
+ export type ViolationCode = Extract<keyof ViolationCodeRegistry, string>
127
+
128
+ /** Registered codes that provide a full contract entry instead of a legacy `never` marker. */
129
+ export type KnownViolationCode = Extract<{
130
+ [C in ViolationCode]:
131
+ [ViolationCodeRegistry[C]] extends [never]
132
+ ? never
133
+ : ViolationCodeRegistry[C] extends ViolationCodeEntry
134
+ ? C
135
+ : never
136
+ }[ViolationCode], string>
137
+
138
+ /** Contract entry derived from `ViolationCodeRegistry`, with a generic fallback for unknown or legacy codes. */
139
+ export type ViolationEntry<C extends string = string> = C extends ViolationCode
140
+ ? ResolveViolationEntry<ViolationCodeRegistry[C]>
141
+ : ViolationCodeEntry
142
+
143
+ /** Tuple of machine-readable arguments associated with a violation code. */
144
+ export type ViolationArgs<C extends string = string> = ViolationEntry<C>['args']
145
+
146
+ /** Origin layer associated with a violation code. */
147
+ export type ViolationKindOf<C extends string = string> = ViolationEntry<C>['kind']
148
+
149
+ /** Constraint name associated with a violation code. */
150
+ export type ViolationNameOf<C extends string = string> = ViolationEntry<C>['name']
151
+
152
+ /** Strict code-driven violation subject for a fully registered violation code. */
153
+ export type KnownViolationSubject<C extends KnownViolationCode> = {
154
+ kind: ViolationKindOf<C>;
155
+ name: ViolationNameOf<C>;
156
+ code: C;
157
+ args: ViolationArgs<C>;
22
158
  }
23
159
 
24
- export type Violation<M = unknown> = {
160
+ /** Machine-readable description of a validation failure. */
161
+ export type ViolationSubject<
162
+ COrT extends string | readonly unknown[] = string,
163
+ K extends ViolationKind = COrT extends string ? ViolationKindOf<COrT> : ViolationKind,
164
+ C extends string = ResolveViolationSubjectCode<COrT>,
165
+ T extends readonly unknown[] = ResolveViolationSubjectArgs<COrT, C>,
166
+ > = [COrT] extends [readonly unknown[]]
167
+ ? {
168
+ kind: K;
169
+ name: string;
170
+ code: C;
171
+ args: T;
172
+ }
173
+ : C extends KnownViolationCode
174
+ ? {
175
+ kind: K & ViolationKindOf<C>;
176
+ name: ViolationNameOf<C>;
177
+ code: C;
178
+ args: T & ViolationArgs<C>;
179
+ }
180
+ : {
181
+ kind: K;
182
+ name: string;
183
+ code: C;
184
+ args: T;
185
+ }
186
+
187
+ /**
188
+ * Structured validation error returned by assertions and composed validators.
189
+ *
190
+ * `path` points to the nested property or array index that failed.
191
+ *
192
+ * Example:
193
+ * `violation.violates.code === 'length.min'`
194
+ */
195
+ export type Violation<S extends ViolationSubject = ViolationSubject> = {
25
196
  value: unknown;
26
- /** Path to a property, if a constraint is used as part of a `Collection` for checking some object's structure */
27
197
  path?: PropertyKey[];
28
- violates: string | symbol;
29
- reason?: string | symbol;
30
- meta?: M;
198
+ violates: S;
31
199
  }
32
200
 
33
- export type Validator<M = unknown> = ((
34
- value: unknown,
35
- path?: PropertyKey[]
36
- ) => MaybePromise<Violation<M> | null>) & {
37
- fqn: string;
38
- bail: boolean;
201
+ /** Read-only utility wrapper for working with `Violation[]` results. */
202
+ export declare class ViolationCollection<V extends Violation = Violation> implements Iterable<V> {
203
+ constructor(violations: readonly V[]);
204
+ readonly size: number;
205
+ [Symbol.iterator](): Iterator<V>;
206
+ forEach(callback: (violation: V, index: number, collection: ViolationCollection<V>) => void): void;
207
+ map<T>(callback: (violation: V, index: number, collection: ViolationCollection<V>) => T): T[];
208
+ at(path: readonly PropertyKey[]): ViolationCollection<V>;
209
+ tree(): ViolationTreeNode<V>;
210
+ }
211
+
212
+ /** Tree node built from a `ViolationCollection` for nested path traversal. */
213
+ export type ViolationTreeNode<V extends Violation = Violation> = {
214
+ readonly path: readonly PropertyKey[];
215
+ readonly self: ViolationCollection<V>;
216
+ readonly subtree: ViolationCollection<V>;
217
+ readonly children: ReadonlyMap<PropertyKey, ViolationTreeNode<V>>;
218
+ at(path: readonly PropertyKey[]): ViolationTreeNode<V> | undefined;
219
+ }
220
+
221
+ /** Standard entrypoint for wrapping `Violation[]` into a collection utility API. */
222
+ export declare const collection: <V extends Violation>(violations: readonly V[]) => ViolationCollection<V>
223
+
224
+ /** Read-only machine-readable metadata attached to a constraint. */
225
+ export type ConstraintMetadata = Readonly<Record<string, unknown>>
226
+
227
+ /** Public descriptor entry for additional assertion-level checks. */
228
+ export interface AssertionDescriptorConstraint<
229
+ C extends string = string,
230
+ A extends readonly unknown[] = readonly unknown[],
231
+ > {
232
+ readonly code: C;
233
+ readonly args: A
234
+ }
235
+
236
+ /** Shared descriptor shape returned by `describe(...)`. */
237
+ export interface ConstraintDescriptorBase<K extends string = string> {
238
+ readonly kind: K;
239
+ readonly metadata?: ConstraintMetadata
240
+ }
241
+
242
+ /** Descriptor for leaf assertions created with `assert(...)` or compatible custom assertions. */
243
+ export interface AssertionDescriptor<
244
+ C extends string = string,
245
+ A extends readonly unknown[] = readonly unknown[],
246
+ T extends readonly AssertionDescriptorConstraint[] = readonly AssertionDescriptorConstraint[],
247
+ > extends ConstraintDescriptorBase<'assertion'> {
248
+ readonly name: string;
249
+ readonly bail: boolean;
250
+ readonly code: C;
251
+ readonly args: A;
252
+ readonly constraints: T
39
253
  }
40
254
 
41
- export type Constraint = Assertion | Validator | ValidationRunner
255
+ /** Generic fallback descriptor for custom validators without structural instrumentation. */
256
+ export type ValidatorDescriptor = ConstraintDescriptorBase<'validator'>
257
+
258
+ /** Public extension descriptor for custom validators that expose their own `describe()` contract. */
259
+ export interface CustomConstraintDescriptor<K extends string = string> extends ConstraintDescriptorBase<K> {
260
+ readonly [key: string]: unknown
261
+ }
262
+
263
+ /** Descriptor for sequential arrays of constraints used in a single slot. */
264
+ export interface AllOfConstraintDescriptor<
265
+ T extends readonly unknown[] = readonly ConstraintDescriptor[],
266
+ > extends ConstraintDescriptorBase<'allOf'> {
267
+ readonly constraints: T
268
+ }
269
+
270
+ /** Descriptor for wrapper combinators such as `optional(...)`. */
271
+ export interface WrapperConstraintDescriptor<
272
+ K extends 'optional' | 'nullable' | 'nullish' = 'optional' | 'nullable' | 'nullish',
273
+ C = ConstraintDescriptor,
274
+ > extends ConstraintDescriptorBase<K> {
275
+ readonly child: C
276
+ }
277
+
278
+ /** Descriptor for `each(...)`. */
279
+ export interface EachConstraintDescriptor<
280
+ C = ConstraintDescriptor,
281
+ > extends ConstraintDescriptorBase<'each'> {
282
+ readonly item: C
283
+ }
284
+
285
+ /** Descriptor for `tuple(...)`. */
286
+ export interface TupleConstraintDescriptor<
287
+ T extends readonly unknown[] = readonly ConstraintDescriptor[],
288
+ > extends ConstraintDescriptorBase<'tuple'> {
289
+ readonly items: T
290
+ }
291
+
292
+ /** Descriptor for `union(...)`. */
293
+ export interface UnionConstraintDescriptor<
294
+ T extends readonly unknown[] = readonly ConstraintDescriptor[],
295
+ > extends ConstraintDescriptorBase<'union'> {
296
+ readonly branches: T
297
+ }
298
+
299
+ /** Descriptor for `record(...)`. */
300
+ export interface RecordConstraintDescriptor<
301
+ C = ConstraintDescriptor,
302
+ > extends ConstraintDescriptorBase<'record'> {
303
+ readonly values: C
304
+ }
305
+
306
+ /** Descriptor for `discriminatedUnion(...)`. */
307
+ export interface DiscriminatedUnionConstraintDescriptor<
308
+ V = Readonly<Record<PropertyKey, ConstraintDescriptor>>,
309
+ > extends ConstraintDescriptorBase<'discriminatedUnion'> {
310
+ readonly key: PropertyKey;
311
+ readonly variants: V
312
+ }
313
+
314
+ /** Machine-readable summary of object-level rules registered on a shape. */
315
+ export interface ObjectShapeRuleDescriptorBase<K extends string = string> {
316
+ readonly kind: K;
317
+ readonly metadata?: ConstraintMetadata
318
+ }
319
+
320
+ /** Generic compact descriptor for sync object-level rules added via `.refine(...)`. */
321
+ export type GenericObjectShapeRuleDescriptor<
322
+ K extends string = 'refine',
323
+ > = ObjectShapeRuleDescriptorBase<K>
324
+
325
+ /** Descriptor for the built-in `.fieldsMatch(...)` helper. */
326
+ export interface FieldsMatchObjectShapeRuleDescriptor<
327
+ Left extends ObjectShapeFieldSelector = ObjectShapeFieldSelector,
328
+ Right extends ObjectShapeFieldSelector = ObjectShapeFieldSelector,
329
+ > extends ObjectShapeRuleDescriptorBase<'fieldsMatch'> {
330
+ readonly selectors: readonly [Left, Right]
331
+ }
332
+
333
+ /** Machine-readable summary of object-level rules registered on a shape. */
334
+ export type ObjectShapeRuleDescriptor =
335
+ | GenericObjectShapeRuleDescriptor<string>
336
+ | FieldsMatchObjectShapeRuleDescriptor
337
+
338
+ /** Descriptor for `shape(...)`. */
339
+ export interface ShapeConstraintDescriptor<
340
+ F = Readonly<Record<PropertyKey, ConstraintDescriptor>>,
341
+ R extends readonly ObjectShapeRuleDescriptor[] = readonly ObjectShapeRuleDescriptor[],
342
+ > extends ConstraintDescriptorBase<'shape'> {
343
+ readonly unknownKeys: UnknownKeysMode;
344
+ readonly fields: F;
345
+ readonly rules: R
346
+ }
347
+
348
+ /** Stable machine-readable description of a constraint tree. */
349
+ export type BuiltInConstraintDescriptor =
350
+ | AssertionDescriptor
351
+ | AllOfConstraintDescriptor
352
+ | ValidatorDescriptor
353
+ | WrapperConstraintDescriptor
354
+ | EachConstraintDescriptor
355
+ | TupleConstraintDescriptor
356
+ | UnionConstraintDescriptor
357
+ | RecordConstraintDescriptor
358
+ | DiscriminatedUnionConstraintDescriptor
359
+ | ShapeConstraintDescriptor
360
+
361
+ /** Stable machine-readable description of a constraint tree. */
362
+ export type ConstraintDescriptor = BuiltInConstraintDescriptor | CustomConstraintDescriptor
363
+
364
+ /**
365
+ * Extra checker pipeline for an assertion.
366
+ *
367
+ * Example:
368
+ * `type LengthConstraint = AssertionConstraint<string, number, [min: number], 'length.min'>`
369
+ */
370
+ export type AssertionConstraint<
371
+ T = unknown,
372
+ V = unknown,
373
+ A extends readonly unknown[] = readonly unknown[],
374
+ C extends string = string
375
+ > = readonly [
376
+ Extractor<T, V>,
377
+ Checker<V, A>,
378
+ C,
379
+ ...A
380
+ ]
381
+
382
+ /** Maps an assertion checker tuple into its public descriptor entry. */
383
+ export type DescribeAssertionConstraint<C extends AssertionConstraint> =
384
+ C extends AssertionConstraint<unknown, unknown, infer A, infer Code>
385
+ ? AssertionDescriptorConstraint<Code, A>
386
+ : never
42
387
 
43
- export type Validate = <T> (
44
- value: T,
388
+ /** Maps an assertion checker tuple into the violation subject it can produce. */
389
+ export type AssertionConstraintSubject<C extends AssertionConstraint, N extends string = string> =
390
+ C extends AssertionConstraint<unknown, unknown, infer A, infer Code>
391
+ ? {
392
+ kind: 'assertion';
393
+ name: N;
394
+ code: Code;
395
+ args: A;
396
+ }
397
+ : never
398
+
399
+ /** Maps assertion checker tuples into their public descriptor entries. */
400
+ export type DescribeAssertionConstraintTuple<T extends readonly AssertionConstraint[]> = {
401
+ readonly [K in keyof T]: T[K] extends AssertionConstraint
402
+ ? DescribeAssertionConstraint<T[K]>
403
+ : never
404
+ } & ReadonlyArray<DescribeAssertionConstraint<T[number]>>
405
+
406
+ /**
407
+ * Leaf-level validator that checks a single value and either succeeds with `null`
408
+ * or returns a structured violation.
409
+ *
410
+ * `check` is the synchronous type guard used for inference and sync narrowing.
411
+ */
412
+ export type Assertion<
413
+ T = unknown,
414
+ C extends readonly AssertionConstraint[] = readonly AssertionConstraint[],
415
+ Code extends string = string,
416
+ A extends readonly unknown[] = readonly unknown[],
417
+ Name extends string = string,
418
+ > = ((value: unknown) => MaybePromise<Omit<Violation<
419
+ {
420
+ kind: 'assertion';
421
+ name: Name;
422
+ code: Code;
423
+ args: A;
424
+ } | AssertionConstraintSubject<C[number], Name>
425
+ >, 'path'> | null>) & {
426
+ readonly name: Name;
427
+ readonly bail: boolean;
428
+ readonly constraints: C;
429
+ readonly check: Predicate<T>;
430
+ }
431
+
432
+ /**
433
+ * Any reusable validation unit: either a leaf `Assertion` or a composed `Validator`.
434
+ *
435
+ * This is the main building block accepted by `validate(...)`, `matches.sync(...)`,
436
+ * `shape(...)`, and `each(...)`.
437
+ */
438
+ export type Constraint<T = unknown> = Assertion<T> | Validator<T>
439
+
440
+ /** Extracts the validated TypeScript type from a single constraint. */
441
+ export type InferConstraint<C> =
442
+ C extends Assertion<infer T, readonly AssertionConstraint[], string, readonly unknown[], string>
443
+ ? T
444
+ : C extends Validator<infer T>
445
+ ? T
446
+ : never
447
+
448
+ /**
449
+ * Extracts the validated TypeScript type from one or many constraints.
450
+ *
451
+ * When an array of constraints is provided, their inferred types are intersected.
452
+ *
453
+ * Example:
454
+ * `InferConstraints<[typeof isDefined, typeof isString]> // string`
455
+ *
456
+ * Example:
457
+ * `InferConstraints<typeof shape({ name: [isDefined, isString] })> // { name: string }`
458
+ */
459
+ export type InferConstraints<C> =
460
+ C extends readonly []
461
+ ? unknown
462
+ : C extends readonly unknown[]
463
+ ? UnionToIntersection<InferConstraint<C[number]>>
464
+ : InferConstraint<C>
465
+
466
+ /** Internal async runner signature used by composed validators. */
467
+ export type Validate = (
468
+ value: unknown,
45
469
  constraints: MaybeMany<Constraint>,
46
470
  path?: PropertyKey[]
47
471
  ) => Promise<Violation[]>
48
472
 
49
- export type ValidateSync = <T>(
50
- value: T,
473
+ /** Internal sync runner signature used by composed validators. */
474
+ export type ValidateSync = (
475
+ value: unknown,
51
476
  constraints: MaybeMany<Constraint>,
52
477
  path?: PropertyKey[]
53
478
  ) => Violation[]
54
479
 
55
- export type Validation<F extends Validate | ValidateSync> = Validate extends F
480
+ /** Internal union of async and sync runner signatures. */
481
+ export type ValidateLike = Validate | ValidateSync
482
+
483
+ /** Internal helper that maps a runner kind to nested validation results. */
484
+ export type Validation<F extends ValidateLike> = F extends Validate
56
485
  ? MaybePromise<Violation[]>
57
486
  : Violation[]
58
487
 
59
- export interface ValidationRunner {
60
- run <F extends Validate | ValidateSync> (
488
+ /** Controls how object shapes handle keys missing from the descriptor. */
489
+ export type UnknownKeysMode = 'passthrough' | 'strict'
490
+
491
+ /** Field selector accepted by shape helpers that can point to the current level or a nested path. */
492
+ export type ObjectShapeFieldSelector = PropertyKey | readonly PropertyKey[]
493
+
494
+ type ResolveObjectShapeRefinementIssueCode<COrA extends string | readonly unknown[]> = COrA extends string ? COrA : string
495
+ type ResolveObjectShapeRefinementIssueArgs<COrA extends string | readonly unknown[], C extends string> = [COrA] extends [readonly unknown[]]
496
+ ? COrA
497
+ : ViolationArgs<C>
498
+
499
+ /** Machine-readable issue returned by an object-level shape refinement. */
500
+ export type ObjectShapeRefinementIssue<
501
+ COrA extends string | readonly unknown[] = string,
502
+ C extends string = ResolveObjectShapeRefinementIssueCode<COrA>,
503
+ A extends readonly unknown[] = ResolveObjectShapeRefinementIssueArgs<COrA, C>,
504
+ > = {
505
+ path?: PropertyKey[];
506
+ code: C;
507
+ args?: [COrA] extends [readonly unknown[]]
508
+ ? A
509
+ : C extends KnownViolationCode
510
+ ? A & ViolationArgs<C>
511
+ : A;
512
+ value?: unknown;
513
+ }
514
+
515
+ /** Sync object-level rule that runs after the base shape has validated successfully. */
516
+ export type ObjectShapeRefinement<
517
+ T,
518
+ I extends ObjectShapeRefinementIssue = ObjectShapeRefinementIssue,
519
+ > = (
520
+ value: T
521
+ ) => MaybeMany<I | null | undefined> | null | undefined
522
+
523
+ type KnownCodeViolation<C extends KnownViolationCode> = Violation<KnownViolationSubject<C>>
524
+
525
+ type ShapeRefinementIssueSubject<I extends ObjectShapeRefinementIssue> =
526
+ I extends ObjectShapeRefinementIssue<string | readonly unknown[], infer C, infer A>
527
+ ? C extends KnownViolationCode
528
+ ? {
529
+ kind: 'validator';
530
+ name: 'shape' & ViolationNameOf<C>;
531
+ code: C;
532
+ args: A & ViolationArgs<C>;
533
+ }
534
+ : ViolationSubject<A, 'validator', C>
535
+ : never
536
+
537
+ type ShapeRefinementIssueViolation<I extends ObjectShapeRefinementIssue> =
538
+ I extends ObjectShapeRefinementIssue
539
+ ? Violation<ShapeRefinementIssueSubject<I>>
540
+ : never
541
+
542
+ type InferObjectDescriptorViolations<D extends ObjectDescriptor> = {
543
+ [K in keyof D]: InferMaybeManyViolations<D[K]>
544
+ }[keyof D]
545
+
546
+ /** Maps a single constraint into the union of violations it can produce. */
547
+ export type InferConstraintViolations<C extends Constraint> =
548
+ C extends Assertion<unknown, infer AC, infer Code, infer Args, infer Name>
549
+ ? Violation<{
550
+ kind: 'assertion';
551
+ name: Name;
552
+ code: Code;
553
+ args: Args;
554
+ } | AssertionConstraintSubject<AC[number], Name>>
555
+ : C extends ObjectShape<infer D, infer M, readonly ObjectShapeRuleDescriptor[], infer RI>
556
+ ? KnownCodeViolation<'type.record'>
557
+ | (M extends 'strict' ? KnownCodeViolation<'shape.unknown-key'> : never)
558
+ | InferObjectDescriptorViolations<D>
559
+ | ShapeRefinementIssueViolation<RI>
560
+ : C extends OptionalValidator<infer Child>
561
+ ? InferMaybeManyViolations<Child>
562
+ : C extends NullableValidator<infer Child>
563
+ ? InferMaybeManyViolations<Child>
564
+ : C extends NullishValidator<infer Child>
565
+ ? InferMaybeManyViolations<Child>
566
+ : C extends EachValidator<infer Child>
567
+ ? KnownCodeViolation<'type.array'> | InferMaybeManyViolations<Child>
568
+ : C extends TupleValidator<infer Items>
569
+ ? KnownCodeViolation<'type.array'>
570
+ | KnownCodeViolation<'tuple.length'>
571
+ | InferMaybeManyViolations<Items[number]>
572
+ : C extends UnionValidator<infer Branches>
573
+ ? KnownCodeViolation<'union.no-match'> | InferMaybeManyViolations<Branches[number]>
574
+ : C extends DiscriminatedUnionValidator<PropertyKey, infer Variants>
575
+ ? KnownCodeViolation<'type.record'>
576
+ | KnownCodeViolation<'union.invalid-discriminator'>
577
+ | InferMaybeManyViolations<Variants[keyof Variants]>
578
+ : C extends RecordValidator<infer Values>
579
+ ? KnownCodeViolation<'type.record'> | InferMaybeManyViolations<Values>
580
+ : C extends Validator
581
+ ? Violation
582
+ : never
583
+
584
+ /** Maps one-or-many constraints into the union of violations they can produce. */
585
+ export type InferMaybeManyViolations<C extends MaybeMany<Constraint>> =
586
+ C extends readonly []
587
+ ? never
588
+ : C extends readonly Constraint[]
589
+ ? InferConstraintViolations<C[number]>
590
+ : C extends Constraint
591
+ ? InferConstraintViolations<C>
592
+ : never
593
+
594
+ /** Successful `validate(...)` tuple with typed `validated` value. */
595
+ export type ValidationSuccess<T> = [ok: true, validated: T, violations: []]
596
+
597
+ /** Failed `validate(...)` tuple with original value and collected violations. */
598
+ export type ValidationFailure<V extends Violation = Violation> = [ok: false, validated: unknown, violations: V[]]
599
+
600
+ /**
601
+ * The result tuple returned by `validate(...)` and `validate.sync(...)`.
602
+ *
603
+ * Example:
604
+ * `const [ok, validated, violations] = await validate(value, schema)`
605
+ *
606
+ * Example:
607
+ * `if (ok) validated.name.toUpperCase()`
608
+ */
609
+ export type ValidationTuple<T, V extends Violation = Violation> = ValidationSuccess<T> | ValidationFailure<V>
610
+
611
+ /** Alias for `ValidationTuple<T>`. */
612
+ export type ValidationResult<T, V extends Violation = Violation> = ValidationTuple<T, V>
613
+
614
+ /** Attaches read-only metadata to a constraint without changing validation semantics. */
615
+ export declare const meta: <const C extends Constraint, const M extends ConstraintMetadata>(constraint: C, metadata: M) => C
616
+
617
+ /**
618
+ * Composed validator used by recursive helpers such as `shape(...)` and `each(...)`.
619
+ *
620
+ * Custom validators should keep `check` aligned with runtime behavior so that
621
+ * inference and sync narrowing stay trustworthy.
622
+ *
623
+ * Example:
624
+ * `const schema: Validator<{ name: string }>`
625
+ */
626
+ export interface Validator<T = unknown> {
627
+ readonly check: Predicate<T>;
628
+ run <F extends ValidateLike> (
61
629
  validate: F,
62
630
  value: unknown,
63
631
  path: PropertyKey[]
64
632
  ): Validation<F>[];
65
633
  }
634
+
635
+ /** Public validator extension contract for participating in `describe(...)` without private runtime knowledge. */
636
+ export interface DescribedValidator<
637
+ T = unknown,
638
+ D extends ConstraintDescriptor = ConstraintDescriptor,
639
+ > extends Validator<T> {
640
+ describe(): D;
641
+ }
642
+
643
+ /** Identity helper that preserves the exact shape of custom validators, including public descriptors. */
644
+ export declare const custom: <const V extends Validator>(validator: V) => V
645
+
646
+ /** Descriptor that maps object keys to one or many constraints. */
647
+ export type ObjectDescriptor = Record<PropertyKey, MaybeMany<Constraint>>
648
+
649
+ /** Runtime type inferred from an object descriptor. */
650
+ export type InferObjectDescriptor<D extends ObjectDescriptor> = {
651
+ [K in keyof D]: InferConstraints<D[K]>
652
+ }
653
+
654
+ /** Descriptor produced by `.partial()` where every field accepts `undefined`. */
655
+ export type PartialObjectDescriptor<D extends ObjectDescriptor> = {
656
+ [K in keyof D]: Validator<InferConstraints<D[K]> | undefined>
657
+ }
658
+
659
+ /** Utility type for overriding descriptor keys from left to right. */
660
+ export type MergeObjectDescriptors<
661
+ Left extends ObjectDescriptor,
662
+ Right extends ObjectDescriptor,
663
+ > = Omit<Left, keyof Right> & Right
664
+
665
+ /**
666
+ * Object-aware validator with descriptor introspection and immutable shape helpers.
667
+ *
668
+ * Example:
669
+ * `const user = shape({ name: isString }).strict()`
670
+ */
671
+ export interface ObjectShape<
672
+ D extends ObjectDescriptor = ObjectDescriptor,
673
+ M extends UnknownKeysMode = 'passthrough',
674
+ R extends readonly ObjectShapeRuleDescriptor[] = readonly ObjectShapeRuleDescriptor[],
675
+ RI extends ObjectShapeRefinementIssue = never,
676
+ > extends Validator<InferObjectDescriptor<D>> {
677
+ readonly descriptor: D;
678
+ readonly unknownKeys: M;
679
+ refine<const I extends ObjectShapeRefinementIssue = ObjectShapeRefinementIssue>(
680
+ refinement: ObjectShapeRefinement<InferObjectDescriptor<D>, I>
681
+ ): ObjectShape<D, M, [...R, GenericObjectShapeRuleDescriptor<'refine'>], RI | I>;
682
+ refine<
683
+ const I extends ObjectShapeRefinementIssue = ObjectShapeRefinementIssue,
684
+ const RD extends GenericObjectShapeRuleDescriptor<string> = GenericObjectShapeRuleDescriptor<'refine'>
685
+ >(
686
+ refinement: ObjectShapeRefinement<InferObjectDescriptor<D>, I>,
687
+ descriptor: RD
688
+ ): ObjectShape<D, M, [...R, RD], RI | I>;
689
+ fieldsMatch<const K extends readonly [ObjectShapeFieldSelector, ObjectShapeFieldSelector]>(
690
+ keys: K
691
+ ): ObjectShape<D, M, [...R, FieldsMatchObjectShapeRuleDescriptor<K[0], K[1]>], RI | ObjectShapeRefinementIssue<'shape.fields.mismatch'>>;
692
+ strict(): ObjectShape<D, 'strict', R, RI>;
693
+ passthrough(): ObjectShape<D, 'passthrough', R, RI>;
694
+ pick<const K extends readonly (keyof D)[]>(keys: K): ObjectShape<Pick<D, K[number]>, M, [], never>;
695
+ omit<const K extends readonly (keyof D)[]>(keys: K): ObjectShape<Omit<D, K[number]>, M, [], never>;
696
+ partial(): ObjectShape<PartialObjectDescriptor<D>, M, [], never>;
697
+ extend<const E extends ObjectDescriptor>(descriptor: E): ObjectShape<MergeObjectDescriptors<D, E>, M, [], never>;
698
+ merge<const E extends ObjectDescriptor, OM extends UnknownKeysMode, OR extends readonly ObjectShapeRuleDescriptor[]>(
699
+ shape: ObjectShape<E, OM, OR, ObjectShapeRefinementIssue>
700
+ ): ObjectShape<MergeObjectDescriptors<D, E>, M, [], never>;
701
+ }
702
+
703
+ /** Helper that maps a single constraint into its public `describe(...)` result. */
704
+ export type DescribeConstraint<C extends Constraint> =
705
+ C extends Assertion<unknown, infer AC, infer Code, infer Args, string>
706
+ ? AssertionDescriptor<Code, Args, DescribeAssertionConstraintTuple<AC>>
707
+ : C extends ObjectShape<infer D, infer M, infer R, ObjectShapeRefinementIssue>
708
+ ? ShapeConstraintDescriptor<DescribeObjectDescriptor<D>, R> & { readonly unknownKeys: M }
709
+ : C extends OptionalValidator<infer Child>
710
+ ? WrapperConstraintDescriptor<'optional', DescribeMaybeMany<Child>>
711
+ : C extends NullableValidator<infer Child>
712
+ ? WrapperConstraintDescriptor<'nullable', DescribeMaybeMany<Child>>
713
+ : C extends NullishValidator<infer Child>
714
+ ? WrapperConstraintDescriptor<'nullish', DescribeMaybeMany<Child>>
715
+ : C extends EachValidator<infer Child>
716
+ ? EachConstraintDescriptor<DescribeMaybeMany<Child>>
717
+ : C extends TupleValidator<infer Items>
718
+ ? TupleConstraintDescriptor<DescribeConstraintTuple<Items>>
719
+ : C extends UnionValidator<infer Branches>
720
+ ? UnionConstraintDescriptor<DescribeConstraintTuple<Branches>>
721
+ : C extends DiscriminatedUnionValidator<PropertyKey, infer Variants>
722
+ ? DiscriminatedUnionConstraintDescriptor<DescribeObjectDescriptor<Variants>>
723
+ : C extends RecordValidator<infer Values>
724
+ ? RecordConstraintDescriptor<DescribeMaybeMany<Values>>
725
+ : C extends DescribedValidator<unknown, infer D>
726
+ ? D & { readonly metadata?: ConstraintMetadata }
727
+ : C extends Validator
728
+ ? ValidatorDescriptor
729
+ : never
730
+
731
+ /** Helper that maps a one-or-many constraint slot into its public `describe(...)` result. */
732
+ export type DescribeMaybeMany<C extends MaybeMany<Constraint>> =
733
+ C extends readonly [infer Only]
734
+ ? Only extends Constraint
735
+ ? DescribeConstraint<Only>
736
+ : never
737
+ : C extends readonly [Constraint, Constraint, ...Constraint[]]
738
+ ? AllOfConstraintDescriptor<DescribeConstraintTuple<C>>
739
+ : C extends readonly Constraint[]
740
+ ? DescribeConstraint<C[number]> | AllOfConstraintDescriptor
741
+ : C extends Constraint
742
+ ? DescribeConstraint<C>
743
+ : never
744
+
745
+ /** Helper that maps object descriptors into their `describe(...)` field tree. */
746
+ export type DescribeObjectDescriptor<D extends ObjectDescriptor> = {
747
+ [K in keyof D]: DescribeMaybeMany<D[K]>
748
+ }
749
+
750
+ /** Helper that maps tuples of constraints into tuples of descriptors. */
751
+ export type DescribeConstraintTuple<T extends readonly MaybeMany<Constraint>[]> = {
752
+ readonly [K in keyof T]: DescribeMaybeMany<T[K]>
753
+ } & ReadonlyArray<DescribeMaybeMany<T[number]>>
754
+
755
+ declare const optionalValidatorBrand: unique symbol
756
+ declare const nullableValidatorBrand: unique symbol
757
+ declare const nullishValidatorBrand: unique symbol
758
+ declare const eachValidatorBrand: unique symbol
759
+ declare const tupleValidatorBrand: unique symbol
760
+ declare const unionValidatorBrand: unique symbol
761
+ declare const discriminatedUnionValidatorBrand: unique symbol
762
+ declare const recordValidatorBrand: unique symbol
763
+
764
+ /** Typed validator returned by `optional(...)`. */
765
+ export type OptionalValidator<C extends MaybeMany<Constraint> = MaybeMany<Constraint>> =
766
+ Validator<InferConstraints<C> | undefined> & {
767
+ readonly [optionalValidatorBrand]: C
768
+ }
769
+
770
+ /** Typed validator returned by `nullable(...)`. */
771
+ export type NullableValidator<C extends MaybeMany<Constraint> = MaybeMany<Constraint>> =
772
+ Validator<InferConstraints<C> | null> & {
773
+ readonly [nullableValidatorBrand]: C
774
+ }
775
+
776
+ /** Typed validator returned by `nullish(...)`. */
777
+ export type NullishValidator<C extends MaybeMany<Constraint> = MaybeMany<Constraint>> =
778
+ Validator<InferConstraints<C> | null | undefined> & {
779
+ readonly [nullishValidatorBrand]: C
780
+ }
781
+
782
+ /** Typed validator returned by `each(...)`. */
783
+ export type EachValidator<C extends MaybeMany<Constraint> = MaybeMany<Constraint>> =
784
+ Validator<InferConstraints<C>[]> & {
785
+ readonly [eachValidatorBrand]: C
786
+ }
787
+
788
+ /** Typed validator returned by `tuple(...)`. */
789
+ export type TupleValidator<T extends readonly MaybeMany<Constraint>[] = readonly MaybeMany<Constraint>[]> =
790
+ Validator<{
791
+ -readonly [K in keyof T]: InferConstraints<T[K]>
792
+ }> & {
793
+ readonly [tupleValidatorBrand]: T
794
+ }
795
+
796
+ /** Typed validator returned by `union(...)`. */
797
+ export type UnionValidator<T extends readonly MaybeMany<Constraint>[] = readonly MaybeMany<Constraint>[]> =
798
+ Validator<{
799
+ [K in keyof T]: InferConstraints<T[K]>
800
+ }[number]> & {
801
+ readonly [unionValidatorBrand]: T
802
+ }
803
+
804
+ /** Typed validator returned by `discriminatedUnion(...)`. */
805
+ export type DiscriminatedUnionValidator<
806
+ K extends PropertyKey = PropertyKey,
807
+ T extends Record<PropertyKey, MaybeMany<Constraint>> = Record<PropertyKey, MaybeMany<Constraint>>,
808
+ > = Validator<{
809
+ [P in keyof T]: InferConstraints<T[P]>
810
+ }[keyof T]> & {
811
+ readonly [discriminatedUnionValidatorBrand]: {
812
+ readonly key: K;
813
+ readonly variants: T;
814
+ }
815
+ }
816
+
817
+ /** Typed validator returned by `record(...)`. */
818
+ export type RecordValidator<C extends MaybeMany<Constraint> = MaybeMany<Constraint>> =
819
+ Validator<Record<string, InferConstraints<C>>> & {
820
+ readonly [recordValidatorBrand]: C
821
+ }
822
+
823
+ /** Returns a stable machine-readable description of a constraint tree. */
824
+ export declare const describe: <const C extends Constraint>(constraint: C) => DescribeConstraint<C>