@naturalcycles/nodejs-lib 15.84.0 → 15.86.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.
@@ -1,4 +1,4 @@
1
- import { _isBetween, _lazyValue } from '@naturalcycles/js-lib';
1
+ import { _isBetween, _lazyValue, _round } from '@naturalcycles/js-lib';
2
2
  import { _assert } from '@naturalcycles/js-lib/error';
3
3
  import { _deepCopy, _mapObject, Set2 } from '@naturalcycles/js-lib/object';
4
4
  import { _substringAfterLast } from '@naturalcycles/js-lib/string';
@@ -560,6 +560,20 @@ export function createAjv(opt) {
560
560
  return validate;
561
561
  },
562
562
  });
563
+ ajv.addKeyword({
564
+ keyword: 'precision',
565
+ type: ['number'],
566
+ modifying: true,
567
+ errors: false,
568
+ schemaType: 'number',
569
+ validate: function validate(numberOfDigits, data, _schema, ctx) {
570
+ if (!numberOfDigits)
571
+ return true;
572
+ _assert(ctx?.parentData && ctx.parentDataProperty !== undefined, 'You should only use `precision(n) on a property of an object, or on an element of an array due to Ajv mutation issues.');
573
+ ctx.parentData[ctx.parentDataProperty] = _round(data, 10 ** (-1 * numberOfDigits));
574
+ return true;
575
+ },
576
+ });
563
577
  return ajv;
564
578
  }
565
579
  const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
@@ -279,6 +279,12 @@ export declare class JsonSchemaNumberBuilder<IN extends number | undefined = num
279
279
  unixTimestamp2000Millis(): JsonSchemaNumberBuilder<UnixTimestampMillis, UnixTimestampMillis, Opt>;
280
280
  utcOffset(): this;
281
281
  utcOffsetHour(): this;
282
+ /**
283
+ * Specify the precision of the floating point numbers by the number of digits after the ".".
284
+ * Excess digits will be cut-off when the current schema is nested in an object or array schema,
285
+ * due to how mutability works in Ajv.
286
+ */
287
+ precision(numberOfDigits: number): this;
282
288
  }
283
289
  export declare class JsonSchemaBooleanBuilder<IN extends boolean | undefined = boolean, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
284
290
  constructor();
@@ -293,6 +299,16 @@ export declare class JsonSchemaBooleanBuilder<IN extends boolean | undefined = b
293
299
  export declare class JsonSchemaObjectBuilder<IN extends AnyObject, OUT extends AnyObject, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
294
300
  constructor(props?: AnyObject, opt?: JsonSchemaObjectBuilderOpts);
295
301
  addProperties(props: AnyObject): this;
302
+ /**
303
+ * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
304
+ *
305
+ * This `null` feature only works when the current schema is nested in an object or array schema,
306
+ * due to how mutability works in Ajv.
307
+ *
308
+ * When `null` is passed, the return type becomes `JsonSchemaTerminal`
309
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
310
+ */
311
+ optional<N extends null | undefined = undefined>(nullValue?: N): N extends null ? JsonSchemaTerminal<IN | undefined, OUT | undefined, true> : JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true>;
296
312
  /**
297
313
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
298
314
  */
@@ -355,6 +371,32 @@ export declare class JsonSchemaObjectInferringBuilder<PROPS extends Record<strin
355
371
  }>, Opt> {
356
372
  constructor(props?: PROPS);
357
373
  addProperties(props: PROPS): this;
374
+ /**
375
+ * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
376
+ *
377
+ * This `null` feature only works when the current schema is nested in an object or array schema,
378
+ * due to how mutability works in Ajv.
379
+ *
380
+ * When `null` is passed, the return type becomes `JsonSchemaTerminal`
381
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
382
+ */
383
+ optional<N extends null | undefined = undefined>(nullValue?: N): N extends null ? JsonSchemaTerminal<Expand<{
384
+ [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;
385
+ } & {
386
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
387
+ }> | undefined, Expand<{
388
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
389
+ } & {
390
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
391
+ }> | undefined, true> : JsonSchemaAnyBuilder<Expand<{
392
+ [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;
393
+ } & {
394
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
395
+ }> | undefined, Expand<{
396
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
397
+ } & {
398
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
399
+ }> | undefined, true>;
358
400
  /**
359
401
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
360
402
  */
@@ -476,6 +518,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
476
518
  schemaDictionary: Record<string, JsonSchema>;
477
519
  };
478
520
  anyOfThese?: JsonSchema[];
521
+ precision?: number;
479
522
  }
480
523
  declare function object(props: AnyObject): never;
481
524
  declare function object<IN extends AnyObject>(props: {
@@ -632,6 +632,14 @@ export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder {
632
632
  utcOffsetHour() {
633
633
  return this.integer().min(-12).max(14);
634
634
  }
635
+ /**
636
+ * Specify the precision of the floating point numbers by the number of digits after the ".".
637
+ * Excess digits will be cut-off when the current schema is nested in an object or array schema,
638
+ * due to how mutability works in Ajv.
639
+ */
640
+ precision(numberOfDigits) {
641
+ return this.cloneAndUpdateSchema({ precision: numberOfDigits });
642
+ }
635
643
  }
636
644
  export class JsonSchemaBooleanBuilder extends JsonSchemaAnyBuilder {
637
645
  constructor() {
@@ -687,6 +695,28 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
687
695
  this.schema.required = _uniq(required).sort();
688
696
  return this;
689
697
  }
698
+ /**
699
+ * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
700
+ *
701
+ * This `null` feature only works when the current schema is nested in an object or array schema,
702
+ * due to how mutability works in Ajv.
703
+ *
704
+ * When `null` is passed, the return type becomes `JsonSchemaTerminal`
705
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
706
+ */
707
+ optional(nullValue) {
708
+ if (typeof nullValue === 'undefined') {
709
+ return super.optional();
710
+ }
711
+ // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
712
+ // so we must allow `null` values to be parsed by Ajv,
713
+ // but the typing should not reflect that.
714
+ // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
715
+ return new JsonSchemaTerminal({
716
+ anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
717
+ optionalField: true,
718
+ });
719
+ }
690
720
  /**
691
721
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
692
722
  */
@@ -773,6 +803,29 @@ export class JsonSchemaObjectInferringBuilder extends JsonSchemaAnyBuilder {
773
803
  this.schema.required = _uniq(required).sort();
774
804
  return this;
775
805
  }
806
+ /**
807
+ * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
808
+ *
809
+ * This `null` feature only works when the current schema is nested in an object or array schema,
810
+ * due to how mutability works in Ajv.
811
+ *
812
+ * When `null` is passed, the return type becomes `JsonSchemaTerminal`
813
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
814
+ */
815
+ // @ts-expect-error override adds optional parameter which is compatible but TS can't verify complex mapped types
816
+ optional(nullValue) {
817
+ if (nullValue === undefined) {
818
+ return super.optional();
819
+ }
820
+ // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
821
+ // so we must allow `null` values to be parsed by Ajv,
822
+ // but the typing should not reflect that.
823
+ // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
824
+ return new JsonSchemaTerminal({
825
+ anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
826
+ optionalField: true,
827
+ });
828
+ }
776
829
  /**
777
830
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
778
831
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.84.0",
4
+ "version": "15.86.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -1,4 +1,4 @@
1
- import { _isBetween, _lazyValue } from '@naturalcycles/js-lib'
1
+ import { _isBetween, _lazyValue, _round } from '@naturalcycles/js-lib'
2
2
  import { _assert } from '@naturalcycles/js-lib/error'
3
3
  import { _deepCopy, _mapObject, Set2 } from '@naturalcycles/js-lib/object'
4
4
  import { _substringAfterLast } from '@naturalcycles/js-lib/string'
@@ -639,6 +639,26 @@ export function createAjv(opt?: Options): Ajv2020 {
639
639
  },
640
640
  })
641
641
 
642
+ ajv.addKeyword({
643
+ keyword: 'precision',
644
+ type: ['number'],
645
+ modifying: true,
646
+ errors: false,
647
+ schemaType: 'number',
648
+ validate: function validate(numberOfDigits: number, data: number, _schema, ctx) {
649
+ if (!numberOfDigits) return true
650
+
651
+ _assert(
652
+ ctx?.parentData && ctx.parentDataProperty !== undefined,
653
+ 'You should only use `precision(n) on a property of an object, or on an element of an array due to Ajv mutation issues.',
654
+ )
655
+
656
+ ctx.parentData[ctx.parentDataProperty] = _round(data, 10 ** (-1 * numberOfDigits))
657
+
658
+ return true
659
+ },
660
+ })
661
+
642
662
  return ajv
643
663
  }
644
664
 
@@ -888,6 +888,15 @@ export class JsonSchemaNumberBuilder<
888
888
  utcOffsetHour(): this {
889
889
  return this.integer().min(-12).max(14)
890
890
  }
891
+
892
+ /**
893
+ * Specify the precision of the floating point numbers by the number of digits after the ".".
894
+ * Excess digits will be cut-off when the current schema is nested in an object or array schema,
895
+ * due to how mutability works in Ajv.
896
+ */
897
+ precision(numberOfDigits: number): this {
898
+ return this.cloneAndUpdateSchema({ precision: numberOfDigits })
899
+ }
891
900
  }
892
901
 
893
902
  export class JsonSchemaBooleanBuilder<
@@ -968,6 +977,34 @@ export class JsonSchemaObjectBuilder<
968
977
  return this
969
978
  }
970
979
 
980
+ /**
981
+ * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
982
+ *
983
+ * This `null` feature only works when the current schema is nested in an object or array schema,
984
+ * due to how mutability works in Ajv.
985
+ *
986
+ * When `null` is passed, the return type becomes `JsonSchemaTerminal`
987
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
988
+ */
989
+ override optional<N extends null | undefined = undefined>(
990
+ nullValue?: N,
991
+ ): N extends null
992
+ ? JsonSchemaTerminal<IN | undefined, OUT | undefined, true>
993
+ : JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true> {
994
+ if (typeof nullValue === 'undefined') {
995
+ return super.optional()
996
+ }
997
+
998
+ // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
999
+ // so we must allow `null` values to be parsed by Ajv,
1000
+ // but the typing should not reflect that.
1001
+ // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
1002
+ return new JsonSchemaTerminal({
1003
+ anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
1004
+ optionalField: true,
1005
+ }) as any
1006
+ }
1007
+
971
1008
  /**
972
1009
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
973
1010
  */
@@ -1146,6 +1183,103 @@ export class JsonSchemaObjectInferringBuilder<
1146
1183
  return this
1147
1184
  }
1148
1185
 
1186
+ /**
1187
+ * @param nullValue Pass `null` to have `null` values be considered/converted as `undefined`.
1188
+ *
1189
+ * This `null` feature only works when the current schema is nested in an object or array schema,
1190
+ * due to how mutability works in Ajv.
1191
+ *
1192
+ * When `null` is passed, the return type becomes `JsonSchemaTerminal`
1193
+ * (no further chaining allowed) because the schema is wrapped in an anyOf structure.
1194
+ */
1195
+ // @ts-expect-error override adds optional parameter which is compatible but TS can't verify complex mapped types
1196
+ override optional<N extends null | undefined = undefined>(
1197
+ nullValue?: N,
1198
+ ): N extends null
1199
+ ? JsonSchemaTerminal<
1200
+ | Expand<
1201
+ {
1202
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1203
+ ? IsOpt extends true
1204
+ ? never
1205
+ : K
1206
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1207
+ } & {
1208
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1209
+ ? IsOpt extends true
1210
+ ? K
1211
+ : never
1212
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1213
+ }
1214
+ >
1215
+ | undefined,
1216
+ | Expand<
1217
+ {
1218
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1219
+ ? IsOpt extends true
1220
+ ? never
1221
+ : K
1222
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1223
+ } & {
1224
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1225
+ ? IsOpt extends true
1226
+ ? K
1227
+ : never
1228
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1229
+ }
1230
+ >
1231
+ | undefined,
1232
+ true
1233
+ >
1234
+ : JsonSchemaAnyBuilder<
1235
+ | Expand<
1236
+ {
1237
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1238
+ ? IsOpt extends true
1239
+ ? never
1240
+ : K
1241
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1242
+ } & {
1243
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1244
+ ? IsOpt extends true
1245
+ ? K
1246
+ : never
1247
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never
1248
+ }
1249
+ >
1250
+ | undefined,
1251
+ | Expand<
1252
+ {
1253
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1254
+ ? IsOpt extends true
1255
+ ? never
1256
+ : K
1257
+ : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1258
+ } & {
1259
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt>
1260
+ ? IsOpt extends true
1261
+ ? K
1262
+ : never
1263
+ : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never
1264
+ }
1265
+ >
1266
+ | undefined,
1267
+ true
1268
+ > {
1269
+ if (nullValue === undefined) {
1270
+ return super.optional() as any
1271
+ }
1272
+
1273
+ // When `null` is specified, we want `null` to be stripped and the value to become `undefined`,
1274
+ // so we must allow `null` values to be parsed by Ajv,
1275
+ // but the typing should not reflect that.
1276
+ // We also cannot accept more rules attached, since we're not building an ObjectSchema anymore.
1277
+ return new JsonSchemaTerminal({
1278
+ anyOf: [{ type: 'null', optionalValues: [null] }, this.build()],
1279
+ optionalField: true,
1280
+ }) as any
1281
+ }
1282
+
1149
1283
  /**
1150
1284
  * When set, the validation will not strip away properties that are not specified explicitly in the schema.
1151
1285
  */
@@ -1447,6 +1581,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
1447
1581
  schemaDictionary: Record<string, JsonSchema>
1448
1582
  }
1449
1583
  anyOfThese?: JsonSchema[]
1584
+ precision?: number
1450
1585
  }
1451
1586
 
1452
1587
  function object(props: AnyObject): never