decoders 2.3.0 → 2.4.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/README.md +1 -1
- package/dist/index.cjs +87 -63
- package/dist/index.d.cts +40 -21
- package/dist/index.d.ts +40 -21
- package/dist/index.js +87 -63
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/decoders)
|
|
4
4
|
[](https://github.com/nvie/decoders/actions)
|
|
5
|
-
[](https://pkg-size.dev/decoders)
|
|
6
6
|
|
|
7
7
|
Elegant and battle-tested validation library for type-safe input data for
|
|
8
8
|
[TypeScript](https://www.typescriptlang.org/).
|
package/dist/index.cjs
CHANGED
|
@@ -66,7 +66,8 @@ function annotateArray(arr, text, seen) {
|
|
|
66
66
|
function annotateObject(obj, text, seen) {
|
|
67
67
|
seen.add(obj);
|
|
68
68
|
const fields = /* @__PURE__ */ new Map();
|
|
69
|
-
for (const
|
|
69
|
+
for (const key of Object.keys(obj)) {
|
|
70
|
+
const value = obj[key];
|
|
70
71
|
fields.set(key, annotate(value, void 0, seen));
|
|
71
72
|
}
|
|
72
73
|
return makeObjectAnn(fields, text);
|
|
@@ -116,6 +117,10 @@ function indent(s, prefix = INDENT) {
|
|
|
116
117
|
return `${prefix}${s}`;
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
var quotePattern = /'/g;
|
|
121
|
+
function quote(value) {
|
|
122
|
+
return typeof value === "string" ? "'" + value.replace(quotePattern, "\\'") + "'" : JSON.stringify(value);
|
|
123
|
+
}
|
|
119
124
|
|
|
120
125
|
// src/core/format.ts
|
|
121
126
|
function summarize(ann, keypath = []) {
|
|
@@ -144,9 +149,9 @@ function summarize(ann, keypath = []) {
|
|
|
144
149
|
if (keypath.length === 0) {
|
|
145
150
|
prefix = "";
|
|
146
151
|
} else if (keypath.length === 1) {
|
|
147
|
-
prefix = typeof keypath[0] === "number" ? `Value at index ${keypath[0]}: ` : `Value at key ${
|
|
152
|
+
prefix = typeof keypath[0] === "number" ? `Value at index ${keypath[0]}: ` : `Value at key ${quote(keypath[0])}: `;
|
|
148
153
|
} else {
|
|
149
|
-
prefix = `Value at keypath ${keypath.map(String).join(".")}: `;
|
|
154
|
+
prefix = `Value at keypath ${quote(keypath.map(String).join("."))}: `;
|
|
150
155
|
}
|
|
151
156
|
return [...result, `${prefix}${text}`];
|
|
152
157
|
}
|
|
@@ -202,7 +207,7 @@ function serializeValue(value) {
|
|
|
202
207
|
return "undefined";
|
|
203
208
|
} else {
|
|
204
209
|
if (isDate(value)) {
|
|
205
|
-
return `new Date(${
|
|
210
|
+
return `new Date(${quote(value.toISOString())})`;
|
|
206
211
|
} else if (value instanceof Date) {
|
|
207
212
|
return "(Invalid Date)";
|
|
208
213
|
} else {
|
|
@@ -311,10 +316,16 @@ function define(fn) {
|
|
|
311
316
|
}
|
|
312
317
|
function then(next) {
|
|
313
318
|
return define((blob, ok2, err2) => {
|
|
314
|
-
const
|
|
315
|
-
|
|
319
|
+
const r1 = decode(blob);
|
|
320
|
+
if (!r1.ok)
|
|
321
|
+
return r1;
|
|
322
|
+
const r2 = isDecoder(next) ? next : next(r1.value, ok2, err2);
|
|
323
|
+
return isDecoder(r2) ? r2.decode(r1.value) : r2;
|
|
316
324
|
});
|
|
317
325
|
}
|
|
326
|
+
function pipe(next) {
|
|
327
|
+
return then(next);
|
|
328
|
+
}
|
|
318
329
|
function reject(rejectFn) {
|
|
319
330
|
return then((blob, ok2, err2) => {
|
|
320
331
|
const errmsg = rejectFn(blob);
|
|
@@ -331,13 +342,7 @@ function define(fn) {
|
|
|
331
342
|
}
|
|
332
343
|
});
|
|
333
344
|
}
|
|
334
|
-
|
|
335
|
-
return define((blob, ok2, err2) => {
|
|
336
|
-
const result = decode(blob);
|
|
337
|
-
return result.ok ? next([blob, result.value], ok2, err2) : result;
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
return Object.freeze({
|
|
345
|
+
return brand2({
|
|
341
346
|
verify,
|
|
342
347
|
value,
|
|
343
348
|
decode,
|
|
@@ -346,9 +351,17 @@ function define(fn) {
|
|
|
346
351
|
reject,
|
|
347
352
|
describe,
|
|
348
353
|
then,
|
|
349
|
-
|
|
354
|
+
pipe
|
|
350
355
|
});
|
|
351
356
|
}
|
|
357
|
+
var _register2 = /* @__PURE__ */ new WeakSet();
|
|
358
|
+
function brand2(decoder) {
|
|
359
|
+
_register2.add(decoder);
|
|
360
|
+
return decoder;
|
|
361
|
+
}
|
|
362
|
+
function isDecoder(thing) {
|
|
363
|
+
return _register2.has(thing);
|
|
364
|
+
}
|
|
352
365
|
|
|
353
366
|
// src/arrays.ts
|
|
354
367
|
var poja = define((blob, ok2, err2) => {
|
|
@@ -489,7 +502,7 @@ function object(decoders) {
|
|
|
489
502
|
objAnn = merge(objAnn, errors);
|
|
490
503
|
}
|
|
491
504
|
if (missingKeys.size > 0) {
|
|
492
|
-
const errMsg = Array.from(missingKeys).map(
|
|
505
|
+
const errMsg = Array.from(missingKeys).map(quote).join(", ");
|
|
493
506
|
const pluralized = missingKeys.size > 1 ? "keys" : "key";
|
|
494
507
|
objAnn = updateText(objAnn, `Missing ${pluralized}: ${errMsg}`);
|
|
495
508
|
}
|
|
@@ -503,17 +516,14 @@ function exact(decoders) {
|
|
|
503
516
|
const checked = pojo.reject((plainObj) => {
|
|
504
517
|
const actualKeys = new Set(Object.keys(plainObj));
|
|
505
518
|
const extraKeys = difference(actualKeys, allowedKeys);
|
|
506
|
-
return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).join(", ")}` :
|
|
507
|
-
// Don't reject
|
|
508
|
-
null
|
|
509
|
-
);
|
|
519
|
+
return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}` : null;
|
|
510
520
|
});
|
|
511
|
-
return checked.
|
|
521
|
+
return checked.pipe(object(decoders));
|
|
512
522
|
}
|
|
513
523
|
function inexact(decoders) {
|
|
514
|
-
return pojo.
|
|
524
|
+
return pojo.pipe((plainObj) => {
|
|
515
525
|
const allkeys = new Set(Object.keys(plainObj));
|
|
516
|
-
|
|
526
|
+
return object(decoders).transform((safepart) => {
|
|
517
527
|
const safekeys = new Set(Object.keys(decoders));
|
|
518
528
|
for (const k of safekeys)
|
|
519
529
|
allkeys.add(k);
|
|
@@ -530,7 +540,6 @@ function inexact(decoders) {
|
|
|
530
540
|
}
|
|
531
541
|
return rv;
|
|
532
542
|
});
|
|
533
|
-
return decoder.decode(plainObj);
|
|
534
543
|
});
|
|
535
544
|
}
|
|
536
545
|
|
|
@@ -566,9 +575,7 @@ function oneOf(constants) {
|
|
|
566
575
|
if (winner !== void 0) {
|
|
567
576
|
return ok2(winner);
|
|
568
577
|
}
|
|
569
|
-
return err2(
|
|
570
|
-
`Must be one of ${constants.map((value) => JSON.stringify(value)).join(", ")}`
|
|
571
|
-
);
|
|
578
|
+
return err2(`Must be one of ${constants.map((value) => quote(value)).join(", ")}`);
|
|
572
579
|
});
|
|
573
580
|
}
|
|
574
581
|
function enum_(enumObj) {
|
|
@@ -594,9 +601,9 @@ function taggedUnion(field, mapping2) {
|
|
|
594
601
|
);
|
|
595
602
|
}
|
|
596
603
|
function select(scout, selectFn) {
|
|
597
|
-
return
|
|
598
|
-
const
|
|
599
|
-
return
|
|
604
|
+
return define((blob) => {
|
|
605
|
+
const result = scout.decode(blob);
|
|
606
|
+
return result.ok ? selectFn(result.value).decode(blob) : result;
|
|
600
607
|
});
|
|
601
608
|
}
|
|
602
609
|
|
|
@@ -627,9 +634,7 @@ function nullish(decoder, defaultValue) {
|
|
|
627
634
|
}
|
|
628
635
|
function constant(value) {
|
|
629
636
|
return define(
|
|
630
|
-
(blob, ok2, err2) => blob === value ? ok2(value) : err2(
|
|
631
|
-
`Must be ${typeof value === "symbol" ? String(value) : JSON.stringify(value)}`
|
|
632
|
-
)
|
|
637
|
+
(blob, ok2, err2) => blob === value ? ok2(value) : err2(`Must be ${typeof value === "symbol" ? String(value) : quote(value)}`)
|
|
633
638
|
);
|
|
634
639
|
}
|
|
635
640
|
function always(value) {
|
|
@@ -645,36 +650,11 @@ var hardcoded = always;
|
|
|
645
650
|
var unknown = define((blob, ok2, _) => ok2(blob));
|
|
646
651
|
var mixed = unknown;
|
|
647
652
|
|
|
648
|
-
// src/numbers.ts
|
|
649
|
-
var anyNumber = define(
|
|
650
|
-
(blob, ok2, err2) => isNumber(blob) ? ok2(blob) : err2("Must be number")
|
|
651
|
-
);
|
|
652
|
-
var number = anyNumber.refine(
|
|
653
|
-
(n) => Number.isFinite(n),
|
|
654
|
-
"Number must be finite"
|
|
655
|
-
);
|
|
656
|
-
var integer = number.refine(
|
|
657
|
-
(n) => Number.isInteger(n),
|
|
658
|
-
"Number must be an integer"
|
|
659
|
-
);
|
|
660
|
-
var positiveNumber = number.refine(
|
|
661
|
-
(n) => n >= 0 && !Object.is(n, -0),
|
|
662
|
-
"Number must be positive"
|
|
663
|
-
);
|
|
664
|
-
var positiveInteger = integer.refine(
|
|
665
|
-
(n) => n >= 0 && !Object.is(n, -0),
|
|
666
|
-
"Number must be positive"
|
|
667
|
-
);
|
|
668
|
-
var bigint = define(
|
|
669
|
-
(blob, ok2, err2) => isBigInt(blob) ? ok2(blob) : err2("Must be bigint")
|
|
670
|
-
);
|
|
671
|
-
|
|
672
653
|
// src/booleans.ts
|
|
673
654
|
var boolean = define((blob, ok2, err2) => {
|
|
674
655
|
return typeof blob === "boolean" ? ok2(blob) : err2("Must be boolean");
|
|
675
656
|
});
|
|
676
657
|
var truthy = define((blob, ok2, _) => ok2(!!blob));
|
|
677
|
-
var numericBoolean = number.transform((n) => !!n);
|
|
678
658
|
|
|
679
659
|
// src/collections.ts
|
|
680
660
|
function record(fst, snd) {
|
|
@@ -683,14 +663,12 @@ function record(fst, snd) {
|
|
|
683
663
|
return pojo.then((input, ok2, err2) => {
|
|
684
664
|
let rv = {};
|
|
685
665
|
const errors = /* @__PURE__ */ new Map();
|
|
686
|
-
for (const
|
|
666
|
+
for (const key of Object.keys(input)) {
|
|
667
|
+
const value = input[key];
|
|
687
668
|
const keyResult = _optionalChain([keyDecoder, 'optionalAccess', _2 => _2.decode, 'call', _3 => _3(key)]);
|
|
688
669
|
if (_optionalChain([keyResult, 'optionalAccess', _4 => _4.ok]) === false) {
|
|
689
670
|
return err2(
|
|
690
|
-
public_annotate(
|
|
691
|
-
input,
|
|
692
|
-
`Invalid key ${JSON.stringify(key)}: ${formatShort(keyResult.error)}`
|
|
693
|
-
)
|
|
671
|
+
public_annotate(input, `Invalid key ${quote(key)}: ${formatShort(keyResult.error)}`)
|
|
694
672
|
);
|
|
695
673
|
}
|
|
696
674
|
const k = _nullishCoalesce(_optionalChain([keyResult, 'optionalAccess', _5 => _5.value]), () => ( key));
|
|
@@ -720,6 +698,18 @@ function mapping(decoder) {
|
|
|
720
698
|
return record(decoder).transform((obj) => new Map(Object.entries(obj)));
|
|
721
699
|
}
|
|
722
700
|
|
|
701
|
+
// src/lib/size-options.ts
|
|
702
|
+
function bySizeOptions(options) {
|
|
703
|
+
const size = _optionalChain([options, 'optionalAccess', _6 => _6.size]);
|
|
704
|
+
const min = _nullishCoalesce(size, () => ( _optionalChain([options, 'optionalAccess', _7 => _7.min])));
|
|
705
|
+
const max = _nullishCoalesce(size, () => ( _optionalChain([options, 'optionalAccess', _8 => _8.max])));
|
|
706
|
+
const atLeast = min === max ? "" : "at least ";
|
|
707
|
+
const atMost = min === max ? "" : "at most ";
|
|
708
|
+
const tooShort = min !== void 0 && `Too short, must be ${atLeast}${min} chars`;
|
|
709
|
+
const tooLong = max !== void 0 && `Too long, must be ${atMost}${max} chars`;
|
|
710
|
+
return tooShort && tooLong ? (s) => s.length < min ? tooShort : s.length > max ? tooLong : null : tooShort ? (s) => s.length < min ? tooShort : null : tooLong ? (s) => s.length > max ? tooLong : null : () => null;
|
|
711
|
+
}
|
|
712
|
+
|
|
723
713
|
// src/strings.ts
|
|
724
714
|
var url_re = /^([A-Za-z]{3,9}(?:[+][A-Za-z]{3,9})?):\/\/(?:([-;:&=+$,\w]+)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,/\w]*)?(?:#[.,!/\w]*)?)?$/;
|
|
725
715
|
var string = define(
|
|
@@ -742,6 +732,15 @@ var httpsUrl = url.refine(
|
|
|
742
732
|
(value) => value.protocol === "https:",
|
|
743
733
|
"Must be an HTTPS URL"
|
|
744
734
|
);
|
|
735
|
+
var identifier = regex(
|
|
736
|
+
/^[a-z_][a-z0-9_]*$/i,
|
|
737
|
+
"Must be valid identifier"
|
|
738
|
+
);
|
|
739
|
+
function nanoid(options) {
|
|
740
|
+
return regex(/^[a-z0-9_-]+$/i, "Must be nano ID").reject(
|
|
741
|
+
bySizeOptions(_nullishCoalesce(options, () => ( { size: 21 })))
|
|
742
|
+
);
|
|
743
|
+
}
|
|
745
744
|
var uuid = regex(
|
|
746
745
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
747
746
|
"Must be uuid"
|
|
@@ -781,6 +780,30 @@ var iso8601 = (
|
|
|
781
780
|
);
|
|
782
781
|
var datelike = either(date, iso8601).describe("Must be a Date or date string");
|
|
783
782
|
|
|
783
|
+
// src/numbers.ts
|
|
784
|
+
var anyNumber = define(
|
|
785
|
+
(blob, ok2, err2) => isNumber(blob) ? ok2(blob) : err2("Must be number")
|
|
786
|
+
);
|
|
787
|
+
var number = anyNumber.refine(
|
|
788
|
+
(n) => Number.isFinite(n),
|
|
789
|
+
"Number must be finite"
|
|
790
|
+
);
|
|
791
|
+
var integer = number.refine(
|
|
792
|
+
(n) => Number.isInteger(n),
|
|
793
|
+
"Number must be an integer"
|
|
794
|
+
);
|
|
795
|
+
var positiveNumber = number.refine(
|
|
796
|
+
(n) => n >= 0 && !Object.is(n, -0),
|
|
797
|
+
"Number must be positive"
|
|
798
|
+
);
|
|
799
|
+
var positiveInteger = integer.refine(
|
|
800
|
+
(n) => n >= 0 && !Object.is(n, -0),
|
|
801
|
+
"Number must be positive"
|
|
802
|
+
);
|
|
803
|
+
var bigint = define(
|
|
804
|
+
(blob, ok2, err2) => isBigInt(blob) ? ok2(blob) : err2("Must be bigint")
|
|
805
|
+
);
|
|
806
|
+
|
|
784
807
|
// src/json.ts
|
|
785
808
|
var jsonObject = lazy(() => record(json));
|
|
786
809
|
var jsonArray = lazy(() => array(json));
|
|
@@ -859,4 +882,5 @@ var json = either(
|
|
|
859
882
|
|
|
860
883
|
|
|
861
884
|
|
|
862
|
-
|
|
885
|
+
|
|
886
|
+
exports.always = always; exports.anyNumber = anyNumber; exports.array = array; exports.bigint = bigint; exports.boolean = boolean; exports.constant = constant; exports.date = date; exports.datelike = datelike; exports.decimal = decimal; exports.define = define; exports.dict = dict; exports.either = either; exports.email = email; exports.enum_ = enum_; exports.err = err; exports.exact = exact; exports.fail = fail; exports.formatInline = formatInline; exports.formatShort = formatShort; exports.hardcoded = hardcoded; exports.hexadecimal = hexadecimal; exports.httpsUrl = httpsUrl; exports.identifier = identifier; exports.inexact = inexact; exports.instanceOf = instanceOf; exports.integer = integer; exports.iso8601 = iso8601; exports.json = json; exports.jsonArray = jsonArray; exports.jsonObject = jsonObject; exports.lazy = lazy; exports.mapping = mapping; exports.maybe = maybe; exports.mixed = mixed; exports.nanoid = nanoid; exports.never = never; exports.nonEmptyArray = nonEmptyArray; exports.nonEmptyString = nonEmptyString; exports.null_ = null_; exports.nullable = nullable; exports.nullish = nullish; exports.number = number; exports.numeric = numeric; exports.object = object; exports.ok = ok; exports.oneOf = oneOf; exports.optional = optional; exports.poja = poja; exports.pojo = pojo; exports.positiveInteger = positiveInteger; exports.positiveNumber = positiveNumber; exports.prep = prep; exports.record = record; exports.regex = regex; exports.select = select; exports.set = set; exports.setFromArray = setFromArray; exports.string = string; exports.taggedUnion = taggedUnion; exports.truthy = truthy; exports.tuple = tuple; exports.undefined_ = undefined_; exports.unknown = unknown; exports.url = url; exports.uuid = uuid; exports.uuidv1 = uuidv1; exports.uuidv4 = uuidv4;
|
package/dist/index.d.cts
CHANGED
|
@@ -59,7 +59,8 @@ type DecodeResult<T> = Result<T, Annotation>;
|
|
|
59
59
|
* `ok()` and `err()` constructor functions are provided as the 2nd and 3rd
|
|
60
60
|
* param. One of these should be called and its value returned.
|
|
61
61
|
*/
|
|
62
|
-
type AcceptanceFn<
|
|
62
|
+
type AcceptanceFn<O, I = unknown> = (blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O>;
|
|
63
|
+
type Next<O, I = unknown> = Decoder<O> | ((blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O> | Decoder<O>);
|
|
63
64
|
interface Decoder<T> {
|
|
64
65
|
/**
|
|
65
66
|
* Verifies untrusted input. Either returns a value, or throws a decoding
|
|
@@ -96,23 +97,31 @@ interface Decoder<T> {
|
|
|
96
97
|
*/
|
|
97
98
|
describe(message: string): Decoder<T>;
|
|
98
99
|
/**
|
|
99
|
-
* Send the output of the current decoder into another acceptance
|
|
100
|
-
* The given acceptance function will receive the output of the
|
|
101
|
-
* decoder as its input
|
|
102
|
-
*
|
|
103
|
-
* This works similar to how you would `define()` a new decoder, except
|
|
104
|
-
* that the ``blob`` param will now be ``T`` (a known type), rather than
|
|
105
|
-
* ``unknown``. This will allow the function to make a stronger assumption
|
|
106
|
-
* about its input and avoid re-refining inputs.
|
|
100
|
+
* Send the output of the current decoder into another decoder or acceptance
|
|
101
|
+
* function. The given acceptance function will receive the output of the
|
|
102
|
+
* current decoder as its input.
|
|
107
103
|
*
|
|
108
104
|
* > _**NOTE:** This is an advanced, low-level, API. It's not recommended
|
|
109
105
|
* > to reach for this construct unless there is no other way. Most cases can
|
|
110
|
-
* > be covered more elegantly by `.transform()
|
|
106
|
+
* > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
|
|
107
|
+
* > instead._
|
|
108
|
+
*/
|
|
109
|
+
then<V>(next: Next<V, T>): Decoder<V>;
|
|
110
|
+
/**
|
|
111
|
+
* Send the output of this decoder as input to another decoder.
|
|
112
|
+
*
|
|
113
|
+
* This can be useful to validate the results of a transform, i.e.:
|
|
111
114
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
115
|
+
* string
|
|
116
|
+
* .transform((s) => s.split(','))
|
|
117
|
+
* .pipe(array(nonEmptyString))
|
|
118
|
+
*
|
|
119
|
+
* You can also conditionally pipe:
|
|
120
|
+
*
|
|
121
|
+
* string.pipe((s) => s.startsWith('@') ? username : email)
|
|
114
122
|
*/
|
|
115
|
-
|
|
123
|
+
pipe<V, D extends Decoder<V>>(next: D): Decoder<DecoderType<D>>;
|
|
124
|
+
pipe<V, D extends Decoder<V>>(next: (blob: T) => D): Decoder<DecoderType<D>>;
|
|
116
125
|
}
|
|
117
126
|
/**
|
|
118
127
|
* Helper type to return the output type of a Decoder.
|
|
@@ -264,13 +273,6 @@ declare const boolean: Decoder<boolean>;
|
|
|
264
273
|
* Accepts anything and will return its "truth" value. Will never reject.
|
|
265
274
|
*/
|
|
266
275
|
declare const truthy: Decoder<boolean>;
|
|
267
|
-
/**
|
|
268
|
-
* Accepts numbers, but return their boolean representation.
|
|
269
|
-
*
|
|
270
|
-
* @deprecated This decoder will be removed in a future version. You can use
|
|
271
|
-
* `truthy` to get almost the same effect.
|
|
272
|
-
*/
|
|
273
|
-
declare const numericBoolean: Decoder<boolean>;
|
|
274
276
|
|
|
275
277
|
/**
|
|
276
278
|
* Accepts objects where all values match the given decoder, and returns the
|
|
@@ -358,6 +360,12 @@ declare const jsonArray: Decoder<JSONArray>;
|
|
|
358
360
|
*/
|
|
359
361
|
declare const json: Decoder<JSONValue>;
|
|
360
362
|
|
|
363
|
+
type SizeOptions = {
|
|
364
|
+
min?: number;
|
|
365
|
+
max?: number;
|
|
366
|
+
size?: number;
|
|
367
|
+
};
|
|
368
|
+
|
|
361
369
|
interface Klass<T> extends Function {
|
|
362
370
|
new (...args: readonly any[]): T;
|
|
363
371
|
}
|
|
@@ -490,6 +498,17 @@ declare const url: Decoder<URL>;
|
|
|
490
498
|
* as a URL instance.
|
|
491
499
|
*/
|
|
492
500
|
declare const httpsUrl: Decoder<URL>;
|
|
501
|
+
/**
|
|
502
|
+
* Accepts and returns strings that are valid identifiers in most programming
|
|
503
|
+
* languages.
|
|
504
|
+
*/
|
|
505
|
+
declare const identifier: Decoder<string>;
|
|
506
|
+
/**
|
|
507
|
+
* Accepts and returns [nanoid](https://zelark.github.io/nano-id-cc) string
|
|
508
|
+
* values. It assumes the default nanoid alphabet. If you're using a custom
|
|
509
|
+
* alphabet, use `regex()` instead.
|
|
510
|
+
*/
|
|
511
|
+
declare function nanoid(options?: SizeOptions): Decoder<string>;
|
|
493
512
|
/**
|
|
494
513
|
* Accepts strings that are valid
|
|
495
514
|
* [UUIDs](https://en.wikipedia.org/wiki/universally_unique_identifier)
|
|
@@ -581,4 +600,4 @@ declare function taggedUnion<O extends Record<string, Decoder<unknown>>, T = Dec
|
|
|
581
600
|
*/
|
|
582
601
|
declare function select<T, D extends Decoder<unknown>>(scout: Decoder<T>, selectFn: (result: T) => D): Decoder<DecoderType<D>>;
|
|
583
602
|
|
|
584
|
-
export { type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type Ok, type Result, type Scalar, always, anyNumber, array, bigint, boolean, constant, date, datelike, decimal, define, dict, either, email, enum_, err, exact, fail, formatInline, formatShort, hardcoded, hexadecimal, httpsUrl, inexact, instanceOf, integer, iso8601, json, jsonArray, jsonObject, lazy, mapping, maybe, mixed, never, nonEmptyArray, nonEmptyString, null_, nullable, nullish, number, numeric,
|
|
603
|
+
export { type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type Ok, type Result, type Scalar, type SizeOptions, always, anyNumber, array, bigint, boolean, constant, date, datelike, decimal, define, dict, either, email, enum_, err, exact, fail, formatInline, formatShort, hardcoded, hexadecimal, httpsUrl, identifier, inexact, instanceOf, integer, iso8601, json, jsonArray, jsonObject, lazy, mapping, maybe, mixed, nanoid, never, nonEmptyArray, nonEmptyString, null_, nullable, nullish, number, numeric, object, ok, oneOf, optional, poja, pojo, positiveInteger, positiveNumber, prep, record, regex, select, set, setFromArray, string, taggedUnion, truthy, tuple, undefined_, unknown, url, uuid, uuidv1, uuidv4 };
|
package/dist/index.d.ts
CHANGED
|
@@ -59,7 +59,8 @@ type DecodeResult<T> = Result<T, Annotation>;
|
|
|
59
59
|
* `ok()` and `err()` constructor functions are provided as the 2nd and 3rd
|
|
60
60
|
* param. One of these should be called and its value returned.
|
|
61
61
|
*/
|
|
62
|
-
type AcceptanceFn<
|
|
62
|
+
type AcceptanceFn<O, I = unknown> = (blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O>;
|
|
63
|
+
type Next<O, I = unknown> = Decoder<O> | ((blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O> | Decoder<O>);
|
|
63
64
|
interface Decoder<T> {
|
|
64
65
|
/**
|
|
65
66
|
* Verifies untrusted input. Either returns a value, or throws a decoding
|
|
@@ -96,23 +97,31 @@ interface Decoder<T> {
|
|
|
96
97
|
*/
|
|
97
98
|
describe(message: string): Decoder<T>;
|
|
98
99
|
/**
|
|
99
|
-
* Send the output of the current decoder into another acceptance
|
|
100
|
-
* The given acceptance function will receive the output of the
|
|
101
|
-
* decoder as its input
|
|
102
|
-
*
|
|
103
|
-
* This works similar to how you would `define()` a new decoder, except
|
|
104
|
-
* that the ``blob`` param will now be ``T`` (a known type), rather than
|
|
105
|
-
* ``unknown``. This will allow the function to make a stronger assumption
|
|
106
|
-
* about its input and avoid re-refining inputs.
|
|
100
|
+
* Send the output of the current decoder into another decoder or acceptance
|
|
101
|
+
* function. The given acceptance function will receive the output of the
|
|
102
|
+
* current decoder as its input.
|
|
107
103
|
*
|
|
108
104
|
* > _**NOTE:** This is an advanced, low-level, API. It's not recommended
|
|
109
105
|
* > to reach for this construct unless there is no other way. Most cases can
|
|
110
|
-
* > be covered more elegantly by `.transform()
|
|
106
|
+
* > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
|
|
107
|
+
* > instead._
|
|
108
|
+
*/
|
|
109
|
+
then<V>(next: Next<V, T>): Decoder<V>;
|
|
110
|
+
/**
|
|
111
|
+
* Send the output of this decoder as input to another decoder.
|
|
112
|
+
*
|
|
113
|
+
* This can be useful to validate the results of a transform, i.e.:
|
|
111
114
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
115
|
+
* string
|
|
116
|
+
* .transform((s) => s.split(','))
|
|
117
|
+
* .pipe(array(nonEmptyString))
|
|
118
|
+
*
|
|
119
|
+
* You can also conditionally pipe:
|
|
120
|
+
*
|
|
121
|
+
* string.pipe((s) => s.startsWith('@') ? username : email)
|
|
114
122
|
*/
|
|
115
|
-
|
|
123
|
+
pipe<V, D extends Decoder<V>>(next: D): Decoder<DecoderType<D>>;
|
|
124
|
+
pipe<V, D extends Decoder<V>>(next: (blob: T) => D): Decoder<DecoderType<D>>;
|
|
116
125
|
}
|
|
117
126
|
/**
|
|
118
127
|
* Helper type to return the output type of a Decoder.
|
|
@@ -264,13 +273,6 @@ declare const boolean: Decoder<boolean>;
|
|
|
264
273
|
* Accepts anything and will return its "truth" value. Will never reject.
|
|
265
274
|
*/
|
|
266
275
|
declare const truthy: Decoder<boolean>;
|
|
267
|
-
/**
|
|
268
|
-
* Accepts numbers, but return their boolean representation.
|
|
269
|
-
*
|
|
270
|
-
* @deprecated This decoder will be removed in a future version. You can use
|
|
271
|
-
* `truthy` to get almost the same effect.
|
|
272
|
-
*/
|
|
273
|
-
declare const numericBoolean: Decoder<boolean>;
|
|
274
276
|
|
|
275
277
|
/**
|
|
276
278
|
* Accepts objects where all values match the given decoder, and returns the
|
|
@@ -358,6 +360,12 @@ declare const jsonArray: Decoder<JSONArray>;
|
|
|
358
360
|
*/
|
|
359
361
|
declare const json: Decoder<JSONValue>;
|
|
360
362
|
|
|
363
|
+
type SizeOptions = {
|
|
364
|
+
min?: number;
|
|
365
|
+
max?: number;
|
|
366
|
+
size?: number;
|
|
367
|
+
};
|
|
368
|
+
|
|
361
369
|
interface Klass<T> extends Function {
|
|
362
370
|
new (...args: readonly any[]): T;
|
|
363
371
|
}
|
|
@@ -490,6 +498,17 @@ declare const url: Decoder<URL>;
|
|
|
490
498
|
* as a URL instance.
|
|
491
499
|
*/
|
|
492
500
|
declare const httpsUrl: Decoder<URL>;
|
|
501
|
+
/**
|
|
502
|
+
* Accepts and returns strings that are valid identifiers in most programming
|
|
503
|
+
* languages.
|
|
504
|
+
*/
|
|
505
|
+
declare const identifier: Decoder<string>;
|
|
506
|
+
/**
|
|
507
|
+
* Accepts and returns [nanoid](https://zelark.github.io/nano-id-cc) string
|
|
508
|
+
* values. It assumes the default nanoid alphabet. If you're using a custom
|
|
509
|
+
* alphabet, use `regex()` instead.
|
|
510
|
+
*/
|
|
511
|
+
declare function nanoid(options?: SizeOptions): Decoder<string>;
|
|
493
512
|
/**
|
|
494
513
|
* Accepts strings that are valid
|
|
495
514
|
* [UUIDs](https://en.wikipedia.org/wiki/universally_unique_identifier)
|
|
@@ -581,4 +600,4 @@ declare function taggedUnion<O extends Record<string, Decoder<unknown>>, T = Dec
|
|
|
581
600
|
*/
|
|
582
601
|
declare function select<T, D extends Decoder<unknown>>(scout: Decoder<T>, selectFn: (result: T) => D): Decoder<DecoderType<D>>;
|
|
583
602
|
|
|
584
|
-
export { type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type Ok, type Result, type Scalar, always, anyNumber, array, bigint, boolean, constant, date, datelike, decimal, define, dict, either, email, enum_, err, exact, fail, formatInline, formatShort, hardcoded, hexadecimal, httpsUrl, inexact, instanceOf, integer, iso8601, json, jsonArray, jsonObject, lazy, mapping, maybe, mixed, never, nonEmptyArray, nonEmptyString, null_, nullable, nullish, number, numeric,
|
|
603
|
+
export { type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type Ok, type Result, type Scalar, type SizeOptions, always, anyNumber, array, bigint, boolean, constant, date, datelike, decimal, define, dict, either, email, enum_, err, exact, fail, formatInline, formatShort, hardcoded, hexadecimal, httpsUrl, identifier, inexact, instanceOf, integer, iso8601, json, jsonArray, jsonObject, lazy, mapping, maybe, mixed, nanoid, never, nonEmptyArray, nonEmptyString, null_, nullable, nullish, number, numeric, object, ok, oneOf, optional, poja, pojo, positiveInteger, positiveNumber, prep, record, regex, select, set, setFromArray, string, taggedUnion, truthy, tuple, undefined_, unknown, url, uuid, uuidv1, uuidv4 };
|
package/dist/index.js
CHANGED
|
@@ -66,7 +66,8 @@ function annotateArray(arr, text, seen) {
|
|
|
66
66
|
function annotateObject(obj, text, seen) {
|
|
67
67
|
seen.add(obj);
|
|
68
68
|
const fields = /* @__PURE__ */ new Map();
|
|
69
|
-
for (const
|
|
69
|
+
for (const key of Object.keys(obj)) {
|
|
70
|
+
const value = obj[key];
|
|
70
71
|
fields.set(key, annotate(value, void 0, seen));
|
|
71
72
|
}
|
|
72
73
|
return makeObjectAnn(fields, text);
|
|
@@ -116,6 +117,10 @@ function indent(s, prefix = INDENT) {
|
|
|
116
117
|
return `${prefix}${s}`;
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
var quotePattern = /'/g;
|
|
121
|
+
function quote(value) {
|
|
122
|
+
return typeof value === "string" ? "'" + value.replace(quotePattern, "\\'") + "'" : JSON.stringify(value);
|
|
123
|
+
}
|
|
119
124
|
|
|
120
125
|
// src/core/format.ts
|
|
121
126
|
function summarize(ann, keypath = []) {
|
|
@@ -144,9 +149,9 @@ function summarize(ann, keypath = []) {
|
|
|
144
149
|
if (keypath.length === 0) {
|
|
145
150
|
prefix = "";
|
|
146
151
|
} else if (keypath.length === 1) {
|
|
147
|
-
prefix = typeof keypath[0] === "number" ? `Value at index ${keypath[0]}: ` : `Value at key ${
|
|
152
|
+
prefix = typeof keypath[0] === "number" ? `Value at index ${keypath[0]}: ` : `Value at key ${quote(keypath[0])}: `;
|
|
148
153
|
} else {
|
|
149
|
-
prefix = `Value at keypath ${keypath.map(String).join(".")}: `;
|
|
154
|
+
prefix = `Value at keypath ${quote(keypath.map(String).join("."))}: `;
|
|
150
155
|
}
|
|
151
156
|
return [...result, `${prefix}${text}`];
|
|
152
157
|
}
|
|
@@ -202,7 +207,7 @@ function serializeValue(value) {
|
|
|
202
207
|
return "undefined";
|
|
203
208
|
} else {
|
|
204
209
|
if (isDate(value)) {
|
|
205
|
-
return `new Date(${
|
|
210
|
+
return `new Date(${quote(value.toISOString())})`;
|
|
206
211
|
} else if (value instanceof Date) {
|
|
207
212
|
return "(Invalid Date)";
|
|
208
213
|
} else {
|
|
@@ -311,10 +316,16 @@ function define(fn) {
|
|
|
311
316
|
}
|
|
312
317
|
function then(next) {
|
|
313
318
|
return define((blob, ok2, err2) => {
|
|
314
|
-
const
|
|
315
|
-
|
|
319
|
+
const r1 = decode(blob);
|
|
320
|
+
if (!r1.ok)
|
|
321
|
+
return r1;
|
|
322
|
+
const r2 = isDecoder(next) ? next : next(r1.value, ok2, err2);
|
|
323
|
+
return isDecoder(r2) ? r2.decode(r1.value) : r2;
|
|
316
324
|
});
|
|
317
325
|
}
|
|
326
|
+
function pipe(next) {
|
|
327
|
+
return then(next);
|
|
328
|
+
}
|
|
318
329
|
function reject(rejectFn) {
|
|
319
330
|
return then((blob, ok2, err2) => {
|
|
320
331
|
const errmsg = rejectFn(blob);
|
|
@@ -331,13 +342,7 @@ function define(fn) {
|
|
|
331
342
|
}
|
|
332
343
|
});
|
|
333
344
|
}
|
|
334
|
-
|
|
335
|
-
return define((blob, ok2, err2) => {
|
|
336
|
-
const result = decode(blob);
|
|
337
|
-
return result.ok ? next([blob, result.value], ok2, err2) : result;
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
return Object.freeze({
|
|
345
|
+
return brand2({
|
|
341
346
|
verify,
|
|
342
347
|
value,
|
|
343
348
|
decode,
|
|
@@ -346,9 +351,17 @@ function define(fn) {
|
|
|
346
351
|
reject,
|
|
347
352
|
describe,
|
|
348
353
|
then,
|
|
349
|
-
|
|
354
|
+
pipe
|
|
350
355
|
});
|
|
351
356
|
}
|
|
357
|
+
var _register2 = /* @__PURE__ */ new WeakSet();
|
|
358
|
+
function brand2(decoder) {
|
|
359
|
+
_register2.add(decoder);
|
|
360
|
+
return decoder;
|
|
361
|
+
}
|
|
362
|
+
function isDecoder(thing) {
|
|
363
|
+
return _register2.has(thing);
|
|
364
|
+
}
|
|
352
365
|
|
|
353
366
|
// src/arrays.ts
|
|
354
367
|
var poja = define((blob, ok2, err2) => {
|
|
@@ -489,7 +502,7 @@ function object(decoders) {
|
|
|
489
502
|
objAnn = merge(objAnn, errors);
|
|
490
503
|
}
|
|
491
504
|
if (missingKeys.size > 0) {
|
|
492
|
-
const errMsg = Array.from(missingKeys).map(
|
|
505
|
+
const errMsg = Array.from(missingKeys).map(quote).join(", ");
|
|
493
506
|
const pluralized = missingKeys.size > 1 ? "keys" : "key";
|
|
494
507
|
objAnn = updateText(objAnn, `Missing ${pluralized}: ${errMsg}`);
|
|
495
508
|
}
|
|
@@ -503,17 +516,14 @@ function exact(decoders) {
|
|
|
503
516
|
const checked = pojo.reject((plainObj) => {
|
|
504
517
|
const actualKeys = new Set(Object.keys(plainObj));
|
|
505
518
|
const extraKeys = difference(actualKeys, allowedKeys);
|
|
506
|
-
return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).join(", ")}` :
|
|
507
|
-
// Don't reject
|
|
508
|
-
null
|
|
509
|
-
);
|
|
519
|
+
return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}` : null;
|
|
510
520
|
});
|
|
511
|
-
return checked.
|
|
521
|
+
return checked.pipe(object(decoders));
|
|
512
522
|
}
|
|
513
523
|
function inexact(decoders) {
|
|
514
|
-
return pojo.
|
|
524
|
+
return pojo.pipe((plainObj) => {
|
|
515
525
|
const allkeys = new Set(Object.keys(plainObj));
|
|
516
|
-
|
|
526
|
+
return object(decoders).transform((safepart) => {
|
|
517
527
|
const safekeys = new Set(Object.keys(decoders));
|
|
518
528
|
for (const k of safekeys)
|
|
519
529
|
allkeys.add(k);
|
|
@@ -530,7 +540,6 @@ function inexact(decoders) {
|
|
|
530
540
|
}
|
|
531
541
|
return rv;
|
|
532
542
|
});
|
|
533
|
-
return decoder.decode(plainObj);
|
|
534
543
|
});
|
|
535
544
|
}
|
|
536
545
|
|
|
@@ -566,9 +575,7 @@ function oneOf(constants) {
|
|
|
566
575
|
if (winner !== void 0) {
|
|
567
576
|
return ok2(winner);
|
|
568
577
|
}
|
|
569
|
-
return err2(
|
|
570
|
-
`Must be one of ${constants.map((value) => JSON.stringify(value)).join(", ")}`
|
|
571
|
-
);
|
|
578
|
+
return err2(`Must be one of ${constants.map((value) => quote(value)).join(", ")}`);
|
|
572
579
|
});
|
|
573
580
|
}
|
|
574
581
|
function enum_(enumObj) {
|
|
@@ -594,9 +601,9 @@ function taggedUnion(field, mapping2) {
|
|
|
594
601
|
);
|
|
595
602
|
}
|
|
596
603
|
function select(scout, selectFn) {
|
|
597
|
-
return
|
|
598
|
-
const
|
|
599
|
-
return
|
|
604
|
+
return define((blob) => {
|
|
605
|
+
const result = scout.decode(blob);
|
|
606
|
+
return result.ok ? selectFn(result.value).decode(blob) : result;
|
|
600
607
|
});
|
|
601
608
|
}
|
|
602
609
|
|
|
@@ -627,9 +634,7 @@ function nullish(decoder, defaultValue) {
|
|
|
627
634
|
}
|
|
628
635
|
function constant(value) {
|
|
629
636
|
return define(
|
|
630
|
-
(blob, ok2, err2) => blob === value ? ok2(value) : err2(
|
|
631
|
-
`Must be ${typeof value === "symbol" ? String(value) : JSON.stringify(value)}`
|
|
632
|
-
)
|
|
637
|
+
(blob, ok2, err2) => blob === value ? ok2(value) : err2(`Must be ${typeof value === "symbol" ? String(value) : quote(value)}`)
|
|
633
638
|
);
|
|
634
639
|
}
|
|
635
640
|
function always(value) {
|
|
@@ -645,36 +650,11 @@ var hardcoded = always;
|
|
|
645
650
|
var unknown = define((blob, ok2, _) => ok2(blob));
|
|
646
651
|
var mixed = unknown;
|
|
647
652
|
|
|
648
|
-
// src/numbers.ts
|
|
649
|
-
var anyNumber = define(
|
|
650
|
-
(blob, ok2, err2) => isNumber(blob) ? ok2(blob) : err2("Must be number")
|
|
651
|
-
);
|
|
652
|
-
var number = anyNumber.refine(
|
|
653
|
-
(n) => Number.isFinite(n),
|
|
654
|
-
"Number must be finite"
|
|
655
|
-
);
|
|
656
|
-
var integer = number.refine(
|
|
657
|
-
(n) => Number.isInteger(n),
|
|
658
|
-
"Number must be an integer"
|
|
659
|
-
);
|
|
660
|
-
var positiveNumber = number.refine(
|
|
661
|
-
(n) => n >= 0 && !Object.is(n, -0),
|
|
662
|
-
"Number must be positive"
|
|
663
|
-
);
|
|
664
|
-
var positiveInteger = integer.refine(
|
|
665
|
-
(n) => n >= 0 && !Object.is(n, -0),
|
|
666
|
-
"Number must be positive"
|
|
667
|
-
);
|
|
668
|
-
var bigint = define(
|
|
669
|
-
(blob, ok2, err2) => isBigInt(blob) ? ok2(blob) : err2("Must be bigint")
|
|
670
|
-
);
|
|
671
|
-
|
|
672
653
|
// src/booleans.ts
|
|
673
654
|
var boolean = define((blob, ok2, err2) => {
|
|
674
655
|
return typeof blob === "boolean" ? ok2(blob) : err2("Must be boolean");
|
|
675
656
|
});
|
|
676
657
|
var truthy = define((blob, ok2, _) => ok2(!!blob));
|
|
677
|
-
var numericBoolean = number.transform((n) => !!n);
|
|
678
658
|
|
|
679
659
|
// src/collections.ts
|
|
680
660
|
function record(fst, snd) {
|
|
@@ -683,14 +663,12 @@ function record(fst, snd) {
|
|
|
683
663
|
return pojo.then((input, ok2, err2) => {
|
|
684
664
|
let rv = {};
|
|
685
665
|
const errors = /* @__PURE__ */ new Map();
|
|
686
|
-
for (const
|
|
666
|
+
for (const key of Object.keys(input)) {
|
|
667
|
+
const value = input[key];
|
|
687
668
|
const keyResult = keyDecoder?.decode(key);
|
|
688
669
|
if (keyResult?.ok === false) {
|
|
689
670
|
return err2(
|
|
690
|
-
public_annotate(
|
|
691
|
-
input,
|
|
692
|
-
`Invalid key ${JSON.stringify(key)}: ${formatShort(keyResult.error)}`
|
|
693
|
-
)
|
|
671
|
+
public_annotate(input, `Invalid key ${quote(key)}: ${formatShort(keyResult.error)}`)
|
|
694
672
|
);
|
|
695
673
|
}
|
|
696
674
|
const k = keyResult?.value ?? key;
|
|
@@ -720,6 +698,18 @@ function mapping(decoder) {
|
|
|
720
698
|
return record(decoder).transform((obj) => new Map(Object.entries(obj)));
|
|
721
699
|
}
|
|
722
700
|
|
|
701
|
+
// src/lib/size-options.ts
|
|
702
|
+
function bySizeOptions(options) {
|
|
703
|
+
const size = options?.size;
|
|
704
|
+
const min = size ?? options?.min;
|
|
705
|
+
const max = size ?? options?.max;
|
|
706
|
+
const atLeast = min === max ? "" : "at least ";
|
|
707
|
+
const atMost = min === max ? "" : "at most ";
|
|
708
|
+
const tooShort = min !== void 0 && `Too short, must be ${atLeast}${min} chars`;
|
|
709
|
+
const tooLong = max !== void 0 && `Too long, must be ${atMost}${max} chars`;
|
|
710
|
+
return tooShort && tooLong ? (s) => s.length < min ? tooShort : s.length > max ? tooLong : null : tooShort ? (s) => s.length < min ? tooShort : null : tooLong ? (s) => s.length > max ? tooLong : null : () => null;
|
|
711
|
+
}
|
|
712
|
+
|
|
723
713
|
// src/strings.ts
|
|
724
714
|
var url_re = /^([A-Za-z]{3,9}(?:[+][A-Za-z]{3,9})?):\/\/(?:([-;:&=+$,\w]+)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,/\w]*)?(?:#[.,!/\w]*)?)?$/;
|
|
725
715
|
var string = define(
|
|
@@ -742,6 +732,15 @@ var httpsUrl = url.refine(
|
|
|
742
732
|
(value) => value.protocol === "https:",
|
|
743
733
|
"Must be an HTTPS URL"
|
|
744
734
|
);
|
|
735
|
+
var identifier = regex(
|
|
736
|
+
/^[a-z_][a-z0-9_]*$/i,
|
|
737
|
+
"Must be valid identifier"
|
|
738
|
+
);
|
|
739
|
+
function nanoid(options) {
|
|
740
|
+
return regex(/^[a-z0-9_-]+$/i, "Must be nano ID").reject(
|
|
741
|
+
bySizeOptions(options ?? { size: 21 })
|
|
742
|
+
);
|
|
743
|
+
}
|
|
745
744
|
var uuid = regex(
|
|
746
745
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
747
746
|
"Must be uuid"
|
|
@@ -781,6 +780,30 @@ var iso8601 = (
|
|
|
781
780
|
);
|
|
782
781
|
var datelike = either(date, iso8601).describe("Must be a Date or date string");
|
|
783
782
|
|
|
783
|
+
// src/numbers.ts
|
|
784
|
+
var anyNumber = define(
|
|
785
|
+
(blob, ok2, err2) => isNumber(blob) ? ok2(blob) : err2("Must be number")
|
|
786
|
+
);
|
|
787
|
+
var number = anyNumber.refine(
|
|
788
|
+
(n) => Number.isFinite(n),
|
|
789
|
+
"Number must be finite"
|
|
790
|
+
);
|
|
791
|
+
var integer = number.refine(
|
|
792
|
+
(n) => Number.isInteger(n),
|
|
793
|
+
"Number must be an integer"
|
|
794
|
+
);
|
|
795
|
+
var positiveNumber = number.refine(
|
|
796
|
+
(n) => n >= 0 && !Object.is(n, -0),
|
|
797
|
+
"Number must be positive"
|
|
798
|
+
);
|
|
799
|
+
var positiveInteger = integer.refine(
|
|
800
|
+
(n) => n >= 0 && !Object.is(n, -0),
|
|
801
|
+
"Number must be positive"
|
|
802
|
+
);
|
|
803
|
+
var bigint = define(
|
|
804
|
+
(blob, ok2, err2) => isBigInt(blob) ? ok2(blob) : err2("Must be bigint")
|
|
805
|
+
);
|
|
806
|
+
|
|
784
807
|
// src/json.ts
|
|
785
808
|
var jsonObject = lazy(() => record(json));
|
|
786
809
|
var jsonArray = lazy(() => array(json));
|
|
@@ -815,6 +838,7 @@ export {
|
|
|
815
838
|
hardcoded,
|
|
816
839
|
hexadecimal,
|
|
817
840
|
httpsUrl,
|
|
841
|
+
identifier,
|
|
818
842
|
inexact,
|
|
819
843
|
instanceOf,
|
|
820
844
|
integer,
|
|
@@ -826,6 +850,7 @@ export {
|
|
|
826
850
|
mapping,
|
|
827
851
|
maybe,
|
|
828
852
|
mixed,
|
|
853
|
+
nanoid,
|
|
829
854
|
never,
|
|
830
855
|
nonEmptyArray,
|
|
831
856
|
nonEmptyString,
|
|
@@ -834,7 +859,6 @@ export {
|
|
|
834
859
|
nullish,
|
|
835
860
|
number,
|
|
836
861
|
numeric,
|
|
837
|
-
numericBoolean,
|
|
838
862
|
object,
|
|
839
863
|
ok,
|
|
840
864
|
oneOf,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decoders",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -63,23 +63,23 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@arethetypeswrong/cli": "^0.13.5",
|
|
65
65
|
"@release-it/keep-a-changelog": "^5.0.0",
|
|
66
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
67
|
-
"@typescript-eslint/parser": "^6.
|
|
68
|
-
"@vitest/coverage-istanbul": "^1.1
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
67
|
+
"@typescript-eslint/parser": "^6.19.0",
|
|
68
|
+
"@vitest/coverage-istanbul": "^1.2.1",
|
|
69
69
|
"eslint": "^8.56.0",
|
|
70
70
|
"eslint-plugin-import": "^2.29.1",
|
|
71
71
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
|
72
72
|
"fast-check": "^3.15.0",
|
|
73
|
-
"itertools": "^2.2.
|
|
74
|
-
"prettier": "^3.
|
|
73
|
+
"itertools": "^2.2.3",
|
|
74
|
+
"prettier": "^3.2.4",
|
|
75
75
|
"publint": "^0.2.7",
|
|
76
76
|
"release-it": "^17.0.1",
|
|
77
77
|
"ts-morph": "^21.0.1",
|
|
78
|
-
"tsd": "^0.30.
|
|
78
|
+
"tsd": "^0.30.4",
|
|
79
79
|
"tsup": "^8.0.1",
|
|
80
80
|
"typescript": "^5.3.3",
|
|
81
|
-
"vite-tsconfig-paths": "^4.
|
|
82
|
-
"vitest": "^1.1
|
|
81
|
+
"vite-tsconfig-paths": "^4.3.1",
|
|
82
|
+
"vitest": "^1.2.1"
|
|
83
83
|
},
|
|
84
84
|
"githubUrl": "https://github.com/nvie/decoders",
|
|
85
85
|
"sideEffects": false
|