@naturalcycles/nodejs-lib 15.51.2 → 15.52.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.
@@ -8,6 +8,10 @@ export declare const j: {
8
8
  dbEntity: typeof objectDbEntity;
9
9
  infer: typeof objectInfer;
10
10
  any(): JsonSchemaObjectBuilder<AnyObject, AnyObject, false>;
11
+ /**
12
+ * Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
13
+ */
14
+ withEnumKeys<const T extends readonly (string | number)[] | StringEnum | NumberEnum, S extends JsonSchemaTerminal<any, any, any>, K extends string | number = EnumKeyUnion<T>>(keys: T, schema: S): JsonSchemaObjectBuilder<Record<K, SchemaIn<S>>, Record<K, SchemaOut<S>>>;
11
15
  };
12
16
  array<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaArrayBuilder<IN, OUT, Opt>;
13
17
  set<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaSet2Builder<IN, OUT, Opt>;
@@ -158,7 +162,7 @@ export declare class JsonSchemaBooleanBuilder<IN extends boolean = boolean, OUT
158
162
  constructor();
159
163
  }
160
164
  export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends AnyObject, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
161
- constructor(props?: AnyObject);
165
+ constructor(props?: AnyObject, opt?: JsonSchemaObjectBuilderOpts);
162
166
  addProperties(props: AnyObject): this;
163
167
  /**
164
168
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
@@ -172,6 +176,9 @@ export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends A
172
176
  minProperties(minProperties: number): this;
173
177
  maxProperties(maxProperties: number): this;
174
178
  }
179
+ interface JsonSchemaObjectBuilderOpts {
180
+ hasIsOfTypeCheck?: false;
181
+ }
175
182
  export declare class JsonSchemaObjectInferringBuilder<PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>, Opt extends boolean = false> extends JsonSchemaAnyBuilder<Expand<{
176
183
  [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
177
184
  } & {
@@ -326,4 +333,7 @@ interface JsonBuilderRuleOpt {
326
333
  */
327
334
  name?: string;
328
335
  }
336
+ type EnumKeyUnion<T> = T extends readonly (infer U)[] ? U : T extends StringEnum | NumberEnum ? T[keyof T] : never;
337
+ type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
338
+ type SchemaOut<S> = S extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
329
339
  export {};
@@ -7,7 +7,7 @@ import { _deepCopy, _sortObject } from '@naturalcycles/js-lib/object';
7
7
  import { _objectAssign, JWT_REGEX, } from '@naturalcycles/js-lib/types';
8
8
  import { BASE64URL_REGEX, COUNTRY_CODE_REGEX, CURRENCY_REGEX, IPV4_REGEX, IPV6_REGEX, LANGUAGE_TAG_REGEX, SEMVER_REGEX, SLUG_REGEX, } from '../regexes.js';
9
9
  import { TIMEZONES } from '../timezones.js';
10
- import { isEveryItemNumber, isEveryItemString, JSON_SCHEMA_ORDER, mergeJsonSchemaObjects, } from './jsonSchemaBuilder.util.js';
10
+ import { isEveryItemNumber, isEveryItemPrimitive, isEveryItemString, JSON_SCHEMA_ORDER, mergeJsonSchemaObjects, } from './jsonSchemaBuilder.util.js';
11
11
  export const j = {
12
12
  string() {
13
13
  return new JsonSchemaStringBuilder();
@@ -24,6 +24,30 @@ export const j = {
24
24
  any() {
25
25
  return j.object({}).allowAdditionalProperties();
26
26
  },
27
+ /**
28
+ * Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
29
+ */
30
+ withEnumKeys(keys, schema) {
31
+ let enumValues;
32
+ if (Array.isArray(keys)) {
33
+ _assert(isEveryItemPrimitive(keys), 'Every item in the key list should be string, number or symbol');
34
+ enumValues = keys;
35
+ }
36
+ else if (typeof keys === 'object') {
37
+ const enumType = getEnumType(keys);
38
+ _assert(enumType === 'NumberEnum' || enumType === 'StringEnum', 'The key list should be StringEnum or NumberEnum');
39
+ if (enumType === 'NumberEnum') {
40
+ enumValues = _numberEnumValues(keys);
41
+ }
42
+ else if (enumType === 'StringEnum') {
43
+ enumValues = _stringEnumValues(keys);
44
+ }
45
+ }
46
+ _assert(enumValues, 'The key list should be an array of values, NumberEnum or a StrinEnum');
47
+ const typedValues = enumValues;
48
+ const props = Object.fromEntries(typedValues.map(key => [key, schema]));
49
+ return new JsonSchemaObjectBuilder(props, { hasIsOfTypeCheck: false });
50
+ },
27
51
  }),
28
52
  array(itemSchema) {
29
53
  return new JsonSchemaArrayBuilder(itemSchema);
@@ -441,13 +465,13 @@ export class JsonSchemaBooleanBuilder extends JsonSchemaAnyBuilder {
441
465
  }
442
466
  }
443
467
  export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
444
- constructor(props) {
468
+ constructor(props, opt) {
445
469
  super({
446
470
  type: 'object',
447
471
  properties: {},
448
472
  required: [],
449
473
  additionalProperties: false,
450
- hasIsOfTypeCheck: true,
474
+ hasIsOfTypeCheck: opt?.hasIsOfTypeCheck ?? true,
451
475
  });
452
476
  if (props)
453
477
  this.addProperties(props);
@@ -9,3 +9,4 @@ export declare const JSON_SCHEMA_ORDER: string[];
9
9
  export declare function mergeJsonSchemaObjects<T1 extends AnyObject, T2 extends AnyObject>(schema1: JsonSchema<T1>, schema2: JsonSchema<T2>): JsonSchema<T1 & T2>;
10
10
  export declare function isEveryItemString(arr: any[]): boolean;
11
11
  export declare function isEveryItemNumber(arr: any[]): boolean;
12
+ export declare function isEveryItemPrimitive(arr: any[]): boolean;
@@ -77,3 +77,11 @@ export function isEveryItemNumber(arr) {
77
77
  }
78
78
  return true;
79
79
  }
80
+ export function isEveryItemPrimitive(arr) {
81
+ for (const item of arr) {
82
+ if (typeof item !== 'number' && typeof item !== 'string' && typeof item !== 'symbol') {
83
+ return false;
84
+ }
85
+ }
86
+ return true;
87
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.51.2",
4
+ "version": "15.52.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -39,6 +39,7 @@ import {
39
39
  import { TIMEZONES } from '../timezones.js'
40
40
  import {
41
41
  isEveryItemNumber,
42
+ isEveryItemPrimitive,
42
43
  isEveryItemString,
43
44
  JSON_SCHEMA_ORDER,
44
45
  mergeJsonSchemaObjects,
@@ -63,6 +64,48 @@ export const j = {
63
64
  any() {
64
65
  return j.object<AnyObject>({}).allowAdditionalProperties()
65
66
  },
67
+
68
+ /**
69
+ * Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
70
+ */
71
+ withEnumKeys<
72
+ const T extends readonly (string | number)[] | StringEnum | NumberEnum,
73
+ S extends JsonSchemaTerminal<any, any, any>,
74
+ K extends string | number = EnumKeyUnion<T>,
75
+ >(
76
+ keys: T,
77
+ schema: S,
78
+ ): JsonSchemaObjectBuilder<Record<K, SchemaIn<S>>, Record<K, SchemaOut<S>>> {
79
+ let enumValues: readonly (string | number)[] | undefined
80
+ if (Array.isArray(keys)) {
81
+ _assert(
82
+ isEveryItemPrimitive(keys),
83
+ 'Every item in the key list should be string, number or symbol',
84
+ )
85
+ enumValues = keys
86
+ } else if (typeof keys === 'object') {
87
+ const enumType = getEnumType(keys)
88
+ _assert(
89
+ enumType === 'NumberEnum' || enumType === 'StringEnum',
90
+ 'The key list should be StringEnum or NumberEnum',
91
+ )
92
+ if (enumType === 'NumberEnum') {
93
+ enumValues = _numberEnumValues(keys as NumberEnum)
94
+ } else if (enumType === 'StringEnum') {
95
+ enumValues = _stringEnumValues(keys as StringEnum)
96
+ }
97
+ }
98
+
99
+ _assert(enumValues, 'The key list should be an array of values, NumberEnum or a StrinEnum')
100
+
101
+ const typedValues = enumValues as readonly K[]
102
+ const props = Object.fromEntries(typedValues.map(key => [key, schema])) as any
103
+
104
+ return new JsonSchemaObjectBuilder<Record<K, SchemaIn<S>>, Record<K, SchemaOut<S>>, false>(
105
+ props,
106
+ { hasIsOfTypeCheck: false },
107
+ )
108
+ },
66
109
  }),
67
110
 
68
111
  array<IN, OUT, Opt>(
@@ -617,13 +660,13 @@ export class JsonSchemaObjectBuilder<
617
660
  OUT extends AnyObject,
618
661
  Opt extends boolean = false,
619
662
  > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
620
- constructor(props?: AnyObject) {
663
+ constructor(props?: AnyObject, opt?: JsonSchemaObjectBuilderOpts) {
621
664
  super({
622
665
  type: 'object',
623
666
  properties: {},
624
667
  required: [],
625
668
  additionalProperties: false,
626
- hasIsOfTypeCheck: true,
669
+ hasIsOfTypeCheck: opt?.hasIsOfTypeCheck ?? true,
627
670
  })
628
671
 
629
672
  if (props) this.addProperties(props)
@@ -692,6 +735,10 @@ export class JsonSchemaObjectBuilder<
692
735
  }
693
736
  }
694
737
 
738
+ interface JsonSchemaObjectBuilderOpts {
739
+ hasIsOfTypeCheck?: false
740
+ }
741
+
695
742
  export class JsonSchemaObjectInferringBuilder<
696
743
  PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>,
697
744
  Opt extends boolean = false,
@@ -1049,3 +1096,15 @@ interface JsonBuilderRuleOpt {
1049
1096
  */
1050
1097
  name?: string
1051
1098
  }
1099
+
1100
+ type EnumKeyUnion<T> =
1101
+ // array of literals -> union of its elements
1102
+ T extends readonly (infer U)[]
1103
+ ? U
1104
+ : // enum object -> union of its values
1105
+ T extends StringEnum | NumberEnum
1106
+ ? T[keyof T]
1107
+ : never
1108
+
1109
+ type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1110
+ type SchemaOut<S> = S extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
@@ -90,3 +90,12 @@ export function isEveryItemNumber(arr: any[]): boolean {
90
90
  }
91
91
  return true
92
92
  }
93
+
94
+ export function isEveryItemPrimitive(arr: any[]): boolean {
95
+ for (const item of arr) {
96
+ if (typeof item !== 'number' && typeof item !== 'string' && typeof item !== 'symbol') {
97
+ return false
98
+ }
99
+ }
100
+ return true
101
+ }