@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.
@@ -2,7 +2,7 @@ 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
4
  import { _, Ajv } from 'ajv';
5
- import { validTLDs } from './tlds.js';
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
8
8
  const AJV_OPTIONS = {
@@ -1,5 +1,5 @@
1
1
  import type { Set2 } from '@naturalcycles/js-lib/object';
2
- import { type AnyObject, type IsoDate, type IsoDateTime, type NumberEnum, type StringEnum, type StringMap, type UnixTimestamp, type UnixTimestampMillis } from '@naturalcycles/js-lib/types';
2
+ import { type AnyObject, type IANATimezone, type Inclusiveness, type IsoDate, type IsoDateTime, type NumberEnum, type StringEnum, type StringMap, type UnixTimestamp, type UnixTimestampMillis } from '@naturalcycles/js-lib/types';
3
3
  export declare const j: {
4
4
  string(): JsonSchemaStringBuilder<string, string, false>;
5
5
  number(): JsonSchemaNumberBuilder<number, number, false>;
@@ -12,7 +12,7 @@ export declare const j: {
12
12
  array<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaArrayBuilder<IN, OUT, Opt>;
13
13
  set<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaSet2Builder<IN, OUT, Opt>;
14
14
  buffer(): JsonSchemaBufferBuilder;
15
- enum<const T extends readonly (string | number | boolean | null)[] | StringEnum | NumberEnum>(input: T): JsonSchemaEnumBuilder<T extends readonly (infer U)[] ? U : T extends StringEnum ? T[keyof T] : T extends NumberEnum ? T[keyof T] : never>;
15
+ enum<const T extends readonly (string | number | boolean | null)[] | StringEnum | NumberEnum>(input: T, opt?: JsonBuilderRuleOpt): JsonSchemaEnumBuilder<T extends readonly (infer U)[] ? U : T extends StringEnum ? T[keyof T] : T extends NumberEnum ? T[keyof T] : never>;
16
16
  oneOf<B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[], IN = BuilderInUnion<B>, OUT = BuilderOutUnion<B>>(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false>;
17
17
  };
18
18
  export declare class JsonSchemaAnyBuilder<IN, OUT, Opt> {
@@ -91,6 +91,13 @@ export declare class JsonSchemaStringBuilder<IN extends string = string, OUT = I
91
91
  languageTag(): this;
92
92
  countryCode(): this;
93
93
  currency(): this;
94
+ /**
95
+ * Validates that the input is a valid IANATimzone value.
96
+ *
97
+ * All previous expectations in the schema chain are dropped - including `.optional()` -
98
+ * because this call effectively starts a new schema chain as an `enum` validation.
99
+ */
100
+ ianaTimezone(): JsonSchemaEnumBuilder<string | IANATimezone, IANATimezone, false>;
94
101
  }
95
102
  export interface JsonSchemaStringEmailOptions {
96
103
  checkTLD: boolean;
@@ -104,10 +111,11 @@ export declare class JsonSchemaNumberBuilder<IN extends number = number, OUT = I
104
111
  exclusiveMin(exclusiveMinimum: number): this;
105
112
  max(maximum: number): this;
106
113
  exclusiveMax(exclusiveMaximum: number): this;
107
- /**
108
- * Both ranges are inclusive.
109
- */
110
- range(minimum: number, maximum: number): this;
114
+ lessThan(value: number): this;
115
+ lessThanOrEqual(value: number): this;
116
+ moreThan(value: number): this;
117
+ moreThanOrEqual(value: number): this;
118
+ range(minimum: number, maximum: number, incl: Inclusiveness): this;
111
119
  int32(): this;
112
120
  int64(): this;
113
121
  float(): this;
@@ -156,7 +164,7 @@ export declare class JsonSchemaObjectInferringBuilder<PROPS extends Record<strin
156
164
  /**
157
165
  * Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
158
166
  */
159
- dbEntity(): JsonSchemaObjectInferringBuilder<{ [K in keyof PROPS | "id" | "created" | "updated"]: K extends "id" | "created" | "updated" ? {
167
+ dbEntity(): JsonSchemaObjectInferringBuilder<{ [K in "id" | keyof PROPS | "created" | "updated"]: K extends "id" | "created" | "updated" ? {
160
168
  id: JsonSchemaStringBuilder<string, string, false>;
161
169
  created: JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, false>;
162
170
  updated: JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, false>;
@@ -177,7 +185,8 @@ export declare class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder<string
177
185
  constructor();
178
186
  }
179
187
  export declare class JsonSchemaEnumBuilder<IN extends string | number | boolean | null, OUT extends IN = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
180
- constructor(enumValues: readonly IN[]);
188
+ constructor(enumValues: readonly IN[], opt?: JsonBuilderRuleOpt);
189
+ branded<B extends IN>(): JsonSchemaEnumBuilder<B | IN, B, Opt>;
181
190
  }
182
191
  export interface JsonSchema<IN = unknown, OUT = IN> {
183
192
  readonly in?: IN;
@@ -256,6 +265,17 @@ type BuilderInUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> =
256
265
  [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<infer I, any, any> ? I : never;
257
266
  }[number];
258
267
  interface JsonBuilderRuleOpt {
268
+ /**
269
+ * Text of error message to return when the validation fails for the given rule:
270
+ *
271
+ * `{ msg: "is not a valid Oompa-loompa" } => "Object.property is not a valid Oompa-loompa"`
272
+ */
259
273
  msg?: string;
274
+ /**
275
+ * A friendly name for what we are validating, that will be used in error messages:
276
+ *
277
+ * `{ name: "Oompa-loompa" } => "Object.property is not a valid Oompa-loompa"`
278
+ */
279
+ name?: string;
260
280
  }
261
281
  export {};
@@ -5,6 +5,7 @@ import { _uniq } from '@naturalcycles/js-lib/array';
5
5
  import { _assert } from '@naturalcycles/js-lib/error';
6
6
  import { _deepCopy, _sortObject } from '@naturalcycles/js-lib/object';
7
7
  import { JWT_REGEX, } from '@naturalcycles/js-lib/types';
8
+ import { TIMEZONES } from '../timezones.js';
8
9
  import { JSON_SCHEMA_ORDER, mergeJsonSchemaObjects } from './jsonSchemaBuilder.util.js';
9
10
  export const j = {
10
11
  string() {
@@ -32,7 +33,7 @@ export const j = {
32
33
  buffer() {
33
34
  return new JsonSchemaBufferBuilder();
34
35
  },
35
- enum(input) {
36
+ enum(input, opt) {
36
37
  let enumValues;
37
38
  if (Array.isArray(input)) {
38
39
  enumValues = input;
@@ -47,7 +48,7 @@ export const j = {
47
48
  }
48
49
  }
49
50
  _assert(enumValues, 'Unsupported enum input');
50
- return new JsonSchemaEnumBuilder(enumValues);
51
+ return new JsonSchemaEnumBuilder(enumValues, opt);
51
52
  },
52
53
  oneOf(items) {
53
54
  const schemas = items.map(b => b.build());
@@ -177,6 +178,8 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
177
178
  return this.pattern(pattern.source, opt);
178
179
  }
179
180
  pattern(pattern, opt) {
181
+ if (opt?.name)
182
+ this.setErrorMessage('pattern', `is not a valid ${opt.name}`);
180
183
  if (opt?.msg)
181
184
  this.setErrorMessage('pattern', opt.msg);
182
185
  Object.assign(this.schema, { pattern });
@@ -275,6 +278,16 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
275
278
  const regex = /^[A-Z]{3}$/;
276
279
  return this.regex(regex, { msg: 'is not a valid currency format' });
277
280
  }
281
+ /**
282
+ * Validates that the input is a valid IANATimzone value.
283
+ *
284
+ * All previous expectations in the schema chain are dropped - including `.optional()` -
285
+ * because this call effectively starts a new schema chain as an `enum` validation.
286
+ */
287
+ ianaTimezone() {
288
+ // UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
289
+ return j.enum(TIMEZONES, { msg: 'is an invalid IANA timezone' }).branded();
290
+ }
278
291
  }
279
292
  export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder {
280
293
  constructor() {
@@ -309,12 +322,23 @@ export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder {
309
322
  Object.assign(this.schema, { exclusiveMaximum });
310
323
  return this;
311
324
  }
312
- /**
313
- * Both ranges are inclusive.
314
- */
315
- range(minimum, maximum) {
316
- Object.assign(this.schema, { minimum, maximum });
317
- return this;
325
+ lessThan(value) {
326
+ return this.exclusiveMax(value);
327
+ }
328
+ lessThanOrEqual(value) {
329
+ return this.max(value);
330
+ }
331
+ moreThan(value) {
332
+ return this.exclusiveMin(value);
333
+ }
334
+ moreThanOrEqual(value) {
335
+ return this.min(value);
336
+ }
337
+ range(minimum, maximum, incl) {
338
+ if (incl === '[)') {
339
+ return this.moreThanOrEqual(minimum).lessThan(maximum);
340
+ }
341
+ return this.moreThanOrEqual(minimum).lessThanOrEqual(maximum);
318
342
  }
319
343
  int32() {
320
344
  const MIN_INT32 = -(2 ** 31);
@@ -520,8 +544,15 @@ export class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder {
520
544
  }
521
545
  }
522
546
  export class JsonSchemaEnumBuilder extends JsonSchemaAnyBuilder {
523
- constructor(enumValues) {
547
+ constructor(enumValues, opt) {
524
548
  super({ enum: enumValues });
549
+ if (opt?.name)
550
+ this.setErrorMessage('pattern', `is not a valid ${opt.name}`);
551
+ if (opt?.msg)
552
+ this.setErrorMessage('enum', opt.msg);
553
+ }
554
+ branded() {
555
+ return this;
525
556
  }
526
557
  }
527
558
  function object(props) {
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A complicated merge of timezones from the underlying Javascript engine
3
+ * and a list exported from Wikipedia.
4
+ *
5
+ * Why? The `Int.supportedValuesOf('timeZone')` we use provide only
6
+ * canonical/standardized timezone values like `Europe/Stockholm`
7
+ * but does not list accepted aliases like `ETC/Gmt`, `CET` or `Canada/Atlantic`.
8
+ */
9
+ export declare const TIMEZONES: any;