@naturalcycles/nodejs-lib 15.86.0 → 15.87.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.
@@ -417,6 +417,15 @@ export function createAjv(opt) {
417
417
  return validate;
418
418
  },
419
419
  });
420
+ // Validates that the value is undefined. Used in record/stringMap with optional value schemas
421
+ // to allow undefined values in patternProperties via anyOf.
422
+ ajv.addKeyword({
423
+ keyword: 'isUndefined',
424
+ modifying: false,
425
+ errors: false,
426
+ schemaType: 'boolean',
427
+ validate: (_schema, data) => data === undefined,
428
+ });
420
429
  // This is added because Ajv validates the `min/maxProperties` before validating the properties.
421
430
  // So, in case of `minProperties(1)` and `{ foo: 'bar' }` Ajv will let it pass, even
422
431
  // if the property validation would strip `foo` from the data.
@@ -511,6 +511,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
511
511
  errorMessages?: StringMap<string>;
512
512
  optionalValues?: (string | number | boolean | null)[];
513
513
  keySchema?: JsonSchema;
514
+ isUndefined?: true;
514
515
  minProperties2?: number;
515
516
  exclusiveProperties?: (readonly string[])[];
516
517
  anyOfBy?: {
@@ -32,11 +32,15 @@ export const j = {
32
32
  return j.object({}).allowAdditionalProperties();
33
33
  },
34
34
  stringMap(schema) {
35
+ const isValueOptional = schema.getSchema().optionalField;
35
36
  const builtSchema = schema.build();
37
+ const finalValueSchema = isValueOptional
38
+ ? { anyOf: [{ isUndefined: true }, builtSchema] }
39
+ : builtSchema;
36
40
  return new JsonSchemaObjectBuilder({}, {
37
41
  hasIsOfTypeCheck: false,
38
42
  patternProperties: {
39
- '^.+$': builtSchema,
43
+ '^.+$': finalValueSchema,
40
44
  },
41
45
  });
42
46
  },
@@ -979,12 +983,19 @@ function objectDbEntity(props) {
979
983
  }
980
984
  function record(keySchema, valueSchema) {
981
985
  const keyJsonSchema = keySchema.build();
986
+ // Check if value schema is optional before build() strips the optionalField flag
987
+ const isValueOptional = valueSchema.getSchema()
988
+ .optionalField;
982
989
  const valueJsonSchema = valueSchema.build();
990
+ // When value schema is optional, wrap in anyOf to allow undefined values
991
+ const finalValueSchema = isValueOptional
992
+ ? { anyOf: [{ isUndefined: true }, valueJsonSchema] }
993
+ : valueJsonSchema;
983
994
  return new JsonSchemaObjectBuilder([], {
984
995
  hasIsOfTypeCheck: false,
985
996
  keySchema: keyJsonSchema,
986
997
  patternProperties: {
987
- ['^.*$']: valueJsonSchema,
998
+ ['^.*$']: finalValueSchema,
988
999
  },
989
1000
  });
990
1001
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.86.0",
4
+ "version": "15.87.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -486,6 +486,16 @@ export function createAjv(opt?: Options): Ajv2020 {
486
486
  },
487
487
  })
488
488
 
489
+ // Validates that the value is undefined. Used in record/stringMap with optional value schemas
490
+ // to allow undefined values in patternProperties via anyOf.
491
+ ajv.addKeyword({
492
+ keyword: 'isUndefined',
493
+ modifying: false,
494
+ errors: false,
495
+ schemaType: 'boolean',
496
+ validate: (_schema: boolean, data: unknown) => data === undefined,
497
+ })
498
+
489
499
  // This is added because Ajv validates the `min/maxProperties` before validating the properties.
490
500
  // So, in case of `minProperties(1)` and `{ foo: 'bar' }` Ajv will let it pass, even
491
501
  // if the property validation would strip `foo` from the data.
@@ -80,14 +80,18 @@ export const j = {
80
80
  stringMap<S extends JsonSchemaTerminal<any, any, any>>(
81
81
  schema: S,
82
82
  ): JsonSchemaObjectBuilder<StringMap<SchemaIn<S>>, StringMap<SchemaOut<S>>> {
83
+ const isValueOptional = schema.getSchema().optionalField
83
84
  const builtSchema = schema.build()
85
+ const finalValueSchema: JsonSchema = isValueOptional
86
+ ? { anyOf: [{ isUndefined: true }, builtSchema] }
87
+ : builtSchema
84
88
 
85
89
  return new JsonSchemaObjectBuilder<StringMap<SchemaIn<S>>, StringMap<SchemaOut<S>>>(
86
90
  {},
87
91
  {
88
92
  hasIsOfTypeCheck: false,
89
93
  patternProperties: {
90
- '^.+$': builtSchema,
94
+ '^.+$': finalValueSchema,
91
95
  },
92
96
  },
93
97
  )
@@ -1574,6 +1578,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
1574
1578
  errorMessages?: StringMap<string>
1575
1579
  optionalValues?: (string | number | boolean | null)[]
1576
1580
  keySchema?: JsonSchema
1581
+ isUndefined?: true
1577
1582
  minProperties2?: number
1578
1583
  exclusiveProperties?: (readonly string[])[]
1579
1584
  anyOfBy?: {
@@ -1648,8 +1653,16 @@ function record<
1648
1653
  false
1649
1654
  > {
1650
1655
  const keyJsonSchema = keySchema.build()
1656
+ // Check if value schema is optional before build() strips the optionalField flag
1657
+ const isValueOptional = (valueSchema as JsonSchemaTerminal<any, any, any>).getSchema()
1658
+ .optionalField
1651
1659
  const valueJsonSchema = valueSchema.build()
1652
1660
 
1661
+ // When value schema is optional, wrap in anyOf to allow undefined values
1662
+ const finalValueSchema: JsonSchema = isValueOptional
1663
+ ? { anyOf: [{ isUndefined: true }, valueJsonSchema] }
1664
+ : valueJsonSchema
1665
+
1653
1666
  return new JsonSchemaObjectBuilder<
1654
1667
  Opt extends true
1655
1668
  ? Partial<Record<SchemaIn<KS>, SchemaIn<VS>>>
@@ -1662,7 +1675,7 @@ function record<
1662
1675
  hasIsOfTypeCheck: false,
1663
1676
  keySchema: keyJsonSchema,
1664
1677
  patternProperties: {
1665
- ['^.*$']: valueJsonSchema,
1678
+ ['^.*$']: finalValueSchema,
1666
1679
  },
1667
1680
  })
1668
1681
  }