@naturalcycles/nodejs-lib 15.57.1 → 15.58.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.
|
@@ -374,6 +374,27 @@ export function createAjv(opt) {
|
|
|
374
374
|
return true;
|
|
375
375
|
},
|
|
376
376
|
});
|
|
377
|
+
ajv.addKeyword({
|
|
378
|
+
keyword: 'keySchema',
|
|
379
|
+
type: 'object',
|
|
380
|
+
modifying: true,
|
|
381
|
+
errors: false,
|
|
382
|
+
schemaType: 'object',
|
|
383
|
+
compile(innerSchema, _parentSchema, _it) {
|
|
384
|
+
const isValidKeyFn = ajv.compile(innerSchema);
|
|
385
|
+
function validate(data, _ctx) {
|
|
386
|
+
if (typeof data !== 'object' || data === null)
|
|
387
|
+
return true;
|
|
388
|
+
for (const key of Object.keys(data)) {
|
|
389
|
+
if (!isValidKeyFn(key)) {
|
|
390
|
+
delete data[key];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
return validate;
|
|
396
|
+
},
|
|
397
|
+
});
|
|
377
398
|
return ajv;
|
|
378
399
|
}
|
|
379
400
|
const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
@@ -9,7 +9,40 @@ export declare const j: {
|
|
|
9
9
|
infer: typeof objectInfer;
|
|
10
10
|
any(): JsonSchemaObjectBuilder<AnyObject, AnyObject, false>;
|
|
11
11
|
stringMap<S extends JsonSchemaTerminal<any, any, any>>(schema: S): JsonSchemaObjectBuilder<StringMap<SchemaIn<S>>, StringMap<SchemaOut<S>>>;
|
|
12
|
+
/**
|
|
13
|
+
* @experimental Look around, maybe you find a rule that is better for your use-case.
|
|
14
|
+
*
|
|
15
|
+
* For Record<K, V> type of validations.
|
|
16
|
+
* ```ts
|
|
17
|
+
* const schema = j.object
|
|
18
|
+
* .record(
|
|
19
|
+
* j
|
|
20
|
+
* .string()
|
|
21
|
+
* .regex(/^\d{3,4}$/)
|
|
22
|
+
* .branded<B>(),
|
|
23
|
+
* j.number().nullable(),
|
|
24
|
+
* )
|
|
25
|
+
* .isOfType<Record<B, number | null>>()
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* When the keys of the Record are values from an Enum, prefer `j.object.withEnumKeys`!
|
|
29
|
+
*
|
|
30
|
+
* Non-matching keys will be stripped from the object, i.e. they will not cause an error.
|
|
31
|
+
*
|
|
32
|
+
* Caveat: This rule first validates values of every properties of the object, and only then validates the keys.
|
|
33
|
+
* A consequence of that is that the validation will throw when there is an unexpected property with a value not matching the value schema.
|
|
34
|
+
*/
|
|
35
|
+
record: typeof record;
|
|
36
|
+
/**
|
|
37
|
+
* For Record<ENUM, V> type of validations.
|
|
38
|
+
*
|
|
39
|
+
* When the keys of the Record are values from an Enum,
|
|
40
|
+
* this helper is more performant and behaves in a more conventional manner than `j.object.record` would.
|
|
41
|
+
*
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
12
44
|
withEnumKeys: typeof withEnumKeys;
|
|
45
|
+
withRegexKeys: typeof withRegexKeys;
|
|
13
46
|
};
|
|
14
47
|
array<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaArrayBuilder<IN, OUT, Opt>;
|
|
15
48
|
set<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaSet2Builder<IN, OUT, Opt>;
|
|
@@ -205,6 +238,7 @@ export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends A
|
|
|
205
238
|
interface JsonSchemaObjectBuilderOpts {
|
|
206
239
|
hasIsOfTypeCheck?: false;
|
|
207
240
|
patternProperties?: StringMap<JsonSchema<any, any>>;
|
|
241
|
+
keySchema?: JsonSchema;
|
|
208
242
|
}
|
|
209
243
|
export declare class JsonSchemaObjectInferringBuilder<PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>, Opt extends boolean = false> extends JsonSchemaAnyBuilder<Expand<{
|
|
210
244
|
[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;
|
|
@@ -316,6 +350,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
|
|
|
316
350
|
};
|
|
317
351
|
errorMessages?: StringMap<string>;
|
|
318
352
|
optionalValues?: (string | number | boolean)[];
|
|
353
|
+
keySchema?: JsonSchema;
|
|
319
354
|
}
|
|
320
355
|
declare function object(props: AnyObject): never;
|
|
321
356
|
declare function object<IN extends AnyObject>(props: {
|
|
@@ -338,6 +373,8 @@ declare function objectDbEntity<IN extends BaseDBEntity & AnyObject, EXTRA_KEYS
|
|
|
338
373
|
} : {
|
|
339
374
|
updated: BuilderFor<IN['updated']>;
|
|
340
375
|
})): JsonSchemaObjectBuilder<IN, IN, false>;
|
|
376
|
+
declare function record<KS extends JsonSchemaAnyBuilder<any, any, any>, VS extends JsonSchemaAnyBuilder<any, any, any>, Opt extends boolean = SchemaOpt<VS>>(keySchema: KS, valueSchema: VS): JsonSchemaObjectBuilder<Opt extends true ? Partial<Record<SchemaIn<KS>, SchemaIn<VS>>> : Record<SchemaIn<KS>, SchemaIn<VS>>, Opt extends true ? Partial<Record<SchemaOut<KS>, SchemaOut<VS>>> : Record<SchemaOut<KS>, SchemaOut<VS>>, false>;
|
|
377
|
+
declare function withRegexKeys<S extends JsonSchemaAnyBuilder<any, any, any>, Opt extends boolean = SchemaOpt<S>>(keyRegex: RegExp | string, schema: S): JsonSchemaObjectBuilder<Opt extends true ? StringMap<SchemaIn<S>> : StringMap<SchemaIn<S>>, Opt extends true ? StringMap<SchemaOut<S>> : StringMap<SchemaOut<S>>, false>;
|
|
341
378
|
/**
|
|
342
379
|
* Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
|
|
343
380
|
*/
|
|
@@ -378,5 +415,5 @@ interface JsonBuilderRuleOpt {
|
|
|
378
415
|
type EnumKeyUnion<T> = T extends readonly (infer U)[] ? U : T extends StringEnum | NumberEnum ? T[keyof T] : never;
|
|
379
416
|
type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
|
|
380
417
|
type SchemaOut<S> = S extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
|
|
381
|
-
type SchemaOpt<S> = S extends JsonSchemaAnyBuilder<any, any, infer Opt> ? Opt : false;
|
|
418
|
+
type SchemaOpt<S> = S extends JsonSchemaAnyBuilder<any, any, infer Opt> ? (Opt extends true ? true : false) : false;
|
|
382
419
|
export {};
|
|
@@ -33,7 +33,40 @@ export const j = {
|
|
|
33
33
|
},
|
|
34
34
|
});
|
|
35
35
|
},
|
|
36
|
+
/**
|
|
37
|
+
* @experimental Look around, maybe you find a rule that is better for your use-case.
|
|
38
|
+
*
|
|
39
|
+
* For Record<K, V> type of validations.
|
|
40
|
+
* ```ts
|
|
41
|
+
* const schema = j.object
|
|
42
|
+
* .record(
|
|
43
|
+
* j
|
|
44
|
+
* .string()
|
|
45
|
+
* .regex(/^\d{3,4}$/)
|
|
46
|
+
* .branded<B>(),
|
|
47
|
+
* j.number().nullable(),
|
|
48
|
+
* )
|
|
49
|
+
* .isOfType<Record<B, number | null>>()
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* When the keys of the Record are values from an Enum, prefer `j.object.withEnumKeys`!
|
|
53
|
+
*
|
|
54
|
+
* Non-matching keys will be stripped from the object, i.e. they will not cause an error.
|
|
55
|
+
*
|
|
56
|
+
* Caveat: This rule first validates values of every properties of the object, and only then validates the keys.
|
|
57
|
+
* A consequence of that is that the validation will throw when there is an unexpected property with a value not matching the value schema.
|
|
58
|
+
*/
|
|
59
|
+
record,
|
|
60
|
+
/**
|
|
61
|
+
* For Record<ENUM, V> type of validations.
|
|
62
|
+
*
|
|
63
|
+
* When the keys of the Record are values from an Enum,
|
|
64
|
+
* this helper is more performant and behaves in a more conventional manner than `j.object.record` would.
|
|
65
|
+
*
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
36
68
|
withEnumKeys,
|
|
69
|
+
withRegexKeys,
|
|
37
70
|
}),
|
|
38
71
|
array(itemSchema) {
|
|
39
72
|
return new JsonSchemaArrayBuilder(itemSchema);
|
|
@@ -527,6 +560,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
527
560
|
additionalProperties: false,
|
|
528
561
|
hasIsOfTypeCheck: opt?.hasIsOfTypeCheck ?? true,
|
|
529
562
|
patternProperties: opt?.patternProperties ?? undefined,
|
|
563
|
+
keySchema: opt?.keySchema ?? undefined,
|
|
530
564
|
});
|
|
531
565
|
if (props)
|
|
532
566
|
this.addProperties(props);
|
|
@@ -715,6 +749,27 @@ function objectDbEntity(props) {
|
|
|
715
749
|
...props,
|
|
716
750
|
});
|
|
717
751
|
}
|
|
752
|
+
function record(keySchema, valueSchema) {
|
|
753
|
+
const keyJsonSchema = keySchema.build();
|
|
754
|
+
const valueJsonSchema = valueSchema.build();
|
|
755
|
+
return new JsonSchemaObjectBuilder([], {
|
|
756
|
+
hasIsOfTypeCheck: false,
|
|
757
|
+
keySchema: keyJsonSchema,
|
|
758
|
+
patternProperties: {
|
|
759
|
+
['^.*$']: valueJsonSchema,
|
|
760
|
+
},
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
function withRegexKeys(keyRegex, schema) {
|
|
764
|
+
const pattern = keyRegex instanceof RegExp ? keyRegex.source : keyRegex;
|
|
765
|
+
const jsonSchema = schema.build();
|
|
766
|
+
return new JsonSchemaObjectBuilder([], {
|
|
767
|
+
hasIsOfTypeCheck: false,
|
|
768
|
+
patternProperties: {
|
|
769
|
+
[pattern]: jsonSchema,
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
}
|
|
718
773
|
/**
|
|
719
774
|
* Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
|
|
720
775
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { _lazyValue } from '@naturalcycles/js-lib'
|
|
2
2
|
import { Set2 } from '@naturalcycles/js-lib/object'
|
|
3
3
|
import { _substringAfterLast } from '@naturalcycles/js-lib/string'
|
|
4
|
+
import type { AnyObject } from '@naturalcycles/js-lib/types'
|
|
4
5
|
import { Ajv, type Options, type ValidateFunction } from 'ajv'
|
|
5
6
|
import { validTLDs } from '../tlds.js'
|
|
6
7
|
import type { JsonSchemaIsoDateOptions, JsonSchemaStringEmailOptions } from './jsonSchemaBuilder.js'
|
|
@@ -429,6 +430,31 @@ export function createAjv(opt?: Options): Ajv {
|
|
|
429
430
|
},
|
|
430
431
|
})
|
|
431
432
|
|
|
433
|
+
ajv.addKeyword({
|
|
434
|
+
keyword: 'keySchema',
|
|
435
|
+
type: 'object',
|
|
436
|
+
modifying: true,
|
|
437
|
+
errors: false,
|
|
438
|
+
schemaType: 'object',
|
|
439
|
+
compile(innerSchema, _parentSchema, _it) {
|
|
440
|
+
const isValidKeyFn: ValidateFunction = ajv.compile(innerSchema)
|
|
441
|
+
|
|
442
|
+
function validate(data: AnyObject, _ctx: any): boolean {
|
|
443
|
+
if (typeof data !== 'object' || data === null) return true
|
|
444
|
+
|
|
445
|
+
for (const key of Object.keys(data)) {
|
|
446
|
+
if (!isValidKeyFn(key)) {
|
|
447
|
+
delete data[key]
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return true
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return validate
|
|
455
|
+
},
|
|
456
|
+
})
|
|
457
|
+
|
|
432
458
|
return ajv
|
|
433
459
|
}
|
|
434
460
|
|
|
@@ -82,7 +82,41 @@ export const j = {
|
|
|
82
82
|
)
|
|
83
83
|
},
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* @experimental Look around, maybe you find a rule that is better for your use-case.
|
|
87
|
+
*
|
|
88
|
+
* For Record<K, V> type of validations.
|
|
89
|
+
* ```ts
|
|
90
|
+
* const schema = j.object
|
|
91
|
+
* .record(
|
|
92
|
+
* j
|
|
93
|
+
* .string()
|
|
94
|
+
* .regex(/^\d{3,4}$/)
|
|
95
|
+
* .branded<B>(),
|
|
96
|
+
* j.number().nullable(),
|
|
97
|
+
* )
|
|
98
|
+
* .isOfType<Record<B, number | null>>()
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* When the keys of the Record are values from an Enum, prefer `j.object.withEnumKeys`!
|
|
102
|
+
*
|
|
103
|
+
* Non-matching keys will be stripped from the object, i.e. they will not cause an error.
|
|
104
|
+
*
|
|
105
|
+
* Caveat: This rule first validates values of every properties of the object, and only then validates the keys.
|
|
106
|
+
* A consequence of that is that the validation will throw when there is an unexpected property with a value not matching the value schema.
|
|
107
|
+
*/
|
|
108
|
+
record,
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* For Record<ENUM, V> type of validations.
|
|
112
|
+
*
|
|
113
|
+
* When the keys of the Record are values from an Enum,
|
|
114
|
+
* this helper is more performant and behaves in a more conventional manner than `j.object.record` would.
|
|
115
|
+
*
|
|
116
|
+
*
|
|
117
|
+
*/
|
|
85
118
|
withEnumKeys,
|
|
119
|
+
withRegexKeys,
|
|
86
120
|
}),
|
|
87
121
|
|
|
88
122
|
array<IN, OUT, Opt>(
|
|
@@ -749,6 +783,7 @@ export class JsonSchemaObjectBuilder<
|
|
|
749
783
|
additionalProperties: false,
|
|
750
784
|
hasIsOfTypeCheck: opt?.hasIsOfTypeCheck ?? true,
|
|
751
785
|
patternProperties: opt?.patternProperties ?? undefined,
|
|
786
|
+
keySchema: opt?.keySchema ?? undefined,
|
|
752
787
|
})
|
|
753
788
|
|
|
754
789
|
if (props) this.addProperties(props)
|
|
@@ -820,6 +855,7 @@ export class JsonSchemaObjectBuilder<
|
|
|
820
855
|
interface JsonSchemaObjectBuilderOpts {
|
|
821
856
|
hasIsOfTypeCheck?: false
|
|
822
857
|
patternProperties?: StringMap<JsonSchema<any, any>>
|
|
858
|
+
keySchema?: JsonSchema
|
|
823
859
|
}
|
|
824
860
|
|
|
825
861
|
export class JsonSchemaObjectInferringBuilder<
|
|
@@ -1107,6 +1143,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
|
|
|
1107
1143
|
transform?: { trim?: true; toLowerCase?: true; toUpperCase?: true; truncate?: number }
|
|
1108
1144
|
errorMessages?: StringMap<string>
|
|
1109
1145
|
optionalValues?: (string | number | boolean)[]
|
|
1146
|
+
keySchema?: JsonSchema
|
|
1110
1147
|
}
|
|
1111
1148
|
|
|
1112
1149
|
function object(props: AnyObject): never
|
|
@@ -1156,6 +1193,68 @@ function objectDbEntity(props: AnyObject): any {
|
|
|
1156
1193
|
})
|
|
1157
1194
|
}
|
|
1158
1195
|
|
|
1196
|
+
function record<
|
|
1197
|
+
KS extends JsonSchemaAnyBuilder<any, any, any>,
|
|
1198
|
+
VS extends JsonSchemaAnyBuilder<any, any, any>,
|
|
1199
|
+
Opt extends boolean = SchemaOpt<VS>,
|
|
1200
|
+
>(
|
|
1201
|
+
keySchema: KS,
|
|
1202
|
+
valueSchema: VS,
|
|
1203
|
+
): JsonSchemaObjectBuilder<
|
|
1204
|
+
Opt extends true
|
|
1205
|
+
? Partial<Record<SchemaIn<KS>, SchemaIn<VS>>>
|
|
1206
|
+
: Record<SchemaIn<KS>, SchemaIn<VS>>,
|
|
1207
|
+
Opt extends true
|
|
1208
|
+
? Partial<Record<SchemaOut<KS>, SchemaOut<VS>>>
|
|
1209
|
+
: Record<SchemaOut<KS>, SchemaOut<VS>>,
|
|
1210
|
+
false
|
|
1211
|
+
> {
|
|
1212
|
+
const keyJsonSchema = keySchema.build()
|
|
1213
|
+
const valueJsonSchema = valueSchema.build()
|
|
1214
|
+
|
|
1215
|
+
return new JsonSchemaObjectBuilder<
|
|
1216
|
+
Opt extends true
|
|
1217
|
+
? Partial<Record<SchemaIn<KS>, SchemaIn<VS>>>
|
|
1218
|
+
: Record<SchemaIn<KS>, SchemaIn<VS>>,
|
|
1219
|
+
Opt extends true
|
|
1220
|
+
? Partial<Record<SchemaOut<KS>, SchemaOut<VS>>>
|
|
1221
|
+
: Record<SchemaOut<KS>, SchemaOut<VS>>,
|
|
1222
|
+
false
|
|
1223
|
+
>([], {
|
|
1224
|
+
hasIsOfTypeCheck: false,
|
|
1225
|
+
keySchema: keyJsonSchema,
|
|
1226
|
+
patternProperties: {
|
|
1227
|
+
['^.*$']: valueJsonSchema,
|
|
1228
|
+
},
|
|
1229
|
+
})
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function withRegexKeys<
|
|
1233
|
+
S extends JsonSchemaAnyBuilder<any, any, any>,
|
|
1234
|
+
Opt extends boolean = SchemaOpt<S>,
|
|
1235
|
+
>(
|
|
1236
|
+
keyRegex: RegExp | string,
|
|
1237
|
+
schema: S,
|
|
1238
|
+
): JsonSchemaObjectBuilder<
|
|
1239
|
+
Opt extends true ? StringMap<SchemaIn<S>> : StringMap<SchemaIn<S>>,
|
|
1240
|
+
Opt extends true ? StringMap<SchemaOut<S>> : StringMap<SchemaOut<S>>,
|
|
1241
|
+
false
|
|
1242
|
+
> {
|
|
1243
|
+
const pattern = keyRegex instanceof RegExp ? keyRegex.source : keyRegex
|
|
1244
|
+
const jsonSchema = schema.build()
|
|
1245
|
+
|
|
1246
|
+
return new JsonSchemaObjectBuilder<
|
|
1247
|
+
Opt extends true ? StringMap<SchemaIn<S>> : StringMap<SchemaIn<S>>,
|
|
1248
|
+
Opt extends true ? StringMap<SchemaOut<S>> : StringMap<SchemaOut<S>>,
|
|
1249
|
+
false
|
|
1250
|
+
>([], {
|
|
1251
|
+
hasIsOfTypeCheck: false,
|
|
1252
|
+
patternProperties: {
|
|
1253
|
+
[pattern]: jsonSchema,
|
|
1254
|
+
},
|
|
1255
|
+
})
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1159
1258
|
/**
|
|
1160
1259
|
* Builds the object schema with the indicated `keys` and uses the `schema` for their validation.
|
|
1161
1260
|
*/
|
|
@@ -1245,4 +1344,5 @@ type EnumKeyUnion<T> =
|
|
|
1245
1344
|
|
|
1246
1345
|
type SchemaIn<S> = S extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
|
|
1247
1346
|
type SchemaOut<S> = S extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
|
|
1248
|
-
type SchemaOpt<S> =
|
|
1347
|
+
type SchemaOpt<S> =
|
|
1348
|
+
S extends JsonSchemaAnyBuilder<any, any, infer Opt> ? (Opt extends true ? true : false) : false
|