@naturalcycles/nodejs-lib 15.96.0 → 15.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -163,6 +163,8 @@ export declare class JSchema<OUT, Opt> implements StandardSchemaV1<unknown, OUT>
163
163
  */
164
164
  out: OUT;
165
165
  opt: Opt;
166
+ /** Forces OUT to be invariant (prevents covariant subtype matching in object property constraints). */
167
+ protected _invariantOut: (x: OUT) => void;
166
168
  }
167
169
  export declare class JBuilder<OUT, Opt> extends JSchema<OUT, Opt> {
168
170
  protected setErrorMessage(ruleName: string, errorMessage: string | undefined): void;
@@ -182,7 +184,18 @@ export declare class JBuilder<OUT, Opt> extends JSchema<OUT, Opt> {
182
184
  type(type: string): this;
183
185
  default(v: any): this;
184
186
  instanceof(of: string): this;
185
- optional(): JBuilder<OUT | undefined, true>;
187
+ /**
188
+ * @param optionalValues List of values that should be considered/converted as `undefined`.
189
+ *
190
+ * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
191
+ * due to how mutability works in Ajv.
192
+ *
193
+ * Make sure this `optional()` call is at the end of your call chain.
194
+ *
195
+ * When `null` is included in optionalValues, the return type becomes `JSchema`
196
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
197
+ */
198
+ optional<T extends readonly (string | number | boolean | null)[] | undefined = undefined>(optionalValues?: T): T extends readonly (string | number | boolean | null)[] ? JSchema<OUT | undefined, true> : JBuilder<OUT | undefined, true>;
186
199
  nullable(): JBuilder<OUT | null, Opt>;
187
200
  /**
188
201
  * @deprecated
@@ -214,18 +227,6 @@ export declare class JBuilder<OUT, Opt> extends JSchema<OUT, Opt> {
214
227
  }
215
228
  export declare class JString<OUT extends string | undefined = string, Opt extends boolean = false> extends JBuilder<OUT, Opt> {
216
229
  constructor();
217
- /**
218
- * @param optionalValues List of values that should be considered/converted as `undefined`.
219
- *
220
- * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
221
- * due to how mutability works in Ajv.
222
- *
223
- * Make sure this `optional()` call is at the end of your call chain.
224
- *
225
- * When `null` is included in optionalValues, the return type becomes `JSchema`
226
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
227
- */
228
- optional<T extends readonly (string | null)[] | undefined = undefined>(optionalValues?: T): T extends readonly (infer U)[] ? null extends U ? JSchema<OUT | undefined, true> : JString<OUT | undefined, true> : JString<OUT | undefined, true>;
229
230
  regex(pattern: RegExp, opt?: JsonBuilderRuleOpt): this;
230
231
  pattern(pattern: string, opt?: JsonBuilderRuleOpt): this;
231
232
  minLength(minLength: number): this;
@@ -275,16 +276,6 @@ export interface JsonSchemaStringEmailOptions {
275
276
  }
276
277
  export declare class JIsoDate<Opt extends boolean = false> extends JBuilder<IsoDate, Opt> {
277
278
  constructor();
278
- /**
279
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
280
- *
281
- * This `null` feature only works when the current schema is nested in an object or array schema,
282
- * due to how mutability works in Ajv.
283
- *
284
- * When `null` is passed, the return type becomes `JSchema`
285
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
286
- */
287
- optional<N extends null | undefined = undefined>(nullValue?: N): N extends null ? JSchema<IsoDate | undefined, true> : JBuilder<IsoDate | undefined, true>;
288
279
  before(date: string): this;
289
280
  sameOrBefore(date: string): this;
290
281
  after(date: string): this;
@@ -301,18 +292,6 @@ export interface JsonSchemaIsoMonthOptions {
301
292
  }
302
293
  export declare class JNumber<OUT extends number | undefined = number, Opt extends boolean = false> extends JBuilder<OUT, Opt> {
303
294
  constructor();
304
- /**
305
- * @param optionalValues List of values that should be considered/converted as `undefined`.
306
- *
307
- * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
308
- * due to how mutability works in Ajv.
309
- *
310
- * Make sure this `optional()` call is at the end of your call chain.
311
- *
312
- * When `null` is included in optionalValues, the return type becomes `JSchema`
313
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
314
- */
315
- optional<T extends readonly (number | null)[] | undefined = undefined>(optionalValues?: T): T extends readonly (infer U)[] ? null extends U ? JSchema<OUT | undefined, true> : JNumber<OUT | undefined, true> : JNumber<OUT | undefined, true>;
316
295
  integer(): this;
317
296
  branded<B extends number>(): JNumber<B, Opt>;
318
297
  multipleOf(multipleOf: number): this;
@@ -345,26 +324,9 @@ export declare class JNumber<OUT extends number | undefined = number, Opt extend
345
324
  }
346
325
  export declare class JBoolean<OUT extends boolean | undefined = boolean, Opt extends boolean = false> extends JBuilder<OUT, Opt> {
347
326
  constructor();
348
- /**
349
- * @param optionalValue One of the two possible boolean values that should be considered/converted as `undefined`.
350
- *
351
- * This `optionalValue` feature only works when the current schema is nested in an object or array schema,
352
- * due to how mutability works in Ajv.
353
- */
354
- optional(optionalValue?: boolean): JBoolean<OUT | undefined, true>;
355
327
  }
356
328
  export declare class JObject<OUT extends AnyObject, Opt extends boolean = false> extends JBuilder<OUT, Opt> {
357
329
  constructor(props?: AnyObject, opt?: JObjectOpts);
358
- /**
359
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
360
- *
361
- * This `null` feature only works when the current schema is nested in an object or array schema,
362
- * due to how mutability works in Ajv.
363
- *
364
- * When `null` is passed, the return type becomes `JSchema`
365
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
366
- */
367
- optional<N extends null | undefined = undefined>(nullValue?: N): N extends null ? JSchema<OUT | undefined, true> : JBuilder<OUT | undefined, true>;
368
330
  /**
369
331
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
370
332
  */
@@ -405,24 +367,6 @@ export declare class JObjectInfer<PROPS extends Record<string, JBuilder<any, any
405
367
  [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never;
406
368
  }>, Opt> {
407
369
  constructor(props?: PROPS);
408
- /**
409
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
410
- *
411
- * This `null` feature only works when the current schema is nested in an object or array schema,
412
- * due to how mutability works in Ajv.
413
- *
414
- * When `null` is passed, the return type becomes `JSchema`
415
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
416
- */
417
- optional<N extends null | undefined = undefined>(nullValue?: N): N extends null ? JSchema<Expand<{
418
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never;
419
- } & {
420
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never;
421
- }> | undefined, true> : JBuilder<Expand<{
422
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never;
423
- } & {
424
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never;
425
- }> | undefined, true>;
426
370
  /**
427
371
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
428
372
  */
@@ -405,9 +405,58 @@ export class JBuilder extends JSchema {
405
405
  instanceof(of) {
406
406
  return this.cloneAndUpdateSchema({ type: 'object', instanceof: of });
407
407
  }
408
- optional() {
409
- const clone = this.cloneAndUpdateSchema({ optionalField: true });
410
- return clone;
408
+ /**
409
+ * @param optionalValues List of values that should be considered/converted as `undefined`.
410
+ *
411
+ * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
412
+ * due to how mutability works in Ajv.
413
+ *
414
+ * Make sure this `optional()` call is at the end of your call chain.
415
+ *
416
+ * When `null` is included in optionalValues, the return type becomes `JSchema`
417
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
418
+ */
419
+ optional(optionalValues) {
420
+ if (!optionalValues?.length) {
421
+ const clone = this.cloneAndUpdateSchema({ optionalField: true });
422
+ return clone;
423
+ }
424
+ const builtSchema = this.build();
425
+ // When optionalValues is just [null], use a simple null-wrapping structure.
426
+ // If the schema already has anyOf with a null branch (from nullable()),
427
+ // inject optionalValues directly into it.
428
+ if (optionalValues.length === 1 && optionalValues[0] === null) {
429
+ if (builtSchema.anyOf) {
430
+ const nullBranch = builtSchema.anyOf.find(b => b.type === 'null');
431
+ if (nullBranch) {
432
+ nullBranch.optionalValues = [null];
433
+ return new JSchema({ ...builtSchema, optionalField: true });
434
+ }
435
+ }
436
+ // Wrap with null type branch
437
+ return new JSchema({
438
+ anyOf: [{ type: 'null', optionalValues: [null] }, builtSchema],
439
+ optionalField: true,
440
+ });
441
+ }
442
+ // General case: create anyOf with current schema + alternatives.
443
+ // Preserve the original type for Ajv strict mode (optionalValues keyword requires a type).
444
+ const alternativesSchema = j.enum(optionalValues).build();
445
+ const innerSchema = {
446
+ ...(builtSchema.type ? { type: builtSchema.type } : {}),
447
+ anyOf: [builtSchema, alternativesSchema],
448
+ optionalValues: [...optionalValues],
449
+ };
450
+ // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
451
+ // so we must allow `null` values to be parsed by Ajv,
452
+ // but the typing should not reflect that.
453
+ if (optionalValues.includes(null)) {
454
+ return new JSchema({
455
+ anyOf: [{ type: 'null', optionalValues: [...optionalValues] }, innerSchema],
456
+ optionalField: true,
457
+ });
458
+ }
459
+ return new JSchema({ ...innerSchema, optionalField: true });
411
460
  }
412
461
  nullable() {
413
462
  return new JBuilder({
@@ -468,40 +517,6 @@ export class JString extends JBuilder {
468
517
  type: 'string',
469
518
  });
470
519
  }
471
- /**
472
- * @param optionalValues List of values that should be considered/converted as `undefined`.
473
- *
474
- * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
475
- * due to how mutability works in Ajv.
476
- *
477
- * Make sure this `optional()` call is at the end of your call chain.
478
- *
479
- * When `null` is included in optionalValues, the return type becomes `JSchema`
480
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
481
- */
482
- optional(optionalValues) {
483
- if (!optionalValues) {
484
- return super.optional();
485
- }
486
- _typeCast(optionalValues);
487
- let newBuilder = new JString().optional();
488
- const alternativesSchema = j.enum(optionalValues);
489
- Object.assign(newBuilder.getSchema(), {
490
- anyOf: [this.build(), alternativesSchema.build()],
491
- optionalValues,
492
- });
493
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
494
- // so we must allow `null` values to be parsed by Ajv,
495
- // but the typing should not reflect that.
496
- // We also cannot accept more rules attached, since we're not building a StringSchema anymore.
497
- if (optionalValues.includes(null)) {
498
- newBuilder = new JSchema({
499
- anyOf: [{ type: 'null', optionalValues }, newBuilder.build()],
500
- optionalField: true,
501
- });
502
- }
503
- return newBuilder;
504
- }
505
520
  regex(pattern, opt) {
506
521
  _assert(!pattern.flags, `Regex flags are not supported by JSON Schema. Received: /${pattern.source}/${pattern.flags}`);
507
522
  return this.pattern(pattern.source, opt);
@@ -626,28 +641,6 @@ export class JIsoDate extends JBuilder {
626
641
  IsoDate: {},
627
642
  });
628
643
  }
629
- /**
630
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
631
- *
632
- * This `null` feature only works when the current schema is nested in an object or array schema,
633
- * due to how mutability works in Ajv.
634
- *
635
- * When `null` is passed, the return type becomes `JSchema`
636
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
637
- */
638
- optional(nullValue) {
639
- if (nullValue === undefined) {
640
- return super.optional();
641
- }
642
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
643
- // so we must allow `null` values to be parsed by Ajv,
644
- // but the typing should not reflect that.
645
- // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
646
- return new JSchema({
647
- anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
648
- optionalField: true,
649
- });
650
- }
651
644
  before(date) {
652
645
  return this.cloneAndUpdateSchema({ IsoDate: { before: date } });
653
646
  }
@@ -677,40 +670,6 @@ export class JNumber extends JBuilder {
677
670
  type: 'number',
678
671
  });
679
672
  }
680
- /**
681
- * @param optionalValues List of values that should be considered/converted as `undefined`.
682
- *
683
- * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
684
- * due to how mutability works in Ajv.
685
- *
686
- * Make sure this `optional()` call is at the end of your call chain.
687
- *
688
- * When `null` is included in optionalValues, the return type becomes `JSchema`
689
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
690
- */
691
- optional(optionalValues) {
692
- if (!optionalValues) {
693
- return super.optional();
694
- }
695
- _typeCast(optionalValues);
696
- let newBuilder = new JNumber().optional();
697
- const alternativesSchema = j.enum(optionalValues);
698
- Object.assign(newBuilder.getSchema(), {
699
- anyOf: [this.build(), alternativesSchema.build()],
700
- optionalValues,
701
- });
702
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
703
- // so we must allow `null` values to be parsed by Ajv,
704
- // but the typing should not reflect that.
705
- // We also cannot accept more rules attached, since we're not building a NumberSchema anymore.
706
- if (optionalValues.includes(null)) {
707
- newBuilder = new JSchema({
708
- anyOf: [{ type: 'null', optionalValues }, newBuilder.build()],
709
- optionalField: true,
710
- });
711
- }
712
- return newBuilder;
713
- }
714
673
  integer() {
715
674
  return this.cloneAndUpdateSchema({ type: 'integer' });
716
675
  }
@@ -811,24 +770,6 @@ export class JBoolean extends JBuilder {
811
770
  type: 'boolean',
812
771
  });
813
772
  }
814
- /**
815
- * @param optionalValue One of the two possible boolean values that should be considered/converted as `undefined`.
816
- *
817
- * This `optionalValue` feature only works when the current schema is nested in an object or array schema,
818
- * due to how mutability works in Ajv.
819
- */
820
- optional(optionalValue) {
821
- if (typeof optionalValue === 'undefined') {
822
- return super.optional();
823
- }
824
- const newBuilder = new JBoolean().optional();
825
- const alternativesSchema = j.enum([optionalValue]);
826
- Object.assign(newBuilder.getSchema(), {
827
- anyOf: [this.build(), alternativesSchema.build()],
828
- optionalValues: [optionalValue],
829
- });
830
- return newBuilder;
831
- }
832
773
  }
833
774
  export class JObject extends JBuilder {
834
775
  constructor(props, opt) {
@@ -844,28 +785,6 @@ export class JObject extends JBuilder {
844
785
  if (props)
845
786
  addPropertiesToSchema(this.schema, props);
846
787
  }
847
- /**
848
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
849
- *
850
- * This `null` feature only works when the current schema is nested in an object or array schema,
851
- * due to how mutability works in Ajv.
852
- *
853
- * When `null` is passed, the return type becomes `JSchema`
854
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
855
- */
856
- optional(nullValue) {
857
- if (nullValue === undefined) {
858
- return super.optional();
859
- }
860
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
861
- // so we must allow `null` values to be parsed by Ajv,
862
- // but the typing should not reflect that.
863
- // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
864
- return new JSchema({
865
- anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
866
- optionalField: true,
867
- });
868
- }
869
788
  /**
870
789
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
871
790
  */
@@ -926,29 +845,6 @@ export class JObjectInfer extends JBuilder {
926
845
  if (props)
927
846
  addPropertiesToSchema(this.schema, props);
928
847
  }
929
- /**
930
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
931
- *
932
- * This `null` feature only works when the current schema is nested in an object or array schema,
933
- * due to how mutability works in Ajv.
934
- *
935
- * When `null` is passed, the return type becomes `JSchema`
936
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
937
- */
938
- // @ts-expect-error override adds optional parameter which is compatible but TS can't verify complex mapped types
939
- optional(nullValue) {
940
- if (nullValue === undefined) {
941
- return super.optional();
942
- }
943
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
944
- // so we must allow `null` values to be parsed by Ajv,
945
- // but the typing should not reflect that.
946
- // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
947
- return new JSchema({
948
- anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
949
- optionalField: true,
950
- });
951
- }
952
848
  /**
953
849
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
954
850
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.96.0",
4
+ "version": "15.97.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@standard-schema/spec": "^1",
@@ -506,6 +506,9 @@ export class JSchema<OUT, Opt>
506
506
  */
507
507
  out!: OUT
508
508
  opt!: Opt
509
+
510
+ /** Forces OUT to be invariant (prevents covariant subtype matching in object property constraints). */
511
+ declare protected _invariantOut: (x: OUT) => void
509
512
  }
510
513
 
511
514
  // ==== JBuilder (chainable base) ====
@@ -564,9 +567,68 @@ export class JBuilder<OUT, Opt> extends JSchema<OUT, Opt> {
564
567
  return this.cloneAndUpdateSchema({ type: 'object', instanceof: of })
565
568
  }
566
569
 
567
- optional(): JBuilder<OUT | undefined, true> {
568
- const clone = this.cloneAndUpdateSchema({ optionalField: true })
569
- return clone as unknown as JBuilder<OUT | undefined, true>
570
+ /**
571
+ * @param optionalValues List of values that should be considered/converted as `undefined`.
572
+ *
573
+ * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
574
+ * due to how mutability works in Ajv.
575
+ *
576
+ * Make sure this `optional()` call is at the end of your call chain.
577
+ *
578
+ * When `null` is included in optionalValues, the return type becomes `JSchema`
579
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
580
+ */
581
+ optional<T extends readonly (string | number | boolean | null)[] | undefined = undefined>(
582
+ optionalValues?: T,
583
+ ): T extends readonly (string | number | boolean | null)[]
584
+ ? JSchema<OUT | undefined, true>
585
+ : JBuilder<OUT | undefined, true> {
586
+ if (!optionalValues?.length) {
587
+ const clone = this.cloneAndUpdateSchema({ optionalField: true })
588
+ return clone as any
589
+ }
590
+
591
+ const builtSchema = this.build()
592
+
593
+ // When optionalValues is just [null], use a simple null-wrapping structure.
594
+ // If the schema already has anyOf with a null branch (from nullable()),
595
+ // inject optionalValues directly into it.
596
+ if (optionalValues.length === 1 && optionalValues[0] === null) {
597
+ if (builtSchema.anyOf) {
598
+ const nullBranch = builtSchema.anyOf.find(b => b.type === 'null')
599
+ if (nullBranch) {
600
+ nullBranch.optionalValues = [null]
601
+ return new JSchema({ ...builtSchema, optionalField: true }) as any
602
+ }
603
+ }
604
+
605
+ // Wrap with null type branch
606
+ return new JSchema({
607
+ anyOf: [{ type: 'null', optionalValues: [null] }, builtSchema],
608
+ optionalField: true,
609
+ }) as any
610
+ }
611
+
612
+ // General case: create anyOf with current schema + alternatives.
613
+ // Preserve the original type for Ajv strict mode (optionalValues keyword requires a type).
614
+ const alternativesSchema = j.enum(optionalValues).build()
615
+ const innerSchema: JsonSchema = {
616
+ ...(builtSchema.type ? { type: builtSchema.type } : {}),
617
+ anyOf: [builtSchema, alternativesSchema],
618
+ optionalValues: [...optionalValues],
619
+ }
620
+
621
+ // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
622
+ // so we must allow `null` values to be parsed by Ajv,
623
+ // but the typing should not reflect that.
624
+ if (optionalValues.includes(null)) {
625
+ return new JSchema({
626
+ anyOf: [{ type: 'null', optionalValues: [...optionalValues] }, innerSchema],
627
+ optionalField: true,
628
+ }) as any
629
+ }
630
+
631
+ return new JSchema({ ...innerSchema, optionalField: true }) as any
570
632
  }
571
633
 
572
634
  nullable(): JBuilder<OUT | null, Opt> {
@@ -640,51 +702,6 @@ export class JString<
640
702
  })
641
703
  }
642
704
 
643
- /**
644
- * @param optionalValues List of values that should be considered/converted as `undefined`.
645
- *
646
- * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
647
- * due to how mutability works in Ajv.
648
- *
649
- * Make sure this `optional()` call is at the end of your call chain.
650
- *
651
- * When `null` is included in optionalValues, the return type becomes `JSchema`
652
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
653
- */
654
- override optional<T extends readonly (string | null)[] | undefined = undefined>(
655
- optionalValues?: T,
656
- ): T extends readonly (infer U)[]
657
- ? null extends U
658
- ? JSchema<OUT | undefined, true>
659
- : JString<OUT | undefined, true>
660
- : JString<OUT | undefined, true> {
661
- if (!optionalValues) {
662
- return super.optional() as any
663
- }
664
-
665
- _typeCast<(string | null)[]>(optionalValues)
666
-
667
- let newBuilder: JSchema<OUT | undefined, true> = new JString<OUT, Opt>().optional()
668
- const alternativesSchema = j.enum(optionalValues)
669
- Object.assign(newBuilder.getSchema(), {
670
- anyOf: [this.build(), alternativesSchema.build()],
671
- optionalValues,
672
- })
673
-
674
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
675
- // so we must allow `null` values to be parsed by Ajv,
676
- // but the typing should not reflect that.
677
- // We also cannot accept more rules attached, since we're not building a StringSchema anymore.
678
- if (optionalValues.includes(null)) {
679
- newBuilder = new JSchema({
680
- anyOf: [{ type: 'null', optionalValues }, newBuilder.build()],
681
- optionalField: true,
682
- })
683
- }
684
-
685
- return newBuilder as any
686
- }
687
-
688
705
  regex(pattern: RegExp, opt?: JsonBuilderRuleOpt): this {
689
706
  _assert(
690
707
  !pattern.flags,
@@ -843,32 +860,6 @@ export class JIsoDate<Opt extends boolean = false> extends JBuilder<IsoDate, Opt
843
860
  })
844
861
  }
845
862
 
846
- /**
847
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
848
- *
849
- * This `null` feature only works when the current schema is nested in an object or array schema,
850
- * due to how mutability works in Ajv.
851
- *
852
- * When `null` is passed, the return type becomes `JSchema`
853
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
854
- */
855
- override optional<N extends null | undefined = undefined>(
856
- nullValue?: N,
857
- ): N extends null ? JSchema<IsoDate | undefined, true> : JBuilder<IsoDate | undefined, true> {
858
- if (nullValue === undefined) {
859
- return super.optional() as any
860
- }
861
-
862
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
863
- // so we must allow `null` values to be parsed by Ajv,
864
- // but the typing should not reflect that.
865
- // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
866
- return new JSchema({
867
- anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
868
- optionalField: true,
869
- }) as any
870
- }
871
-
872
863
  before(date: string): this {
873
864
  return this.cloneAndUpdateSchema({ IsoDate: { before: date } })
874
865
  }
@@ -917,51 +908,6 @@ export class JNumber<
917
908
  })
918
909
  }
919
910
 
920
- /**
921
- * @param optionalValues List of values that should be considered/converted as `undefined`.
922
- *
923
- * This `optionalValues` feature only works when the current schema is nested in an object or array schema,
924
- * due to how mutability works in Ajv.
925
- *
926
- * Make sure this `optional()` call is at the end of your call chain.
927
- *
928
- * When `null` is included in optionalValues, the return type becomes `JSchema`
929
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
930
- */
931
- override optional<T extends readonly (number | null)[] | undefined = undefined>(
932
- optionalValues?: T,
933
- ): T extends readonly (infer U)[]
934
- ? null extends U
935
- ? JSchema<OUT | undefined, true>
936
- : JNumber<OUT | undefined, true>
937
- : JNumber<OUT | undefined, true> {
938
- if (!optionalValues) {
939
- return super.optional() as any
940
- }
941
-
942
- _typeCast<(number | null)[]>(optionalValues)
943
-
944
- let newBuilder: JSchema<OUT | undefined, true> = new JNumber<OUT, Opt>().optional()
945
- const alternativesSchema = j.enum(optionalValues)
946
- Object.assign(newBuilder.getSchema(), {
947
- anyOf: [this.build(), alternativesSchema.build()],
948
- optionalValues,
949
- })
950
-
951
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
952
- // so we must allow `null` values to be parsed by Ajv,
953
- // but the typing should not reflect that.
954
- // We also cannot accept more rules attached, since we're not building a NumberSchema anymore.
955
- if (optionalValues.includes(null)) {
956
- newBuilder = new JSchema({
957
- anyOf: [{ type: 'null', optionalValues }, newBuilder.build()],
958
- optionalField: true,
959
- })
960
- }
961
-
962
- return newBuilder as any
963
- }
964
-
965
911
  integer(): this {
966
912
  return this.cloneAndUpdateSchema({ type: 'integer' })
967
913
  }
@@ -1089,27 +1035,6 @@ export class JBoolean<
1089
1035
  type: 'boolean',
1090
1036
  })
1091
1037
  }
1092
-
1093
- /**
1094
- * @param optionalValue One of the two possible boolean values that should be considered/converted as `undefined`.
1095
- *
1096
- * This `optionalValue` feature only works when the current schema is nested in an object or array schema,
1097
- * due to how mutability works in Ajv.
1098
- */
1099
- override optional(optionalValue?: boolean): JBoolean<OUT | undefined, true> {
1100
- if (typeof optionalValue === 'undefined') {
1101
- return super.optional() as unknown as JBoolean<OUT | undefined, true>
1102
- }
1103
-
1104
- const newBuilder = new JBoolean<OUT, Opt>().optional()
1105
- const alternativesSchema = j.enum([optionalValue])
1106
- Object.assign(newBuilder.getSchema(), {
1107
- anyOf: [this.build(), alternativesSchema.build()],
1108
- optionalValues: [optionalValue],
1109
- })
1110
-
1111
- return newBuilder
1112
- }
1113
1038
  }
1114
1039
 
1115
1040
  export class JObject<OUT extends AnyObject, Opt extends boolean = false> extends JBuilder<
@@ -1130,32 +1055,6 @@ export class JObject<OUT extends AnyObject, Opt extends boolean = false> extends
1130
1055
  if (props) addPropertiesToSchema(this.schema, props)
1131
1056
  }
1132
1057
 
1133
- /**
1134
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
1135
- *
1136
- * This `null` feature only works when the current schema is nested in an object or array schema,
1137
- * due to how mutability works in Ajv.
1138
- *
1139
- * When `null` is passed, the return type becomes `JSchema`
1140
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
1141
- */
1142
- override optional<N extends null | undefined = undefined>(
1143
- nullValue?: N,
1144
- ): N extends null ? JSchema<OUT | undefined, true> : JBuilder<OUT | undefined, true> {
1145
- if (nullValue === undefined) {
1146
- return super.optional() as any
1147
- }
1148
-
1149
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
1150
- // so we must allow `null` values to be parsed by Ajv,
1151
- // but the typing should not reflect that.
1152
- // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
1153
- return new JSchema({
1154
- anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
1155
- optionalField: true,
1156
- }) as any
1157
- }
1158
-
1159
1058
  /**
1160
1059
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
1161
1060
  */
@@ -1276,71 +1175,6 @@ export class JObjectInfer<
1276
1175
  if (props) addPropertiesToSchema(this.schema, props)
1277
1176
  }
1278
1177
 
1279
- /**
1280
- * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
1281
- *
1282
- * This `null` feature only works when the current schema is nested in an object or array schema,
1283
- * due to how mutability works in Ajv.
1284
- *
1285
- * When `null` is passed, the return type becomes `JSchema`
1286
- * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
1287
- */
1288
- // @ts-expect-error override adds optional parameter which is compatible but TS can't verify complex mapped types
1289
- override optional<N extends null | undefined = undefined>(
1290
- nullValue?: N,
1291
- ): N extends null
1292
- ? JSchema<
1293
- | Expand<
1294
- {
1295
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt>
1296
- ? IsOpt extends true
1297
- ? never
1298
- : K
1299
- : never]: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never
1300
- } & {
1301
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt>
1302
- ? IsOpt extends true
1303
- ? K
1304
- : never
1305
- : never]?: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never
1306
- }
1307
- >
1308
- | undefined,
1309
- true
1310
- >
1311
- : JBuilder<
1312
- | Expand<
1313
- {
1314
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt>
1315
- ? IsOpt extends true
1316
- ? never
1317
- : K
1318
- : never]: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never
1319
- } & {
1320
- [K in keyof PROPS as PROPS[K] extends JBuilder<any, infer IsOpt>
1321
- ? IsOpt extends true
1322
- ? K
1323
- : never
1324
- : never]?: PROPS[K] extends JBuilder<infer OUT, any> ? OUT : never
1325
- }
1326
- >
1327
- | undefined,
1328
- true
1329
- > {
1330
- if (nullValue === undefined) {
1331
- return super.optional() as any
1332
- }
1333
-
1334
- // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
1335
- // so we must allow `null` values to be parsed by Ajv,
1336
- // but the typing should not reflect that.
1337
- // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
1338
- return new JSchema({
1339
- anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
1340
- optionalField: true,
1341
- }) as any
1342
- }
1343
-
1344
1178
  /**
1345
1179
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
1346
1180
  */