@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.
- package/dist/validation/ajv/jsonSchemaBuilder.d.ts +1 -1
- package/dist/validation/ajv/jsonSchemaBuilder.js +13 -21
- package/dist/validation/joi/joi.shared.schemas.d.ts +1 -10
- package/dist/validation/joi/joi.shared.schemas.js +5 -12
- package/dist/validation/regexes.d.ts +19 -0
- package/dist/validation/regexes.js +23 -0
- package/package.json +1 -1
- package/src/validation/ajv/jsonSchemaBuilder.ts +23 -24
- package/src/validation/joi/joi.shared.schemas.ts +24 -25
- package/src/validation/regexes.ts +24 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
8
|
-
AnyObject,
|
|
9
|
-
BaseDBEntity,
|
|
10
|
-
IANATimezone,
|
|
11
|
-
IsoDate,
|
|
12
|
-
IsoDateTime,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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-]+$/
|