@naturalcycles/nodejs-lib 15.76.0 → 15.77.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.
@@ -448,6 +448,35 @@ export function createAjv(opt) {
448
448
  return isValid;
449
449
  },
450
450
  });
451
+ ajv.addKeyword({
452
+ keyword: 'exclusiveProperties',
453
+ type: 'object',
454
+ modifying: false,
455
+ errors: true,
456
+ schemaType: 'array',
457
+ validate: function validate(exclusiveProperties, data, _schema, ctx) {
458
+ if (typeof data !== 'object')
459
+ return true;
460
+ for (const props of exclusiveProperties) {
461
+ let numberOfDefinedProperties = 0;
462
+ for (const prop of props) {
463
+ if (data[prop] !== undefined)
464
+ numberOfDefinedProperties++;
465
+ if (numberOfDefinedProperties > 1) {
466
+ ;
467
+ validate.errors = [
468
+ {
469
+ instancePath: ctx?.instancePath ?? '',
470
+ message: `must have only one of the "${props.join(', ')}" properties`,
471
+ },
472
+ ];
473
+ return false;
474
+ }
475
+ }
476
+ }
477
+ return true;
478
+ },
479
+ });
451
480
  return ajv;
452
481
  }
453
482
  const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
@@ -248,15 +248,13 @@ export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends A
248
248
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
249
249
  */
250
250
  allowAdditionalProperties(): this;
251
- extend<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(props: P): JsonSchemaObjectBuilder<IN & {
252
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never;
253
- } & {
254
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never;
255
- }, OUT & {
251
+ extend<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(props: P): JsonSchemaObjectBuilder<RelaxIndexSignature<IN & {
252
+ [K in keyof P]?: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never;
253
+ }>, AllowExtraKeys<RelaxIndexSignature<OUT & {
256
254
  [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never;
257
255
  } & {
258
256
  [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never;
259
- }, false>;
257
+ }>>, false>;
260
258
  /**
261
259
  * Concatenates another schema to the current schema.
262
260
  *
@@ -279,17 +277,18 @@ export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends A
279
277
  /**
280
278
  * Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
281
279
  */
282
- dbEntity(): JsonSchemaObjectBuilder<IN & {
280
+ dbEntity(): JsonSchemaObjectBuilder<RelaxIndexSignature<IN & {
281
+ id?: string | undefined;
282
+ created?: any;
283
+ updated?: any;
284
+ }>, AllowExtraKeys<RelaxIndexSignature<OUT & {
283
285
  id: string;
284
286
  created: any;
285
287
  updated: any;
286
- } & {}, OUT & {
287
- id: string;
288
- created: any;
289
- updated: any;
290
- } & {}, false>;
288
+ } & {}>>, false>;
291
289
  minProperties(minProperties: number): this;
292
290
  maxProperties(maxProperties: number): this;
291
+ exclusiveProperties(propNames: readonly (keyof IN & string)[]): this;
293
292
  }
294
293
  interface JsonSchemaObjectBuilderOpts {
295
294
  hasIsOfTypeCheck?: false;
@@ -414,6 +413,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
414
413
  optionalValues?: (string | number | boolean)[];
415
414
  keySchema?: JsonSchema;
416
415
  minProperties2?: number;
416
+ exclusiveProperties?: (readonly string[])[];
417
417
  }
418
418
  declare function object(props: AnyObject): never;
419
419
  declare function object<IN extends AnyObject>(props: {
@@ -453,7 +453,22 @@ declare function withEnumKeys<const T extends readonly (string | number)[] | Str
453
453
  type Expand<T> = {
454
454
  [K in keyof T]: T[K];
455
455
  };
456
- type ExactMatch<A, B> = (<T>() => T extends Expand<A> ? 1 : 2) extends <T>() => T extends Expand<B> ? 1 : 2 ? (<T>() => T extends Expand<B> ? 1 : 2) extends <T>() => T extends Expand<A> ? 1 : 2 ? true : false : false;
456
+ type StripIndexSignatureDeep<T> = T extends readonly unknown[] ? T : T extends Record<string, any> ? {
457
+ [K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: StripIndexSignatureDeep<T[K]>;
458
+ } : T;
459
+ type RelaxIndexSignature<T> = T extends readonly unknown[] ? T : T extends AnyObject ? string extends keyof T ? any : {
460
+ [K in keyof T]: RelaxIndexSignature<T[K]>;
461
+ } : T;
462
+ declare const allowExtraKeysSymbol: unique symbol;
463
+ type AllowExtraKeys<T> = T & {
464
+ readonly [allowExtraKeysSymbol]?: true;
465
+ };
466
+ type HasAllowExtraKeys<T> = T extends {
467
+ readonly [allowExtraKeysSymbol]?: true;
468
+ } ? true : false;
469
+ type IsAssignableRelaxed<A, B> = [RelaxIndexSignature<A>] extends [B] ? true : false;
470
+ type ExactMatchBase<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? (<T>() => T extends B ? 1 : 2) extends <T>() => T extends A ? 1 : 2 ? true : false : false;
471
+ type ExactMatch<A, B> = HasAllowExtraKeys<B> extends true ? IsAssignableRelaxed<B, A> : ExactMatchBase<Expand<A>, Expand<B>> extends true ? true : ExactMatchBase<Expand<StripIndexSignatureDeep<A>>, Expand<StripIndexSignatureDeep<B>>>;
457
472
  type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
458
473
  [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never;
459
474
  }[number];
@@ -656,6 +656,10 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
656
656
  maxProperties(maxProperties) {
657
657
  return this.cloneAndUpdateSchema({ maxProperties });
658
658
  }
659
+ exclusiveProperties(propNames) {
660
+ const exclusiveProperties = this.schema.exclusiveProperties ?? [];
661
+ return this.cloneAndUpdateSchema({ exclusiveProperties: [...exclusiveProperties, propNames] });
662
+ }
659
663
  }
660
664
  export class JsonSchemaObjectInferringBuilder extends JsonSchemaAnyBuilder {
661
665
  constructor(props) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.76.0",
4
+ "version": "15.77.1",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -513,6 +513,35 @@ export function createAjv(opt?: Options): Ajv2020 {
513
513
  },
514
514
  })
515
515
 
516
+ ajv.addKeyword({
517
+ keyword: 'exclusiveProperties',
518
+ type: 'object',
519
+ modifying: false,
520
+ errors: true,
521
+ schemaType: 'array',
522
+ validate: function validate(exclusiveProperties: string[][], data: AnyObject, _schema, ctx) {
523
+ if (typeof data !== 'object') return true
524
+
525
+ for (const props of exclusiveProperties) {
526
+ let numberOfDefinedProperties = 0
527
+ for (const prop of props) {
528
+ if (data[prop] !== undefined) numberOfDefinedProperties++
529
+ if (numberOfDefinedProperties > 1) {
530
+ ;(validate as any).errors = [
531
+ {
532
+ instancePath: ctx?.instancePath ?? '',
533
+ message: `must have only one of the "${props.join(', ')}" properties`,
534
+ },
535
+ ]
536
+ return false
537
+ }
538
+ }
539
+ }
540
+
541
+ return true
542
+ },
543
+ })
544
+
516
545
  return ajv
517
546
  }
518
547
 
@@ -869,36 +869,30 @@ export class JsonSchemaObjectBuilder<
869
869
  extend<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(
870
870
  props: P,
871
871
  ): JsonSchemaObjectBuilder<
872
- IN & {
873
- // required keys
874
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
875
- ? IsOpt extends true
876
- ? never
877
- : K
878
- : never]: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never
879
- } & {
880
- // optional keys
881
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
882
- ? IsOpt extends true
883
- ? K
884
- : never
885
- : never]?: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never
886
- },
887
- OUT & {
888
- // required keys
889
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
890
- ? IsOpt extends true
891
- ? never
892
- : K
893
- : never]: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never
894
- } & {
895
- // optional keys
896
- [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
897
- ? IsOpt extends true
898
- ? K
899
- : never
900
- : never]?: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never
901
- },
872
+ RelaxIndexSignature<
873
+ IN & {
874
+ [K in keyof P]?: P[K] extends JsonSchemaAnyBuilder<infer IN2, any, any> ? IN2 : never
875
+ }
876
+ >,
877
+ AllowExtraKeys<
878
+ RelaxIndexSignature<
879
+ OUT & {
880
+ // required keys
881
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
882
+ ? IsOpt extends true
883
+ ? never
884
+ : K
885
+ : never]: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never
886
+ } & {
887
+ // optional keys
888
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
889
+ ? IsOpt extends true
890
+ ? K
891
+ : never
892
+ : never]?: P[K] extends JsonSchemaAnyBuilder<any, infer OUT2, any> ? OUT2 : never
893
+ }
894
+ >
895
+ >,
902
896
  false
903
897
  > {
904
898
  const newBuilder = new JsonSchemaObjectBuilder()
@@ -958,6 +952,11 @@ export class JsonSchemaObjectBuilder<
958
952
  maxProperties(maxProperties: number): this {
959
953
  return this.cloneAndUpdateSchema({ maxProperties })
960
954
  }
955
+
956
+ exclusiveProperties(propNames: readonly (keyof IN & string)[]): this {
957
+ const exclusiveProperties = this.schema.exclusiveProperties ?? []
958
+ return this.cloneAndUpdateSchema({ exclusiveProperties: [...exclusiveProperties, propNames] })
959
+ }
961
960
  }
962
961
 
963
962
  interface JsonSchemaObjectBuilderOpts {
@@ -1272,6 +1271,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
1272
1271
  optionalValues?: (string | number | boolean)[]
1273
1272
  keySchema?: JsonSchema
1274
1273
  minProperties2?: number
1274
+ exclusiveProperties?: (readonly string[])[]
1275
1275
  }
1276
1276
 
1277
1277
  function object(props: AnyObject): never
@@ -1439,13 +1439,50 @@ function withEnumKeys<
1439
1439
 
1440
1440
  type Expand<T> = { [K in keyof T]: T[K] }
1441
1441
 
1442
- type ExactMatch<A, B> =
1443
- (<T>() => T extends Expand<A> ? 1 : 2) extends <T>() => T extends Expand<B> ? 1 : 2
1444
- ? (<T>() => T extends Expand<B> ? 1 : 2) extends <T>() => T extends Expand<A> ? 1 : 2
1442
+ type StripIndexSignatureDeep<T> = T extends readonly unknown[]
1443
+ ? T
1444
+ : T extends Record<string, any>
1445
+ ? {
1446
+ [K in keyof T as string extends K
1447
+ ? never
1448
+ : number extends K
1449
+ ? never
1450
+ : symbol extends K
1451
+ ? never
1452
+ : K]: StripIndexSignatureDeep<T[K]>
1453
+ }
1454
+ : T
1455
+
1456
+ type RelaxIndexSignature<T> = T extends readonly unknown[]
1457
+ ? T
1458
+ : T extends AnyObject
1459
+ ? string extends keyof T
1460
+ ? any
1461
+ : { [K in keyof T]: RelaxIndexSignature<T[K]> }
1462
+ : T
1463
+
1464
+ declare const allowExtraKeysSymbol: unique symbol
1465
+
1466
+ type AllowExtraKeys<T> = T & { readonly [allowExtraKeysSymbol]?: true }
1467
+
1468
+ type HasAllowExtraKeys<T> = T extends { readonly [allowExtraKeysSymbol]?: true } ? true : false
1469
+
1470
+ type IsAssignableRelaxed<A, B> = [RelaxIndexSignature<A>] extends [B] ? true : false
1471
+
1472
+ type ExactMatchBase<A, B> =
1473
+ (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
1474
+ ? (<T>() => T extends B ? 1 : 2) extends <T>() => T extends A ? 1 : 2
1445
1475
  ? true
1446
1476
  : false
1447
1477
  : false
1448
1478
 
1479
+ type ExactMatch<A, B> =
1480
+ HasAllowExtraKeys<B> extends true
1481
+ ? IsAssignableRelaxed<B, A>
1482
+ : ExactMatchBase<Expand<A>, Expand<B>> extends true
1483
+ ? true
1484
+ : ExactMatchBase<Expand<StripIndexSignatureDeep<A>>, Expand<StripIndexSignatureDeep<B>>>
1485
+
1449
1486
  type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
1450
1487
  [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never
1451
1488
  }[number]