@naturalcycles/nodejs-lib 15.72.2 → 15.74.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.
@@ -50,6 +50,13 @@ export declare const j: {
50
50
  buffer(): JsonSchemaBufferBuilder;
51
51
  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>;
52
52
  oneOf<B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[], IN = BuilderInUnion<B>, OUT = BuilderOutUnion<B>>(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false>;
53
+ /**
54
+ * Value must match at least one of the provided schemas.
55
+ *
56
+ * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format).
57
+ * Use `oneOf` when schemas are mutually exclusive.
58
+ */
59
+ anyOf<B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[], IN = BuilderInUnion<B>, OUT = BuilderOutUnion<B>>(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false>;
53
60
  and(): {
54
61
  silentBob: () => never;
55
62
  };
@@ -5,7 +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 { _objectAssign, JWT_REGEX, } from '@naturalcycles/js-lib/types';
8
- import { BASE64URL_REGEX, COUNTRY_CODE_REGEX, CURRENCY_REGEX, IPV4_REGEX, IPV6_REGEX, LANGUAGE_TAG_REGEX, SEMVER_REGEX, SLUG_REGEX, UUID_REGEX, } from '../regexes.js';
8
+ import { BASE64URL_REGEX, COUNTRY_CODE_REGEX, CURRENCY_REGEX, IPV4_REGEX, IPV6_REGEX, LANGUAGE_TAG_REGEX, SEMVER_REGEX, SLUG_REGEX, URL_REGEX, UUID_REGEX, } from '../regexes.js';
9
9
  import { TIMEZONES } from '../timezones.js';
10
10
  import { isEveryItemNumber, isEveryItemPrimitive, isEveryItemString, JSON_SCHEMA_ORDER, mergeJsonSchemaObjects, } from './jsonSchemaBuilder.util.js';
11
11
  export const j = {
@@ -112,6 +112,18 @@ export const j = {
112
112
  oneOf: schemas,
113
113
  });
114
114
  },
115
+ /**
116
+ * Value must match at least one of the provided schemas.
117
+ *
118
+ * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format).
119
+ * Use `oneOf` when schemas are mutually exclusive.
120
+ */
121
+ anyOf(items) {
122
+ const schemas = items.map(b => b.build());
123
+ return new JsonSchemaAnyBuilder({
124
+ anyOf: schemas,
125
+ });
126
+ },
115
127
  and() {
116
128
  return {
117
129
  silentBob: () => {
@@ -266,6 +278,7 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
266
278
  return newBuilder;
267
279
  }
268
280
  regex(pattern, opt) {
281
+ _assert(!pattern.flags, `Regex flags are not supported by JSON Schema. Received: /${pattern.source}/${pattern.flags}`);
269
282
  return this.pattern(pattern.source, opt);
270
283
  }
271
284
  pattern(pattern, opt) {
@@ -336,9 +349,7 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
336
349
  return this.regex(JWT_REGEX, { msg: 'is not a valid JWT format' });
337
350
  }
338
351
  url() {
339
- // from `ajv-formats`
340
- const regex = /^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00A1}-\u{FFFF}]+-)*[a-z0-9\u{00A1}-\u{FFFF}]+)(?:\.(?:[a-z0-9\u{00A1}-\u{FFFF}]+-)*[a-z0-9\u{00A1}-\u{FFFF}]+)*(?:\.(?:[a-z\u{00A1}-\u{FFFF}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu;
341
- return this.regex(regex, { msg: 'is not a valid URL format' });
352
+ return this.regex(URL_REGEX, { msg: 'is not a valid URL format' });
342
353
  }
343
354
  ipv4() {
344
355
  return this.regex(IPV4_REGEX, { msg: 'is not a valid IPv4 format' });
@@ -795,6 +806,9 @@ function record(keySchema, valueSchema) {
795
806
  });
796
807
  }
797
808
  function withRegexKeys(keyRegex, schema) {
809
+ if (keyRegex instanceof RegExp) {
810
+ _assert(!keyRegex.flags, `Regex flags are not supported by JSON Schema. Received: /${keyRegex.source}/${keyRegex.flags}`);
811
+ }
798
812
  const pattern = keyRegex instanceof RegExp ? keyRegex.source : keyRegex;
799
813
  const jsonSchema = schema.build();
800
814
  return new JsonSchemaObjectBuilder([], {
@@ -18,3 +18,4 @@ export declare const LANGUAGE_TAG_REGEX: RegExp;
18
18
  export declare const MAC_ADDRESS_REGEX: RegExp;
19
19
  export declare const SEMVER_REGEX: RegExp;
20
20
  export declare const SLUG_REGEX: RegExp;
21
+ export declare const URL_REGEX: RegExp;
@@ -23,3 +23,7 @@ export const LANGUAGE_TAG_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/;
23
23
  export const MAC_ADDRESS_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
24
24
  export const SEMVER_REGEX = /^[0-9]+\.[0-9]+\.[0-9]+$/;
25
25
  export const SLUG_REGEX = /^[a-z0-9-]+$/;
26
+ // URL regex based on `ajv-formats`, but without flags for JSON Schema compatibility.
27
+ // Uses [a-zA-Z] instead of [a-z] with i flag. Simplified to not require unicode flag.
28
+ // Without the unicode flag - it DOES NOT support urls like https://münchen.de
29
+ export const URL_REGEX = /^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+)(?:\.(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+)*(?:\.(?:[a-zA-Z]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.72.2",
4
+ "version": "15.74.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -36,6 +36,7 @@ import {
36
36
  LANGUAGE_TAG_REGEX,
37
37
  SEMVER_REGEX,
38
38
  SLUG_REGEX,
39
+ URL_REGEX,
39
40
  UUID_REGEX,
40
41
  } from '../regexes.js'
41
42
  import { TIMEZONES } from '../timezones.js'
@@ -190,6 +191,23 @@ export const j = {
190
191
  })
191
192
  },
192
193
 
194
+ /**
195
+ * Value must match at least one of the provided schemas.
196
+ *
197
+ * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format).
198
+ * Use `oneOf` when schemas are mutually exclusive.
199
+ */
200
+ anyOf<
201
+ B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[],
202
+ IN = BuilderInUnion<B>,
203
+ OUT = BuilderOutUnion<B>,
204
+ >(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false> {
205
+ const schemas = items.map(b => b.build())
206
+ return new JsonSchemaAnyBuilder<IN, OUT, false>({
207
+ anyOf: schemas,
208
+ })
209
+ },
210
+
193
211
  and() {
194
212
  return {
195
213
  silentBob: () => {
@@ -388,6 +406,10 @@ export class JsonSchemaStringBuilder<
388
406
  }
389
407
 
390
408
  regex(pattern: RegExp, opt?: JsonBuilderRuleOpt): this {
409
+ _assert(
410
+ !pattern.flags,
411
+ `Regex flags are not supported by JSON Schema. Received: /${pattern.source}/${pattern.flags}`,
412
+ )
391
413
  return this.pattern(pattern.source, opt)
392
414
  }
393
415
 
@@ -473,10 +495,7 @@ export class JsonSchemaStringBuilder<
473
495
  }
474
496
 
475
497
  url(): this {
476
- // from `ajv-formats`
477
- const regex =
478
- /^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00A1}-\u{FFFF}]+-)*[a-z0-9\u{00A1}-\u{FFFF}]+)(?:\.(?:[a-z0-9\u{00A1}-\u{FFFF}]+-)*[a-z0-9\u{00A1}-\u{FFFF}]+)*(?:\.(?:[a-z\u{00A1}-\u{FFFF}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu
479
- return this.regex(regex, { msg: 'is not a valid URL format' })
498
+ return this.regex(URL_REGEX, { msg: 'is not a valid URL format' })
480
499
  }
481
500
 
482
501
  ipv4(): this {
@@ -1336,6 +1355,12 @@ function withRegexKeys<
1336
1355
  Opt extends true ? StringMap<SchemaOut<S>> : StringMap<SchemaOut<S>>,
1337
1356
  false
1338
1357
  > {
1358
+ if (keyRegex instanceof RegExp) {
1359
+ _assert(
1360
+ !keyRegex.flags,
1361
+ `Regex flags are not supported by JSON Schema. Received: /${keyRegex.source}/${keyRegex.flags}`,
1362
+ )
1363
+ }
1339
1364
  const pattern = keyRegex instanceof RegExp ? keyRegex.source : keyRegex
1340
1365
  const jsonSchema = schema.build()
1341
1366
 
@@ -24,3 +24,8 @@ export const LANGUAGE_TAG_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/
24
24
  export const MAC_ADDRESS_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/
25
25
  export const SEMVER_REGEX = /^[0-9]+\.[0-9]+\.[0-9]+$/
26
26
  export const SLUG_REGEX = /^[a-z0-9-]+$/
27
+ // URL regex based on `ajv-formats`, but without flags for JSON Schema compatibility.
28
+ // Uses [a-zA-Z] instead of [a-z] with i flag. Simplified to not require unicode flag.
29
+ // Without the unicode flag - it DOES NOT support urls like https://münchen.de
30
+ export const URL_REGEX =
31
+ /^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+)(?:\.(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+)*(?:\.(?:[a-zA-Z]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/