@naturalcycles/nodejs-lib 15.65.1 → 15.65.3

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.
@@ -414,6 +414,37 @@ export function createAjv(opt) {
414
414
  return validate;
415
415
  },
416
416
  });
417
+ // This and `maxProperties2` are added because Ajv validates the `min/maxProperties` before validating the properties.
418
+ // So, in case of `minProperties(1)` and `{ foo: 'bar' }` Ajv will let it pass, even
419
+ // if the property validation would strip `foo` from the data.
420
+ // And Ajv would return `{}` as a successful validation.
421
+ // Since the keyword validation runs after JSON Schema validation,
422
+ // here we can make sure the number of properties are right ex-post property validation.
423
+ // It's named with the `2` suffix, because `minProperties` is reserved.
424
+ // And `maxProperties` does not suffer from this error due to the nature of how maximum works.
425
+ ajv.addKeyword({
426
+ keyword: 'minProperties2',
427
+ type: 'object',
428
+ modifying: false,
429
+ errors: true,
430
+ schemaType: 'number',
431
+ validate: function validate(minProperties, data, _schema, ctx) {
432
+ if (typeof data !== 'object')
433
+ return true;
434
+ const numberOfProperties = Object.getOwnPropertyNames(data).length;
435
+ const isValid = numberOfProperties >= minProperties;
436
+ if (!isValid) {
437
+ ;
438
+ validate.errors = [
439
+ {
440
+ instancePath: ctx?.instancePath ?? '',
441
+ message: `must NOT have fewer than ${minProperties} properties`,
442
+ },
443
+ ];
444
+ }
445
+ return isValid;
446
+ },
447
+ });
417
448
  return ajv;
418
449
  }
419
450
  const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
@@ -401,6 +401,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
401
401
  errorMessages?: StringMap<string>;
402
402
  optionalValues?: (string | number | boolean)[];
403
403
  keySchema?: JsonSchema;
404
+ minProperties2?: number;
404
405
  }
405
406
  declare function object(props: AnyObject): never;
406
407
  declare function object<IN extends AnyObject>(props: {
@@ -632,7 +632,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
632
632
  });
633
633
  }
634
634
  minProperties(minProperties) {
635
- return this.cloneAndUpdateSchema({ minProperties });
635
+ return this.cloneAndUpdateSchema({ minProperties, minProperties2: minProperties });
636
636
  }
637
637
  maxProperties(maxProperties) {
638
638
  return this.cloneAndUpdateSchema({ maxProperties });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.65.1",
4
+ "version": "15.65.3",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/through2-concurrent": "^2",
24
- "@naturalcycles/dev-lib": "20.12.10"
24
+ "@naturalcycles/dev-lib": "18.4.2"
25
25
  },
26
26
  "exports": {
27
27
  ".": "./dist/index.js",
@@ -478,6 +478,38 @@ export function createAjv(opt?: Options): Ajv2020 {
478
478
  },
479
479
  })
480
480
 
481
+ // This and `maxProperties2` are added because Ajv validates the `min/maxProperties` before validating the properties.
482
+ // So, in case of `minProperties(1)` and `{ foo: 'bar' }` Ajv will let it pass, even
483
+ // if the property validation would strip `foo` from the data.
484
+ // And Ajv would return `{}` as a successful validation.
485
+ // Since the keyword validation runs after JSON Schema validation,
486
+ // here we can make sure the number of properties are right ex-post property validation.
487
+ // It's named with the `2` suffix, because `minProperties` is reserved.
488
+ // And `maxProperties` does not suffer from this error due to the nature of how maximum works.
489
+ ajv.addKeyword({
490
+ keyword: 'minProperties2',
491
+ type: 'object',
492
+ modifying: false,
493
+ errors: true,
494
+ schemaType: 'number',
495
+ validate: function validate(minProperties: number, data: AnyObject, _schema, ctx) {
496
+ if (typeof data !== 'object') return true
497
+
498
+ const numberOfProperties = Object.getOwnPropertyNames(data).length
499
+ const isValid = numberOfProperties >= minProperties
500
+ if (!isValid) {
501
+ ;(validate as any).errors = [
502
+ {
503
+ instancePath: ctx?.instancePath ?? '',
504
+ message: `must NOT have fewer than ${minProperties} properties`,
505
+ },
506
+ ]
507
+ }
508
+
509
+ return isValid
510
+ },
511
+ })
512
+
481
513
  return ajv
482
514
  }
483
515
 
@@ -920,7 +920,7 @@ export class JsonSchemaObjectBuilder<
920
920
  }
921
921
 
922
922
  minProperties(minProperties: number): this {
923
- return this.cloneAndUpdateSchema({ minProperties })
923
+ return this.cloneAndUpdateSchema({ minProperties, minProperties2: minProperties })
924
924
  }
925
925
 
926
926
  maxProperties(maxProperties: number): this {
@@ -1239,6 +1239,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
1239
1239
  errorMessages?: StringMap<string>
1240
1240
  optionalValues?: (string | number | boolean)[]
1241
1241
  keySchema?: JsonSchema
1242
+ minProperties2?: number
1242
1243
  }
1243
1244
 
1244
1245
  function object(props: AnyObject): never