@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.
@@ -53,6 +53,7 @@ export class AjvSchema {
53
53
  }
54
54
  let jsonSchema;
55
55
  if (AjvSchema.isJsonSchemaBuilder(schema)) {
56
+ // oxlint-disable typescript-eslint(no-unnecessary-type-assertion)
56
57
  jsonSchema = schema.build();
57
58
  AjvSchema.requireValidJsonSchema(jsonSchema);
58
59
  }
@@ -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 { _, Ajv } from 'ajv';
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
- code(ctx) {
70
- const { gen, data, schema, it } = ctx;
71
- const { parentData, parentDataProperty } = it;
72
- if (schema.trim) {
73
- gen.assign(_ `${data}`, _ `${data}.trim()`);
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 (schema.toLowerCase) {
76
- gen.assign(_ `${data}`, _ `${data}.toLowerCase()`);
76
+ if (transform.toLowerCase) {
77
+ transformedData = transformedData.toLocaleLowerCase();
77
78
  }
78
- if (schema.toUpperCase) {
79
- gen.assign(_ `${data}`, _ `${data}.toUpperCase()`);
79
+ if (transform.toUpperCase) {
80
+ transformedData = transformedData.toLocaleUpperCase();
80
81
  }
81
- if (typeof schema.truncate === 'number' && schema.truncate >= 0) {
82
- gen.assign(_ `${data}`, _ `${data}.slice(0, ${schema.truncate})`);
83
- if (schema.trim) {
84
- gen.assign(_ `${data}`, _ `${data}.trim()`);
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
- gen.if(_ `${parentData} !== undefined`, () => {
88
- gen.assign(_ `${parentData}[${parentDataProperty}]`, data);
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] = undefined;
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
- _objectAssign(this.schema, { optionalValues });
222
- return super.optional();
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(minLength, maxLength) {
244
- _objectAssign(this.schema, { minLength, maxLength });
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
- _objectAssign(this.schema, { optionalValues });
384
- return super.optional();
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 !== 'undefined') {
492
- _objectAssign(this.schema, { optionalValues: [optionalValue] });
507
+ if (typeof optionalValue === 'undefined') {
508
+ return super.optional();
493
509
  }
494
- return super.optional();
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(minItems, maxItems) {
631
- return this.minLength(minItems).maxLength(maxItems);
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.56.0",
4
+ "version": "15.57.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -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 { _, Ajv, type Options, type ValidateFunction } from 'ajv'
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
- code(ctx) {
81
- const { gen, data, schema, it } = ctx
82
- const { parentData, parentDataProperty } = it
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
- if (schema.trim) {
85
- gen.assign(_`${data}`, _`${data}.trim()`)
88
+ let transformedData = data
89
+
90
+ if (transform.trim) {
91
+ transformedData = transformedData.trim()
86
92
  }
87
93
 
88
- if (schema.toLowerCase) {
89
- gen.assign(_`${data}`, _`${data}.toLowerCase()`)
94
+ if (transform.toLowerCase) {
95
+ transformedData = transformedData.toLocaleLowerCase()
90
96
  }
91
97
 
92
- if (schema.toUpperCase) {
93
- gen.assign(_`${data}`, _`${data}.toUpperCase()`)
98
+ if (transform.toUpperCase) {
99
+ transformedData = transformedData.toLocaleUpperCase()
94
100
  }
95
101
 
96
- if (typeof schema.truncate === 'number' && schema.truncate >= 0) {
97
- gen.assign(_`${data}`, _`${data}.slice(0, ${schema.truncate})`)
102
+ if (typeof transform.truncate === 'number' && transform.truncate >= 0) {
103
+ transformedData = transformedData.slice(0, transform.truncate)
98
104
 
99
- if (schema.trim) {
100
- gen.assign(_`${data}`, _`${data}.trim()`)
105
+ if (transform.trim) {
106
+ transformedData = transformedData.trim()
101
107
  }
102
108
  }
103
109
 
104
- gen.if(_`${parentData} !== undefined`, () => {
105
- gen.assign(_`${parentData}[${parentDataProperty}]`, data)
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] = undefined
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
- _objectAssign(this.schema, { optionalValues })
329
- return super.optional() as unknown as JsonSchemaStringBuilder<
330
- IN | undefined,
331
- OUT | undefined,
332
- true
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(minLength: number, maxLength: number): this {
358
- _objectAssign(this.schema, { minLength, maxLength })
359
- return this
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
- _objectAssign(this.schema, { optionalValues })
548
- return super.optional() as unknown as JsonSchemaNumberBuilder<
549
- IN | undefined,
550
- OUT | undefined,
551
- true
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 !== 'undefined') {
695
- _objectAssign(this.schema, { optionalValues: [optionalValue] })
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
- return super.optional() as unknown as JsonSchemaBooleanBuilder<
699
- IN | undefined,
700
- OUT | undefined,
701
- true
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(minItems: number, maxItems: number): this {
929
- return this.minLength(minItems).maxLength(maxItems)
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 {