@naturalcycles/nodejs-lib 15.60.0 → 15.61.1
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _lazyValue } from '@naturalcycles/js-lib';
|
|
1
|
+
import { _isBetween, _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';
|
|
@@ -349,6 +349,25 @@ export function createAjv(opt) {
|
|
|
349
349
|
return false;
|
|
350
350
|
},
|
|
351
351
|
});
|
|
352
|
+
ajv.addKeyword({
|
|
353
|
+
keyword: 'IsoMonth',
|
|
354
|
+
type: 'string',
|
|
355
|
+
modifying: false,
|
|
356
|
+
errors: true,
|
|
357
|
+
schemaType: 'object',
|
|
358
|
+
validate: function validate(_opt, data, _schema, ctx) {
|
|
359
|
+
const isValid = isIsoMonthValid(data);
|
|
360
|
+
if (isValid)
|
|
361
|
+
return true;
|
|
362
|
+
validate.errors = [
|
|
363
|
+
{
|
|
364
|
+
instancePath: ctx?.instancePath ?? '',
|
|
365
|
+
message: `is an invalid IsoMonth`,
|
|
366
|
+
},
|
|
367
|
+
];
|
|
368
|
+
return false;
|
|
369
|
+
},
|
|
370
|
+
});
|
|
352
371
|
ajv.addKeyword({
|
|
353
372
|
keyword: 'errorMessages',
|
|
354
373
|
schemaType: 'object',
|
|
@@ -516,3 +535,21 @@ function isIsoTimezoneValid(s) {
|
|
|
516
535
|
return false; // min is -12:00
|
|
517
536
|
return true;
|
|
518
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* This is a performance optimized correct validation
|
|
540
|
+
* for ISO month formatted as "YYYY-MM".
|
|
541
|
+
*/
|
|
542
|
+
function isIsoMonthValid(s) {
|
|
543
|
+
// must be exactly "YYYY-MM"
|
|
544
|
+
if (s.length !== 7)
|
|
545
|
+
return false;
|
|
546
|
+
if (s.charCodeAt(4) !== DASH_CODE)
|
|
547
|
+
return false;
|
|
548
|
+
// fast parse numbers without substrings/Number()
|
|
549
|
+
const year = (s.charCodeAt(0) - ZERO_CODE) * 1000 +
|
|
550
|
+
(s.charCodeAt(1) - ZERO_CODE) * 100 +
|
|
551
|
+
(s.charCodeAt(2) - ZERO_CODE) * 10 +
|
|
552
|
+
(s.charCodeAt(3) - ZERO_CODE);
|
|
553
|
+
const month = (s.charCodeAt(5) - ZERO_CODE) * 10 + (s.charCodeAt(6) - ZERO_CODE);
|
|
554
|
+
return _isBetween(year, 1900, 2500, '[]') && _isBetween(month, 1, 12, '[]');
|
|
555
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Set2 } from '@naturalcycles/js-lib/object';
|
|
2
|
-
import { type AnyObject, type BaseDBEntity, type IANATimezone, type Inclusiveness, type IsoDate, type IsoDateTime, type NumberEnum, type StringEnum, type StringMap, type UnixTimestamp, type UnixTimestampMillis } from '@naturalcycles/js-lib/types';
|
|
2
|
+
import { type AnyObject, type BaseDBEntity, type IANATimezone, type Inclusiveness, type IsoDate, type IsoDateTime, type IsoMonth, 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>;
|
|
@@ -139,6 +139,7 @@ export declare class JsonSchemaStringBuilder<IN extends string | undefined = str
|
|
|
139
139
|
*/
|
|
140
140
|
isoDate(): JsonSchemaIsoDateBuilder;
|
|
141
141
|
isoDateTime(): JsonSchemaStringBuilder<IsoDateTime | IN, IsoDateTime, Opt>;
|
|
142
|
+
isoMonth(): JsonSchemaIsoMonthBuilder;
|
|
142
143
|
/**
|
|
143
144
|
* Validates the string format to be JWT.
|
|
144
145
|
* Expects the JWT to be signed!
|
|
@@ -179,6 +180,11 @@ export interface JsonSchemaIsoDateOptions {
|
|
|
179
180
|
after?: string;
|
|
180
181
|
sameOrAfter?: string;
|
|
181
182
|
}
|
|
183
|
+
export declare class JsonSchemaIsoMonthBuilder<Opt extends boolean = false> extends JsonSchemaAnyBuilder<string | IsoDate, IsoMonth, Opt> {
|
|
184
|
+
constructor();
|
|
185
|
+
}
|
|
186
|
+
export interface JsonSchemaIsoMonthOptions {
|
|
187
|
+
}
|
|
182
188
|
export declare class JsonSchemaNumberBuilder<IN extends number | undefined = number, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
183
189
|
constructor();
|
|
184
190
|
/**
|
|
@@ -343,6 +349,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
|
|
|
343
349
|
Buffer?: true;
|
|
344
350
|
IsoDate?: JsonSchemaIsoDateOptions;
|
|
345
351
|
IsoDateTime?: true;
|
|
352
|
+
IsoMonth?: JsonSchemaIsoMonthOptions;
|
|
346
353
|
instanceof?: string | string[];
|
|
347
354
|
transform?: {
|
|
348
355
|
trim?: true;
|
|
@@ -389,10 +396,10 @@ declare function withEnumKeys<const T extends readonly (string | number)[] | Str
|
|
|
389
396
|
} : {
|
|
390
397
|
[P in K]: SchemaOut<S>;
|
|
391
398
|
}, false>;
|
|
392
|
-
type Expand<T> =
|
|
393
|
-
[K in keyof
|
|
394
|
-
}
|
|
395
|
-
type ExactMatch<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
|
|
399
|
+
type Expand<T> = {
|
|
400
|
+
[K in keyof T]: T[K];
|
|
401
|
+
};
|
|
402
|
+
type ExactMatch<A, B> = (<T>() => T extends Expand<A> ? 1 : 2) extends <T>() => T extends Expand<B> ? 1 : 2 ? (<T>() => T extends Expand<B> ? 1 : 2) extends <T>() => T extends Expand<A> ? 1 : 2 ? true : false : false;
|
|
396
403
|
type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
|
|
397
404
|
[K in keyof B]: B[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never;
|
|
398
405
|
}[number];
|
|
@@ -322,6 +322,9 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
|
|
|
322
322
|
isoDateTime() {
|
|
323
323
|
return this.cloneAndUpdateSchema({ IsoDateTime: true }).branded();
|
|
324
324
|
}
|
|
325
|
+
isoMonth() {
|
|
326
|
+
return new JsonSchemaIsoMonthBuilder();
|
|
327
|
+
}
|
|
325
328
|
/**
|
|
326
329
|
* Validates the string format to be JWT.
|
|
327
330
|
* Expects the JWT to be signed!
|
|
@@ -404,6 +407,14 @@ export class JsonSchemaIsoDateBuilder extends JsonSchemaAnyBuilder {
|
|
|
404
407
|
return this.cloneAndUpdateSchema(schemaPatch);
|
|
405
408
|
}
|
|
406
409
|
}
|
|
410
|
+
export class JsonSchemaIsoMonthBuilder extends JsonSchemaAnyBuilder {
|
|
411
|
+
constructor() {
|
|
412
|
+
super({
|
|
413
|
+
type: 'string',
|
|
414
|
+
IsoMonth: {},
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
407
418
|
export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder {
|
|
408
419
|
constructor() {
|
|
409
420
|
super({
|
|
@@ -575,7 +586,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
575
586
|
return this.cloneAndUpdateSchema({ additionalProperties: true });
|
|
576
587
|
}
|
|
577
588
|
extend(props) {
|
|
578
|
-
const clone = this.
|
|
589
|
+
const clone = this.clone();
|
|
579
590
|
const incomingSchemaBuilder = new JsonSchemaObjectBuilder(props);
|
|
580
591
|
mergeJsonSchemaObjects(clone.schema, incomingSchemaBuilder.schema);
|
|
581
592
|
return clone;
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { _lazyValue } from '@naturalcycles/js-lib'
|
|
1
|
+
import { _isBetween, _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 type { AnyObject } from '@naturalcycles/js-lib/types'
|
|
5
5
|
import { Ajv, type Options, type ValidateFunction } from 'ajv'
|
|
6
6
|
import { validTLDs } from '../tlds.js'
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
JsonSchemaIsoDateOptions,
|
|
9
|
+
JsonSchemaIsoMonthOptions,
|
|
10
|
+
JsonSchemaStringEmailOptions,
|
|
11
|
+
} from './jsonSchemaBuilder.js'
|
|
8
12
|
|
|
9
13
|
/* eslint-disable @typescript-eslint/prefer-string-starts-ends-with */
|
|
10
14
|
// oxlint-disable unicorn/prefer-code-point
|
|
@@ -383,7 +387,7 @@ export function createAjv(opt?: Options): Ajv {
|
|
|
383
387
|
modifying: false,
|
|
384
388
|
errors: true,
|
|
385
389
|
schemaType: 'boolean',
|
|
386
|
-
validate: function validate(_opt:
|
|
390
|
+
validate: function validate(_opt: JsonSchemaIsoMonthOptions, data: string, _schema, ctx) {
|
|
387
391
|
const isValid = isIsoDateTimeValid(data)
|
|
388
392
|
if (isValid) return true
|
|
389
393
|
;(validate as any).errors = [
|
|
@@ -396,6 +400,25 @@ export function createAjv(opt?: Options): Ajv {
|
|
|
396
400
|
},
|
|
397
401
|
})
|
|
398
402
|
|
|
403
|
+
ajv.addKeyword({
|
|
404
|
+
keyword: 'IsoMonth',
|
|
405
|
+
type: 'string',
|
|
406
|
+
modifying: false,
|
|
407
|
+
errors: true,
|
|
408
|
+
schemaType: 'object',
|
|
409
|
+
validate: function validate(_opt: true, data: string, _schema, ctx) {
|
|
410
|
+
const isValid = isIsoMonthValid(data)
|
|
411
|
+
if (isValid) return true
|
|
412
|
+
;(validate as any).errors = [
|
|
413
|
+
{
|
|
414
|
+
instancePath: ctx?.instancePath ?? '',
|
|
415
|
+
message: `is an invalid IsoMonth`,
|
|
416
|
+
},
|
|
417
|
+
]
|
|
418
|
+
return false
|
|
419
|
+
},
|
|
420
|
+
})
|
|
421
|
+
|
|
399
422
|
ajv.addKeyword({
|
|
400
423
|
keyword: 'errorMessages',
|
|
401
424
|
schemaType: 'object',
|
|
@@ -577,3 +600,24 @@ function isIsoTimezoneValid(s: string): boolean {
|
|
|
577
600
|
|
|
578
601
|
return true
|
|
579
602
|
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* This is a performance optimized correct validation
|
|
606
|
+
* for ISO month formatted as "YYYY-MM".
|
|
607
|
+
*/
|
|
608
|
+
function isIsoMonthValid(s: string): boolean {
|
|
609
|
+
// must be exactly "YYYY-MM"
|
|
610
|
+
if (s.length !== 7) return false
|
|
611
|
+
if (s.charCodeAt(4) !== DASH_CODE) return false
|
|
612
|
+
|
|
613
|
+
// fast parse numbers without substrings/Number()
|
|
614
|
+
const year =
|
|
615
|
+
(s.charCodeAt(0) - ZERO_CODE) * 1000 +
|
|
616
|
+
(s.charCodeAt(1) - ZERO_CODE) * 100 +
|
|
617
|
+
(s.charCodeAt(2) - ZERO_CODE) * 10 +
|
|
618
|
+
(s.charCodeAt(3) - ZERO_CODE)
|
|
619
|
+
|
|
620
|
+
const month = (s.charCodeAt(5) - ZERO_CODE) * 10 + (s.charCodeAt(6) - ZERO_CODE)
|
|
621
|
+
|
|
622
|
+
return _isBetween(year, 1900, 2500, '[]') && _isBetween(month, 1, 12, '[]')
|
|
623
|
+
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type Inclusiveness,
|
|
20
20
|
type IsoDate,
|
|
21
21
|
type IsoDateTime,
|
|
22
|
+
type IsoMonth,
|
|
22
23
|
JWT_REGEX,
|
|
23
24
|
type NumberEnum,
|
|
24
25
|
type StringEnum,
|
|
@@ -453,6 +454,10 @@ export class JsonSchemaStringBuilder<
|
|
|
453
454
|
return this.cloneAndUpdateSchema({ IsoDateTime: true }).branded<IsoDateTime>()
|
|
454
455
|
}
|
|
455
456
|
|
|
457
|
+
isoMonth(): JsonSchemaIsoMonthBuilder {
|
|
458
|
+
return new JsonSchemaIsoMonthBuilder()
|
|
459
|
+
}
|
|
460
|
+
|
|
456
461
|
/**
|
|
457
462
|
* Validates the string format to be JWT.
|
|
458
463
|
* Expects the JWT to be signed!
|
|
@@ -570,6 +575,21 @@ export interface JsonSchemaIsoDateOptions {
|
|
|
570
575
|
sameOrAfter?: string
|
|
571
576
|
}
|
|
572
577
|
|
|
578
|
+
export class JsonSchemaIsoMonthBuilder<Opt extends boolean = false> extends JsonSchemaAnyBuilder<
|
|
579
|
+
string | IsoDate,
|
|
580
|
+
IsoMonth,
|
|
581
|
+
Opt
|
|
582
|
+
> {
|
|
583
|
+
constructor() {
|
|
584
|
+
super({
|
|
585
|
+
type: 'string',
|
|
586
|
+
IsoMonth: {},
|
|
587
|
+
})
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export interface JsonSchemaIsoMonthOptions {}
|
|
592
|
+
|
|
573
593
|
export class JsonSchemaNumberBuilder<
|
|
574
594
|
IN extends number | undefined = number,
|
|
575
595
|
OUT = IN,
|
|
@@ -811,11 +831,7 @@ export class JsonSchemaObjectBuilder<
|
|
|
811
831
|
extend<IN2 extends AnyObject>(
|
|
812
832
|
props: AnyObject,
|
|
813
833
|
): JsonSchemaObjectBuilder<IN & IN2, OUT & IN2, Opt> {
|
|
814
|
-
const clone = this.
|
|
815
|
-
IN & IN2,
|
|
816
|
-
OUT & IN2,
|
|
817
|
-
Opt
|
|
818
|
-
>
|
|
834
|
+
const clone = this.clone() as JsonSchemaObjectBuilder<IN & IN2, OUT & IN2, Opt>
|
|
819
835
|
|
|
820
836
|
const incomingSchemaBuilder = new JsonSchemaObjectBuilder<IN2, IN2, false>(props)
|
|
821
837
|
mergeJsonSchemaObjects(clone.schema as any, incomingSchemaBuilder.schema as any)
|
|
@@ -1126,6 +1142,7 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
|
|
|
1126
1142
|
Buffer?: true
|
|
1127
1143
|
IsoDate?: JsonSchemaIsoDateOptions
|
|
1128
1144
|
IsoDateTime?: true
|
|
1145
|
+
IsoMonth?: JsonSchemaIsoMonthOptions
|
|
1129
1146
|
instanceof?: string | string[]
|
|
1130
1147
|
transform?: { trim?: true; toLowerCase?: true; toUpperCase?: true; truncate?: number }
|
|
1131
1148
|
errorMessages?: StringMap<string>
|
|
@@ -1290,10 +1307,14 @@ function withEnumKeys<
|
|
|
1290
1307
|
>(props, { hasIsOfTypeCheck: false })
|
|
1291
1308
|
}
|
|
1292
1309
|
|
|
1293
|
-
type Expand<T> =
|
|
1310
|
+
type Expand<T> = { [K in keyof T]: T[K] }
|
|
1294
1311
|
|
|
1295
1312
|
type ExactMatch<A, B> =
|
|
1296
|
-
(<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
|
|
1313
|
+
(<T>() => T extends Expand<A> ? 1 : 2) extends <T>() => T extends Expand<B> ? 1 : 2
|
|
1314
|
+
? (<T>() => T extends Expand<B> ? 1 : 2) extends <T>() => T extends Expand<A> ? 1 : 2
|
|
1315
|
+
? true
|
|
1316
|
+
: false
|
|
1317
|
+
: false
|
|
1297
1318
|
|
|
1298
1319
|
type BuilderOutUnion<B extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
|
|
1299
1320
|
[K in keyof B]: B[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never
|