@naturalcycles/nodejs-lib 15.51.2 → 15.53.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>;
@@ -110,6 +114,7 @@ export declare class JsonSchemaStringBuilder<IN extends string = string, OUT = I
110
114
  */
111
115
  ianaTimezone(): JsonSchemaEnumBuilder<string | IANATimezone, IANATimezone, false>;
112
116
  base64Url(): this;
117
+ uuid(): this;
113
118
  }
114
119
  export interface JsonSchemaStringEmailOptions {
115
120
  checkTLD: boolean;
@@ -158,7 +163,7 @@ export declare class JsonSchemaBooleanBuilder<IN extends boolean = boolean, OUT
158
163
  constructor();
159
164
  }
160
165
  export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends AnyObject, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
161
- constructor(props?: AnyObject);
166
+ constructor(props?: AnyObject, opt?: JsonSchemaObjectBuilderOpts);
162
167
  addProperties(props: AnyObject): this;
163
168
  /**
164
169
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
@@ -172,6 +177,9 @@ export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends A
172
177
  minProperties(minProperties: number): this;
173
178
  maxProperties(maxProperties: number): this;
174
179
  }
180
+ interface JsonSchemaObjectBuilderOpts {
181
+ hasIsOfTypeCheck?: false;
182
+ }
175
183
  export declare class JsonSchemaObjectInferringBuilder<PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>, Opt extends boolean = false> extends JsonSchemaAnyBuilder<Expand<{
176
184
  [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
185
  } & {
@@ -326,4 +334,7 @@ interface JsonBuilderRuleOpt {
326
334
  */
327
335
  name?: string;
328
336
  }
337
+ type EnumKeyUnion<T> = T extends readonly (infer U)[] ? U : T extends StringEnum | NumberEnum ? T[keyof T] : never;
338
+ type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
339
+ type SchemaOut<S> = S extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
329
340
  export {};
@@ -5,9 +5,9 @@ import { _uniq } from '@naturalcycles/js-lib/array';
5
5
  import { _assert } from '@naturalcycles/js-lib/error';
6
6
  import { _deepCopy, _sortObject } from '@naturalcycles/js-lib/object';
7
7
  import { _objectAssign, JWT_REGEX, } from '@naturalcycles/js-lib/types';
8
- import { BASE64URL_REGEX, COUNTRY_CODE_REGEX, CURRENCY_REGEX, IPV4_REGEX, IPV6_REGEX, LANGUAGE_TAG_REGEX, SEMVER_REGEX, SLUG_REGEX, } from '../regexes.js';
8
+ import { BASE64URL_REGEX, COUNTRY_CODE_REGEX, CURRENCY_REGEX, IPV4_REGEX, IPV6_REGEX, LANGUAGE_TAG_REGEX, SEMVER_REGEX, SLUG_REGEX, UUID_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);
@@ -301,6 +325,9 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
301
325
  msg: 'contains characters not allowed in Base64 URL characterset',
302
326
  });
303
327
  }
328
+ uuid() {
329
+ return this.regex(UUID_REGEX, { msg: 'is an invalid UUID' });
330
+ }
304
331
  }
305
332
  export class JsonSchemaIsoDateBuilder extends JsonSchemaAnyBuilder {
306
333
  constructor() {
@@ -441,13 +468,13 @@ export class JsonSchemaBooleanBuilder extends JsonSchemaAnyBuilder {
441
468
  }
442
469
  }
443
470
  export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
444
- constructor(props) {
471
+ constructor(props, opt) {
445
472
  super({
446
473
  type: 'object',
447
474
  properties: {},
448
475
  required: [],
449
476
  additionalProperties: false,
450
- hasIsOfTypeCheck: true,
477
+ hasIsOfTypeCheck: opt?.hasIsOfTypeCheck ?? true,
451
478
  });
452
479
  if (props)
453
480
  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
+ }
@@ -1,6 +1,7 @@
1
1
  export declare const BASE62_REGEX: RegExp;
2
2
  export declare const BASE64_REGEX: RegExp;
3
3
  export declare const BASE64URL_REGEX: RegExp;
4
+ export declare const UUID_REGEX: RegExp;
4
5
  export declare const COUNTRY_CODE_REGEX: RegExp;
5
6
  export declare const CURRENCY_REGEX: RegExp;
6
7
  /**
@@ -1,6 +1,8 @@
1
1
  export const BASE62_REGEX = /^[a-zA-Z0-9]+$/;
2
2
  export const BASE64_REGEX = /^[a-zA-Z0-9+/]+={0,2}$/;
3
3
  export const BASE64URL_REGEX = /^[a-zA-Z0-9_-]+$/;
4
+ // from `ajv-formats`
5
+ export const UUID_REGEX = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
4
6
  export const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/;
5
7
  export const CURRENCY_REGEX = /^[A-Z]{3}$/;
6
8
  /**
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.53.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -35,10 +35,12 @@ import {
35
35
  LANGUAGE_TAG_REGEX,
36
36
  SEMVER_REGEX,
37
37
  SLUG_REGEX,
38
+ UUID_REGEX,
38
39
  } from '../regexes.js'
39
40
  import { TIMEZONES } from '../timezones.js'
40
41
  import {
41
42
  isEveryItemNumber,
43
+ isEveryItemPrimitive,
42
44
  isEveryItemString,
43
45
  JSON_SCHEMA_ORDER,
44
46
  mergeJsonSchemaObjects,
@@ -63,6 +65,48 @@ export const j = {
63
65
  any() {
64
66
  return j.object<AnyObject>({}).allowAdditionalProperties()
65
67
  },
68
+
69
+ /**
70
+ * Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
71
+ */
72
+ withEnumKeys<
73
+ const T extends readonly (string | number)[] | StringEnum | NumberEnum,
74
+ S extends JsonSchemaTerminal<any, any, any>,
75
+ K extends string | number = EnumKeyUnion<T>,
76
+ >(
77
+ keys: T,
78
+ schema: S,
79
+ ): JsonSchemaObjectBuilder<Record<K, SchemaIn<S>>, Record<K, SchemaOut<S>>> {
80
+ let enumValues: readonly (string | number)[] | undefined
81
+ if (Array.isArray(keys)) {
82
+ _assert(
83
+ isEveryItemPrimitive(keys),
84
+ 'Every item in the key list should be string, number or symbol',
85
+ )
86
+ enumValues = keys
87
+ } else if (typeof keys === 'object') {
88
+ const enumType = getEnumType(keys)
89
+ _assert(
90
+ enumType === 'NumberEnum' || enumType === 'StringEnum',
91
+ 'The key list should be StringEnum or NumberEnum',
92
+ )
93
+ if (enumType === 'NumberEnum') {
94
+ enumValues = _numberEnumValues(keys as NumberEnum)
95
+ } else if (enumType === 'StringEnum') {
96
+ enumValues = _stringEnumValues(keys as StringEnum)
97
+ }
98
+ }
99
+
100
+ _assert(enumValues, 'The key list should be an array of values, NumberEnum or a StrinEnum')
101
+
102
+ const typedValues = enumValues as readonly K[]
103
+ const props = Object.fromEntries(typedValues.map(key => [key, schema])) as any
104
+
105
+ return new JsonSchemaObjectBuilder<Record<K, SchemaIn<S>>, Record<K, SchemaOut<S>>, false>(
106
+ props,
107
+ { hasIsOfTypeCheck: false },
108
+ )
109
+ },
66
110
  }),
67
111
 
68
112
  array<IN, OUT, Opt>(
@@ -414,6 +458,10 @@ export class JsonSchemaStringBuilder<
414
458
  msg: 'contains characters not allowed in Base64 URL characterset',
415
459
  })
416
460
  }
461
+
462
+ uuid(): this {
463
+ return this.regex(UUID_REGEX, { msg: 'is an invalid UUID' })
464
+ }
417
465
  }
418
466
 
419
467
  export interface JsonSchemaStringEmailOptions {
@@ -617,13 +665,13 @@ export class JsonSchemaObjectBuilder<
617
665
  OUT extends AnyObject,
618
666
  Opt extends boolean = false,
619
667
  > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
620
- constructor(props?: AnyObject) {
668
+ constructor(props?: AnyObject, opt?: JsonSchemaObjectBuilderOpts) {
621
669
  super({
622
670
  type: 'object',
623
671
  properties: {},
624
672
  required: [],
625
673
  additionalProperties: false,
626
- hasIsOfTypeCheck: true,
674
+ hasIsOfTypeCheck: opt?.hasIsOfTypeCheck ?? true,
627
675
  })
628
676
 
629
677
  if (props) this.addProperties(props)
@@ -692,6 +740,10 @@ export class JsonSchemaObjectBuilder<
692
740
  }
693
741
  }
694
742
 
743
+ interface JsonSchemaObjectBuilderOpts {
744
+ hasIsOfTypeCheck?: false
745
+ }
746
+
695
747
  export class JsonSchemaObjectInferringBuilder<
696
748
  PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>,
697
749
  Opt extends boolean = false,
@@ -1049,3 +1101,15 @@ interface JsonBuilderRuleOpt {
1049
1101
  */
1050
1102
  name?: string
1051
1103
  }
1104
+
1105
+ type EnumKeyUnion<T> =
1106
+ // array of literals -> union of its elements
1107
+ T extends readonly (infer U)[]
1108
+ ? U
1109
+ : // enum object -> union of its values
1110
+ T extends StringEnum | NumberEnum
1111
+ ? T[keyof T]
1112
+ : never
1113
+
1114
+ type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1115
+ 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
+ }
@@ -1,6 +1,8 @@
1
1
  export const BASE62_REGEX = /^[a-zA-Z0-9]+$/
2
2
  export const BASE64_REGEX = /^[a-zA-Z0-9+/]+={0,2}$/
3
3
  export const BASE64URL_REGEX = /^[a-zA-Z0-9_-]+$/
4
+ // from `ajv-formats`
5
+ export const UUID_REGEX = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i
4
6
  export const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/
5
7
  export const CURRENCY_REGEX = /^[A-Z]{3}$/
6
8
  /**