@naturalcycles/nodejs-lib 15.50.1 → 15.51.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.
@@ -97,7 +97,6 @@ export declare class JsonSchemaStringBuilder<IN extends string = string, OUT = I
97
97
  url(): this;
98
98
  ipv4(): this;
99
99
  ipv6(): this;
100
- id(): this;
101
100
  slug(): this;
102
101
  semVer(): this;
103
102
  languageTag(): this;
@@ -110,6 +109,7 @@ export declare class JsonSchemaStringBuilder<IN extends string = string, OUT = I
110
109
  * because this call effectively starts a new schema chain as an `enum` validation.
111
110
  */
112
111
  ianaTimezone(): JsonSchemaEnumBuilder<string | IANATimezone, IANATimezone, false>;
112
+ base64Url(): this;
113
113
  }
114
114
  export interface JsonSchemaStringEmailOptions {
115
115
  checkTLD: boolean;
@@ -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 { _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, } from '../regexes.js';
8
9
  import { TIMEZONES } from '../timezones.js';
9
10
  import { isEveryItemNumber, isEveryItemString, JSON_SCHEMA_ORDER, mergeJsonSchemaObjects, } from './jsonSchemaBuilder.util.js';
10
11
  export const j = {
@@ -265,39 +266,25 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
265
266
  return this.regex(regex, { msg: 'is not a valid URL format' });
266
267
  }
267
268
  ipv4() {
268
- // from `ajv-formats`
269
- const regex = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
270
- return this.regex(regex, { msg: 'is not a valid IPv4 format' });
269
+ return this.regex(IPV4_REGEX, { msg: 'is not a valid IPv4 format' });
271
270
  }
272
271
  ipv6() {
273
- // from `ajv-formats`
274
- const regex = /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i;
275
- return this.regex(regex, { msg: 'is not a valid IPv6 format' });
276
- }
277
- id() {
278
- const regex = /^[a-z0-9_]{6,64}$/;
279
- return this.regex(regex, { msg: 'is not a valid ID format' });
272
+ return this.regex(IPV6_REGEX, { msg: 'is not a valid IPv6 format' });
280
273
  }
281
274
  slug() {
282
- const regex = /^[a-z0-9-]+$/;
283
- return this.regex(regex, { msg: 'is not a valid slug format' });
275
+ return this.regex(SLUG_REGEX, { msg: 'is not a valid slug format' });
284
276
  }
285
277
  semVer() {
286
- const regex = /^[0-9]+\.[0-9]+\.[0-9]+$/;
287
- return this.regex(regex, { msg: 'is not a valid semver format' });
278
+ return this.regex(SEMVER_REGEX, { msg: 'is not a valid semver format' });
288
279
  }
289
280
  languageTag() {
290
- // IETF language tag (https://en.wikipedia.org/wiki/IETF_language_tag)
291
- const regex = /^[a-z]{2}(-[A-Z]{2})?$/;
292
- return this.regex(regex, { msg: 'is not a valid language format' });
281
+ return this.regex(LANGUAGE_TAG_REGEX, { msg: 'is not a valid language format' });
293
282
  }
294
283
  countryCode() {
295
- const regex = /^[A-Z]{2}$/;
296
- return this.regex(regex, { msg: 'is not a valid country code format' });
284
+ return this.regex(COUNTRY_CODE_REGEX, { msg: 'is not a valid country code format' });
297
285
  }
298
286
  currency() {
299
- const regex = /^[A-Z]{3}$/;
300
- return this.regex(regex, { msg: 'is not a valid currency format' });
287
+ return this.regex(CURRENCY_REGEX, { msg: 'is not a valid currency format' });
301
288
  }
302
289
  /**
303
290
  * Validates that the input is a valid IANATimzone value.
@@ -309,6 +296,11 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
309
296
  // UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
310
297
  return j.enum(TIMEZONES, { msg: 'is an invalid IANA timezone' }).branded();
311
298
  }
299
+ base64Url() {
300
+ return this.regex(BASE64URL_REGEX, {
301
+ msg: 'contains characters not allowed in Base64 URL characterset',
302
+ });
303
+ }
312
304
  }
313
305
  export class JsonSchemaIsoDateBuilder extends JsonSchemaAnyBuilder {
314
306
  constructor() {
@@ -1,4 +1,4 @@
1
- import type { AnyObject, BaseDBEntity, IANATimezone, IsoDate, IsoDateTime, NumberEnum, StringEnum, StringMap, UnixTimestamp, UnixTimestampMillis } from '@naturalcycles/js-lib/types';
1
+ import { type AnyObject, type BaseDBEntity, type IANATimezone, type IsoDate, type IsoDateTime, type NumberEnum, type StringEnum, type StringMap, type UnixTimestamp, type UnixTimestampMillis } from '@naturalcycles/js-lib/types';
2
2
  import type { AlternativesSchema, AnySchema, ArraySchema, ObjectSchema } from 'joi';
3
3
  import type { NumberSchema } from './number.extensions.js';
4
4
  import type { StringSchema } from './string.extensions.js';
@@ -41,13 +41,9 @@ export declare function objectSchema<T extends AnyObject>(schema: {
41
41
  export declare function stringMapSchema<T>(key: AnySchema, value: AnySchema<T>): ObjectSchema<StringMap<T>>;
42
42
  export declare function oneOfSchema<T = any>(...schemas: AnySchema[]): AlternativesSchema<T>;
43
43
  export declare const anySchema: AnySchema<any>;
44
- export declare const BASE62_REGEX: RegExp;
45
- export declare const BASE64_REGEX: RegExp;
46
- export declare const BASE64URL_REGEX: RegExp;
47
44
  export declare const base62Schema: StringSchema<string>;
48
45
  export declare const base64Schema: StringSchema<string>;
49
46
  export declare const base64UrlSchema: StringSchema<string>;
50
- export declare const JWT_REGEX: RegExp;
51
47
  export declare const jwtSchema: StringSchema<string>;
52
48
  /**
53
49
  * [a-zA-Z0-9_]*
@@ -57,10 +53,6 @@ export declare const idSchema: StringSchema<string>;
57
53
  export declare const idBase62Schema: StringSchema<string>;
58
54
  export declare const idBase64Schema: StringSchema<string>;
59
55
  export declare const idBase64UrlSchema: StringSchema<string>;
60
- /**
61
- * `_` should NOT be allowed to be able to use slug-ids as part of natural ids with `_` separator.
62
- */
63
- export declare const SLUG_REGEX: RegExp;
64
56
  /**
65
57
  * "Slug" - a valid URL, filename, etc.
66
58
  */
@@ -89,7 +81,6 @@ export declare const emailSchema: StringSchema<string>;
89
81
  /**
90
82
  * Pattern is simplified for our use, it's not a canonical SemVer.
91
83
  */
92
- export declare const SEM_VER_REGEX: RegExp;
93
84
  export declare const semVerSchema: StringSchema<string>;
94
85
  export declare const userAgentSchema: StringSchema<string>;
95
86
  export declare const utcOffsetSchema: NumberSchema<number>;
@@ -1,4 +1,6 @@
1
1
  import { _numberEnumKeys, _numberEnumValues, _stringEnumKeys, _stringEnumValues, } from '@naturalcycles/js-lib';
2
+ import { JWT_REGEX, } from '@naturalcycles/js-lib/types';
3
+ import { BASE62_REGEX, BASE64_REGEX, BASE64URL_REGEX, ID_REGEX, MAC_ADDRESS_REGEX, SEMVER_REGEX, SLUG_REGEX, } from '../regexes.js';
2
4
  import { Joi } from './joi.extensions.js';
3
5
  export const booleanSchema = Joi.boolean();
4
6
  export const booleanDefaultToFalseSchema = Joi.boolean().default(false);
@@ -52,27 +54,19 @@ export function oneOfSchema(...schemas) {
52
54
  return Joi.alternatives(schemas);
53
55
  }
54
56
  export const anySchema = Joi.any();
55
- export const BASE62_REGEX = /^[a-zA-Z0-9]+$/;
56
- export const BASE64_REGEX = /^[a-zA-Z0-9+/]+={0,2}$/;
57
- export const BASE64URL_REGEX = /^[a-zA-Z0-9_-]+$/;
58
57
  export const base62Schema = stringSchema.regex(BASE62_REGEX);
59
58
  export const base64Schema = stringSchema.regex(BASE64_REGEX);
60
59
  export const base64UrlSchema = stringSchema.regex(BASE64URL_REGEX);
61
- export const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/;
62
60
  export const jwtSchema = stringSchema.regex(JWT_REGEX);
63
61
  // 1g498efj5sder3324zer
64
62
  /**
65
63
  * [a-zA-Z0-9_]*
66
64
  * 6-64 length
67
65
  */
68
- export const idSchema = stringSchema.regex(/^[a-zA-Z0-9_]{6,64}$/);
66
+ export const idSchema = stringSchema.regex(ID_REGEX);
69
67
  export const idBase62Schema = base62Schema.min(8).max(64);
70
68
  export const idBase64Schema = base64Schema.min(8).max(64);
71
69
  export const idBase64UrlSchema = base64UrlSchema.min(8).max(64);
72
- /**
73
- * `_` should NOT be allowed to be able to use slug-ids as part of natural ids with `_` separator.
74
- */
75
- export const SLUG_REGEX = /^[a-z0-9-]*$/;
76
70
  /**
77
71
  * "Slug" - a valid URL, filename, etc.
78
72
  */
@@ -116,8 +110,7 @@ export const emailSchema = stringSchema.email().lowercase();
116
110
  /**
117
111
  * Pattern is simplified for our use, it's not a canonical SemVer.
118
112
  */
119
- export const SEM_VER_REGEX = /^[0-9]+\.[0-9]+\.[0-9]+$/;
120
- export const semVerSchema = stringSchema.regex(SEM_VER_REGEX);
113
+ export const semVerSchema = stringSchema.regex(SEMVER_REGEX);
121
114
  // todo: .error(() => 'should be SemVer')
122
115
  export const userAgentSchema = stringSchema
123
116
  .min(5) // I've seen UA of `Android` (7 characters)
@@ -138,5 +131,5 @@ export const baseDBEntitySchema = objectSchema({
138
131
  created: unixTimestamp2000Schema.optional(),
139
132
  updated: unixTimestamp2000Schema.optional(),
140
133
  });
141
- export const macAddressSchema = stringSchema.regex(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/);
134
+ export const macAddressSchema = stringSchema.regex(MAC_ADDRESS_REGEX);
142
135
  export const uuidSchema = stringSchema.uuid();
@@ -0,0 +1,19 @@
1
+ export declare const BASE62_REGEX: RegExp;
2
+ export declare const BASE64_REGEX: RegExp;
3
+ export declare const BASE64URL_REGEX: RegExp;
4
+ export declare const COUNTRY_CODE_REGEX: RegExp;
5
+ export declare const CURRENCY_REGEX: RegExp;
6
+ /**
7
+ * @deprecated
8
+ * Avoid using blanket regex for a concept so ambiguous as "ID".
9
+ * We should always define what kind of an ID we talk about: MongoDB ID, Base64 ID etc.
10
+ *
11
+ * We keep this regex here, because JOI shared schemas has been exporting this check.
12
+ */
13
+ export declare const ID_REGEX: RegExp;
14
+ export declare const IPV4_REGEX: RegExp;
15
+ export declare const IPV6_REGEX: RegExp;
16
+ export declare const LANGUAGE_TAG_REGEX: RegExp;
17
+ export declare const MAC_ADDRESS_REGEX: RegExp;
18
+ export declare const SEMVER_REGEX: RegExp;
19
+ export declare const SLUG_REGEX: RegExp;
@@ -0,0 +1,23 @@
1
+ export const BASE62_REGEX = /^[a-zA-Z0-9]+$/;
2
+ export const BASE64_REGEX = /^[a-zA-Z0-9+/]+={0,2}$/;
3
+ export const BASE64URL_REGEX = /^[a-zA-Z0-9_-]+$/;
4
+ export const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/;
5
+ export const CURRENCY_REGEX = /^[A-Z]{3}$/;
6
+ /**
7
+ * @deprecated
8
+ * Avoid using blanket regex for a concept so ambiguous as "ID".
9
+ * We should always define what kind of an ID we talk about: MongoDB ID, Base64 ID etc.
10
+ *
11
+ * We keep this regex here, because JOI shared schemas has been exporting this check.
12
+ */
13
+ export const ID_REGEX = /^[a-zA-Z0-9_]{6,64}$/;
14
+ export const IPV4_REGEX =
15
+ // from `ajv-formats`
16
+ /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
17
+ // from `ajv-formats`
18
+ export const IPV6_REGEX = /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i;
19
+ // IETF language tag (https://en.wikipedia.org/wiki/IETF_language_tag)
20
+ export const LANGUAGE_TAG_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/;
21
+ export const MAC_ADDRESS_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
22
+ export const SEMVER_REGEX = /^[0-9]+\.[0-9]+\.[0-9]+$/;
23
+ export const SLUG_REGEX = /^[a-z0-9-]+$/;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.50.1",
4
+ "version": "15.51.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -26,6 +26,16 @@ import {
26
26
  type UnixTimestamp,
27
27
  type UnixTimestampMillis,
28
28
  } from '@naturalcycles/js-lib/types'
29
+ import {
30
+ BASE64URL_REGEX,
31
+ COUNTRY_CODE_REGEX,
32
+ CURRENCY_REGEX,
33
+ IPV4_REGEX,
34
+ IPV6_REGEX,
35
+ LANGUAGE_TAG_REGEX,
36
+ SEMVER_REGEX,
37
+ SLUG_REGEX,
38
+ } from '../regexes.js'
29
39
  import { TIMEZONES } from '../timezones.js'
30
40
  import {
31
41
  isEveryItemNumber,
@@ -361,48 +371,31 @@ export class JsonSchemaStringBuilder<
361
371
  }
362
372
 
363
373
  ipv4(): this {
364
- // from `ajv-formats`
365
- const regex =
366
- /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/
367
- return this.regex(regex, { msg: 'is not a valid IPv4 format' })
374
+ return this.regex(IPV4_REGEX, { msg: 'is not a valid IPv4 format' })
368
375
  }
369
376
 
370
377
  ipv6(): this {
371
- // from `ajv-formats`
372
- const regex =
373
- /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i
374
- return this.regex(regex, { msg: 'is not a valid IPv6 format' })
375
- }
376
-
377
- id(): this {
378
- const regex = /^[a-z0-9_]{6,64}$/
379
- return this.regex(regex, { msg: 'is not a valid ID format' })
378
+ return this.regex(IPV6_REGEX, { msg: 'is not a valid IPv6 format' })
380
379
  }
381
380
 
382
381
  slug(): this {
383
- const regex = /^[a-z0-9-]+$/
384
- return this.regex(regex, { msg: 'is not a valid slug format' })
382
+ return this.regex(SLUG_REGEX, { msg: 'is not a valid slug format' })
385
383
  }
386
384
 
387
385
  semVer(): this {
388
- const regex = /^[0-9]+\.[0-9]+\.[0-9]+$/
389
- return this.regex(regex, { msg: 'is not a valid semver format' })
386
+ return this.regex(SEMVER_REGEX, { msg: 'is not a valid semver format' })
390
387
  }
391
388
 
392
389
  languageTag(): this {
393
- // IETF language tag (https://en.wikipedia.org/wiki/IETF_language_tag)
394
- const regex = /^[a-z]{2}(-[A-Z]{2})?$/
395
- return this.regex(regex, { msg: 'is not a valid language format' })
390
+ return this.regex(LANGUAGE_TAG_REGEX, { msg: 'is not a valid language format' })
396
391
  }
397
392
 
398
393
  countryCode(): this {
399
- const regex = /^[A-Z]{2}$/
400
- return this.regex(regex, { msg: 'is not a valid country code format' })
394
+ return this.regex(COUNTRY_CODE_REGEX, { msg: 'is not a valid country code format' })
401
395
  }
402
396
 
403
397
  currency(): this {
404
- const regex = /^[A-Z]{3}$/
405
- return this.regex(regex, { msg: 'is not a valid currency format' })
398
+ return this.regex(CURRENCY_REGEX, { msg: 'is not a valid currency format' })
406
399
  }
407
400
 
408
401
  /**
@@ -415,6 +408,12 @@ export class JsonSchemaStringBuilder<
415
408
  // UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
416
409
  return j.enum(TIMEZONES, { msg: 'is an invalid IANA timezone' }).branded<IANATimezone>()
417
410
  }
411
+
412
+ base64Url(): this {
413
+ return this.regex(BASE64URL_REGEX, {
414
+ msg: 'contains characters not allowed in Base64 URL characterset',
415
+ })
416
+ }
418
417
  }
419
418
 
420
419
  export interface JsonSchemaStringEmailOptions {
@@ -4,19 +4,29 @@ import {
4
4
  _stringEnumKeys,
5
5
  _stringEnumValues,
6
6
  } from '@naturalcycles/js-lib'
7
- import type {
8
- AnyObject,
9
- BaseDBEntity,
10
- IANATimezone,
11
- IsoDate,
12
- IsoDateTime,
13
- NumberEnum,
14
- StringEnum,
15
- StringMap,
16
- UnixTimestamp,
17
- UnixTimestampMillis,
7
+ import {
8
+ type AnyObject,
9
+ type BaseDBEntity,
10
+ type IANATimezone,
11
+ type IsoDate,
12
+ type IsoDateTime,
13
+ JWT_REGEX,
14
+ type NumberEnum,
15
+ type StringEnum,
16
+ type StringMap,
17
+ type UnixTimestamp,
18
+ type UnixTimestampMillis,
18
19
  } from '@naturalcycles/js-lib/types'
19
20
  import type { AlternativesSchema, AnySchema, ArraySchema, ObjectSchema } from 'joi'
21
+ import {
22
+ BASE62_REGEX,
23
+ BASE64_REGEX,
24
+ BASE64URL_REGEX,
25
+ ID_REGEX,
26
+ MAC_ADDRESS_REGEX,
27
+ SEMVER_REGEX,
28
+ SLUG_REGEX,
29
+ } from '../regexes.js'
20
30
  import { Joi } from './joi.extensions.js'
21
31
  import type { NumberSchema } from './number.extensions.js'
22
32
  import type { StringSchema } from './string.extensions.js'
@@ -100,15 +110,10 @@ export function oneOfSchema<T = any>(...schemas: AnySchema[]): AlternativesSchem
100
110
  }
101
111
 
102
112
  export const anySchema = Joi.any()
103
-
104
- export const BASE62_REGEX = /^[a-zA-Z0-9]+$/
105
- export const BASE64_REGEX = /^[a-zA-Z0-9+/]+={0,2}$/
106
- export const BASE64URL_REGEX = /^[a-zA-Z0-9_-]+$/
107
113
  export const base62Schema = stringSchema.regex(BASE62_REGEX)
108
114
  export const base64Schema = stringSchema.regex(BASE64_REGEX)
109
115
  export const base64UrlSchema = stringSchema.regex(BASE64URL_REGEX)
110
116
 
111
- export const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/
112
117
  export const jwtSchema = stringSchema.regex(JWT_REGEX)
113
118
 
114
119
  // 1g498efj5sder3324zer
@@ -116,17 +121,12 @@ export const jwtSchema = stringSchema.regex(JWT_REGEX)
116
121
  * [a-zA-Z0-9_]*
117
122
  * 6-64 length
118
123
  */
119
- export const idSchema = stringSchema.regex(/^[a-zA-Z0-9_]{6,64}$/)
124
+ export const idSchema = stringSchema.regex(ID_REGEX)
120
125
 
121
126
  export const idBase62Schema = base62Schema.min(8).max(64)
122
127
  export const idBase64Schema = base64Schema.min(8).max(64)
123
128
  export const idBase64UrlSchema = base64UrlSchema.min(8).max(64)
124
129
 
125
- /**
126
- * `_` should NOT be allowed to be able to use slug-ids as part of natural ids with `_` separator.
127
- */
128
- export const SLUG_REGEX = /^[a-z0-9-]*$/
129
-
130
130
  /**
131
131
  * "Slug" - a valid URL, filename, etc.
132
132
  */
@@ -175,8 +175,7 @@ export const emailSchema = stringSchema.email().lowercase()
175
175
  /**
176
176
  * Pattern is simplified for our use, it's not a canonical SemVer.
177
177
  */
178
- export const SEM_VER_REGEX = /^[0-9]+\.[0-9]+\.[0-9]+$/
179
- export const semVerSchema = stringSchema.regex(SEM_VER_REGEX)
178
+ export const semVerSchema = stringSchema.regex(SEMVER_REGEX)
180
179
  // todo: .error(() => 'should be SemVer')
181
180
 
182
181
  export const userAgentSchema = stringSchema
@@ -203,6 +202,6 @@ export const baseDBEntitySchema: ObjectSchema<BaseDBEntity> = objectSchema<BaseD
203
202
  updated: unixTimestamp2000Schema.optional(),
204
203
  })
205
204
 
206
- export const macAddressSchema = stringSchema.regex(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/)
205
+ export const macAddressSchema = stringSchema.regex(MAC_ADDRESS_REGEX)
207
206
 
208
207
  export const uuidSchema = stringSchema.uuid()
@@ -0,0 +1,24 @@
1
+ export const BASE62_REGEX = /^[a-zA-Z0-9]+$/
2
+ export const BASE64_REGEX = /^[a-zA-Z0-9+/]+={0,2}$/
3
+ export const BASE64URL_REGEX = /^[a-zA-Z0-9_-]+$/
4
+ export const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/
5
+ export const CURRENCY_REGEX = /^[A-Z]{3}$/
6
+ /**
7
+ * @deprecated
8
+ * Avoid using blanket regex for a concept so ambiguous as "ID".
9
+ * We should always define what kind of an ID we talk about: MongoDB ID, Base64 ID etc.
10
+ *
11
+ * We keep this regex here, because JOI shared schemas has been exporting this check.
12
+ */
13
+ export const ID_REGEX = /^[a-zA-Z0-9_]{6,64}$/
14
+ export const IPV4_REGEX =
15
+ // from `ajv-formats`
16
+ /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/
17
+ // from `ajv-formats`
18
+ export const IPV6_REGEX =
19
+ /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i
20
+ // IETF language tag (https://en.wikipedia.org/wiki/IETF_language_tag)
21
+ export const LANGUAGE_TAG_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/
22
+ export const MAC_ADDRESS_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/
23
+ export const SEMVER_REGEX = /^[0-9]+\.[0-9]+\.[0-9]+$/
24
+ export const SLUG_REGEX = /^[a-z0-9-]+$/