@naturalcycles/nodejs-lib 15.56.0 → 15.57.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.
- package/dist/validation/ajv/ajvSchema.js +1 -0
- package/dist/validation/ajv/getAjv.js +21 -18
- package/dist/validation/ajv/jsonSchemaBuilder.d.ts +2 -0
- package/dist/validation/ajv/jsonSchemaBuilder.js +35 -12
- package/package.json +1 -1
- package/src/validation/ajv/ajvSchema.ts +1 -0
- package/src/validation/ajv/getAjv.ts +27 -18
- package/src/validation/ajv/jsonSchemaBuilder.ts +56 -24
|
@@ -1,7 +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 {
|
|
4
|
+
import { Ajv } from 'ajv';
|
|
5
5
|
import { validTLDs } from '../tlds.js';
|
|
6
6
|
/* eslint-disable @typescript-eslint/prefer-string-starts-ends-with */
|
|
7
7
|
// oxlint-disable unicorn/prefer-code-point
|
|
@@ -66,27 +66,30 @@ export function createAjv(opt) {
|
|
|
66
66
|
modifying: true,
|
|
67
67
|
schemaType: 'object',
|
|
68
68
|
errors: true,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
validate: function validate(transform, data, _schema, ctx) {
|
|
70
|
+
if (!data)
|
|
71
|
+
return true;
|
|
72
|
+
let transformedData = data;
|
|
73
|
+
if (transform.trim) {
|
|
74
|
+
transformedData = transformedData.trim();
|
|
74
75
|
}
|
|
75
|
-
if (
|
|
76
|
-
|
|
76
|
+
if (transform.toLowerCase) {
|
|
77
|
+
transformedData = transformedData.toLocaleLowerCase();
|
|
77
78
|
}
|
|
78
|
-
if (
|
|
79
|
-
|
|
79
|
+
if (transform.toUpperCase) {
|
|
80
|
+
transformedData = transformedData.toLocaleUpperCase();
|
|
80
81
|
}
|
|
81
|
-
if (typeof
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
82
|
+
if (typeof transform.truncate === 'number' && transform.truncate >= 0) {
|
|
83
|
+
transformedData = transformedData.slice(0, transform.truncate);
|
|
84
|
+
if (transform.trim) {
|
|
85
|
+
transformedData = transformedData.trim();
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// Explicit check for `undefined` because parentDataProperty can be `0` when it comes to arrays.
|
|
89
|
+
if (ctx?.parentData && typeof ctx.parentDataProperty !== 'undefined') {
|
|
90
|
+
ctx.parentData[ctx.parentDataProperty] = transformedData;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
90
93
|
},
|
|
91
94
|
});
|
|
92
95
|
ajv.addKeyword({
|
|
@@ -366,7 +369,7 @@ export function createAjv(opt) {
|
|
|
366
369
|
if (!optionalValues.includes(data))
|
|
367
370
|
return true;
|
|
368
371
|
if (ctx?.parentData && ctx.parentDataProperty) {
|
|
369
|
-
ctx.parentData[ctx.parentDataProperty]
|
|
372
|
+
delete ctx.parentData[ctx.parentDataProperty];
|
|
370
373
|
}
|
|
371
374
|
return true;
|
|
372
375
|
},
|
|
@@ -86,6 +86,7 @@ export declare class JsonSchemaStringBuilder<IN extends string | undefined = str
|
|
|
86
86
|
pattern(pattern: string, opt?: JsonBuilderRuleOpt): this;
|
|
87
87
|
minLength(minLength: number): this;
|
|
88
88
|
maxLength(maxLength: number): this;
|
|
89
|
+
length(exactLength: number): this;
|
|
89
90
|
length(minLength: number, maxLength: number): this;
|
|
90
91
|
email(opt?: Partial<JsonSchemaStringEmailOptions>): this;
|
|
91
92
|
trim(): this;
|
|
@@ -234,6 +235,7 @@ export declare class JsonSchemaArrayBuilder<IN, OUT, Opt> extends JsonSchemaAnyB
|
|
|
234
235
|
constructor(itemsSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>);
|
|
235
236
|
minLength(minItems: number): this;
|
|
236
237
|
maxLength(maxItems: number): this;
|
|
238
|
+
length(exactLength: number): this;
|
|
237
239
|
length(minItems: number, maxItems: number): this;
|
|
238
240
|
exactLength(length: number): this;
|
|
239
241
|
unique(): this;
|
|
@@ -218,8 +218,16 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
|
|
|
218
218
|
* due to how mutability works in Ajv.
|
|
219
219
|
*/
|
|
220
220
|
optional(optionalValues) {
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
if (!optionalValues) {
|
|
222
|
+
return super.optional();
|
|
223
|
+
}
|
|
224
|
+
const newBuilder = new JsonSchemaStringBuilder().optional();
|
|
225
|
+
const alternativesSchema = j.enum(optionalValues);
|
|
226
|
+
Object.assign(newBuilder.getSchema(), {
|
|
227
|
+
anyOf: [this.build(), alternativesSchema.build()],
|
|
228
|
+
optionalValues,
|
|
229
|
+
});
|
|
230
|
+
return newBuilder;
|
|
223
231
|
}
|
|
224
232
|
regex(pattern, opt) {
|
|
225
233
|
return this.pattern(pattern.source, opt);
|
|
@@ -240,9 +248,9 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
|
|
|
240
248
|
_objectAssign(this.schema, { maxLength });
|
|
241
249
|
return this;
|
|
242
250
|
}
|
|
243
|
-
length(
|
|
244
|
-
|
|
245
|
-
return this;
|
|
251
|
+
length(minLengthOrExactLength, maxLength) {
|
|
252
|
+
const maxLengthActual = maxLength ?? minLengthOrExactLength;
|
|
253
|
+
return this.minLength(minLengthOrExactLength).maxLength(maxLengthActual);
|
|
246
254
|
}
|
|
247
255
|
email(opt) {
|
|
248
256
|
const defaultOptions = { checkTLD: true };
|
|
@@ -380,8 +388,16 @@ export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder {
|
|
|
380
388
|
* due to how mutability works in Ajv.
|
|
381
389
|
*/
|
|
382
390
|
optional(optionalValues) {
|
|
383
|
-
|
|
384
|
-
|
|
391
|
+
if (!optionalValues) {
|
|
392
|
+
return super.optional();
|
|
393
|
+
}
|
|
394
|
+
const newBuilder = new JsonSchemaNumberBuilder().optional();
|
|
395
|
+
const alternativesSchema = j.enum(optionalValues);
|
|
396
|
+
Object.assign(newBuilder.getSchema(), {
|
|
397
|
+
anyOf: [this.build(), alternativesSchema.build()],
|
|
398
|
+
optionalValues,
|
|
399
|
+
});
|
|
400
|
+
return newBuilder;
|
|
385
401
|
}
|
|
386
402
|
integer() {
|
|
387
403
|
_objectAssign(this.schema, { type: 'integer' });
|
|
@@ -488,10 +504,16 @@ export class JsonSchemaBooleanBuilder extends JsonSchemaAnyBuilder {
|
|
|
488
504
|
* due to how mutability works in Ajv.
|
|
489
505
|
*/
|
|
490
506
|
optional(optionalValue) {
|
|
491
|
-
if (typeof optionalValue
|
|
492
|
-
|
|
507
|
+
if (typeof optionalValue === 'undefined') {
|
|
508
|
+
return super.optional();
|
|
493
509
|
}
|
|
494
|
-
|
|
510
|
+
const newBuilder = new JsonSchemaBooleanBuilder().optional();
|
|
511
|
+
const alternativesSchema = j.enum([optionalValue]);
|
|
512
|
+
Object.assign(newBuilder.getSchema(), {
|
|
513
|
+
anyOf: [this.build(), alternativesSchema.build()],
|
|
514
|
+
optionalValues: [optionalValue],
|
|
515
|
+
});
|
|
516
|
+
return newBuilder;
|
|
495
517
|
}
|
|
496
518
|
}
|
|
497
519
|
export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
@@ -627,8 +649,9 @@ export class JsonSchemaArrayBuilder extends JsonSchemaAnyBuilder {
|
|
|
627
649
|
_objectAssign(this.schema, { maxItems });
|
|
628
650
|
return this;
|
|
629
651
|
}
|
|
630
|
-
length(
|
|
631
|
-
|
|
652
|
+
length(minItemsOrExact, maxItems) {
|
|
653
|
+
const maxItemsActual = maxItems ?? minItemsOrExact;
|
|
654
|
+
return this.minLength(minItemsOrExact).maxLength(maxItemsActual);
|
|
632
655
|
}
|
|
633
656
|
exactLength(length) {
|
|
634
657
|
return this.minLength(length).maxLength(length);
|
package/package.json
CHANGED
|
@@ -73,6 +73,7 @@ export class AjvSchema<IN = unknown, OUT = IN> {
|
|
|
73
73
|
let jsonSchema: JsonSchema<IN, OUT>
|
|
74
74
|
|
|
75
75
|
if (AjvSchema.isJsonSchemaBuilder(schema)) {
|
|
76
|
+
// oxlint-disable typescript-eslint(no-unnecessary-type-assertion)
|
|
76
77
|
jsonSchema = (schema as JsonSchemaTerminal<IN, OUT, any>).build()
|
|
77
78
|
AjvSchema.requireValidJsonSchema(jsonSchema)
|
|
78
79
|
} else {
|
|
@@ -1,7 +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 {
|
|
4
|
+
import { Ajv, type Options, type ValidateFunction } from 'ajv'
|
|
5
5
|
import { validTLDs } from '../tlds.js'
|
|
6
6
|
import type { JsonSchemaIsoDateOptions, JsonSchemaStringEmailOptions } from './jsonSchemaBuilder.js'
|
|
7
7
|
|
|
@@ -77,33 +77,42 @@ export function createAjv(opt?: Options): Ajv {
|
|
|
77
77
|
modifying: true,
|
|
78
78
|
schemaType: 'object',
|
|
79
79
|
errors: true,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
validate: function validate(
|
|
81
|
+
transform: { trim?: true; toLowerCase?: true; toUpperCase?: true; truncate?: number },
|
|
82
|
+
data: string,
|
|
83
|
+
_schema,
|
|
84
|
+
ctx,
|
|
85
|
+
) {
|
|
86
|
+
if (!data) return true
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
let transformedData = data
|
|
89
|
+
|
|
90
|
+
if (transform.trim) {
|
|
91
|
+
transformedData = transformedData.trim()
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
94
|
+
if (transform.toLowerCase) {
|
|
95
|
+
transformedData = transformedData.toLocaleLowerCase()
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
if (
|
|
93
|
-
|
|
98
|
+
if (transform.toUpperCase) {
|
|
99
|
+
transformedData = transformedData.toLocaleUpperCase()
|
|
94
100
|
}
|
|
95
101
|
|
|
96
|
-
if (typeof
|
|
97
|
-
|
|
102
|
+
if (typeof transform.truncate === 'number' && transform.truncate >= 0) {
|
|
103
|
+
transformedData = transformedData.slice(0, transform.truncate)
|
|
98
104
|
|
|
99
|
-
if (
|
|
100
|
-
|
|
105
|
+
if (transform.trim) {
|
|
106
|
+
transformedData = transformedData.trim()
|
|
101
107
|
}
|
|
102
108
|
}
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
// Explicit check for `undefined` because parentDataProperty can be `0` when it comes to arrays.
|
|
111
|
+
if (ctx?.parentData && typeof ctx.parentDataProperty !== 'undefined') {
|
|
112
|
+
ctx.parentData[ctx.parentDataProperty] = transformedData
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return true
|
|
107
116
|
},
|
|
108
117
|
})
|
|
109
118
|
|
|
@@ -413,7 +422,7 @@ export function createAjv(opt?: Options): Ajv {
|
|
|
413
422
|
if (!optionalValues.includes(data)) return true
|
|
414
423
|
|
|
415
424
|
if (ctx?.parentData && ctx.parentDataProperty) {
|
|
416
|
-
ctx.parentData[ctx.parentDataProperty]
|
|
425
|
+
delete ctx.parentData[ctx.parentDataProperty]
|
|
417
426
|
}
|
|
418
427
|
|
|
419
428
|
return true
|
|
@@ -325,12 +325,22 @@ export class JsonSchemaStringBuilder<
|
|
|
325
325
|
override optional(
|
|
326
326
|
optionalValues?: string[],
|
|
327
327
|
): JsonSchemaStringBuilder<IN | undefined, OUT | undefined, true> {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
328
|
+
if (!optionalValues) {
|
|
329
|
+
return super.optional() as unknown as JsonSchemaStringBuilder<
|
|
330
|
+
IN | undefined,
|
|
331
|
+
OUT | undefined,
|
|
332
|
+
true
|
|
333
|
+
>
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const newBuilder = new JsonSchemaStringBuilder<IN, OUT, Opt>().optional()
|
|
337
|
+
const alternativesSchema = j.enum(optionalValues)
|
|
338
|
+
Object.assign(newBuilder.getSchema(), {
|
|
339
|
+
anyOf: [this.build(), alternativesSchema.build()],
|
|
340
|
+
optionalValues,
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
return newBuilder
|
|
334
344
|
}
|
|
335
345
|
|
|
336
346
|
regex(pattern: RegExp, opt?: JsonBuilderRuleOpt): this {
|
|
@@ -354,9 +364,11 @@ export class JsonSchemaStringBuilder<
|
|
|
354
364
|
return this
|
|
355
365
|
}
|
|
356
366
|
|
|
357
|
-
length(
|
|
358
|
-
|
|
359
|
-
|
|
367
|
+
length(exactLength: number): this
|
|
368
|
+
length(minLength: number, maxLength: number): this
|
|
369
|
+
length(minLengthOrExactLength: number, maxLength?: number): this {
|
|
370
|
+
const maxLengthActual = maxLength ?? minLengthOrExactLength
|
|
371
|
+
return this.minLength(minLengthOrExactLength).maxLength(maxLengthActual)
|
|
360
372
|
}
|
|
361
373
|
|
|
362
374
|
email(opt?: Partial<JsonSchemaStringEmailOptions>): this {
|
|
@@ -544,12 +556,22 @@ export class JsonSchemaNumberBuilder<
|
|
|
544
556
|
override optional(
|
|
545
557
|
optionalValues?: number[],
|
|
546
558
|
): JsonSchemaNumberBuilder<IN | undefined, OUT | undefined, true> {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
559
|
+
if (!optionalValues) {
|
|
560
|
+
return super.optional() as unknown as JsonSchemaNumberBuilder<
|
|
561
|
+
IN | undefined,
|
|
562
|
+
OUT | undefined,
|
|
563
|
+
true
|
|
564
|
+
>
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const newBuilder = new JsonSchemaNumberBuilder<IN, OUT, Opt>().optional()
|
|
568
|
+
const alternativesSchema = j.enum(optionalValues)
|
|
569
|
+
Object.assign(newBuilder.getSchema(), {
|
|
570
|
+
anyOf: [this.build(), alternativesSchema.build()],
|
|
571
|
+
optionalValues,
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
return newBuilder
|
|
553
575
|
}
|
|
554
576
|
|
|
555
577
|
integer(): this {
|
|
@@ -691,15 +713,22 @@ export class JsonSchemaBooleanBuilder<
|
|
|
691
713
|
override optional(
|
|
692
714
|
optionalValue?: boolean,
|
|
693
715
|
): JsonSchemaBooleanBuilder<IN | undefined, OUT | undefined, true> {
|
|
694
|
-
if (typeof optionalValue
|
|
695
|
-
|
|
716
|
+
if (typeof optionalValue === 'undefined') {
|
|
717
|
+
return super.optional() as unknown as JsonSchemaBooleanBuilder<
|
|
718
|
+
IN | undefined,
|
|
719
|
+
OUT | undefined,
|
|
720
|
+
true
|
|
721
|
+
>
|
|
696
722
|
}
|
|
697
723
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
724
|
+
const newBuilder = new JsonSchemaBooleanBuilder<IN, OUT, Opt>().optional()
|
|
725
|
+
const alternativesSchema = j.enum([optionalValue])
|
|
726
|
+
Object.assign(newBuilder.getSchema(), {
|
|
727
|
+
anyOf: [this.build(), alternativesSchema.build()],
|
|
728
|
+
optionalValues: [optionalValue],
|
|
729
|
+
})
|
|
730
|
+
|
|
731
|
+
return newBuilder
|
|
703
732
|
}
|
|
704
733
|
}
|
|
705
734
|
|
|
@@ -925,8 +954,11 @@ export class JsonSchemaArrayBuilder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<I
|
|
|
925
954
|
return this
|
|
926
955
|
}
|
|
927
956
|
|
|
928
|
-
length(
|
|
929
|
-
|
|
957
|
+
length(exactLength: number): this
|
|
958
|
+
length(minItems: number, maxItems: number): this
|
|
959
|
+
length(minItemsOrExact: number, maxItems?: number): this {
|
|
960
|
+
const maxItemsActual = maxItems ?? minItemsOrExact
|
|
961
|
+
return this.minLength(minItemsOrExact).maxLength(maxItemsActual)
|
|
930
962
|
}
|
|
931
963
|
|
|
932
964
|
exactLength(length: number): this {
|