@naturalcycles/nodejs-lib 15.91.0 → 15.92.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.
@@ -11,7 +11,7 @@ import {
11
11
  getEnumType,
12
12
  } from '@naturalcycles/js-lib'
13
13
  import { _uniq } from '@naturalcycles/js-lib/array'
14
- import { _assert } from '@naturalcycles/js-lib/error'
14
+ import { _assert, _try } from '@naturalcycles/js-lib/error'
15
15
  import type { Set2 } from '@naturalcycles/js-lib/object'
16
16
  import { _deepCopy, _filterNullishValues, _sortObject } from '@naturalcycles/js-lib/object'
17
17
  import { _substringBefore } from '@naturalcycles/js-lib/string'
@@ -61,9 +61,9 @@ import {
61
61
  * On creation - compiles ajv validation function.
62
62
  * Provides convenient methods, error reporting, etc.
63
63
  */
64
- export class AjvSchema<IN = unknown, OUT = IN> {
64
+ export class AjvSchema<OUT> {
65
65
  private constructor(
66
- public schema: JsonSchema<IN, OUT>,
66
+ public schema: JsonSchema<OUT>,
67
67
  cfg: Partial<AjvSchemaCfg> = {},
68
68
  ) {
69
69
  this.cfg = {
@@ -82,10 +82,10 @@ export class AjvSchema<IN = unknown, OUT = IN> {
82
82
  /**
83
83
  * Shortcut for AjvSchema.create(schema, { lazy: true })
84
84
  */
85
- static createLazy<IN, OUT>(
86
- schema: SchemaHandledByAjv<IN, OUT>,
85
+ static createLazy<OUT>(
86
+ schema: SchemaHandledByAjv<OUT>,
87
87
  cfg?: Partial<AjvSchemaCfg>,
88
- ): AjvSchema<IN, OUT> {
88
+ ): AjvSchema<OUT> {
89
89
  return AjvSchema.create(schema, {
90
90
  lazy: true,
91
91
  ...cfg,
@@ -101,21 +101,18 @@ export class AjvSchema<IN = unknown, OUT = IN> {
101
101
  * Implementation note: JsonSchemaBuilder goes first in the union type, otherwise TypeScript fails to infer <T> type
102
102
  * correctly for some reason.
103
103
  */
104
- static create<IN, OUT = IN>(
105
- schema: SchemaHandledByAjv<IN, OUT>,
106
- cfg?: Partial<AjvSchemaCfg>,
107
- ): AjvSchema<IN, OUT> {
104
+ static create<OUT>(schema: SchemaHandledByAjv<OUT>, cfg?: Partial<AjvSchemaCfg>): AjvSchema<OUT> {
108
105
  if (schema instanceof AjvSchema) return schema
109
106
 
110
- if (AjvSchema.isSchemaWithCachedAjvSchema<typeof schema, IN, OUT>(schema)) {
111
- return AjvSchema.requireCachedAjvSchema<typeof schema, IN, OUT>(schema)
107
+ if (AjvSchema.isSchemaWithCachedAjvSchema<typeof schema, OUT>(schema)) {
108
+ return AjvSchema.requireCachedAjvSchema<typeof schema, OUT>(schema)
112
109
  }
113
110
 
114
- let jsonSchema: JsonSchema<IN, OUT>
111
+ let jsonSchema: JsonSchema<OUT>
115
112
 
116
113
  if (AjvSchema.isJsonSchemaBuilder(schema)) {
117
114
  // oxlint-disable typescript-eslint(no-unnecessary-type-assertion)
118
- jsonSchema = (schema as JsonSchemaTerminal<IN, OUT, any>).build()
115
+ jsonSchema = (schema as JsonSchemaTerminal<OUT, any>).build()
119
116
  AjvSchema.requireValidJsonSchema(jsonSchema)
120
117
  } else {
121
118
  jsonSchema = schema
@@ -128,13 +125,13 @@ export class AjvSchema<IN = unknown, OUT = IN> {
128
125
  // really upsets Ajv.
129
126
  delete jsonSchema.optionalField
130
127
 
131
- const ajvSchema = new AjvSchema<IN, OUT>(jsonSchema, cfg)
128
+ const ajvSchema = new AjvSchema<OUT>(jsonSchema, cfg)
132
129
  AjvSchema.cacheAjvSchema(schema, ajvSchema)
133
130
 
134
131
  return ajvSchema
135
132
  }
136
133
 
137
- static isJsonSchemaBuilder<IN, OUT>(schema: unknown): schema is JsonSchemaTerminal<IN, OUT, any> {
134
+ static isJsonSchemaBuilder<OUT>(schema: unknown): schema is JsonSchemaTerminal<OUT, any> {
138
135
  return schema instanceof JsonSchemaTerminal
139
136
  }
140
137
 
@@ -148,13 +145,13 @@ export class AjvSchema<IN = unknown, OUT = IN> {
148
145
  *
149
146
  * Returned object is always the same object (`===`) that was passed, so it is returned just for convenience.
150
147
  */
151
- validate(input: IN, opt: AjvValidationOptions<IN> = {}): OUT {
148
+ validate(input: unknown, opt: AjvValidationOptions = {}): OUT {
152
149
  const [err, output] = this.getValidationResult(input, opt)
153
150
  if (err) throw err
154
151
  return output
155
152
  }
156
153
 
157
- isValid(input: IN, opt?: AjvValidationOptions<IN>): boolean {
154
+ isValid(input: unknown, opt?: AjvValidationOptions): boolean {
158
155
  // todo: we can make it both fast and non-mutating by using Ajv
159
156
  // with "removeAdditional" and "useDefaults" disabled.
160
157
  const [err] = this.getValidationResult(input, opt)
@@ -162,8 +159,8 @@ export class AjvSchema<IN = unknown, OUT = IN> {
162
159
  }
163
160
 
164
161
  getValidationResult(
165
- input: IN,
166
- opt: AjvValidationOptions<IN> = {},
162
+ input: unknown,
163
+ opt: AjvValidationOptions = {},
167
164
  ): ValidationFunctionResult<OUT, AjvValidationError> {
168
165
  const fn = this.getAJVValidateFunction()
169
166
 
@@ -172,14 +169,31 @@ export class AjvSchema<IN = unknown, OUT = IN> {
172
169
  ? input // mutate
173
170
  : _deepCopy(input) // not mutate
174
171
 
175
- const valid = fn(item) // mutates item, but not input
172
+ let valid = fn(item) // mutates item, but not input
176
173
  _typeCast<OUT>(item)
177
- if (valid) return [null, item]
174
+
175
+ let output: OUT = item
176
+ if (valid && this.schema.postValidation) {
177
+ const [err, result] = _try(() => this.schema.postValidation!(output))
178
+ if (err) {
179
+ valid = false
180
+ ;(fn as any).errors = [
181
+ {
182
+ instancePath: '',
183
+ message: err.message,
184
+ },
185
+ ]
186
+ } else {
187
+ output = result
188
+ }
189
+ }
190
+
191
+ if (valid) return [null, output]
178
192
 
179
193
  const errors = fn.errors!
180
194
 
181
195
  const {
182
- inputId = _isObject(input) ? input['id' as keyof IN] : undefined,
196
+ inputId = _isObject(input) ? (input as any)['id'] : undefined,
183
197
  inputName = this.cfg.inputName || 'Object',
184
198
  } = opt
185
199
  const dataVar = [inputName, inputId].filter(Boolean).join('.')
@@ -205,10 +219,10 @@ export class AjvSchema<IN = unknown, OUT = IN> {
205
219
  inputId,
206
220
  }),
207
221
  )
208
- return [err, item]
222
+ return [err, output]
209
223
  }
210
224
 
211
- getValidationFunction(): ValidationFunction<IN, OUT, AjvValidationError> {
225
+ getValidationFunction(): ValidationFunction<OUT, AjvValidationError> {
212
226
  return (input, opt) => {
213
227
  return this.getValidationResult(input, {
214
228
  mutateInput: opt?.mutateInput,
@@ -218,22 +232,20 @@ export class AjvSchema<IN = unknown, OUT = IN> {
218
232
  }
219
233
  }
220
234
 
221
- static isSchemaWithCachedAjvSchema<Base, IN, OUT>(
235
+ static isSchemaWithCachedAjvSchema<Base, OUT>(
222
236
  schema: Base,
223
- ): schema is WithCachedAjvSchema<Base, IN, OUT> {
237
+ ): schema is WithCachedAjvSchema<Base, OUT> {
224
238
  return !!(schema as any)?.[HIDDEN_AJV_SCHEMA]
225
239
  }
226
240
 
227
- static cacheAjvSchema<Base extends AnyObject, IN, OUT>(
241
+ static cacheAjvSchema<Base extends AnyObject, OUT>(
228
242
  schema: Base,
229
- ajvSchema: AjvSchema<IN, OUT>,
230
- ): WithCachedAjvSchema<Base, IN, OUT> {
243
+ ajvSchema: AjvSchema<OUT>,
244
+ ): WithCachedAjvSchema<Base, OUT> {
231
245
  return Object.assign(schema, { [HIDDEN_AJV_SCHEMA]: ajvSchema })
232
246
  }
233
247
 
234
- static requireCachedAjvSchema<Base, IN, OUT>(
235
- schema: WithCachedAjvSchema<Base, IN, OUT>,
236
- ): AjvSchema<IN, OUT> {
248
+ static requireCachedAjvSchema<Base, OUT>(schema: WithCachedAjvSchema<Base, OUT>): AjvSchema<OUT> {
237
249
  return schema[HIDDEN_AJV_SCHEMA]
238
250
  }
239
251
 
@@ -254,6 +266,8 @@ export class AjvSchema<IN = unknown, OUT = IN> {
254
266
  ): void {
255
267
  if (!errors) return
256
268
 
269
+ this.filterNullableAnyOfErrors(errors)
270
+
257
271
  const { errorMessages } = this.schema
258
272
 
259
273
  for (const error of errors) {
@@ -267,14 +281,75 @@ export class AjvSchema<IN = unknown, OUT = IN> {
267
281
  error.message = errorMessage
268
282
  } else if (errorMessages?.[error.keyword]) {
269
283
  error.message = errorMessages[error.keyword]
284
+ } else {
285
+ const unwrapped = unwrapNullableAnyOf(this.schema)
286
+ if (unwrapped?.errorMessages?.[error.keyword]) {
287
+ error.message = unwrapped.errorMessages[error.keyword]
288
+ }
270
289
  }
271
290
 
272
291
  error.instancePath = error.instancePath.replaceAll(/\/(\d+)/g, `[$1]`).replaceAll('/', '.')
273
292
  }
274
293
  }
275
294
 
295
+ /**
296
+ * Filters out noisy errors produced by nullable anyOf patterns.
297
+ * When `nullable()` wraps a schema in `anyOf: [realSchema, { type: 'null' }]`,
298
+ * AJV produces "must be null" and "must match a schema in anyOf" errors
299
+ * that are confusing. This method splices them out, keeping only the real errors.
300
+ */
301
+ private filterNullableAnyOfErrors(
302
+ errors: ErrorObject<string, Record<string, any>, unknown>[],
303
+ ): void {
304
+ // Collect exact schemaPaths to remove (anyOf aggregates) and prefixes (null branches)
305
+ const exactPaths: string[] = []
306
+ const nullBranchPrefixes: string[] = []
307
+
308
+ for (const error of errors) {
309
+ if (error.keyword !== 'anyOf') continue
310
+
311
+ const parentSchema = this.resolveSchemaPath(error.schemaPath)
312
+ if (!parentSchema) continue
313
+
314
+ const nullIndex = unwrapNullableAnyOfIndex(parentSchema)
315
+ if (nullIndex === -1) continue
316
+
317
+ exactPaths.push(error.schemaPath) // e.g. "#/anyOf"
318
+ const anyOfBase = error.schemaPath.slice(0, -'anyOf'.length)
319
+ nullBranchPrefixes.push(`${anyOfBase}anyOf/${nullIndex}/`) // e.g. "#/anyOf/1/"
320
+ }
321
+
322
+ if (!exactPaths.length) return
323
+
324
+ for (let i = errors.length - 1; i >= 0; i--) {
325
+ const sp = errors[i]!.schemaPath
326
+ if (exactPaths.includes(sp) || nullBranchPrefixes.some(p => sp.startsWith(p))) {
327
+ errors.splice(i, 1)
328
+ }
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Navigates the schema tree using an AJV schemaPath (e.g. "#/properties/foo/anyOf")
334
+ * and returns the parent schema containing the last keyword.
335
+ */
336
+ private resolveSchemaPath(schemaPath: string): JsonSchema | undefined {
337
+ // schemaPath looks like "#/properties/foo/anyOf" or "#/anyOf"
338
+ // We want the schema that contains the final keyword (e.g. "anyOf")
339
+ const segments = schemaPath.replace(/^#\//, '').split('/')
340
+ // Remove the last segment (the keyword itself, e.g. "anyOf")
341
+ segments.pop()
342
+
343
+ let current: any = this.schema
344
+ for (const segment of segments) {
345
+ if (!current || typeof current !== 'object') return undefined
346
+ current = current[segment]
347
+ }
348
+ return current as JsonSchema | undefined
349
+ }
350
+
276
351
  private getErrorMessageForInstancePath(
277
- schema: JsonSchema<IN, OUT> | undefined,
352
+ schema: JsonSchema<OUT> | undefined,
278
353
  instancePath: string,
279
354
  keyword: string,
280
355
  ): string | undefined {
@@ -284,8 +359,8 @@ export class AjvSchema<IN = unknown, OUT = IN> {
284
359
  return this.traverseSchemaPath(schema, segments, keyword)
285
360
  }
286
361
 
287
- private traverseSchemaPath<IN = unknown, OUT = IN>(
288
- schema: JsonSchema<IN, OUT>,
362
+ private traverseSchemaPath<T>(
363
+ schema: JsonSchema<T>,
289
364
  segments: string[],
290
365
  keyword: string,
291
366
  ): string | undefined {
@@ -300,6 +375,12 @@ export class AjvSchema<IN = unknown, OUT = IN> {
300
375
  return nextSchema.errorMessages[keyword]
301
376
  }
302
377
 
378
+ // Check through nullable wrapper
379
+ const unwrapped = unwrapNullableAnyOf(nextSchema)
380
+ if (unwrapped?.errorMessages?.[keyword]) {
381
+ return unwrapped.errorMessages[keyword]
382
+ }
383
+
303
384
  if (remainingSegments.length) {
304
385
  return this.traverseSchemaPath(nextSchema, remainingSegments, keyword)
305
386
  }
@@ -309,11 +390,15 @@ export class AjvSchema<IN = unknown, OUT = IN> {
309
390
 
310
391
  private getChildSchema(schema: JsonSchema, segment: string | undefined): JsonSchema | undefined {
311
392
  if (!segment) return undefined
312
- if (/^\d+$/.test(segment) && schema.items) {
313
- return this.getArrayItemSchema(schema, segment)
393
+
394
+ // Unwrap nullable anyOf to find properties/items through nullable wrappers
395
+ const effectiveSchema = unwrapNullableAnyOf(schema) ?? schema
396
+
397
+ if (/^\d+$/.test(segment) && effectiveSchema.items) {
398
+ return this.getArrayItemSchema(effectiveSchema, segment)
314
399
  }
315
400
 
316
- return this.getObjectPropertySchema(schema, segment)
401
+ return this.getObjectPropertySchema(effectiveSchema, segment)
317
402
  }
318
403
 
319
404
  private getArrayItemSchema(schema: JsonSchema, indexSegment: string): JsonSchema | undefined {
@@ -331,15 +416,27 @@ export class AjvSchema<IN = unknown, OUT = IN> {
331
416
  }
332
417
  }
333
418
 
419
+ function unwrapNullableAnyOf(schema: JsonSchema): JsonSchema | undefined {
420
+ const nullIndex = unwrapNullableAnyOfIndex(schema)
421
+ if (nullIndex === -1) return undefined
422
+ return schema.anyOf![1 - nullIndex]!
423
+ }
424
+
425
+ function unwrapNullableAnyOfIndex(schema: JsonSchema): number {
426
+ if (schema.anyOf?.length !== 2) return -1
427
+ const nullIndex = schema.anyOf.findIndex(s => s.type === 'null')
428
+ return nullIndex
429
+ }
430
+
334
431
  const separator = '\n'
335
432
 
336
433
  export const HIDDEN_AJV_SCHEMA = Symbol('HIDDEN_AJV_SCHEMA')
337
434
 
338
- export type WithCachedAjvSchema<Base, IN, OUT> = Base & {
339
- [HIDDEN_AJV_SCHEMA]: AjvSchema<IN, OUT>
435
+ export type WithCachedAjvSchema<Base, OUT> = Base & {
436
+ [HIDDEN_AJV_SCHEMA]: AjvSchema<OUT>
340
437
  }
341
438
 
342
- export interface AjvValidationOptions<IN> {
439
+ export interface AjvValidationOptions {
343
440
  /**
344
441
  * Defaults to true,
345
442
  * because that's how AJV works by default,
@@ -367,7 +464,7 @@ export interface AjvValidationOptions<IN> {
367
464
  * can include it "how it was" in an error message. So, for that reason we'll use
368
465
  * `getOriginalInput()`, if it's provided.
369
466
  */
370
- getOriginalInput?: () => IN
467
+ getOriginalInput?: () => unknown
371
468
  }
372
469
 
373
470
  export interface AjvSchemaCfg {
@@ -395,10 +492,10 @@ export interface AjvSchemaCfg {
395
492
  lazy?: boolean
396
493
  }
397
494
 
398
- export type SchemaHandledByAjv<IN, OUT = IN> =
399
- | JsonSchemaTerminal<IN, OUT, any>
400
- | JsonSchema<IN, OUT>
401
- | AjvSchema<IN, OUT>
495
+ export type SchemaHandledByAjv<OUT> =
496
+ | JsonSchemaTerminal<OUT, any>
497
+ | JsonSchema<OUT>
498
+ | AjvSchema<OUT>
402
499
 
403
500
  // ===== JsonSchemaBuilders ===== //
404
501
 
@@ -407,19 +504,19 @@ export const j = {
407
504
  * Matches literally any value - equivalent to TypeScript's `any` type.
408
505
  * Use sparingly, as it bypasses type validation entirely.
409
506
  */
410
- any(): JsonSchemaAnyBuilder<any, any, false> {
507
+ any(): JsonSchemaAnyBuilder<any, false> {
411
508
  return new JsonSchemaAnyBuilder({})
412
509
  },
413
510
 
414
- string(): JsonSchemaStringBuilder<string, string, false> {
511
+ string(): JsonSchemaStringBuilder<string, false> {
415
512
  return new JsonSchemaStringBuilder()
416
513
  },
417
514
 
418
- number(): JsonSchemaNumberBuilder<number, number, false> {
515
+ number(): JsonSchemaNumberBuilder<number, false> {
419
516
  return new JsonSchemaNumberBuilder()
420
517
  },
421
518
 
422
- boolean(): JsonSchemaBooleanBuilder<boolean, boolean, false> {
519
+ boolean(): JsonSchemaBooleanBuilder<boolean, false> {
423
520
  return new JsonSchemaBooleanBuilder()
424
521
  },
425
522
 
@@ -430,16 +527,16 @@ export const j = {
430
527
  return j.object<AnyObject>({}).allowAdditionalProperties()
431
528
  },
432
529
 
433
- stringMap<S extends JsonSchemaTerminal<any, any, any>>(
530
+ stringMap<S extends JsonSchemaTerminal<any, any>>(
434
531
  schema: S,
435
- ): JsonSchemaObjectBuilder<StringMap<SchemaIn<S>>, StringMap<SchemaOut<S>>> {
532
+ ): JsonSchemaObjectBuilder<StringMap<SchemaOut<S>>> {
436
533
  const isValueOptional = schema.getSchema().optionalField
437
534
  const builtSchema = schema.build()
438
535
  const finalValueSchema: JsonSchema = isValueOptional
439
536
  ? { anyOf: [{ isUndefined: true }, builtSchema] }
440
537
  : builtSchema
441
538
 
442
- return new JsonSchemaObjectBuilder<StringMap<SchemaIn<S>>, StringMap<SchemaOut<S>>>(
539
+ return new JsonSchemaObjectBuilder<StringMap<SchemaOut<S>>>(
443
540
  {},
444
541
  {
445
542
  hasIsOfTypeCheck: false,
@@ -487,21 +584,15 @@ export const j = {
487
584
  withRegexKeys,
488
585
  }),
489
586
 
490
- array<IN, OUT, Opt>(
491
- itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>,
492
- ): JsonSchemaArrayBuilder<IN, OUT, Opt> {
587
+ array<OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<OUT, Opt>): JsonSchemaArrayBuilder<OUT, Opt> {
493
588
  return new JsonSchemaArrayBuilder(itemSchema)
494
589
  },
495
590
 
496
- tuple<const S extends JsonSchemaAnyBuilder<any, any, any>[]>(
497
- items: S,
498
- ): JsonSchemaTupleBuilder<S> {
591
+ tuple<const S extends JsonSchemaAnyBuilder<any, any>[]>(items: S): JsonSchemaTupleBuilder<S> {
499
592
  return new JsonSchemaTupleBuilder<S>(items)
500
593
  },
501
594
 
502
- set<IN, OUT, Opt>(
503
- itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>,
504
- ): JsonSchemaSet2Builder<IN, OUT, Opt> {
595
+ set<OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<OUT, Opt>): JsonSchemaSet2Builder<OUT, Opt> {
505
596
  return new JsonSchemaSet2Builder(itemSchema)
506
597
  },
507
598
 
@@ -557,18 +648,16 @@ export const j = {
557
648
  * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format).
558
649
  * Use `oneOf` when schemas are mutually exclusive.
559
650
  */
560
- oneOf<
561
- B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[],
562
- IN = BuilderInUnion<B>,
563
- OUT = BuilderOutUnion<B>,
564
- >(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false> {
651
+ oneOf<B extends readonly JsonSchemaAnyBuilder<any, boolean>[], OUT = BuilderOutUnion<B>>(
652
+ items: [...B],
653
+ ): JsonSchemaAnyBuilder<OUT, false> {
565
654
  const schemas = items.map(b => b.build())
566
655
  _assert(
567
656
  schemas.every(hasNoObjectSchemas),
568
657
  'Do not use `oneOf` validation with non-primitive types!',
569
658
  )
570
659
 
571
- return new JsonSchemaAnyBuilder<IN, OUT, false>({
660
+ return new JsonSchemaAnyBuilder<OUT, false>({
572
661
  oneOf: schemas,
573
662
  })
574
663
  },
@@ -584,18 +673,16 @@ export const j = {
584
673
  * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format).
585
674
  * Use `oneOf` when schemas are mutually exclusive.
586
675
  */
587
- anyOf<
588
- B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[],
589
- IN = BuilderInUnion<B>,
590
- OUT = BuilderOutUnion<B>,
591
- >(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false> {
676
+ anyOf<B extends readonly JsonSchemaAnyBuilder<any, boolean>[], OUT = BuilderOutUnion<B>>(
677
+ items: [...B],
678
+ ): JsonSchemaAnyBuilder<OUT, false> {
592
679
  const schemas = items.map(b => b.build())
593
680
  _assert(
594
681
  schemas.every(hasNoObjectSchemas),
595
682
  'Do not use `anyOf` validation with non-primitive types!',
596
683
  )
597
684
 
598
- return new JsonSchemaAnyBuilder<IN, OUT, false>({
685
+ return new JsonSchemaAnyBuilder<OUT, false>({
599
686
  anyOf: schemas,
600
687
  })
601
688
  },
@@ -612,13 +699,11 @@ export const j = {
612
699
  * const schema = j.anyOfBy('success', schemaMap)
613
700
  * ```
614
701
  */
615
- anyOfBy<
616
- P extends string,
617
- D extends Record<PropertyKey, JsonSchemaTerminal<any, any, any>>,
618
- IN = AnyOfByIn<D>,
619
- OUT = AnyOfByOut<D>,
620
- >(propertyName: P, schemaDictionary: D): JsonSchemaAnyOfByBuilder<IN, OUT, P> {
621
- return new JsonSchemaAnyOfByBuilder<IN, OUT, P>(propertyName, schemaDictionary)
702
+ anyOfBy<D extends Record<PropertyKey, JsonSchemaTerminal<any, any>>, OUT = AnyOfByOut<D>>(
703
+ propertyName: string,
704
+ schemaDictionary: D,
705
+ ): JsonSchemaAnyOfByBuilder<OUT> {
706
+ return new JsonSchemaAnyOfByBuilder<OUT>(propertyName, schemaDictionary)
622
707
  },
623
708
 
624
709
  /**
@@ -629,12 +714,10 @@ export const j = {
629
714
  * const schema = j.anyOfThese([successSchema, errorSchema])
630
715
  * ```
631
716
  */
632
- anyOfThese<
633
- B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[],
634
- IN = BuilderInUnion<B>,
635
- OUT = BuilderOutUnion<B>,
636
- >(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false> {
637
- return new JsonSchemaAnyBuilder<IN, OUT, false>({
717
+ anyOfThese<B extends readonly JsonSchemaAnyBuilder<any, boolean>[], OUT = BuilderOutUnion<B>>(
718
+ items: [...B],
719
+ ): JsonSchemaAnyBuilder<OUT, false> {
720
+ return new JsonSchemaAnyBuilder<OUT, false>({
638
721
  anyOfThese: items.map(b => b.build()),
639
722
  })
640
723
  },
@@ -669,17 +752,17 @@ const TS_2000_MILLIS = TS_2000 * 1000
669
752
  With `Opt`, we can infer it as `{ foo?: string | undefined }`.
670
753
  */
671
754
 
672
- export class JsonSchemaTerminal<IN, OUT, Opt> {
673
- protected [HIDDEN_AJV_SCHEMA]: AjvSchema<any, any> | undefined
755
+ export class JsonSchemaTerminal<OUT, Opt> {
756
+ protected [HIDDEN_AJV_SCHEMA]: AjvSchema<any> | undefined
674
757
  protected schema: JsonSchema
675
758
 
676
759
  constructor(schema: JsonSchema) {
677
760
  this.schema = schema
678
761
  }
679
762
 
680
- get ajvSchema(): AjvSchema<any, any> {
763
+ get ajvSchema(): AjvSchema<any> {
681
764
  if (!this[HIDDEN_AJV_SCHEMA]) {
682
- this[HIDDEN_AJV_SCHEMA] = AjvSchema.create<IN, OUT>(this)
765
+ this[HIDDEN_AJV_SCHEMA] = AjvSchema.create<OUT>(this)
683
766
  }
684
767
 
685
768
  return this[HIDDEN_AJV_SCHEMA]
@@ -693,7 +776,7 @@ export class JsonSchemaTerminal<IN, OUT, Opt> {
693
776
  * Produces a "clean schema object" without methods.
694
777
  * Same as if it would be JSON.stringified.
695
778
  */
696
- build(): JsonSchema<IN, OUT> {
779
+ build(): JsonSchema<OUT> {
697
780
  _assert(
698
781
  !(this.schema.optionalField && this.schema.default !== undefined),
699
782
  '.optional() and .default() should not be used together - the default value makes .optional() redundant and causes incorrect type inference',
@@ -702,7 +785,7 @@ export class JsonSchemaTerminal<IN, OUT, Opt> {
702
785
  const jsonSchema = _sortObject(
703
786
  deepCopyPreservingFunctions(this.schema) as AnyObject,
704
787
  JSON_SCHEMA_ORDER,
705
- ) as JsonSchema<IN, OUT>
788
+ ) as JsonSchema<OUT>
706
789
 
707
790
  delete jsonSchema.optionalField
708
791
 
@@ -721,34 +804,49 @@ export class JsonSchemaTerminal<IN, OUT, Opt> {
721
804
  return clone
722
805
  }
723
806
 
724
- validate(input: any, opt?: AjvValidationOptions<IN>): OUT {
807
+ validate(input: unknown, opt?: AjvValidationOptions): OUT {
725
808
  return this.ajvSchema.validate(input, opt)
726
809
  }
727
810
 
728
- isValid(input: any, opt?: AjvValidationOptions<IN>): boolean {
811
+ isValid(input: unknown, opt?: AjvValidationOptions): boolean {
729
812
  return this.ajvSchema.isValid(input, opt)
730
813
  }
731
814
 
732
815
  getValidationResult(
733
- input: any,
734
- opt: AjvValidationOptions<IN> = {},
816
+ input: unknown,
817
+ opt: AjvValidationOptions = {},
735
818
  ): ValidationFunctionResult<OUT, AjvValidationError> {
736
819
  return this.ajvSchema.getValidationResult(input, opt)
737
820
  }
738
821
 
739
- getValidationFunction(): ValidationFunction<any, OUT, AjvValidationError> {
822
+ getValidationFunction(): ValidationFunction<OUT, AjvValidationError> {
740
823
  return this.ajvSchema.getValidationFunction()
741
824
  }
742
825
 
826
+ /**
827
+ * Specify a function to be called after the normal validation is finished.
828
+ *
829
+ * This function will receive the validated, type-safe data, and you can use it
830
+ * to do further validations, e.g. conditional validations based on certain property values,
831
+ * or to do data modifications either by mutating the input or returning a new value.
832
+ *
833
+ * If you throw an error from this function, it will show up as an error in the validation.
834
+ */
835
+ postValidation<OUT2 = OUT>(fn: PostValidatonFn<OUT, OUT2>): JsonSchemaTerminal<OUT2, Opt> {
836
+ const clone = this.cloneAndUpdateSchema({
837
+ postValidation: fn,
838
+ })
839
+ return clone as unknown as JsonSchemaTerminal<OUT2, Opt>
840
+ }
841
+
743
842
  /**
744
843
  * @experimental
745
844
  */
746
- in!: IN
747
845
  out!: OUT
748
846
  opt!: Opt
749
847
  }
750
848
 
751
- export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, OUT, Opt> {
849
+ export class JsonSchemaAnyBuilder<OUT, Opt> extends JsonSchemaTerminal<OUT, Opt> {
752
850
  protected setErrorMessage(ruleName: string, errorMessage: string | undefined): void {
753
851
  if (_isUndefined(errorMessage)) return
754
852
 
@@ -811,12 +909,12 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, O
811
909
  return this.cloneAndUpdateSchema({ type: 'object', instanceof: of })
812
910
  }
813
911
 
814
- optional(): JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true> {
912
+ optional(): JsonSchemaAnyBuilder<OUT | undefined, true> {
815
913
  const clone = this.cloneAndUpdateSchema({ optionalField: true })
816
- return clone as unknown as JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true>
914
+ return clone as unknown as JsonSchemaAnyBuilder<OUT | undefined, true>
817
915
  }
818
916
 
819
- nullable(): JsonSchemaAnyBuilder<IN | null, OUT | null, Opt> {
917
+ nullable(): JsonSchemaAnyBuilder<OUT | null, Opt> {
820
918
  return new JsonSchemaAnyBuilder({
821
919
  anyOf: [this.build(), { type: 'null' }],
822
920
  })
@@ -826,15 +924,15 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, O
826
924
  * @deprecated
827
925
  * The usage of this function is discouraged as it defeats the purpose of having type-safe validation.
828
926
  */
829
- castAs<T>(): JsonSchemaAnyBuilder<T, T, Opt> {
830
- return this as unknown as JsonSchemaAnyBuilder<T, T, Opt>
927
+ castAs<T>(): JsonSchemaAnyBuilder<T, Opt> {
928
+ return this as unknown as JsonSchemaAnyBuilder<T, Opt>
831
929
  }
832
930
 
833
931
  /**
834
932
  * Locks the given schema chain and no other modification can be done to it.
835
933
  */
836
- final(): JsonSchemaTerminal<IN, OUT, Opt> {
837
- return new JsonSchemaTerminal<IN, OUT, Opt>(this.schema)
934
+ final(): JsonSchemaTerminal<OUT, Opt> {
935
+ return new JsonSchemaTerminal<OUT, Opt>(this.schema)
838
936
  }
839
937
 
840
938
  /**
@@ -843,11 +941,11 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, O
843
941
  *
844
942
  * You may add multiple custom validators and they will be executed in the order you added them.
845
943
  */
846
- custom<OUT2 = OUT>(validator: CustomValidatorFn): JsonSchemaAnyBuilder<IN, OUT2, Opt> {
944
+ custom<OUT2 = OUT>(validator: CustomValidatorFn): JsonSchemaAnyBuilder<OUT2, Opt> {
847
945
  const { customValidations = [] } = this.schema
848
946
  return this.cloneAndUpdateSchema({
849
947
  customValidations: [...customValidations, validator],
850
- }) as unknown as JsonSchemaAnyBuilder<IN, OUT2, Opt>
948
+ }) as unknown as JsonSchemaAnyBuilder<OUT2, Opt>
851
949
  }
852
950
 
853
951
  /**
@@ -860,19 +958,18 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, O
860
958
  * This feature only works when the current schema is nested in an object or array schema,
861
959
  * due to how mutability works in Ajv.
862
960
  */
863
- convert<OUT2>(converter: CustomConverterFn<OUT2>): JsonSchemaAnyBuilder<IN, OUT2, Opt> {
961
+ convert<OUT2>(converter: CustomConverterFn<OUT2>): JsonSchemaAnyBuilder<OUT2, Opt> {
864
962
  const { customConversions = [] } = this.schema
865
963
  return this.cloneAndUpdateSchema({
866
964
  customConversions: [...customConversions, converter],
867
- }) as unknown as JsonSchemaAnyBuilder<IN, OUT2, Opt>
965
+ }) as unknown as JsonSchemaAnyBuilder<OUT2, Opt>
868
966
  }
869
967
  }
870
968
 
871
969
  export class JsonSchemaStringBuilder<
872
- IN extends string | undefined = string,
873
- OUT = IN,
970
+ OUT extends string | undefined = string,
874
971
  Opt extends boolean = false,
875
- > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
972
+ > extends JsonSchemaAnyBuilder<OUT, Opt> {
876
973
  constructor() {
877
974
  super({
878
975
  type: 'string',
@@ -894,17 +991,19 @@ export class JsonSchemaStringBuilder<
894
991
  optionalValues?: T,
895
992
  ): T extends readonly (infer U)[]
896
993
  ? null extends U
897
- ? JsonSchemaTerminal<IN | undefined, OUT | undefined, true>
898
- : JsonSchemaStringBuilder<IN | undefined, OUT | undefined, true>
899
- : JsonSchemaStringBuilder<IN | undefined, OUT | undefined, true> {
994
+ ? JsonSchemaTerminal<OUT | undefined, true>
995
+ : JsonSchemaStringBuilder<OUT | undefined, true>
996
+ : JsonSchemaStringBuilder<OUT | undefined, true> {
900
997
  if (!optionalValues) {
901
998
  return super.optional() as any
902
999
  }
903
1000
 
904
1001
  _typeCast<(string | null)[]>(optionalValues)
905
1002
 
906
- let newBuilder: JsonSchemaTerminal<IN | undefined, OUT | undefined, true> =
907
- new JsonSchemaStringBuilder<IN, OUT, Opt>().optional()
1003
+ let newBuilder: JsonSchemaTerminal<OUT | undefined, true> = new JsonSchemaStringBuilder<
1004
+ OUT,
1005
+ Opt
1006
+ >().optional()
908
1007
  const alternativesSchema = j.enum(optionalValues)
909
1008
  Object.assign(newBuilder.getSchema(), {
910
1009
  anyOf: [this.build(), alternativesSchema.build()],
@@ -984,8 +1083,8 @@ export class JsonSchemaStringBuilder<
984
1083
  })
985
1084
  }
986
1085
 
987
- branded<B extends string>(): JsonSchemaStringBuilder<B, B, Opt> {
988
- return this as unknown as JsonSchemaStringBuilder<B, B, Opt>
1086
+ branded<B extends string>(): JsonSchemaStringBuilder<B, Opt> {
1087
+ return this as unknown as JsonSchemaStringBuilder<B, Opt>
989
1088
  }
990
1089
 
991
1090
  /**
@@ -998,8 +1097,8 @@ export class JsonSchemaStringBuilder<
998
1097
  return new JsonSchemaIsoDateBuilder()
999
1098
  }
1000
1099
 
1001
- isoDateTime(): JsonSchemaStringBuilder<IsoDateTime | IN, IsoDateTime, Opt> {
1002
- return this.cloneAndUpdateSchema({ IsoDateTime: true }).branded<IsoDateTime>() as any
1100
+ isoDateTime(): JsonSchemaStringBuilder<IsoDateTime, Opt> {
1101
+ return this.cloneAndUpdateSchema({ IsoDateTime: true }).branded<IsoDateTime>()
1003
1102
  }
1004
1103
 
1005
1104
  isoMonth(): JsonSchemaIsoMonthBuilder {
@@ -1052,9 +1151,9 @@ export class JsonSchemaStringBuilder<
1052
1151
  * All previous expectations in the schema chain are dropped - including `.optional()` -
1053
1152
  * because this call effectively starts a new schema chain as an `enum` validation.
1054
1153
  */
1055
- ianaTimezone(): JsonSchemaEnumBuilder<string | IANATimezone, IANATimezone, false> {
1154
+ ianaTimezone(): JsonSchemaEnumBuilder<IANATimezone, false> {
1056
1155
  // UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
1057
- return j.enum(TIMEZONES, { msg: 'is an invalid IANA timezone' }).branded<IANATimezone>() as any
1156
+ return j.enum(TIMEZONES, { msg: 'is an invalid IANA timezone' }).branded<IANATimezone>()
1058
1157
  }
1059
1158
 
1060
1159
  base64Url(): this {
@@ -1073,7 +1172,6 @@ export interface JsonSchemaStringEmailOptions {
1073
1172
  }
1074
1173
 
1075
1174
  export class JsonSchemaIsoDateBuilder<Opt extends boolean = false> extends JsonSchemaAnyBuilder<
1076
- string | IsoDate,
1077
1175
  IsoDate,
1078
1176
  Opt
1079
1177
  > {
@@ -1096,8 +1194,8 @@ export class JsonSchemaIsoDateBuilder<Opt extends boolean = false> extends JsonS
1096
1194
  override optional<N extends null | undefined = undefined>(
1097
1195
  nullValue?: N,
1098
1196
  ): N extends null
1099
- ? JsonSchemaTerminal<string | IsoDate | null | undefined, IsoDate | undefined, true>
1100
- : JsonSchemaAnyBuilder<string | IsoDate | undefined, IsoDate | undefined, true> {
1197
+ ? JsonSchemaTerminal<IsoDate | undefined, true>
1198
+ : JsonSchemaAnyBuilder<IsoDate | undefined, true> {
1101
1199
  if (nullValue === undefined) {
1102
1200
  return super.optional() as any
1103
1201
  }
@@ -1149,7 +1247,6 @@ export interface JsonSchemaIsoDateOptions {
1149
1247
  }
1150
1248
 
1151
1249
  export class JsonSchemaIsoMonthBuilder<Opt extends boolean = false> extends JsonSchemaAnyBuilder<
1152
- string | IsoDate,
1153
1250
  IsoMonth,
1154
1251
  Opt
1155
1252
  > {
@@ -1164,10 +1261,9 @@ export class JsonSchemaIsoMonthBuilder<Opt extends boolean = false> extends Json
1164
1261
  export interface JsonSchemaIsoMonthOptions {}
1165
1262
 
1166
1263
  export class JsonSchemaNumberBuilder<
1167
- IN extends number | undefined = number,
1168
- OUT = IN,
1264
+ OUT extends number | undefined = number,
1169
1265
  Opt extends boolean = false,
1170
- > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
1266
+ > extends JsonSchemaAnyBuilder<OUT, Opt> {
1171
1267
  constructor() {
1172
1268
  super({
1173
1269
  type: 'number',
@@ -1189,17 +1285,19 @@ export class JsonSchemaNumberBuilder<
1189
1285
  optionalValues?: T,
1190
1286
  ): T extends readonly (infer U)[]
1191
1287
  ? null extends U
1192
- ? JsonSchemaTerminal<IN | undefined, OUT | undefined, true>
1193
- : JsonSchemaNumberBuilder<IN | undefined, OUT | undefined, true>
1194
- : JsonSchemaNumberBuilder<IN | undefined, OUT | undefined, true> {
1288
+ ? JsonSchemaTerminal<OUT | undefined, true>
1289
+ : JsonSchemaNumberBuilder<OUT | undefined, true>
1290
+ : JsonSchemaNumberBuilder<OUT | undefined, true> {
1195
1291
  if (!optionalValues) {
1196
1292
  return super.optional() as any
1197
1293
  }
1198
1294
 
1199
1295
  _typeCast<(number | null)[]>(optionalValues)
1200
1296
 
1201
- let newBuilder: JsonSchemaTerminal<IN | undefined, OUT | undefined, true> =
1202
- new JsonSchemaNumberBuilder<IN, OUT, Opt>().optional()
1297
+ let newBuilder: JsonSchemaTerminal<OUT | undefined, true> = new JsonSchemaNumberBuilder<
1298
+ OUT,
1299
+ Opt
1300
+ >().optional()
1203
1301
  const alternativesSchema = j.enum(optionalValues)
1204
1302
  Object.assign(newBuilder.getSchema(), {
1205
1303
  anyOf: [this.build(), alternativesSchema.build()],
@@ -1224,8 +1322,8 @@ export class JsonSchemaNumberBuilder<
1224
1322
  return this.cloneAndUpdateSchema({ type: 'integer' })
1225
1323
  }
1226
1324
 
1227
- branded<B extends number>(): JsonSchemaNumberBuilder<B, B, Opt> {
1228
- return this as unknown as JsonSchemaNumberBuilder<B, B, Opt>
1325
+ branded<B extends number>(): JsonSchemaNumberBuilder<B, Opt> {
1326
+ return this as unknown as JsonSchemaNumberBuilder<B, Opt>
1229
1327
  }
1230
1328
 
1231
1329
  multipleOf(multipleOf: number): this {
@@ -1301,23 +1399,19 @@ export class JsonSchemaNumberBuilder<
1301
1399
  return this
1302
1400
  }
1303
1401
 
1304
- unixTimestamp(): JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, Opt> {
1402
+ unixTimestamp(): JsonSchemaNumberBuilder<UnixTimestamp, Opt> {
1305
1403
  return this.integer().min(0).max(TS_2500).branded<UnixTimestamp>()
1306
1404
  }
1307
1405
 
1308
- unixTimestamp2000(): JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, Opt> {
1406
+ unixTimestamp2000(): JsonSchemaNumberBuilder<UnixTimestamp, Opt> {
1309
1407
  return this.integer().min(TS_2000).max(TS_2500).branded<UnixTimestamp>()
1310
1408
  }
1311
1409
 
1312
- unixTimestampMillis(): JsonSchemaNumberBuilder<UnixTimestampMillis, UnixTimestampMillis, Opt> {
1410
+ unixTimestampMillis(): JsonSchemaNumberBuilder<UnixTimestampMillis, Opt> {
1313
1411
  return this.integer().min(0).max(TS_2500_MILLIS).branded<UnixTimestampMillis>()
1314
1412
  }
1315
1413
 
1316
- unixTimestamp2000Millis(): JsonSchemaNumberBuilder<
1317
- UnixTimestampMillis,
1318
- UnixTimestampMillis,
1319
- Opt
1320
- > {
1414
+ unixTimestamp2000Millis(): JsonSchemaNumberBuilder<UnixTimestampMillis, Opt> {
1321
1415
  return this.integer().min(TS_2000_MILLIS).max(TS_2500_MILLIS).branded<UnixTimestampMillis>()
1322
1416
  }
1323
1417
 
@@ -1343,10 +1437,9 @@ export class JsonSchemaNumberBuilder<
1343
1437
  }
1344
1438
 
1345
1439
  export class JsonSchemaBooleanBuilder<
1346
- IN extends boolean | undefined = boolean,
1347
- OUT = IN,
1440
+ OUT extends boolean | undefined = boolean,
1348
1441
  Opt extends boolean = false,
1349
- > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
1442
+ > extends JsonSchemaAnyBuilder<OUT, Opt> {
1350
1443
  constructor() {
1351
1444
  super({
1352
1445
  type: 'boolean',
@@ -1359,18 +1452,12 @@ export class JsonSchemaBooleanBuilder<
1359
1452
  * This `optionalValue` feature only works when the current schema is nested in an object or array schema,
1360
1453
  * due to how mutability works in Ajv.
1361
1454
  */
1362
- override optional(
1363
- optionalValue?: boolean,
1364
- ): JsonSchemaBooleanBuilder<IN | undefined, OUT | undefined, true> {
1455
+ override optional(optionalValue?: boolean): JsonSchemaBooleanBuilder<OUT | undefined, true> {
1365
1456
  if (typeof optionalValue === 'undefined') {
1366
- return super.optional() as unknown as JsonSchemaBooleanBuilder<
1367
- IN | undefined,
1368
- OUT | undefined,
1369
- true
1370
- >
1457
+ return super.optional() as unknown as JsonSchemaBooleanBuilder<OUT | undefined, true>
1371
1458
  }
1372
1459
 
1373
- const newBuilder = new JsonSchemaBooleanBuilder<IN, OUT, Opt>().optional()
1460
+ const newBuilder = new JsonSchemaBooleanBuilder<OUT, Opt>().optional()
1374
1461
  const alternativesSchema = j.enum([optionalValue])
1375
1462
  Object.assign(newBuilder.getSchema(), {
1376
1463
  anyOf: [this.build(), alternativesSchema.build()],
@@ -1382,10 +1469,9 @@ export class JsonSchemaBooleanBuilder<
1382
1469
  }
1383
1470
 
1384
1471
  export class JsonSchemaObjectBuilder<
1385
- IN extends AnyObject,
1386
1472
  OUT extends AnyObject,
1387
1473
  Opt extends boolean = false,
1388
- > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
1474
+ > extends JsonSchemaAnyBuilder<OUT, Opt> {
1389
1475
  constructor(props?: AnyObject, opt?: JsonSchemaObjectBuilderOpts) {
1390
1476
  super({
1391
1477
  type: 'object',
@@ -1405,7 +1491,7 @@ export class JsonSchemaObjectBuilder<
1405
1491
  const required: string[] = []
1406
1492
 
1407
1493
  for (const [key, builder] of Object.entries(props)) {
1408
- const isOptional = (builder as JsonSchemaTerminal<any, any, any>).getSchema().optionalField
1494
+ const isOptional = (builder as JsonSchemaTerminal<any, any>).getSchema().optionalField
1409
1495
  if (!isOptional) {
1410
1496
  required.push(key)
1411
1497
  }
@@ -1432,8 +1518,8 @@ export class JsonSchemaObjectBuilder<
1432
1518
  override optional<N extends null | undefined = undefined>(
1433
1519
  nullValue?: N,
1434
1520
  ): N extends null
1435
- ? JsonSchemaTerminal<IN | null | undefined, OUT | undefined, true>
1436
- : JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true> {
1521
+ ? JsonSchemaTerminal<OUT | undefined, true>
1522
+ : JsonSchemaAnyBuilder<OUT | undefined, true> {
1437
1523
  if (nullValue === undefined) {
1438
1524
  return super.optional() as any
1439
1525
  }
@@ -1456,35 +1542,25 @@ export class JsonSchemaObjectBuilder<
1456
1542
  return this.cloneAndUpdateSchema({ additionalProperties: true })
1457
1543
  }
1458
1544
 
1459
- extend<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(
1545
+ extend<P extends Record<string, JsonSchemaAnyBuilder<any, any>>>(
1460
1546
  props: P,
1461
1547
  ): JsonSchemaObjectBuilder<
1462
- RelaxIndexSignature<
1463
- OptionalDbEntityFields<
1464
- Override<
1465
- IN,
1466
- {
1467
- [K in keyof P]?: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never
1468
- }
1469
- >
1470
- >
1471
- >,
1472
1548
  Override<
1473
1549
  OUT,
1474
1550
  {
1475
1551
  // required keys
1476
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1552
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1477
1553
  ? IsOpt extends true
1478
1554
  ? never
1479
1555
  : K
1480
- : never]: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never
1556
+ : never]: P[K] extends JsonSchemaAnyBuilder<infer OUT2, any> ? OUT2 : never
1481
1557
  } & {
1482
1558
  // optional keys
1483
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1559
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1484
1560
  ? IsOpt extends true
1485
1561
  ? K
1486
1562
  : never
1487
- : never]?: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never
1563
+ : never]?: P[K] extends JsonSchemaAnyBuilder<infer OUT2, any> ? OUT2 : never
1488
1564
  }
1489
1565
  >,
1490
1566
  false
@@ -1518,13 +1594,13 @@ export class JsonSchemaObjectBuilder<
1518
1594
  * const shuSchema = fooSchema.concat(barSchema).isOfType<Shu>() // important
1519
1595
  * ```
1520
1596
  */
1521
- concat<IN2 extends AnyObject, OUT2 extends AnyObject>(
1522
- other: JsonSchemaObjectBuilder<IN2, OUT2, any>,
1523
- ): JsonSchemaObjectBuilder<IN & IN2, OUT & OUT2, false> {
1597
+ concat<OUT2 extends AnyObject>(
1598
+ other: JsonSchemaObjectBuilder<OUT2, any>,
1599
+ ): JsonSchemaObjectBuilder<OUT & OUT2, false> {
1524
1600
  const clone = this.clone()
1525
1601
  mergeJsonSchemaObjects(clone.schema as any, other.schema as any)
1526
1602
  _objectAssign(clone.schema, { hasIsOfTypeCheck: false })
1527
- return clone as unknown as JsonSchemaObjectBuilder<IN & IN2, OUT & OUT2, false>
1603
+ return clone as unknown as JsonSchemaObjectBuilder<OUT & OUT2, false>
1528
1604
  }
1529
1605
 
1530
1606
  /**
@@ -1547,7 +1623,7 @@ export class JsonSchemaObjectBuilder<
1547
1623
  return this.cloneAndUpdateSchema({ maxProperties })
1548
1624
  }
1549
1625
 
1550
- exclusiveProperties(propNames: readonly (keyof IN & string)[]): this {
1626
+ exclusiveProperties(propNames: readonly (keyof OUT & string)[]): this {
1551
1627
  const exclusiveProperties = this.schema.exclusiveProperties ?? []
1552
1628
  return this.cloneAndUpdateSchema({ exclusiveProperties: [...exclusiveProperties, propNames] })
1553
1629
  }
@@ -1555,42 +1631,27 @@ export class JsonSchemaObjectBuilder<
1555
1631
 
1556
1632
  interface JsonSchemaObjectBuilderOpts {
1557
1633
  hasIsOfTypeCheck?: false
1558
- patternProperties?: StringMap<JsonSchema<any, any>>
1634
+ patternProperties?: StringMap<JsonSchema<any>>
1559
1635
  keySchema?: JsonSchema
1560
1636
  }
1561
1637
 
1562
1638
  export class JsonSchemaObjectInferringBuilder<
1563
- PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>,
1639
+ PROPS extends Record<string, JsonSchemaAnyBuilder<any, any>>,
1564
1640
  Opt extends boolean = false,
1565
1641
  > extends JsonSchemaAnyBuilder<
1566
1642
  Expand<
1567
1643
  {
1568
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1569
- ? IsOpt extends true
1570
- ? never
1571
- : K
1572
- : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1573
- } & {
1574
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1575
- ? IsOpt extends true
1576
- ? K
1577
- : never
1578
- : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1579
- }
1580
- >,
1581
- Expand<
1582
- {
1583
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1644
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1584
1645
  ? IsOpt extends true
1585
1646
  ? never
1586
1647
  : K
1587
- : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1648
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
1588
1649
  } & {
1589
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1650
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1590
1651
  ? IsOpt extends true
1591
1652
  ? K
1592
1653
  : never
1593
- : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1654
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
1594
1655
  }
1595
1656
  >,
1596
1657
  Opt
@@ -1611,7 +1672,7 @@ export class JsonSchemaObjectInferringBuilder<
1611
1672
  const required: string[] = []
1612
1673
 
1613
1674
  for (const [key, builder] of Object.entries(props)) {
1614
- const isOptional = (builder as JsonSchemaTerminal<any, any, any>).getSchema().optionalField
1675
+ const isOptional = (builder as JsonSchemaTerminal<any, any>).getSchema().optionalField
1615
1676
  if (!isOptional) {
1616
1677
  required.push(key)
1617
1678
  }
@@ -1642,33 +1703,17 @@ export class JsonSchemaObjectInferringBuilder<
1642
1703
  ? JsonSchemaTerminal<
1643
1704
  | Expand<
1644
1705
  {
1645
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1646
- ? IsOpt extends true
1647
- ? never
1648
- : K
1649
- : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1650
- } & {
1651
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1652
- ? IsOpt extends true
1653
- ? K
1654
- : never
1655
- : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1656
- }
1657
- >
1658
- | undefined,
1659
- | Expand<
1660
- {
1661
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1706
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1662
1707
  ? IsOpt extends true
1663
1708
  ? never
1664
1709
  : K
1665
- : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1710
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
1666
1711
  } & {
1667
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1712
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1668
1713
  ? IsOpt extends true
1669
1714
  ? K
1670
1715
  : never
1671
- : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1716
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
1672
1717
  }
1673
1718
  >
1674
1719
  | undefined,
@@ -1677,33 +1722,17 @@ export class JsonSchemaObjectInferringBuilder<
1677
1722
  : JsonSchemaAnyBuilder<
1678
1723
  | Expand<
1679
1724
  {
1680
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1725
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1681
1726
  ? IsOpt extends true
1682
1727
  ? never
1683
1728
  : K
1684
- : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1729
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
1685
1730
  } & {
1686
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1731
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, infer IsOpt>
1687
1732
  ? IsOpt extends true
1688
1733
  ? K
1689
1734
  : never
1690
- : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1691
- }
1692
- >
1693
- | undefined,
1694
- | Expand<
1695
- {
1696
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1697
- ? IsOpt extends true
1698
- ? never
1699
- : K
1700
- : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1701
- } & {
1702
- [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1703
- ? IsOpt extends true
1704
- ? K
1705
- : never
1706
- : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1735
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
1707
1736
  }
1708
1737
  >
1709
1738
  | undefined,
@@ -1731,7 +1760,7 @@ export class JsonSchemaObjectInferringBuilder<
1731
1760
  return this.cloneAndUpdateSchema({ additionalProperties: true })
1732
1761
  }
1733
1762
 
1734
- extend<NEW_PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(
1763
+ extend<NEW_PROPS extends Record<string, JsonSchemaAnyBuilder<any, any>>>(
1735
1764
  props: NEW_PROPS,
1736
1765
  ): JsonSchemaObjectInferringBuilder<
1737
1766
  {
@@ -1779,8 +1808,8 @@ export class JsonSchemaObjectInferringBuilder<
1779
1808
  }
1780
1809
  }
1781
1810
 
1782
- export class JsonSchemaArrayBuilder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<IN[], OUT[], Opt> {
1783
- constructor(itemsSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>) {
1811
+ export class JsonSchemaArrayBuilder<OUT, Opt> extends JsonSchemaAnyBuilder<OUT[], Opt> {
1812
+ constructor(itemsSchema: JsonSchemaAnyBuilder<OUT, Opt>) {
1784
1813
  super({
1785
1814
  type: 'array',
1786
1815
  items: itemsSchema.build(),
@@ -1811,12 +1840,8 @@ export class JsonSchemaArrayBuilder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<I
1811
1840
  }
1812
1841
  }
1813
1842
 
1814
- export class JsonSchemaSet2Builder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<
1815
- Iterable<IN>,
1816
- Set2<OUT>,
1817
- Opt
1818
- > {
1819
- constructor(itemsSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>) {
1843
+ export class JsonSchemaSet2Builder<OUT, Opt> extends JsonSchemaAnyBuilder<Set2<OUT>, Opt> {
1844
+ constructor(itemsSchema: JsonSchemaAnyBuilder<OUT, Opt>) {
1820
1845
  super({
1821
1846
  type: ['array', 'object'],
1822
1847
  Set2: itemsSchema.build(),
@@ -1832,11 +1857,7 @@ export class JsonSchemaSet2Builder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<
1832
1857
  }
1833
1858
  }
1834
1859
 
1835
- export class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder<
1836
- string | any[] | ArrayBuffer | Buffer,
1837
- Buffer,
1838
- false
1839
- > {
1860
+ export class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder<Buffer, false> {
1840
1861
  constructor() {
1841
1862
  super({
1842
1863
  Buffer: true,
@@ -1845,11 +1866,10 @@ export class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder<
1845
1866
  }
1846
1867
 
1847
1868
  export class JsonSchemaEnumBuilder<
1848
- IN extends string | number | boolean | null,
1849
- OUT extends IN = IN,
1869
+ OUT extends string | number | boolean | null,
1850
1870
  Opt extends boolean = false,
1851
- > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
1852
- constructor(enumValues: readonly IN[], baseType: EnumBaseType, opt?: JsonBuilderRuleOpt) {
1871
+ > extends JsonSchemaAnyBuilder<OUT, Opt> {
1872
+ constructor(enumValues: readonly OUT[], baseType: EnumBaseType, opt?: JsonBuilderRuleOpt) {
1853
1873
  const jsonSchema: JsonSchema = { enum: enumValues }
1854
1874
  // Specifying the base type helps in cases when we ask Ajv to coerce the types.
1855
1875
  // Having only the `enum` in the schema does not trigger a coercion in Ajv.
@@ -1862,16 +1882,14 @@ export class JsonSchemaEnumBuilder<
1862
1882
  if (opt?.msg) this.setErrorMessage('enum', opt.msg)
1863
1883
  }
1864
1884
 
1865
- branded<B extends IN>(): JsonSchemaEnumBuilder<B | IN, B, Opt> {
1866
- return this as unknown as JsonSchemaEnumBuilder<B | IN, B, Opt>
1885
+ branded<B extends OUT>(): JsonSchemaEnumBuilder<B, Opt> {
1886
+ return this as unknown as JsonSchemaEnumBuilder<B, Opt>
1867
1887
  }
1868
1888
  }
1869
1889
 
1870
1890
  export class JsonSchemaTupleBuilder<
1871
- ITEMS extends JsonSchemaAnyBuilder<any, any, any>[],
1872
- > extends JsonSchemaAnyBuilder<TupleIn<ITEMS>, TupleOut<ITEMS>, false> {
1873
- private readonly _items: ITEMS
1874
-
1891
+ ITEMS extends JsonSchemaAnyBuilder<any, any>[],
1892
+ > extends JsonSchemaAnyBuilder<TupleOut<ITEMS>, false> {
1875
1893
  constructor(items: ITEMS) {
1876
1894
  super({
1877
1895
  type: 'array',
@@ -1879,22 +1897,13 @@ export class JsonSchemaTupleBuilder<
1879
1897
  minItems: items.length,
1880
1898
  maxItems: items.length,
1881
1899
  })
1882
-
1883
- this._items = items
1884
1900
  }
1885
1901
  }
1886
1902
 
1887
- export class JsonSchemaAnyOfByBuilder<
1888
- IN,
1889
- OUT,
1890
- // eslint-disable-next-line @typescript-eslint/naming-convention
1891
- _P extends string = string,
1892
- > extends JsonSchemaAnyBuilder<AnyOfByInput<IN, _P> | IN, OUT, false> {
1893
- declare in: IN
1894
-
1903
+ export class JsonSchemaAnyOfByBuilder<OUT> extends JsonSchemaAnyBuilder<OUT, false> {
1895
1904
  constructor(
1896
1905
  propertyName: string,
1897
- schemaDictionary: Record<PropertyKey, JsonSchemaTerminal<any, any, any>>,
1906
+ schemaDictionary: Record<PropertyKey, JsonSchemaTerminal<any, any>>,
1898
1907
  ) {
1899
1908
  const builtSchemaDictionary: Record<string, JsonSchema> = {}
1900
1909
  for (const [key, schema] of Object.entries(schemaDictionary)) {
@@ -1912,17 +1921,10 @@ export class JsonSchemaAnyOfByBuilder<
1912
1921
  }
1913
1922
  }
1914
1923
 
1915
- export class JsonSchemaAnyOfTheseBuilder<
1916
- IN,
1917
- OUT,
1918
- // eslint-disable-next-line @typescript-eslint/naming-convention
1919
- _P extends string = string,
1920
- > extends JsonSchemaAnyBuilder<AnyOfByInput<IN, _P> | IN, OUT, false> {
1921
- declare in: IN
1922
-
1924
+ export class JsonSchemaAnyOfTheseBuilder<OUT> extends JsonSchemaAnyBuilder<OUT, false> {
1923
1925
  constructor(
1924
1926
  propertyName: string,
1925
- schemaDictionary: Record<PropertyKey, JsonSchemaTerminal<any, any, any>>,
1927
+ schemaDictionary: Record<PropertyKey, JsonSchemaTerminal<any, any>>,
1926
1928
  ) {
1927
1929
  const builtSchemaDictionary: Record<string, JsonSchema> = {}
1928
1930
  for (const [key, schema] of Object.entries(schemaDictionary)) {
@@ -1942,8 +1944,7 @@ export class JsonSchemaAnyOfTheseBuilder<
1942
1944
 
1943
1945
  type EnumBaseType = 'string' | 'number' | 'other'
1944
1946
 
1945
- export interface JsonSchema<IN = unknown, OUT = IN> {
1946
- readonly in?: IN
1947
+ export interface JsonSchema<OUT = unknown> {
1947
1948
  readonly out?: OUT
1948
1949
 
1949
1950
  $schema?: string
@@ -1959,15 +1960,15 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
1959
1960
  items?: JsonSchema
1960
1961
  prefixItems?: JsonSchema[]
1961
1962
  properties?: {
1962
- [K in keyof IN & keyof OUT]: JsonSchema<IN[K], OUT[K]>
1963
+ [K in keyof OUT]: JsonSchema<OUT[K]>
1963
1964
  }
1964
- patternProperties?: StringMap<JsonSchema<any, any>>
1965
+ patternProperties?: StringMap<JsonSchema<any>>
1965
1966
  required?: string[]
1966
1967
  additionalProperties?: boolean
1967
1968
  minProperties?: number
1968
1969
  maxProperties?: number
1969
1970
 
1970
- default?: IN
1971
+ default?: OUT
1971
1972
 
1972
1973
  // https://json-schema.org/understanding-json-schema/reference/conditionals.html#id6
1973
1974
  if?: JsonSchema
@@ -2028,20 +2029,21 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
2028
2029
  precision?: number
2029
2030
  customValidations?: CustomValidatorFn[]
2030
2031
  customConversions?: CustomConverterFn<any>[]
2032
+ postValidation?: PostValidatonFn<any, OUT>
2031
2033
  }
2032
2034
 
2033
2035
  function object(props: AnyObject): never
2034
- function object<IN extends AnyObject>(props: {
2035
- [K in keyof Required<IN>]-?: JsonSchemaTerminal<any, IN[K], any>
2036
- }): JsonSchemaObjectBuilder<IN, IN, false>
2037
-
2038
- function object<IN extends AnyObject>(props: {
2039
- [key in keyof IN]: JsonSchemaTerminal<any, IN[key], any>
2040
- }): JsonSchemaObjectBuilder<IN, IN, false> {
2041
- return new JsonSchemaObjectBuilder<IN, IN, false>(props)
2036
+ function object<OUT extends AnyObject>(props: {
2037
+ [K in keyof Required<OUT>]-?: JsonSchemaTerminal<OUT[K], any>
2038
+ }): JsonSchemaObjectBuilder<OUT, false>
2039
+
2040
+ function object<OUT extends AnyObject>(props: {
2041
+ [key in keyof OUT]: JsonSchemaTerminal<OUT[key], any>
2042
+ }): JsonSchemaObjectBuilder<OUT, false> {
2043
+ return new JsonSchemaObjectBuilder<OUT, false>(props)
2042
2044
  }
2043
2045
 
2044
- function objectInfer<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(
2046
+ function objectInfer<P extends Record<string, JsonSchemaAnyBuilder<any, any>>>(
2045
2047
  props: P,
2046
2048
  ): JsonSchemaObjectInferringBuilder<P, false> {
2047
2049
  return new JsonSchemaObjectInferringBuilder<P, false>(props)
@@ -2049,24 +2051,27 @@ function objectInfer<P extends Record<string, JsonSchemaAnyBuilder<any, any, any
2049
2051
 
2050
2052
  function objectDbEntity(props: AnyObject): never
2051
2053
  function objectDbEntity<
2052
- IN extends BaseDBEntity,
2053
- EXTRA_KEYS extends Exclude<keyof IN, keyof BaseDBEntity> = Exclude<keyof IN, keyof BaseDBEntity>,
2054
+ OUT extends BaseDBEntity,
2055
+ EXTRA_KEYS extends Exclude<keyof OUT, keyof BaseDBEntity> = Exclude<
2056
+ keyof OUT,
2057
+ keyof BaseDBEntity
2058
+ >,
2054
2059
  >(
2055
2060
  props: {
2056
2061
  // ✅ all non-system fields must be explicitly provided
2057
- [K in EXTRA_KEYS]-?: BuilderFor<IN[K]>
2062
+ [K in EXTRA_KEYS]-?: BuilderFor<OUT[K]>
2058
2063
  } &
2059
- // ✅ if `id` differs, its required
2060
- (ExactMatch<IN['id'], BaseDBEntity['id']> extends true
2064
+ // ✅ if `id` differs, it's required
2065
+ (ExactMatch<OUT['id'], BaseDBEntity['id']> extends true
2061
2066
  ? { id?: BuilderFor<BaseDBEntity['id']> }
2062
- : { id: BuilderFor<IN['id']> }) &
2063
- (ExactMatch<IN['created'], BaseDBEntity['created']> extends true
2067
+ : { id: BuilderFor<OUT['id']> }) &
2068
+ (ExactMatch<OUT['created'], BaseDBEntity['created']> extends true
2064
2069
  ? { created?: BuilderFor<BaseDBEntity['created']> }
2065
- : { created: BuilderFor<IN['created']> }) &
2066
- (ExactMatch<IN['updated'], BaseDBEntity['updated']> extends true
2070
+ : { created: BuilderFor<OUT['created']> }) &
2071
+ (ExactMatch<OUT['updated'], BaseDBEntity['updated']> extends true
2067
2072
  ? { updated?: BuilderFor<BaseDBEntity['updated']> }
2068
- : { updated: BuilderFor<IN['updated']> }),
2069
- ): JsonSchemaObjectBuilder<IN, IN, false>
2073
+ : { updated: BuilderFor<OUT['updated']> }),
2074
+ ): JsonSchemaObjectBuilder<OUT, false>
2070
2075
 
2071
2076
  function objectDbEntity(props: AnyObject): any {
2072
2077
  return j.object({
@@ -2078,16 +2083,13 @@ function objectDbEntity(props: AnyObject): any {
2078
2083
  }
2079
2084
 
2080
2085
  function record<
2081
- KS extends JsonSchemaAnyBuilder<any, any, any>,
2082
- VS extends JsonSchemaAnyBuilder<any, any, any>,
2086
+ KS extends JsonSchemaAnyBuilder<any, any>,
2087
+ VS extends JsonSchemaAnyBuilder<any, any>,
2083
2088
  Opt extends boolean = SchemaOpt<VS>,
2084
2089
  >(
2085
2090
  keySchema: KS,
2086
2091
  valueSchema: VS,
2087
2092
  ): JsonSchemaObjectBuilder<
2088
- Opt extends true
2089
- ? Partial<Record<SchemaIn<KS>, SchemaIn<VS>>>
2090
- : Record<SchemaIn<KS>, SchemaIn<VS>>,
2091
2093
  Opt extends true
2092
2094
  ? Partial<Record<SchemaOut<KS>, SchemaOut<VS>>>
2093
2095
  : Record<SchemaOut<KS>, SchemaOut<VS>>,
@@ -2095,8 +2097,7 @@ function record<
2095
2097
  > {
2096
2098
  const keyJsonSchema = keySchema.build()
2097
2099
  // Check if value schema is optional before build() strips the optionalField flag
2098
- const isValueOptional = (valueSchema as JsonSchemaTerminal<any, any, any>).getSchema()
2099
- .optionalField
2100
+ const isValueOptional = (valueSchema as JsonSchemaTerminal<any, any>).getSchema().optionalField
2100
2101
  const valueJsonSchema = valueSchema.build()
2101
2102
 
2102
2103
  // When value schema is optional, wrap in anyOf to allow undefined values
@@ -2105,9 +2106,6 @@ function record<
2105
2106
  : valueJsonSchema
2106
2107
 
2107
2108
  return new JsonSchemaObjectBuilder<
2108
- Opt extends true
2109
- ? Partial<Record<SchemaIn<KS>, SchemaIn<VS>>>
2110
- : Record<SchemaIn<KS>, SchemaIn<VS>>,
2111
2109
  Opt extends true
2112
2110
  ? Partial<Record<SchemaOut<KS>, SchemaOut<VS>>>
2113
2111
  : Record<SchemaOut<KS>, SchemaOut<VS>>,
@@ -2121,17 +2119,10 @@ function record<
2121
2119
  })
2122
2120
  }
2123
2121
 
2124
- function withRegexKeys<
2125
- S extends JsonSchemaAnyBuilder<any, any, any>,
2126
- Opt extends boolean = SchemaOpt<S>,
2127
- >(
2122
+ function withRegexKeys<S extends JsonSchemaAnyBuilder<any, any>>(
2128
2123
  keyRegex: RegExp | string,
2129
2124
  schema: S,
2130
- ): JsonSchemaObjectBuilder<
2131
- Opt extends true ? StringMap<SchemaIn<S>> : StringMap<SchemaIn<S>>,
2132
- Opt extends true ? StringMap<SchemaOut<S>> : StringMap<SchemaOut<S>>,
2133
- false
2134
- > {
2125
+ ): JsonSchemaObjectBuilder<StringMap<SchemaOut<S>>, false> {
2135
2126
  if (keyRegex instanceof RegExp) {
2136
2127
  _assert(
2137
2128
  !keyRegex.flags,
@@ -2141,11 +2132,7 @@ function withRegexKeys<
2141
2132
  const pattern = keyRegex instanceof RegExp ? keyRegex.source : keyRegex
2142
2133
  const jsonSchema = schema.build()
2143
2134
 
2144
- return new JsonSchemaObjectBuilder<
2145
- Opt extends true ? StringMap<SchemaIn<S>> : StringMap<SchemaIn<S>>,
2146
- Opt extends true ? StringMap<SchemaOut<S>> : StringMap<SchemaOut<S>>,
2147
- false
2148
- >([], {
2135
+ return new JsonSchemaObjectBuilder<StringMap<SchemaOut<S>>, false>([], {
2149
2136
  hasIsOfTypeCheck: false,
2150
2137
  patternProperties: {
2151
2138
  [pattern]: jsonSchema,
@@ -2158,14 +2145,13 @@ function withRegexKeys<
2158
2145
  */
2159
2146
  function withEnumKeys<
2160
2147
  const T extends readonly (string | number)[] | StringEnum | NumberEnum,
2161
- S extends JsonSchemaAnyBuilder<any, any, any>,
2148
+ S extends JsonSchemaAnyBuilder<any, any>,
2162
2149
  K extends string | number = EnumKeyUnion<T>,
2163
2150
  Opt extends boolean = SchemaOpt<S>,
2164
2151
  >(
2165
2152
  keys: T,
2166
2153
  schema: S,
2167
2154
  ): JsonSchemaObjectBuilder<
2168
- Opt extends true ? { [P in K]?: SchemaIn<S> } : { [P in K]: SchemaIn<S> },
2169
2155
  Opt extends true ? { [P in K]?: SchemaOut<S> } : { [P in K]: SchemaOut<S> },
2170
2156
  false
2171
2157
  > {
@@ -2195,7 +2181,6 @@ function withEnumKeys<
2195
2181
  const props = Object.fromEntries(typedValues.map(key => [key, schema])) as any
2196
2182
 
2197
2183
  return new JsonSchemaObjectBuilder<
2198
- Opt extends true ? { [P in K]?: SchemaIn<S> } : { [P in K]: SchemaIn<S> },
2199
2184
  Opt extends true ? { [P in K]?: SchemaOut<S> } : { [P in K]: SchemaOut<S> },
2200
2185
  false
2201
2186
  >(props, { hasIsOfTypeCheck: false })
@@ -2256,10 +2241,6 @@ type IsAssignableRelaxed<A, B> =
2256
2241
  ? true
2257
2242
  : false
2258
2243
 
2259
- type OptionalDbEntityFields<T> = T extends BaseDBEntity
2260
- ? Omit<T, keyof BaseDBEntity> & Partial<Pick<T, keyof BaseDBEntity>>
2261
- : T
2262
-
2263
2244
  type ExactMatchBase<A, B> =
2264
2245
  (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
2265
2246
  ? (<T>() => T extends B ? 1 : 2) extends <T>() => T extends A ? 1 : 2
@@ -2274,29 +2255,15 @@ type ExactMatch<A, B> =
2274
2255
  ? true
2275
2256
  : ExactMatchBase<Expand<StripIndexSignatureDeep<A>>, Expand<StripIndexSignatureDeep<B>>>
2276
2257
 
2277
- type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
2278
- [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never
2279
- }[number]
2280
-
2281
- type BuilderInUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
2282
- [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<infer I, any, any> ? I : never
2258
+ type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any>[]> = {
2259
+ [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<infer O, any> ? O : never
2283
2260
  }[number]
2284
2261
 
2285
- type AnyOfByIn<D extends Record<PropertyKey, JsonSchemaTerminal<any, any, any>>> = {
2286
- [K in keyof D]: D[K] extends JsonSchemaTerminal<infer I, any, any> ? I : never
2287
- }[keyof D]
2288
-
2289
- type AnyOfByOut<D extends Record<PropertyKey, JsonSchemaTerminal<any, any, any>>> = {
2290
- [K in keyof D]: D[K] extends JsonSchemaTerminal<any, infer O, any> ? O : never
2262
+ type AnyOfByOut<D extends Record<PropertyKey, JsonSchemaTerminal<any, any>>> = {
2263
+ [K in keyof D]: D[K] extends JsonSchemaTerminal<infer O, any> ? O : never
2291
2264
  }[keyof D]
2292
2265
 
2293
- type AnyOfByDiscriminant<IN, P extends string> = IN extends { [K in P]: infer V } ? V : never
2294
-
2295
- type AnyOfByInput<IN, P extends string, D = AnyOfByDiscriminant<IN, P>> = IN extends unknown
2296
- ? Omit<Partial<IN>, P> & { [K in P]?: D }
2297
- : never
2298
-
2299
- type BuilderFor<T> = JsonSchemaAnyBuilder<any, T, any>
2266
+ type BuilderFor<T> = JsonSchemaAnyBuilder<T, any>
2300
2267
 
2301
2268
  interface JsonBuilderRuleOpt {
2302
2269
  /**
@@ -2322,19 +2289,15 @@ type EnumKeyUnion<T> =
2322
2289
  ? T[keyof T]
2323
2290
  : never
2324
2291
 
2325
- type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
2326
- type SchemaOut<S> = S extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
2292
+ type SchemaOut<S> = S extends JsonSchemaAnyBuilder<infer OUT, any> ? OUT : never
2327
2293
  type SchemaOpt<S> =
2328
- S extends JsonSchemaAnyBuilder<any, any, infer Opt> ? (Opt extends true ? true : false) : false
2329
-
2330
- type TupleIn<T extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
2331
- [K in keyof T]: T[K] extends JsonSchemaAnyBuilder<infer I, any, any> ? I : never
2332
- }
2294
+ S extends JsonSchemaAnyBuilder<any, infer Opt> ? (Opt extends true ? true : false) : false
2333
2295
 
2334
- type TupleOut<T extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
2335
- [K in keyof T]: T[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never
2296
+ type TupleOut<T extends readonly JsonSchemaAnyBuilder<any, any>[]> = {
2297
+ [K in keyof T]: T[K] extends JsonSchemaAnyBuilder<infer O, any> ? O : never
2336
2298
  }
2337
2299
 
2300
+ export type PostValidatonFn<OUT, OUT2> = (v: OUT) => OUT2
2338
2301
  export type CustomValidatorFn = (v: any) => string | undefined
2339
2302
  export type CustomConverterFn<OUT> = (v: any) => OUT
2340
2303