@naturalcycles/nodejs-lib 15.37.2 → 15.39.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.
@@ -0,0 +1,225 @@
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';
3
+ export declare const j: {
4
+ string(): JsonSchemaStringBuilder<string, string, false>;
5
+ number(): JsonSchemaNumberBuilder<number, number, false>;
6
+ boolean(): JsonSchemaBooleanBuilder<boolean, boolean, false>;
7
+ object<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(props: P): JsonSchemaObjectBuilder<P, false>;
8
+ array<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaArrayBuilder<IN, OUT, Opt>;
9
+ set<IN, OUT, Opt>(itemSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>): JsonSchemaSet2Builder<IN, OUT, Opt>;
10
+ buffer(): JsonSchemaBufferBuilder;
11
+ 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>;
12
+ oneOf<B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[], IN = BuilderInUnion<B>, OUT = BuilderOutUnion<B>>(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false>;
13
+ };
14
+ export declare class JsonSchemaAnyBuilder<IN, OUT, Opt> {
15
+ protected schema: JsonSchema;
16
+ constructor(schema: JsonSchema);
17
+ protected setErrorMessage(ruleName: string, errorMessage: string | undefined): void;
18
+ /**
19
+ * A helper function that takes a type parameter and compares it with the type inferred from the schema.
20
+ *
21
+ * When the type inferred from the schema differs from the passed-in type,
22
+ * the schema becomes unusable, by turning its type into `never`.
23
+ *
24
+ * ```ts
25
+ * const schemaGood = j.string().isOfType<string>() // ✅
26
+ *
27
+ * const schemaBad = j.string().isOfType<number>() // ❌
28
+ * schemaBad.build() // TypeError: property "build" does not exist on type "never"
29
+ * ```
30
+ */
31
+ isOfType<ExpectedType>(): ExactMatch<ExpectedType, OUT> extends true ? this : never;
32
+ getSchema(): JsonSchema;
33
+ $schema($schema: string): this;
34
+ $schemaDraft7(): this;
35
+ $id($id: string): this;
36
+ title(title: string): this;
37
+ description(description: string): this;
38
+ deprecated(deprecated?: boolean): this;
39
+ type(type: string): this;
40
+ default(v: any): this;
41
+ instanceof(of: string): this;
42
+ optional(): JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true>;
43
+ nullable(): JsonSchemaAnyBuilder<IN | null, OUT | null, Opt>;
44
+ /**
45
+ * Produces a "clean schema object" without methods.
46
+ * Same as if it would be JSON.stringified.
47
+ */
48
+ build(): JsonSchema<IN, OUT>;
49
+ clone(): JsonSchemaAnyBuilder<IN, OUT, Opt>;
50
+ /**
51
+ * @experimental
52
+ */
53
+ in: IN;
54
+ out: OUT;
55
+ }
56
+ export declare class JsonSchemaStringBuilder<IN extends string = string, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
57
+ constructor();
58
+ regex(pattern: RegExp, opt?: JsonBuilderRuleOpt): this;
59
+ pattern(pattern: string, opt?: JsonBuilderRuleOpt): this;
60
+ min(minLength: number): this;
61
+ max(maxLength: number): this;
62
+ length(minLength: number, maxLength: number): this;
63
+ email(opt?: Partial<JsonSchemaStringEmailOptions>): this;
64
+ trim(): this;
65
+ toLowerCase(): this;
66
+ toUpperCase(): this;
67
+ truncate(toLength: number): this;
68
+ branded<B extends string>(): JsonSchemaStringBuilder<B, B, Opt>;
69
+ isoDate(): JsonSchemaStringBuilder<IsoDate | IN, IsoDate, Opt>;
70
+ isoDateTime(): JsonSchemaStringBuilder<IsoDateTime | IN, IsoDateTime, Opt>;
71
+ /**
72
+ * Validates the string format to be JWT.
73
+ * Expects the JWT to be signed!
74
+ */
75
+ jwt(): this;
76
+ url(): this;
77
+ ipv4(): this;
78
+ ipv6(): this;
79
+ id(): this;
80
+ slug(): this;
81
+ semVer(): this;
82
+ languageTag(): this;
83
+ countryCode(): this;
84
+ currency(): this;
85
+ }
86
+ export interface JsonSchemaStringEmailOptions {
87
+ checkTLD: boolean;
88
+ }
89
+ export declare class JsonSchemaNumberBuilder<IN extends number = number, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
90
+ constructor();
91
+ integer(): this;
92
+ branded<B extends number>(): JsonSchemaNumberBuilder<B, B, Opt>;
93
+ multipleOf(multipleOf: number): this;
94
+ min(minimum: number): this;
95
+ exclusiveMin(exclusiveMinimum: number): this;
96
+ max(maximum: number): this;
97
+ exclusiveMax(exclusiveMaximum: number): this;
98
+ /**
99
+ * Both ranges are inclusive.
100
+ */
101
+ range(minimum: number, maximum: number): this;
102
+ int32(): this;
103
+ int64(): this;
104
+ float(): this;
105
+ double(): this;
106
+ unixTimestamp(): JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, Opt>;
107
+ unixTimestamp2000(): JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, Opt>;
108
+ unixTimestampMillis(): JsonSchemaNumberBuilder<UnixTimestampMillis, UnixTimestampMillis, Opt>;
109
+ unixTimestamp2000Millis(): JsonSchemaNumberBuilder<UnixTimestampMillis, UnixTimestampMillis, Opt>;
110
+ utcOffset(): this;
111
+ utcOffsetHour(): this;
112
+ }
113
+ export declare class JsonSchemaBooleanBuilder<IN extends boolean = boolean, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
114
+ constructor();
115
+ }
116
+ export declare class JsonSchemaObjectBuilder<PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>, Opt extends boolean = false> extends JsonSchemaAnyBuilder<Expand<{
117
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
118
+ } & {
119
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JsonSchemaAnyBuilder<infer IN, any, any> ? IN : never;
120
+ }>, Expand<{
121
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? never : K : never]: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
122
+ } & {
123
+ [K in keyof PROPS as PROPS[K] extends JsonSchemaAnyBuilder<any, any, infer IsOpt> ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JsonSchemaAnyBuilder<any, infer OUT, any> ? OUT : never;
124
+ }>, Opt> {
125
+ constructor(props?: PROPS);
126
+ addProperties(props: PROPS): this;
127
+ extend<NEW_PROPS extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(props: NEW_PROPS): JsonSchemaObjectBuilder<{
128
+ [K in keyof PROPS | keyof NEW_PROPS]: K extends keyof NEW_PROPS ? NEW_PROPS[K] : K extends keyof PROPS ? PROPS[K] : never;
129
+ }, Opt>;
130
+ /**
131
+ * Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
132
+ */
133
+ dbEntity(): JsonSchemaObjectBuilder<{ [K in keyof PROPS | "id" | "created" | "updated"]: K extends "id" | "created" | "updated" ? {
134
+ id: JsonSchemaStringBuilder<string, string, false>;
135
+ created: JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, false>;
136
+ updated: JsonSchemaNumberBuilder<UnixTimestamp, UnixTimestamp, false>;
137
+ }[K] : K extends keyof PROPS ? PROPS[K] : never; }, Opt>;
138
+ }
139
+ export declare class JsonSchemaArrayBuilder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<IN[], OUT[], Opt> {
140
+ constructor(itemsSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>);
141
+ min(minItems: number): this;
142
+ max(maxItems: number): this;
143
+ unique(uniqueItems: number): this;
144
+ }
145
+ export declare class JsonSchemaSet2Builder<IN, OUT, Opt> extends JsonSchemaAnyBuilder<Iterable<IN>, Set2<OUT>, Opt> {
146
+ constructor(itemsSchema: JsonSchemaAnyBuilder<IN, OUT, Opt>);
147
+ min(minItems: number): this;
148
+ max(maxItems: number): this;
149
+ }
150
+ export declare class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder<string | any[] | ArrayBuffer | Buffer, Buffer, false> {
151
+ constructor();
152
+ }
153
+ export declare class JsonSchemaEnumBuilder<IN extends string | number | boolean | null, OUT extends IN = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
154
+ constructor(enumValues: readonly IN[]);
155
+ }
156
+ export interface JsonSchema<IN = unknown, OUT = IN> {
157
+ readonly in?: IN;
158
+ readonly out?: OUT;
159
+ $schema?: AnyObject;
160
+ $id?: string;
161
+ title?: string;
162
+ description?: string;
163
+ deprecated?: boolean;
164
+ readOnly?: boolean;
165
+ writeOnly?: boolean;
166
+ type?: string | string[];
167
+ items?: JsonSchema;
168
+ properties?: {
169
+ [K in keyof IN & keyof OUT]: JsonSchema<IN[K], OUT[K]>;
170
+ };
171
+ required?: string[];
172
+ additionalProperties?: boolean;
173
+ minProperties?: number;
174
+ maxProperties?: number;
175
+ default?: IN;
176
+ if?: JsonSchema;
177
+ then?: JsonSchema;
178
+ else?: JsonSchema;
179
+ anyOf?: JsonSchema[];
180
+ oneOf?: JsonSchema[];
181
+ /**
182
+ * This is a temporary "intermediate AST" field that is used inside the parser.
183
+ * In the final schema this field will NOT be present.
184
+ */
185
+ optionalField?: true;
186
+ pattern?: string;
187
+ minLength?: number;
188
+ maxLength?: number;
189
+ format?: string;
190
+ contentMediaType?: string;
191
+ contentEncoding?: string;
192
+ multipleOf?: number;
193
+ minimum?: number;
194
+ exclusiveMinimum?: number;
195
+ maximum?: number;
196
+ exclusiveMaximum?: number;
197
+ enum?: any;
198
+ Set2?: JsonSchema;
199
+ Buffer?: true;
200
+ IsoDate?: true;
201
+ IsoDateTime?: true;
202
+ instanceof?: string | string[];
203
+ transform?: {
204
+ trim?: true;
205
+ toLowerCase?: true;
206
+ toUpperCase?: true;
207
+ truncate?: number;
208
+ };
209
+ errorMessages?: StringMap<string>;
210
+ hasIsOfTypeCheck?: boolean;
211
+ }
212
+ type Expand<T> = T extends infer O ? {
213
+ [K in keyof O]: O[K];
214
+ } : never;
215
+ type ExactMatch<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
216
+ type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
217
+ [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never;
218
+ }[number];
219
+ type BuilderInUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
220
+ [K in keyof B]: B[K] extends JsonSchemaAnyBuilder<infer I, any, any> ? I : never;
221
+ }[number];
222
+ interface JsonBuilderRuleOpt {
223
+ msg?: string;
224
+ }
225
+ export {};
@@ -0,0 +1,453 @@
1
+ /* eslint-disable id-denylist */
2
+ // oxlint-disable max-lines
3
+ import { _isUndefined, _numberEnumValues, _stringEnumValues, getEnumType, } from '@naturalcycles/js-lib';
4
+ import { _uniq } from '@naturalcycles/js-lib/array';
5
+ import { _assert } from '@naturalcycles/js-lib/error';
6
+ import { JSON_SCHEMA_ORDER, mergeJsonSchemaObjects } from '@naturalcycles/js-lib/json-schema';
7
+ import { _deepCopy, _sortObject } from '@naturalcycles/js-lib/object';
8
+ import { JWT_REGEX, } from '@naturalcycles/js-lib/types';
9
+ export const j = {
10
+ string() {
11
+ return new JsonSchemaStringBuilder();
12
+ },
13
+ number() {
14
+ return new JsonSchemaNumberBuilder();
15
+ },
16
+ boolean() {
17
+ return new JsonSchemaBooleanBuilder();
18
+ },
19
+ object(props) {
20
+ return new JsonSchemaObjectBuilder(props);
21
+ },
22
+ array(itemSchema) {
23
+ return new JsonSchemaArrayBuilder(itemSchema);
24
+ },
25
+ set(itemSchema) {
26
+ return new JsonSchemaSet2Builder(itemSchema);
27
+ },
28
+ buffer() {
29
+ return new JsonSchemaBufferBuilder();
30
+ },
31
+ enum(input) {
32
+ let enumValues;
33
+ if (Array.isArray(input)) {
34
+ enumValues = input;
35
+ }
36
+ else if (typeof input === 'object') {
37
+ const enumType = getEnumType(input);
38
+ if (enumType === 'NumberEnum') {
39
+ enumValues = _numberEnumValues(input);
40
+ }
41
+ else if (enumType === 'StringEnum') {
42
+ enumValues = _stringEnumValues(input);
43
+ }
44
+ }
45
+ _assert(enumValues, 'Unsupported enum input');
46
+ return new JsonSchemaEnumBuilder(enumValues);
47
+ },
48
+ oneOf(items) {
49
+ const schemas = items.map(b => b.build());
50
+ return new JsonSchemaAnyBuilder({
51
+ oneOf: schemas,
52
+ });
53
+ },
54
+ };
55
+ const TS_2500 = 16725225600; // 2500-01-01
56
+ const TS_2500_MILLIS = TS_2500 * 1000;
57
+ const TS_2000 = 946684800; // 2000-01-01
58
+ const TS_2000_MILLIS = TS_2000 * 1000;
59
+ /*
60
+ Notes for future reference
61
+
62
+ Q: Why do we need `Opt` - when `IN` and `OUT` already carries the `| undefined`?
63
+ A: Because of objects. Without `Opt`, an optional field would be inferred as `{ foo: string | undefined }`,
64
+ which means that the `foo` property would be mandatory, it's just that its value can be `undefined` as well.
65
+ With `Opt`, we can infer it as `{ foo?: string | undefined }`.
66
+ */
67
+ export class JsonSchemaAnyBuilder {
68
+ schema;
69
+ constructor(schema) {
70
+ this.schema = schema;
71
+ }
72
+ setErrorMessage(ruleName, errorMessage) {
73
+ if (_isUndefined(errorMessage))
74
+ return;
75
+ this.schema.errorMessages ||= {};
76
+ this.schema.errorMessages[ruleName] = errorMessage;
77
+ }
78
+ /**
79
+ * A helper function that takes a type parameter and compares it with the type inferred from the schema.
80
+ *
81
+ * When the type inferred from the schema differs from the passed-in type,
82
+ * the schema becomes unusable, by turning its type into `never`.
83
+ *
84
+ * ```ts
85
+ * const schemaGood = j.string().isOfType<string>() // ✅
86
+ *
87
+ * const schemaBad = j.string().isOfType<number>() // ❌
88
+ * schemaBad.build() // TypeError: property "build" does not exist on type "never"
89
+ * ```
90
+ */
91
+ isOfType() {
92
+ Object.assign(this.schema, { hasIsOfTypeCheck: true });
93
+ return this;
94
+ }
95
+ getSchema() {
96
+ return this.schema;
97
+ }
98
+ $schema($schema) {
99
+ Object.assign(this.schema, { $schema });
100
+ return this;
101
+ }
102
+ $schemaDraft7() {
103
+ this.$schema('http://json-schema.org/draft-07/schema#');
104
+ return this;
105
+ }
106
+ $id($id) {
107
+ Object.assign(this.schema, { $id });
108
+ return this;
109
+ }
110
+ title(title) {
111
+ Object.assign(this.schema, { title });
112
+ return this;
113
+ }
114
+ description(description) {
115
+ Object.assign(this.schema, { description });
116
+ return this;
117
+ }
118
+ deprecated(deprecated = true) {
119
+ Object.assign(this.schema, { deprecated });
120
+ return this;
121
+ }
122
+ type(type) {
123
+ Object.assign(this.schema, { type });
124
+ return this;
125
+ }
126
+ default(v) {
127
+ Object.assign(this.schema, { default: v });
128
+ return this;
129
+ }
130
+ instanceof(of) {
131
+ Object.assign(this.schema, { type: 'object', instanceof: of });
132
+ return this;
133
+ }
134
+ optional() {
135
+ this.schema.optionalField = true;
136
+ return this;
137
+ }
138
+ nullable() {
139
+ return new JsonSchemaAnyBuilder({
140
+ anyOf: [this.build(), { type: 'null' }],
141
+ });
142
+ }
143
+ /**
144
+ * Produces a "clean schema object" without methods.
145
+ * Same as if it would be JSON.stringified.
146
+ */
147
+ build() {
148
+ return _sortObject(JSON.parse(JSON.stringify(this.schema)), JSON_SCHEMA_ORDER);
149
+ }
150
+ clone() {
151
+ return new JsonSchemaAnyBuilder(_deepCopy(this.schema));
152
+ }
153
+ /**
154
+ * @experimental
155
+ */
156
+ in;
157
+ out;
158
+ }
159
+ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
160
+ constructor() {
161
+ super({
162
+ type: 'string',
163
+ });
164
+ }
165
+ regex(pattern, opt) {
166
+ return this.pattern(pattern.source, opt);
167
+ }
168
+ pattern(pattern, opt) {
169
+ if (opt?.msg)
170
+ this.setErrorMessage('pattern', opt.msg);
171
+ Object.assign(this.schema, { pattern });
172
+ return this;
173
+ }
174
+ min(minLength) {
175
+ Object.assign(this.schema, { minLength });
176
+ return this;
177
+ }
178
+ max(maxLength) {
179
+ Object.assign(this.schema, { maxLength });
180
+ return this;
181
+ }
182
+ length(minLength, maxLength) {
183
+ Object.assign(this.schema, { minLength, maxLength });
184
+ return this;
185
+ }
186
+ email(opt) {
187
+ const defaultOptions = { checkTLD: true };
188
+ Object.assign(this.schema, { email: { ...defaultOptions, ...opt } });
189
+ // from `ajv-formats`
190
+ const regex = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
191
+ return this.regex(regex, { msg: 'is not a valid email address' }).trim().toLowerCase();
192
+ }
193
+ trim() {
194
+ Object.assign(this.schema, { transform: { ...this.schema.transform, trim: true } });
195
+ return this;
196
+ }
197
+ toLowerCase() {
198
+ Object.assign(this.schema, { transform: { ...this.schema.transform, toLowerCase: true } });
199
+ return this;
200
+ }
201
+ toUpperCase() {
202
+ Object.assign(this.schema, { transform: { ...this.schema.transform, toUpperCase: true } });
203
+ return this;
204
+ }
205
+ truncate(toLength) {
206
+ Object.assign(this.schema, { transform: { ...this.schema.transform, truncate: toLength } });
207
+ return this;
208
+ }
209
+ branded() {
210
+ return this;
211
+ }
212
+ isoDate() {
213
+ Object.assign(this.schema, { IsoDate: true });
214
+ return this.branded();
215
+ }
216
+ isoDateTime() {
217
+ Object.assign(this.schema, { IsoDateTime: true });
218
+ return this.branded();
219
+ }
220
+ /**
221
+ * Validates the string format to be JWT.
222
+ * Expects the JWT to be signed!
223
+ */
224
+ jwt() {
225
+ return this.regex(JWT_REGEX, { msg: 'is not a valid JWT format' });
226
+ }
227
+ url() {
228
+ // from `ajv-formats`
229
+ 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;
230
+ return this.regex(regex, { msg: 'is not a valid URL format' });
231
+ }
232
+ ipv4() {
233
+ // from `ajv-formats`
234
+ 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)$/;
235
+ return this.regex(regex, { msg: 'is not a valid IPv4 format' });
236
+ }
237
+ ipv6() {
238
+ // from `ajv-formats`
239
+ 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;
240
+ return this.regex(regex, { msg: 'is not a valid IPv6 format' });
241
+ }
242
+ id() {
243
+ const regex = /^[a-z0-9_]{6,64}$/;
244
+ return this.regex(regex, { msg: 'is not a valid ID format' });
245
+ }
246
+ slug() {
247
+ const regex = /^[a-z0-9-]+$/;
248
+ return this.regex(regex, { msg: 'is not a valid slug format' });
249
+ }
250
+ semVer() {
251
+ const regex = /^[0-9]+\.[0-9]+\.[0-9]+$/;
252
+ return this.regex(regex, { msg: 'is not a valid semver format' });
253
+ }
254
+ languageTag() {
255
+ // IETF language tag (https://en.wikipedia.org/wiki/IETF_language_tag)
256
+ const regex = /^[a-z]{2}(-[A-Z]{2})?$/;
257
+ return this.regex(regex, { msg: 'is not a valid language format' });
258
+ }
259
+ countryCode() {
260
+ const regex = /^[A-Z]{2}$/;
261
+ return this.regex(regex, { msg: 'is not a valid country code format' });
262
+ }
263
+ currency() {
264
+ const regex = /^[A-Z]{3}$/;
265
+ return this.regex(regex, { msg: 'is not a valid currency format' });
266
+ }
267
+ }
268
+ export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder {
269
+ constructor() {
270
+ super({
271
+ type: 'number',
272
+ });
273
+ }
274
+ integer() {
275
+ Object.assign(this.schema, { type: 'integer' });
276
+ return this;
277
+ }
278
+ branded() {
279
+ return this;
280
+ }
281
+ multipleOf(multipleOf) {
282
+ Object.assign(this.schema, { multipleOf });
283
+ return this;
284
+ }
285
+ min(minimum) {
286
+ Object.assign(this.schema, { minimum });
287
+ return this;
288
+ }
289
+ exclusiveMin(exclusiveMinimum) {
290
+ Object.assign(this.schema, { exclusiveMinimum });
291
+ return this;
292
+ }
293
+ max(maximum) {
294
+ Object.assign(this.schema, { maximum });
295
+ return this;
296
+ }
297
+ exclusiveMax(exclusiveMaximum) {
298
+ Object.assign(this.schema, { exclusiveMaximum });
299
+ return this;
300
+ }
301
+ /**
302
+ * Both ranges are inclusive.
303
+ */
304
+ range(minimum, maximum) {
305
+ Object.assign(this.schema, { minimum, maximum });
306
+ return this;
307
+ }
308
+ int32() {
309
+ const MIN_INT32 = -(2 ** 31);
310
+ const MAX_INT32 = 2 ** 31 - 1;
311
+ const currentMin = this.schema.minimum ?? Number.MIN_SAFE_INTEGER;
312
+ const currentMax = this.schema.maximum ?? Number.MAX_SAFE_INTEGER;
313
+ const newMin = Math.max(MIN_INT32, currentMin);
314
+ const newMax = Math.min(MAX_INT32, currentMax);
315
+ return this.integer().min(newMin).max(newMax);
316
+ }
317
+ int64() {
318
+ const currentMin = this.schema.minimum ?? Number.MIN_SAFE_INTEGER;
319
+ const currentMax = this.schema.maximum ?? Number.MAX_SAFE_INTEGER;
320
+ const newMin = Math.max(Number.MIN_SAFE_INTEGER, currentMin);
321
+ const newMax = Math.min(Number.MAX_SAFE_INTEGER, currentMax);
322
+ return this.integer().min(newMin).max(newMax);
323
+ }
324
+ float() {
325
+ return this;
326
+ }
327
+ double() {
328
+ return this;
329
+ }
330
+ unixTimestamp() {
331
+ return this.integer().min(0).max(TS_2500).branded();
332
+ }
333
+ unixTimestamp2000() {
334
+ return this.integer().min(TS_2000).max(TS_2500).branded();
335
+ }
336
+ unixTimestampMillis() {
337
+ return this.integer().min(0).max(TS_2500_MILLIS).branded();
338
+ }
339
+ unixTimestamp2000Millis() {
340
+ return this.integer().min(TS_2000_MILLIS).max(TS_2500_MILLIS).branded();
341
+ }
342
+ utcOffset() {
343
+ return this.integer()
344
+ .multipleOf(15)
345
+ .min(-12 * 60)
346
+ .max(14 * 60);
347
+ }
348
+ utcOffsetHour() {
349
+ return this.integer().min(-12).max(14);
350
+ }
351
+ }
352
+ export class JsonSchemaBooleanBuilder extends JsonSchemaAnyBuilder {
353
+ constructor() {
354
+ super({
355
+ type: 'boolean',
356
+ });
357
+ }
358
+ }
359
+ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
360
+ constructor(props) {
361
+ super({
362
+ type: 'object',
363
+ properties: {},
364
+ required: [],
365
+ additionalProperties: false,
366
+ });
367
+ if (props)
368
+ this.addProperties(props);
369
+ }
370
+ addProperties(props) {
371
+ const properties = {};
372
+ const required = [];
373
+ for (const [key, builder] of Object.entries(props)) {
374
+ const schema = builder.build();
375
+ if (!schema.optionalField) {
376
+ required.push(key);
377
+ }
378
+ else {
379
+ schema.optionalField = undefined;
380
+ }
381
+ properties[key] = schema;
382
+ }
383
+ this.schema.properties = properties;
384
+ this.schema.required = _uniq(required).sort();
385
+ return this;
386
+ }
387
+ extend(props) {
388
+ const newBuilder = new JsonSchemaObjectBuilder();
389
+ Object.assign(newBuilder.schema, _deepCopy(this.schema));
390
+ const incomingSchemaBuilder = new JsonSchemaObjectBuilder(props);
391
+ mergeJsonSchemaObjects(newBuilder.schema, incomingSchemaBuilder.schema);
392
+ return newBuilder;
393
+ }
394
+ /**
395
+ * Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
396
+ */
397
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
398
+ dbEntity() {
399
+ return this.extend({
400
+ id: j.string(),
401
+ created: j.number().unixTimestamp2000(),
402
+ updated: j.number().unixTimestamp2000(),
403
+ });
404
+ }
405
+ }
406
+ export class JsonSchemaArrayBuilder extends JsonSchemaAnyBuilder {
407
+ constructor(itemsSchema) {
408
+ super({
409
+ type: 'array',
410
+ items: itemsSchema.build(),
411
+ });
412
+ }
413
+ min(minItems) {
414
+ Object.assign(this.schema, { minItems });
415
+ return this;
416
+ }
417
+ max(maxItems) {
418
+ Object.assign(this.schema, { maxItems });
419
+ return this;
420
+ }
421
+ unique(uniqueItems) {
422
+ Object.assign(this.schema, { uniqueItems });
423
+ return this;
424
+ }
425
+ }
426
+ export class JsonSchemaSet2Builder extends JsonSchemaAnyBuilder {
427
+ constructor(itemsSchema) {
428
+ super({
429
+ type: ['array', 'object'],
430
+ Set2: itemsSchema.build(),
431
+ });
432
+ }
433
+ min(minItems) {
434
+ Object.assign(this.schema, { minItems });
435
+ return this;
436
+ }
437
+ max(maxItems) {
438
+ Object.assign(this.schema, { maxItems });
439
+ return this;
440
+ }
441
+ }
442
+ export class JsonSchemaBufferBuilder extends JsonSchemaAnyBuilder {
443
+ constructor() {
444
+ super({
445
+ Buffer: true,
446
+ });
447
+ }
448
+ }
449
+ export class JsonSchemaEnumBuilder extends JsonSchemaAnyBuilder {
450
+ constructor(enumValues) {
451
+ super({ enum: enumValues });
452
+ }
453
+ }
@@ -0,0 +1 @@
1
+ export declare const validTLDs: any;