@naturalcycles/nodejs-lib 15.43.2 → 15.45.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.
@@ -13,6 +13,8 @@ import type { Set2 } from '@naturalcycles/js-lib/object'
13
13
  import { _deepCopy, _sortObject } from '@naturalcycles/js-lib/object'
14
14
  import {
15
15
  type AnyObject,
16
+ type IANATimezone,
17
+ type Inclusiveness,
16
18
  type IsoDate,
17
19
  type IsoDateTime,
18
20
  JWT_REGEX,
@@ -22,6 +24,7 @@ import {
22
24
  type UnixTimestamp,
23
25
  type UnixTimestampMillis,
24
26
  } from '@naturalcycles/js-lib/types'
27
+ import { TIMEZONES } from '../timezones.js'
25
28
  import { JSON_SCHEMA_ORDER, mergeJsonSchemaObjects } from './jsonSchemaBuilder.util.js'
26
29
 
27
30
  export const j = {
@@ -63,6 +66,7 @@ export const j = {
63
66
 
64
67
  enum<const T extends readonly (string | number | boolean | null)[] | StringEnum | NumberEnum>(
65
68
  input: T,
69
+ opt?: JsonBuilderRuleOpt,
66
70
  ): JsonSchemaEnumBuilder<
67
71
  T extends readonly (infer U)[]
68
72
  ? U
@@ -86,7 +90,7 @@ export const j = {
86
90
  }
87
91
 
88
92
  _assert(enumValues, 'Unsupported enum input')
89
- return new JsonSchemaEnumBuilder(enumValues as any)
93
+ return new JsonSchemaEnumBuilder(enumValues as any, opt)
90
94
  },
91
95
 
92
96
  oneOf<
@@ -246,6 +250,7 @@ export class JsonSchemaStringBuilder<
246
250
  }
247
251
 
248
252
  pattern(pattern: string, opt?: JsonBuilderRuleOpt): this {
253
+ if (opt?.name) this.setErrorMessage('pattern', `is not a valid ${opt.name}`)
249
254
  if (opt?.msg) this.setErrorMessage('pattern', opt.msg)
250
255
  Object.assign(this.schema, { pattern })
251
256
  return this
@@ -369,6 +374,17 @@ export class JsonSchemaStringBuilder<
369
374
  const regex = /^[A-Z]{3}$/
370
375
  return this.regex(regex, { msg: 'is not a valid currency format' })
371
376
  }
377
+
378
+ /**
379
+ * Validates that the input is a valid IANATimzone value.
380
+ *
381
+ * All previous expectations in the schema chain are dropped - including `.optional()` -
382
+ * because this call effectively starts a new schema chain as an `enum` validation.
383
+ */
384
+ ianaTimezone(): JsonSchemaEnumBuilder<string | IANATimezone, IANATimezone, false> {
385
+ // UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
386
+ return j.enum(TIMEZONES, { msg: 'is an invalid IANA timezone' }).branded<IANATimezone>()
387
+ }
372
388
  }
373
389
 
374
390
  export interface JsonSchemaStringEmailOptions {
@@ -420,12 +436,27 @@ export class JsonSchemaNumberBuilder<
420
436
  return this
421
437
  }
422
438
 
423
- /**
424
- * Both ranges are inclusive.
425
- */
426
- range(minimum: number, maximum: number): this {
427
- Object.assign(this.schema, { minimum, maximum })
428
- return this
439
+ lessThan(value: number): this {
440
+ return this.exclusiveMax(value)
441
+ }
442
+
443
+ lessThanOrEqual(value: number): this {
444
+ return this.max(value)
445
+ }
446
+
447
+ moreThan(value: number): this {
448
+ return this.exclusiveMin(value)
449
+ }
450
+
451
+ moreThanOrEqual(value: number): this {
452
+ return this.min(value)
453
+ }
454
+
455
+ range(minimum: number, maximum: number, incl: Inclusiveness): this {
456
+ if (incl === '[)') {
457
+ return this.moreThanOrEqual(minimum).lessThan(maximum)
458
+ }
459
+ return this.moreThanOrEqual(minimum).lessThanOrEqual(maximum)
429
460
  }
430
461
 
431
462
  int32(): this {
@@ -750,8 +781,14 @@ export class JsonSchemaEnumBuilder<
750
781
  OUT extends IN = IN,
751
782
  Opt extends boolean = false,
752
783
  > extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
753
- constructor(enumValues: readonly IN[]) {
784
+ constructor(enumValues: readonly IN[], opt?: JsonBuilderRuleOpt) {
754
785
  super({ enum: enumValues })
786
+ if (opt?.name) this.setErrorMessage('pattern', `is not a valid ${opt.name}`)
787
+ if (opt?.msg) this.setErrorMessage('enum', opt.msg)
788
+ }
789
+
790
+ branded<B extends IN>(): JsonSchemaEnumBuilder<B | IN, B, Opt> {
791
+ return this as unknown as JsonSchemaEnumBuilder<B | IN, B, Opt>
755
792
  }
756
793
  }
757
794
 
@@ -879,5 +916,16 @@ type BuilderInUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> =
879
916
  }[number]
880
917
 
881
918
  interface JsonBuilderRuleOpt {
919
+ /**
920
+ * Text of error message to return when the validation fails for the given rule:
921
+ *
922
+ * `{ msg: "is not a valid Oompa-loompa" } => "Object.property is not a valid Oompa-loompa"`
923
+ */
882
924
  msg?: string
925
+ /**
926
+ * A friendly name for what we are validating, that will be used in error messages:
927
+ *
928
+ * `{ name: "Oompa-loompa" } => "Object.property is not a valid Oompa-loompa"`
929
+ */
930
+ name?: string
883
931
  }