decoders 2.10.0 → 2.11.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/index.cjs CHANGED
@@ -29,56 +29,87 @@ function isPlainObject(value) {
29
29
  // something is a pojo... ¯\_(ツ)_/¯
30
30
  Object.prototype.toString.call(value) === "[object Object]";
31
31
  }
32
+ function assertNever(_value, msg = "Unhandled case") {
33
+ throw new Error(msg);
34
+ }
32
35
 
33
36
  // src/core/annotate.ts
34
37
  var kAnnotationRegistry = /* @__PURE__ */ Symbol.for("decoders.kAnnotationRegistry");
35
- var _register = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
36
- function brand(ann) {
37
- _register.add(ann);
38
+ var _stamped = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
39
+ function stamp(ann) {
40
+ _stamped.add(ann);
38
41
  return ann;
39
42
  }
40
- function makeObjectAnn(fields, text) {
41
- return brand({ type: "object", fields, text });
43
+ function makeObjectAnn(getFields, text) {
44
+ let fields;
45
+ return stamp({
46
+ type: "object",
47
+ get fields() {
48
+ return fields ??= getFields();
49
+ },
50
+ text
51
+ });
42
52
  }
43
- function makeArrayAnn(items, text) {
44
- return brand({ type: "array", items, text });
53
+ function makeArrayAnn(getItems, text) {
54
+ let items;
55
+ return stamp({
56
+ type: "array",
57
+ get items() {
58
+ return items ??= getItems();
59
+ },
60
+ text
61
+ });
45
62
  }
46
63
  function makeOpaqueAnn(value, text) {
47
- return brand({ type: "opaque", value, text });
64
+ return stamp({ type: "opaque", value, text });
48
65
  }
49
66
  function makeScalarAnn(value, text) {
50
- return brand({ type: "scalar", value, text });
67
+ return stamp({ type: "scalar", value, text });
51
68
  }
52
69
  function updateText(annotation, text) {
53
- if (text !== void 0) {
54
- return brand({ ...annotation, text });
55
- } else {
70
+ if (text === void 0) {
56
71
  return annotation;
57
72
  }
73
+ switch (annotation.type) {
74
+ case "object":
75
+ return makeObjectAnn(() => annotation.fields, text);
76
+ case "array":
77
+ return makeArrayAnn(() => annotation.items, text);
78
+ case "scalar":
79
+ return makeScalarAnn(annotation.value, text);
80
+ case "opaque":
81
+ return makeOpaqueAnn(annotation.value, text);
82
+ // istanbul ignore next -- @preserve
83
+ default:
84
+ return assertNever(annotation, "Unknown annotation type");
85
+ }
58
86
  }
59
87
  function merge(objAnnotation, fields) {
60
- const newFields = new Map([...objAnnotation.fields, ...fields]);
61
- return makeObjectAnn(newFields, objAnnotation.text);
88
+ return makeObjectAnn(
89
+ () => new Map([...objAnnotation.fields, ...fields]),
90
+ objAnnotation.text
91
+ );
62
92
  }
63
93
  function isAnnotation(thing) {
64
- return _register.has(thing);
94
+ return _stamped.has(thing);
65
95
  }
66
96
  function annotateArray(arr, text, seen) {
67
97
  seen.add(arr);
68
- const items = [];
69
- for (const value of arr) {
70
- items.push(__annotate(value, void 0, seen));
71
- }
72
- return makeArrayAnn(items, text);
98
+ return makeArrayAnn(
99
+ () => Array.from(arr, (value) => __annotate(value, void 0, seen)),
100
+ text
101
+ );
73
102
  }
74
103
  function annotateObject(obj, text, seen) {
75
104
  seen.add(obj);
76
- const fields = /* @__PURE__ */ new Map();
77
- for (const key of Object.keys(obj)) {
78
- const value = obj[key];
79
- fields.set(key, __annotate(value, void 0, seen));
80
- }
81
- return makeObjectAnn(fields, text);
105
+ return makeObjectAnn(() => {
106
+ const fields = /* @__PURE__ */ new Map();
107
+ for (const key of Object.keys(obj)) {
108
+ const value = obj[key];
109
+ fields.set(key, __annotate(value, void 0, seen));
110
+ }
111
+ return fields;
112
+ }, text);
82
113
  }
83
114
  function __annotate(value, text, seen) {
84
115
  if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "symbol" || typeof value === "bigint" || typeof value.getMonth === "function") {
@@ -299,6 +330,9 @@ function* iterAnnotation(ann, stack) {
299
330
  case "opaque": {
300
331
  break;
301
332
  }
333
+ // istanbul ignore next -- @preserve
334
+ default:
335
+ assertNever(ann, "Unknown annotation type");
302
336
  }
303
337
  }
304
338
  function formatAsIssues(ann) {
@@ -398,7 +432,7 @@ function define(fn) {
398
432
  }
399
433
  });
400
434
  }
401
- const unregistered = {
435
+ const newDecoder = {
402
436
  verify,
403
437
  value,
404
438
  decode,
@@ -423,80 +457,18 @@ function define(fn) {
423
457
  }
424
458
  }
425
459
  };
426
- const self = brand2(unregistered);
460
+ const self = stamp2(newDecoder);
427
461
  return self;
428
462
  }
429
463
  var kDecoderRegistry = /* @__PURE__ */ Symbol.for("decoders.kDecoderRegistry");
430
- var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
431
- function brand2(decoder) {
432
- _register2.add(decoder);
464
+ var _stamped2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
465
+ function stamp2(decoder) {
466
+ _stamped2.add(decoder);
433
467
  return decoder;
434
468
  }
435
469
  // @__NO_SIDE_EFFECTS__
436
470
  function isDecoder(value) {
437
- return _register2.has(value);
438
- }
439
-
440
- // src/arrays.ts
441
- var poja = define((blob, ok2, err2) => {
442
- if (!Array.isArray(blob)) {
443
- return err2("Must be an array");
444
- }
445
- return ok2(blob);
446
- });
447
- // @__NO_SIDE_EFFECTS__
448
- function array(decoder) {
449
- const decodeFn = decoder.decode;
450
- return poja.chain((inputs, ok2, err2) => {
451
- const results = [];
452
- for (let i = 0; i < inputs.length; ++i) {
453
- const blob = inputs[i];
454
- const result = decodeFn(blob);
455
- if (result.ok) {
456
- results.push(result.value);
457
- } else {
458
- results.length = 0;
459
- const ann = result.error;
460
- const clone = inputs.slice();
461
- clone.splice(
462
- i,
463
- 1,
464
- public_annotate(ann, ann.text ? `${ann.text} (at index ${i})` : `index ${i}`)
465
- );
466
- return err2(public_annotate(clone));
467
- }
468
- }
469
- return ok2(results);
470
- });
471
- }
472
- function isNonEmpty(arr) {
473
- return arr.length > 0;
474
- }
475
- // @__NO_SIDE_EFFECTS__
476
- function nonEmptyArray(decoder) {
477
- return (/* @__PURE__ */ array(decoder)).refine(isNonEmpty, "Must have at least 1 item");
478
- }
479
- var ntuple = /* @__NO_SIDE_EFFECTS__ */ (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
480
- // @__NO_SIDE_EFFECTS__
481
- function tuple(...decoders) {
482
- return (/* @__PURE__ */ ntuple(decoders.length)).chain((blobs, ok2, err2) => {
483
- let allOk = true;
484
- const rvs = decoders.map((decoder, i) => {
485
- const blob = blobs[i];
486
- const result = decoder.decode(blob);
487
- if (result.ok) {
488
- return result.value;
489
- } else {
490
- allOk = false;
491
- return result.error;
492
- }
493
- });
494
- if (allOk) {
495
- return ok2(rvs);
496
- } else {
497
- return err2(public_annotate(rvs));
498
- }
499
- });
471
+ return _stamped2.has(value);
500
472
  }
501
473
 
502
474
  // src/lib/size-options.ts
@@ -561,6 +533,66 @@ function prep(mapperFn, decoder) {
561
533
  });
562
534
  }
563
535
 
536
+ // src/arrays.ts
537
+ var poja = define((blob, ok2, err2) => {
538
+ if (!Array.isArray(blob)) {
539
+ return err2("Must be an array");
540
+ }
541
+ return ok2(blob);
542
+ });
543
+ // @__NO_SIDE_EFFECTS__
544
+ function array(decoder, options) {
545
+ const decodeFn = decoder.decode;
546
+ const base = options !== void 0 ? sized(poja, options) : poja;
547
+ return base.chain((inputs, ok2, err2) => {
548
+ const results = [];
549
+ for (let i = 0; i < inputs.length; ++i) {
550
+ const blob = inputs[i];
551
+ const result = decodeFn(blob);
552
+ if (result.ok) {
553
+ results.push(result.value);
554
+ } else {
555
+ results.length = 0;
556
+ const ann = result.error;
557
+ const clone = inputs.slice();
558
+ clone.splice(
559
+ i,
560
+ 1,
561
+ public_annotate(ann, ann.text ? `${ann.text} (at index ${i})` : `index ${i}`)
562
+ );
563
+ return err2(public_annotate(clone));
564
+ }
565
+ }
566
+ return ok2(results);
567
+ });
568
+ }
569
+ // @__NO_SIDE_EFFECTS__
570
+ function nonEmptyArray(decoder) {
571
+ return (/* @__PURE__ */ array(decoder, { min: 1 })).refineType();
572
+ }
573
+ var ntuple = /* @__NO_SIDE_EFFECTS__ */ (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
574
+ // @__NO_SIDE_EFFECTS__
575
+ function tuple(...decoders) {
576
+ return (/* @__PURE__ */ ntuple(decoders.length)).chain((blobs, ok2, err2) => {
577
+ let allOk = true;
578
+ const rvs = decoders.map((decoder, i) => {
579
+ const blob = blobs[i];
580
+ const result = decoder.decode(blob);
581
+ if (result.ok) {
582
+ return result.value;
583
+ } else {
584
+ allOk = false;
585
+ return result.error;
586
+ }
587
+ });
588
+ if (allOk) {
589
+ return ok2(rvs);
590
+ } else {
591
+ return err2(public_annotate(rvs));
592
+ }
593
+ });
594
+ }
595
+
564
596
  // src/lib/set-methods.ts
565
597
  // @__NO_SIDE_EFFECTS__
566
598
  function difference(xs, ys) {
@@ -900,7 +932,9 @@ function nanoid(options) {
900
932
  return sized(/* @__PURE__ */ regex(/^[a-z0-9_-]+$/i, "Must be nano ID"), _nullishCoalesce(options, () => ( { size: 21 })));
901
933
  }
902
934
  var uuid = /* @__PURE__ */ regex(
903
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
935
+ // https://www.rfc-editor.org/rfc/rfc9562#section-4
936
+ // Version 1-8 with variant 8, 9, a, or b, plus the Nil and Max UUIDs (sections 5.9, 5.10)
937
+ /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i,
904
938
  "Must be uuid"
905
939
  );
906
940
  var uuidv1 = (
@@ -953,14 +987,16 @@ var integer = /* @__PURE__ */ number.refine(
953
987
  (n) => Number.isInteger(n),
954
988
  "Number must be an integer"
955
989
  );
956
- var positiveNumber = /* @__PURE__ */ number.refine(
990
+ var nonNegativeNumber = /* @__PURE__ */ number.refine(
957
991
  (n) => n >= 0 && !Object.is(n, -0),
958
992
  "Number must be positive"
959
993
  );
960
- var positiveInteger = /* @__PURE__ */ integer.refine(
994
+ var natural = /* @__PURE__ */ integer.refine(
961
995
  (n) => n >= 0 && !Object.is(n, -0),
962
996
  "Number must be positive"
963
997
  );
998
+ var positiveNumber = nonNegativeNumber;
999
+ var positiveInteger = natural;
964
1000
  // @__NO_SIDE_EFFECTS__
965
1001
  function min(min2, decoder = number) {
966
1002
  return decoder.reject(
@@ -1074,5 +1110,8 @@ var json = /* @__PURE__ */ either(
1074
1110
 
1075
1111
 
1076
1112
 
1077
- exports._annotate = public_annotate; exports.always = always; exports.anyNumber = anyNumber; exports.anything = anything; exports.array = array; exports.between = between; exports.bigint = bigint; exports.boolean = boolean; exports.constant = constant; exports.date = date; exports.dateString = dateString; exports.datelike = datelike; exports.decimal = decimal; exports.define = define; exports.either = either; exports.email = email; exports.endsWith = endsWith; exports.enum_ = enum_; exports.err = err; exports.exact = exact; exports.fail = fail; exports.flexDate = flexDate; exports.formatInline = formatInline; exports.formatShort = formatShort; exports.hexadecimal = hexadecimal; exports.httpsUrl = httpsUrl; exports.identifier = identifier; exports.inexact = inexact; exports.instanceOf = instanceOf; exports.integer = integer; exports.isDate = isDate; exports.isDecoder = isDecoder; exports.isPlainObject = isPlainObject; exports.isPromiseLike = isPromiseLike; exports.iso8601 = iso8601; exports.isoDate = isoDate; exports.isoDateString = isoDateString; exports.json = json; exports.jsonArray = jsonArray; exports.jsonObject = jsonObject; exports.lazy = lazy; exports.mapping = mapping; exports.max = max; exports.min = min; 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.setFromArray = setFromArray; exports.sized = sized; exports.startsWith = startsWith; exports.string = string; exports.taggedUnion = taggedUnion; exports.truthy = truthy; exports.tuple = tuple; exports.undefined_ = undefined_; exports.unknown = unknown; exports.url = url; exports.urlString = urlString; exports.uuid = uuid; exports.uuidv1 = uuidv1; exports.uuidv4 = uuidv4;
1113
+
1114
+
1115
+ exports._annotate = public_annotate; exports.always = always; exports.anyNumber = anyNumber; exports.anything = anything; exports.array = array; exports.between = between; exports.bigint = bigint; exports.boolean = boolean; exports.constant = constant; exports.date = date; exports.dateString = dateString; exports.datelike = datelike; exports.decimal = decimal; exports.define = define; exports.either = either; exports.email = email; exports.endsWith = endsWith; exports.enum_ = enum_; exports.err = err; exports.exact = exact; exports.fail = fail; exports.flexDate = flexDate; exports.formatInline = formatInline; exports.formatShort = formatShort; exports.hexadecimal = hexadecimal; exports.httpsUrl = httpsUrl; exports.identifier = identifier; exports.inexact = inexact; exports.instanceOf = instanceOf; exports.integer = integer; exports.isDate = isDate; exports.isDecoder = isDecoder; exports.isPlainObject = isPlainObject; exports.isPromiseLike = isPromiseLike; exports.iso8601 = iso8601; exports.isoDate = isoDate; exports.isoDateString = isoDateString; exports.json = json; exports.jsonArray = jsonArray; exports.jsonObject = jsonObject; exports.lazy = lazy; exports.mapping = mapping; exports.max = max; exports.min = min; exports.nanoid = nanoid; exports.natural = natural; exports.never = never; exports.nonEmptyArray = nonEmptyArray; exports.nonEmptyString = nonEmptyString; exports.nonNegativeNumber = nonNegativeNumber; 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.setFromArray = setFromArray; exports.sized = sized; exports.startsWith = startsWith; exports.string = string; exports.taggedUnion = taggedUnion; exports.truthy = truthy; exports.tuple = tuple; exports.undefined_ = undefined_; exports.unknown = unknown; exports.url = url; exports.urlString = urlString; exports.uuid = uuid; exports.uuidv1 = uuidv1; exports.uuidv4 = uuidv4;
1116
+ // istanbul ignore next -- @preserve
1078
1117
  // istanbul ignore else -- @preserve
package/dist/index.d.cts CHANGED
@@ -109,7 +109,6 @@ type DecodeResult<T> = Result<T, Annotation>;
109
109
  * param. One of these should be called and its value returned.
110
110
  */
111
111
  type AcceptanceFn<O, I = unknown> = (blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O>;
112
- type Next<O, I = unknown> = Decoder<O> | ((blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O> | Decoder<O>);
113
112
  interface Decoder<T> {
114
113
  /**
115
114
  * Verifies untrusted input. Either returns a value, or throws a decoding
@@ -151,16 +150,18 @@ interface Decoder<T> {
151
150
  */
152
151
  describe(message: string): Decoder<T>;
153
152
  /**
154
- * Send the output of the current decoder into another decoder or acceptance
155
- * function. The given acceptance function will receive the output of the
156
- * current decoder as its input.
153
+ * Send the output of the current decoder into an acceptance function. The
154
+ * given acceptance function will receive the output of the current decoder
155
+ * as its input.
157
156
  *
158
157
  * > _**NOTE:** This is an advanced, low-level, API. It's not recommended
159
158
  * > to reach for this construct unless there is no other way. Most cases can
160
159
  * > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
161
160
  * > instead._
162
161
  */
163
- chain<V>(next: Next<V, T>): Decoder<V>;
162
+ chain<V>(next: (blob: T, ok: (value: V) => DecodeResult<V>, err: (msg: string | Annotation) => DecodeResult<V>) => DecodeResult<V> | Decoder<V>): Decoder<V>;
163
+ /** @deprecated To send the output into another decoder, use `.pipe()` instead. */
164
+ chain<V>(next: Decoder<V>): Decoder<V>;
164
165
  /**
165
166
  * Send the output of this decoder as input to another decoder.
166
167
  *
@@ -218,6 +219,46 @@ type Formatter = (err: Annotation) => string | Error;
218
219
  declare function formatInline(ann: Annotation): string;
219
220
  declare function formatShort(ann: Annotation): string;
220
221
 
222
+ /**
223
+ * Forces TypeScript to "evaluate" named helper types, making API signatures
224
+ * clearer in IDEs.
225
+ *
226
+ * @see https://effectivetypescript.com/2022/02/25/gentips-4-display/
227
+ */
228
+ type Resolve$1<T> = T extends (...args: unknown[]) => unknown ? T : {
229
+ [K in keyof T]: T[K];
230
+ };
231
+ /**
232
+ * Relaxes a discriminated union type definition, by explicitly adding
233
+ * properties defined in any other member as optional `never`.
234
+ *
235
+ * This makes accessing the members much more relaxed in TypeScript.
236
+ */
237
+ type Relax<T> = DistributiveRelax<T, T extends any ? keyof T : never>;
238
+ type DistributiveRelax<T, Ks extends string | number | symbol> = T extends any ? Resolve$1<{
239
+ [K in keyof T]: T[K];
240
+ } & {
241
+ [K in Exclude<Ks, keyof T>]?: never;
242
+ }> : never;
243
+
244
+ type SizeOptions = Relax<{
245
+ size: number;
246
+ } | {
247
+ min: number;
248
+ max?: number;
249
+ } | {
250
+ min?: number;
251
+ max: number;
252
+ }>;
253
+ /**
254
+ * Anything with a .length or .size property, like strings, arrays, or sets.
255
+ */
256
+ type Sized = Relax<{
257
+ length: number;
258
+ } | {
259
+ size: number;
260
+ }>;
261
+
221
262
  /**
222
263
  * Accepts any array, but doesn't validate its items further.
223
264
  *
@@ -227,7 +268,7 @@ declare const poja: Decoder<unknown[]>;
227
268
  /**
228
269
  * Accepts arrays of whatever the given decoder accepts.
229
270
  */
230
- declare function array<T>(decoder: Decoder<T>): Decoder<T[]>;
271
+ declare function array<T>(decoder: Decoder<T>, options?: SizeOptions): Decoder<T[]>;
231
272
  /**
232
273
  * Like `array()`, but will reject arrays with 0 elements.
233
274
  */
@@ -365,7 +406,7 @@ declare const isoDate: Decoder<Date>;
365
406
  declare const flexDate: Decoder<Date>;
366
407
  /** @deprecated Renamed to `isoDateString`. This alias will be removed in 3.x. */
367
408
  declare const dateString: Decoder<string>;
368
- /** Alias of `isoDate`. */
409
+ /** @deprecated Renamed to `isoDate`. */
369
410
  declare const iso8601: Decoder<Date>;
370
411
  /** @deprecated Renamed to `flexDate`. This alias will be removed in 3.x. */
371
412
  declare const datelike: Decoder<Date>;
@@ -401,46 +442,6 @@ declare const jsonArray: Decoder<JSONArray>;
401
442
  */
402
443
  declare const json: Decoder<JSONValue>;
403
444
 
404
- /**
405
- * Forces TypeScript to "evaluate" named helper types, making API signatures
406
- * clearer in IDEs.
407
- *
408
- * @see https://effectivetypescript.com/2022/02/25/gentips-4-display/
409
- */
410
- type Resolve$1<T> = T extends (...args: unknown[]) => unknown ? T : {
411
- [K in keyof T]: T[K];
412
- };
413
- /**
414
- * Relaxes a discriminated union type definition, by explicitly adding
415
- * properties defined in any other member as optional `never`.
416
- *
417
- * This makes accessing the members much more relaxed in TypeScript.
418
- */
419
- type Relax<T> = DistributiveRelax<T, T extends any ? keyof T : never>;
420
- type DistributiveRelax<T, Ks extends string | number | symbol> = T extends any ? Resolve$1<{
421
- [K in keyof T]: T[K];
422
- } & {
423
- [K in Exclude<Ks, keyof T>]?: never;
424
- }> : never;
425
-
426
- type SizeOptions = Relax<{
427
- size: number;
428
- } | {
429
- min: number;
430
- max?: number;
431
- } | {
432
- min?: number;
433
- max: number;
434
- }>;
435
- /**
436
- * Anything with a .length or .size property, like strings, arrays, or sets.
437
- */
438
- type Sized = Relax<{
439
- length: number;
440
- } | {
441
- size: number;
442
- }>;
443
-
444
445
  interface Klass<T> extends Function {
445
446
  new (...args: readonly any[]): T;
446
447
  }
@@ -475,21 +476,28 @@ declare function prep<T>(mapperFn: (blob: unknown) => unknown, decoder: Decoder<
475
476
  */
476
477
  declare const anyNumber: Decoder<number>;
477
478
  /**
478
- * Accepts finite numbers (can be integer or float values). Values `NaN`,
479
- * or positive and negative `Infinity` will get rejected.
479
+ * Accepts only finite numbers (e.g. -3.14, 0, 1, 42, ...).
480
+ * Integers or floats, but not `NaN` or `Infinity`.
480
481
  */
481
482
  declare const number: Decoder<number>;
482
483
  /**
483
- * Accepts only finite whole numbers.
484
+ * Accepts only integers (e.g. ..., -2, -1, 0, 1, 2, ...).
485
+ * Whole numbers, and finite.
484
486
  */
485
487
  declare const integer: Decoder<number>;
486
488
  /**
487
- * Accepts only non-negative (zero or positive) finite numbers.
489
+ * Accepts only non-negative numbers (e.g. 0, 0.5, 1, 3.14, ...).
490
+ * Integers or floats, >= 0, and finite.
488
491
  */
489
- declare const positiveNumber: Decoder<number>;
492
+ declare const nonNegativeNumber: Decoder<number>;
490
493
  /**
491
- * Accepts only non-negative (zero or positive) finite whole numbers.
494
+ * Accepts only the natural numbers (e.g. 0, 1, 2, 3, ...).
495
+ * Whole numbers, >= 0, and finite.
492
496
  */
497
+ declare const natural: Decoder<number>;
498
+ /** @deprecated Renamed to `nonNegativeNumber`. */
499
+ declare const positiveNumber: Decoder<number>;
500
+ /** @deprecated Renamed to `natural`. */
493
501
  declare const positiveInteger: Decoder<number>;
494
502
  /**
495
503
  * Accepts numbers greater than or equal to the given minimum.
@@ -724,4 +732,4 @@ declare function isPromiseLike(value: unknown): value is PromiseLike<unknown>;
724
732
  */
725
733
  declare function isPlainObject(value: unknown): value is Record<string, unknown>;
726
734
 
727
- export { type Annotation, type ArrayAnnotation, type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type ObjectAnnotation, type Ok, type OpaqueAnnotation, type Relax, type Result, type Scalar, type ScalarAnnotation, type SizeOptions, type Sized, public_annotate as _annotate, always, anyNumber, anything, array, between, bigint, boolean, constant, date, dateString, datelike, decimal, define, either, email, endsWith, enum_, err, exact, fail, flexDate, formatInline, formatShort, hexadecimal, httpsUrl, identifier, inexact, instanceOf, integer, isDate, isDecoder, isPlainObject, isPromiseLike, iso8601, isoDate, isoDateString, json, jsonArray, jsonObject, lazy, mapping, max, min, nanoid, never, nonEmptyArray, nonEmptyString, null_, nullable, nullish, number, numeric, object, ok, oneOf, optional, poja, pojo, positiveInteger, positiveNumber, prep, record, regex, select, setFromArray, sized, startsWith, string, taggedUnion, truthy, tuple, undefined_, unknown, url, urlString, uuid, uuidv1, uuidv4 };
735
+ export { type Annotation, type ArrayAnnotation, type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type ObjectAnnotation, type Ok, type OpaqueAnnotation, type Relax, type Result, type Scalar, type ScalarAnnotation, type SizeOptions, type Sized, public_annotate as _annotate, always, anyNumber, anything, array, between, bigint, boolean, constant, date, dateString, datelike, decimal, define, either, email, endsWith, enum_, err, exact, fail, flexDate, formatInline, formatShort, hexadecimal, httpsUrl, identifier, inexact, instanceOf, integer, isDate, isDecoder, isPlainObject, isPromiseLike, iso8601, isoDate, isoDateString, json, jsonArray, jsonObject, lazy, mapping, max, min, nanoid, natural, never, nonEmptyArray, nonEmptyString, nonNegativeNumber, null_, nullable, nullish, number, numeric, object, ok, oneOf, optional, poja, pojo, positiveInteger, positiveNumber, prep, record, regex, select, setFromArray, sized, startsWith, string, taggedUnion, truthy, tuple, undefined_, unknown, url, urlString, uuid, uuidv1, uuidv4 };
package/dist/index.d.ts CHANGED
@@ -109,7 +109,6 @@ type DecodeResult<T> = Result<T, Annotation>;
109
109
  * param. One of these should be called and its value returned.
110
110
  */
111
111
  type AcceptanceFn<O, I = unknown> = (blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O>;
112
- type Next<O, I = unknown> = Decoder<O> | ((blob: I, ok: (value: O) => DecodeResult<O>, err: (msg: string | Annotation) => DecodeResult<O>) => DecodeResult<O> | Decoder<O>);
113
112
  interface Decoder<T> {
114
113
  /**
115
114
  * Verifies untrusted input. Either returns a value, or throws a decoding
@@ -151,16 +150,18 @@ interface Decoder<T> {
151
150
  */
152
151
  describe(message: string): Decoder<T>;
153
152
  /**
154
- * Send the output of the current decoder into another decoder or acceptance
155
- * function. The given acceptance function will receive the output of the
156
- * current decoder as its input.
153
+ * Send the output of the current decoder into an acceptance function. The
154
+ * given acceptance function will receive the output of the current decoder
155
+ * as its input.
157
156
  *
158
157
  * > _**NOTE:** This is an advanced, low-level, API. It's not recommended
159
158
  * > to reach for this construct unless there is no other way. Most cases can
160
159
  * > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
161
160
  * > instead._
162
161
  */
163
- chain<V>(next: Next<V, T>): Decoder<V>;
162
+ chain<V>(next: (blob: T, ok: (value: V) => DecodeResult<V>, err: (msg: string | Annotation) => DecodeResult<V>) => DecodeResult<V> | Decoder<V>): Decoder<V>;
163
+ /** @deprecated To send the output into another decoder, use `.pipe()` instead. */
164
+ chain<V>(next: Decoder<V>): Decoder<V>;
164
165
  /**
165
166
  * Send the output of this decoder as input to another decoder.
166
167
  *
@@ -218,6 +219,46 @@ type Formatter = (err: Annotation) => string | Error;
218
219
  declare function formatInline(ann: Annotation): string;
219
220
  declare function formatShort(ann: Annotation): string;
220
221
 
222
+ /**
223
+ * Forces TypeScript to "evaluate" named helper types, making API signatures
224
+ * clearer in IDEs.
225
+ *
226
+ * @see https://effectivetypescript.com/2022/02/25/gentips-4-display/
227
+ */
228
+ type Resolve$1<T> = T extends (...args: unknown[]) => unknown ? T : {
229
+ [K in keyof T]: T[K];
230
+ };
231
+ /**
232
+ * Relaxes a discriminated union type definition, by explicitly adding
233
+ * properties defined in any other member as optional `never`.
234
+ *
235
+ * This makes accessing the members much more relaxed in TypeScript.
236
+ */
237
+ type Relax<T> = DistributiveRelax<T, T extends any ? keyof T : never>;
238
+ type DistributiveRelax<T, Ks extends string | number | symbol> = T extends any ? Resolve$1<{
239
+ [K in keyof T]: T[K];
240
+ } & {
241
+ [K in Exclude<Ks, keyof T>]?: never;
242
+ }> : never;
243
+
244
+ type SizeOptions = Relax<{
245
+ size: number;
246
+ } | {
247
+ min: number;
248
+ max?: number;
249
+ } | {
250
+ min?: number;
251
+ max: number;
252
+ }>;
253
+ /**
254
+ * Anything with a .length or .size property, like strings, arrays, or sets.
255
+ */
256
+ type Sized = Relax<{
257
+ length: number;
258
+ } | {
259
+ size: number;
260
+ }>;
261
+
221
262
  /**
222
263
  * Accepts any array, but doesn't validate its items further.
223
264
  *
@@ -227,7 +268,7 @@ declare const poja: Decoder<unknown[]>;
227
268
  /**
228
269
  * Accepts arrays of whatever the given decoder accepts.
229
270
  */
230
- declare function array<T>(decoder: Decoder<T>): Decoder<T[]>;
271
+ declare function array<T>(decoder: Decoder<T>, options?: SizeOptions): Decoder<T[]>;
231
272
  /**
232
273
  * Like `array()`, but will reject arrays with 0 elements.
233
274
  */
@@ -365,7 +406,7 @@ declare const isoDate: Decoder<Date>;
365
406
  declare const flexDate: Decoder<Date>;
366
407
  /** @deprecated Renamed to `isoDateString`. This alias will be removed in 3.x. */
367
408
  declare const dateString: Decoder<string>;
368
- /** Alias of `isoDate`. */
409
+ /** @deprecated Renamed to `isoDate`. */
369
410
  declare const iso8601: Decoder<Date>;
370
411
  /** @deprecated Renamed to `flexDate`. This alias will be removed in 3.x. */
371
412
  declare const datelike: Decoder<Date>;
@@ -401,46 +442,6 @@ declare const jsonArray: Decoder<JSONArray>;
401
442
  */
402
443
  declare const json: Decoder<JSONValue>;
403
444
 
404
- /**
405
- * Forces TypeScript to "evaluate" named helper types, making API signatures
406
- * clearer in IDEs.
407
- *
408
- * @see https://effectivetypescript.com/2022/02/25/gentips-4-display/
409
- */
410
- type Resolve$1<T> = T extends (...args: unknown[]) => unknown ? T : {
411
- [K in keyof T]: T[K];
412
- };
413
- /**
414
- * Relaxes a discriminated union type definition, by explicitly adding
415
- * properties defined in any other member as optional `never`.
416
- *
417
- * This makes accessing the members much more relaxed in TypeScript.
418
- */
419
- type Relax<T> = DistributiveRelax<T, T extends any ? keyof T : never>;
420
- type DistributiveRelax<T, Ks extends string | number | symbol> = T extends any ? Resolve$1<{
421
- [K in keyof T]: T[K];
422
- } & {
423
- [K in Exclude<Ks, keyof T>]?: never;
424
- }> : never;
425
-
426
- type SizeOptions = Relax<{
427
- size: number;
428
- } | {
429
- min: number;
430
- max?: number;
431
- } | {
432
- min?: number;
433
- max: number;
434
- }>;
435
- /**
436
- * Anything with a .length or .size property, like strings, arrays, or sets.
437
- */
438
- type Sized = Relax<{
439
- length: number;
440
- } | {
441
- size: number;
442
- }>;
443
-
444
445
  interface Klass<T> extends Function {
445
446
  new (...args: readonly any[]): T;
446
447
  }
@@ -475,21 +476,28 @@ declare function prep<T>(mapperFn: (blob: unknown) => unknown, decoder: Decoder<
475
476
  */
476
477
  declare const anyNumber: Decoder<number>;
477
478
  /**
478
- * Accepts finite numbers (can be integer or float values). Values `NaN`,
479
- * or positive and negative `Infinity` will get rejected.
479
+ * Accepts only finite numbers (e.g. -3.14, 0, 1, 42, ...).
480
+ * Integers or floats, but not `NaN` or `Infinity`.
480
481
  */
481
482
  declare const number: Decoder<number>;
482
483
  /**
483
- * Accepts only finite whole numbers.
484
+ * Accepts only integers (e.g. ..., -2, -1, 0, 1, 2, ...).
485
+ * Whole numbers, and finite.
484
486
  */
485
487
  declare const integer: Decoder<number>;
486
488
  /**
487
- * Accepts only non-negative (zero or positive) finite numbers.
489
+ * Accepts only non-negative numbers (e.g. 0, 0.5, 1, 3.14, ...).
490
+ * Integers or floats, >= 0, and finite.
488
491
  */
489
- declare const positiveNumber: Decoder<number>;
492
+ declare const nonNegativeNumber: Decoder<number>;
490
493
  /**
491
- * Accepts only non-negative (zero or positive) finite whole numbers.
494
+ * Accepts only the natural numbers (e.g. 0, 1, 2, 3, ...).
495
+ * Whole numbers, >= 0, and finite.
492
496
  */
497
+ declare const natural: Decoder<number>;
498
+ /** @deprecated Renamed to `nonNegativeNumber`. */
499
+ declare const positiveNumber: Decoder<number>;
500
+ /** @deprecated Renamed to `natural`. */
493
501
  declare const positiveInteger: Decoder<number>;
494
502
  /**
495
503
  * Accepts numbers greater than or equal to the given minimum.
@@ -724,4 +732,4 @@ declare function isPromiseLike(value: unknown): value is PromiseLike<unknown>;
724
732
  */
725
733
  declare function isPlainObject(value: unknown): value is Record<string, unknown>;
726
734
 
727
- export { type Annotation, type ArrayAnnotation, type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type ObjectAnnotation, type Ok, type OpaqueAnnotation, type Relax, type Result, type Scalar, type ScalarAnnotation, type SizeOptions, type Sized, public_annotate as _annotate, always, anyNumber, anything, array, between, bigint, boolean, constant, date, dateString, datelike, decimal, define, either, email, endsWith, enum_, err, exact, fail, flexDate, formatInline, formatShort, hexadecimal, httpsUrl, identifier, inexact, instanceOf, integer, isDate, isDecoder, isPlainObject, isPromiseLike, iso8601, isoDate, isoDateString, json, jsonArray, jsonObject, lazy, mapping, max, min, nanoid, never, nonEmptyArray, nonEmptyString, null_, nullable, nullish, number, numeric, object, ok, oneOf, optional, poja, pojo, positiveInteger, positiveNumber, prep, record, regex, select, setFromArray, sized, startsWith, string, taggedUnion, truthy, tuple, undefined_, unknown, url, urlString, uuid, uuidv1, uuidv4 };
735
+ export { type Annotation, type ArrayAnnotation, type DecodeResult, type Decoder, type DecoderType, type Err, type Formatter, type JSONArray, type JSONObject, type JSONValue, type ObjectAnnotation, type Ok, type OpaqueAnnotation, type Relax, type Result, type Scalar, type ScalarAnnotation, type SizeOptions, type Sized, public_annotate as _annotate, always, anyNumber, anything, array, between, bigint, boolean, constant, date, dateString, datelike, decimal, define, either, email, endsWith, enum_, err, exact, fail, flexDate, formatInline, formatShort, hexadecimal, httpsUrl, identifier, inexact, instanceOf, integer, isDate, isDecoder, isPlainObject, isPromiseLike, iso8601, isoDate, isoDateString, json, jsonArray, jsonObject, lazy, mapping, max, min, nanoid, natural, never, nonEmptyArray, nonEmptyString, nonNegativeNumber, null_, nullable, nullish, number, numeric, object, ok, oneOf, optional, poja, pojo, positiveInteger, positiveNumber, prep, record, regex, select, setFromArray, sized, startsWith, string, taggedUnion, truthy, tuple, undefined_, unknown, url, urlString, uuid, uuidv1, uuidv4 };
package/dist/index.js CHANGED
@@ -29,56 +29,87 @@ function isPlainObject(value) {
29
29
  // something is a pojo... ¯\_(ツ)_/¯
30
30
  Object.prototype.toString.call(value) === "[object Object]";
31
31
  }
32
+ function assertNever(_value, msg = "Unhandled case") {
33
+ throw new Error(msg);
34
+ }
32
35
 
33
36
  // src/core/annotate.ts
34
37
  var kAnnotationRegistry = /* @__PURE__ */ Symbol.for("decoders.kAnnotationRegistry");
35
- var _register = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
36
- function brand(ann) {
37
- _register.add(ann);
38
+ var _stamped = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
39
+ function stamp(ann) {
40
+ _stamped.add(ann);
38
41
  return ann;
39
42
  }
40
- function makeObjectAnn(fields, text) {
41
- return brand({ type: "object", fields, text });
43
+ function makeObjectAnn(getFields, text) {
44
+ let fields;
45
+ return stamp({
46
+ type: "object",
47
+ get fields() {
48
+ return fields ??= getFields();
49
+ },
50
+ text
51
+ });
42
52
  }
43
- function makeArrayAnn(items, text) {
44
- return brand({ type: "array", items, text });
53
+ function makeArrayAnn(getItems, text) {
54
+ let items;
55
+ return stamp({
56
+ type: "array",
57
+ get items() {
58
+ return items ??= getItems();
59
+ },
60
+ text
61
+ });
45
62
  }
46
63
  function makeOpaqueAnn(value, text) {
47
- return brand({ type: "opaque", value, text });
64
+ return stamp({ type: "opaque", value, text });
48
65
  }
49
66
  function makeScalarAnn(value, text) {
50
- return brand({ type: "scalar", value, text });
67
+ return stamp({ type: "scalar", value, text });
51
68
  }
52
69
  function updateText(annotation, text) {
53
- if (text !== void 0) {
54
- return brand({ ...annotation, text });
55
- } else {
70
+ if (text === void 0) {
56
71
  return annotation;
57
72
  }
73
+ switch (annotation.type) {
74
+ case "object":
75
+ return makeObjectAnn(() => annotation.fields, text);
76
+ case "array":
77
+ return makeArrayAnn(() => annotation.items, text);
78
+ case "scalar":
79
+ return makeScalarAnn(annotation.value, text);
80
+ case "opaque":
81
+ return makeOpaqueAnn(annotation.value, text);
82
+ // istanbul ignore next -- @preserve
83
+ default:
84
+ return assertNever(annotation, "Unknown annotation type");
85
+ }
58
86
  }
59
87
  function merge(objAnnotation, fields) {
60
- const newFields = new Map([...objAnnotation.fields, ...fields]);
61
- return makeObjectAnn(newFields, objAnnotation.text);
88
+ return makeObjectAnn(
89
+ () => new Map([...objAnnotation.fields, ...fields]),
90
+ objAnnotation.text
91
+ );
62
92
  }
63
93
  function isAnnotation(thing) {
64
- return _register.has(thing);
94
+ return _stamped.has(thing);
65
95
  }
66
96
  function annotateArray(arr, text, seen) {
67
97
  seen.add(arr);
68
- const items = [];
69
- for (const value of arr) {
70
- items.push(__annotate(value, void 0, seen));
71
- }
72
- return makeArrayAnn(items, text);
98
+ return makeArrayAnn(
99
+ () => Array.from(arr, (value) => __annotate(value, void 0, seen)),
100
+ text
101
+ );
73
102
  }
74
103
  function annotateObject(obj, text, seen) {
75
104
  seen.add(obj);
76
- const fields = /* @__PURE__ */ new Map();
77
- for (const key of Object.keys(obj)) {
78
- const value = obj[key];
79
- fields.set(key, __annotate(value, void 0, seen));
80
- }
81
- return makeObjectAnn(fields, text);
105
+ return makeObjectAnn(() => {
106
+ const fields = /* @__PURE__ */ new Map();
107
+ for (const key of Object.keys(obj)) {
108
+ const value = obj[key];
109
+ fields.set(key, __annotate(value, void 0, seen));
110
+ }
111
+ return fields;
112
+ }, text);
82
113
  }
83
114
  function __annotate(value, text, seen) {
84
115
  if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "symbol" || typeof value === "bigint" || typeof value.getMonth === "function") {
@@ -299,6 +330,9 @@ function* iterAnnotation(ann, stack) {
299
330
  case "opaque": {
300
331
  break;
301
332
  }
333
+ // istanbul ignore next -- @preserve
334
+ default:
335
+ assertNever(ann, "Unknown annotation type");
302
336
  }
303
337
  }
304
338
  function formatAsIssues(ann) {
@@ -398,7 +432,7 @@ function define(fn) {
398
432
  }
399
433
  });
400
434
  }
401
- const unregistered = {
435
+ const newDecoder = {
402
436
  verify,
403
437
  value,
404
438
  decode,
@@ -423,80 +457,18 @@ function define(fn) {
423
457
  }
424
458
  }
425
459
  };
426
- const self = brand2(unregistered);
460
+ const self = stamp2(newDecoder);
427
461
  return self;
428
462
  }
429
463
  var kDecoderRegistry = /* @__PURE__ */ Symbol.for("decoders.kDecoderRegistry");
430
- var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
431
- function brand2(decoder) {
432
- _register2.add(decoder);
464
+ var _stamped2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
465
+ function stamp2(decoder) {
466
+ _stamped2.add(decoder);
433
467
  return decoder;
434
468
  }
435
469
  // @__NO_SIDE_EFFECTS__
436
470
  function isDecoder(value) {
437
- return _register2.has(value);
438
- }
439
-
440
- // src/arrays.ts
441
- var poja = define((blob, ok2, err2) => {
442
- if (!Array.isArray(blob)) {
443
- return err2("Must be an array");
444
- }
445
- return ok2(blob);
446
- });
447
- // @__NO_SIDE_EFFECTS__
448
- function array(decoder) {
449
- const decodeFn = decoder.decode;
450
- return poja.chain((inputs, ok2, err2) => {
451
- const results = [];
452
- for (let i = 0; i < inputs.length; ++i) {
453
- const blob = inputs[i];
454
- const result = decodeFn(blob);
455
- if (result.ok) {
456
- results.push(result.value);
457
- } else {
458
- results.length = 0;
459
- const ann = result.error;
460
- const clone = inputs.slice();
461
- clone.splice(
462
- i,
463
- 1,
464
- public_annotate(ann, ann.text ? `${ann.text} (at index ${i})` : `index ${i}`)
465
- );
466
- return err2(public_annotate(clone));
467
- }
468
- }
469
- return ok2(results);
470
- });
471
- }
472
- function isNonEmpty(arr) {
473
- return arr.length > 0;
474
- }
475
- // @__NO_SIDE_EFFECTS__
476
- function nonEmptyArray(decoder) {
477
- return (/* @__PURE__ */ array(decoder)).refine(isNonEmpty, "Must have at least 1 item");
478
- }
479
- var ntuple = /* @__NO_SIDE_EFFECTS__ */ (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
480
- // @__NO_SIDE_EFFECTS__
481
- function tuple(...decoders) {
482
- return (/* @__PURE__ */ ntuple(decoders.length)).chain((blobs, ok2, err2) => {
483
- let allOk = true;
484
- const rvs = decoders.map((decoder, i) => {
485
- const blob = blobs[i];
486
- const result = decoder.decode(blob);
487
- if (result.ok) {
488
- return result.value;
489
- } else {
490
- allOk = false;
491
- return result.error;
492
- }
493
- });
494
- if (allOk) {
495
- return ok2(rvs);
496
- } else {
497
- return err2(public_annotate(rvs));
498
- }
499
- });
471
+ return _stamped2.has(value);
500
472
  }
501
473
 
502
474
  // src/lib/size-options.ts
@@ -561,6 +533,66 @@ function prep(mapperFn, decoder) {
561
533
  });
562
534
  }
563
535
 
536
+ // src/arrays.ts
537
+ var poja = define((blob, ok2, err2) => {
538
+ if (!Array.isArray(blob)) {
539
+ return err2("Must be an array");
540
+ }
541
+ return ok2(blob);
542
+ });
543
+ // @__NO_SIDE_EFFECTS__
544
+ function array(decoder, options) {
545
+ const decodeFn = decoder.decode;
546
+ const base = options !== void 0 ? sized(poja, options) : poja;
547
+ return base.chain((inputs, ok2, err2) => {
548
+ const results = [];
549
+ for (let i = 0; i < inputs.length; ++i) {
550
+ const blob = inputs[i];
551
+ const result = decodeFn(blob);
552
+ if (result.ok) {
553
+ results.push(result.value);
554
+ } else {
555
+ results.length = 0;
556
+ const ann = result.error;
557
+ const clone = inputs.slice();
558
+ clone.splice(
559
+ i,
560
+ 1,
561
+ public_annotate(ann, ann.text ? `${ann.text} (at index ${i})` : `index ${i}`)
562
+ );
563
+ return err2(public_annotate(clone));
564
+ }
565
+ }
566
+ return ok2(results);
567
+ });
568
+ }
569
+ // @__NO_SIDE_EFFECTS__
570
+ function nonEmptyArray(decoder) {
571
+ return (/* @__PURE__ */ array(decoder, { min: 1 })).refineType();
572
+ }
573
+ var ntuple = /* @__NO_SIDE_EFFECTS__ */ (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
574
+ // @__NO_SIDE_EFFECTS__
575
+ function tuple(...decoders) {
576
+ return (/* @__PURE__ */ ntuple(decoders.length)).chain((blobs, ok2, err2) => {
577
+ let allOk = true;
578
+ const rvs = decoders.map((decoder, i) => {
579
+ const blob = blobs[i];
580
+ const result = decoder.decode(blob);
581
+ if (result.ok) {
582
+ return result.value;
583
+ } else {
584
+ allOk = false;
585
+ return result.error;
586
+ }
587
+ });
588
+ if (allOk) {
589
+ return ok2(rvs);
590
+ } else {
591
+ return err2(public_annotate(rvs));
592
+ }
593
+ });
594
+ }
595
+
564
596
  // src/lib/set-methods.ts
565
597
  // @__NO_SIDE_EFFECTS__
566
598
  function difference(xs, ys) {
@@ -900,7 +932,9 @@ function nanoid(options) {
900
932
  return sized(/* @__PURE__ */ regex(/^[a-z0-9_-]+$/i, "Must be nano ID"), options ?? { size: 21 });
901
933
  }
902
934
  var uuid = /* @__PURE__ */ regex(
903
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
935
+ // https://www.rfc-editor.org/rfc/rfc9562#section-4
936
+ // Version 1-8 with variant 8, 9, a, or b, plus the Nil and Max UUIDs (sections 5.9, 5.10)
937
+ /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i,
904
938
  "Must be uuid"
905
939
  );
906
940
  var uuidv1 = (
@@ -953,14 +987,16 @@ var integer = /* @__PURE__ */ number.refine(
953
987
  (n) => Number.isInteger(n),
954
988
  "Number must be an integer"
955
989
  );
956
- var positiveNumber = /* @__PURE__ */ number.refine(
990
+ var nonNegativeNumber = /* @__PURE__ */ number.refine(
957
991
  (n) => n >= 0 && !Object.is(n, -0),
958
992
  "Number must be positive"
959
993
  );
960
- var positiveInteger = /* @__PURE__ */ integer.refine(
994
+ var natural = /* @__PURE__ */ integer.refine(
961
995
  (n) => n >= 0 && !Object.is(n, -0),
962
996
  "Number must be positive"
963
997
  );
998
+ var positiveNumber = nonNegativeNumber;
999
+ var positiveInteger = natural;
964
1000
  // @__NO_SIDE_EFFECTS__
965
1001
  function min(min2, decoder = number) {
966
1002
  return decoder.reject(
@@ -1040,9 +1076,11 @@ export {
1040
1076
  max,
1041
1077
  min,
1042
1078
  nanoid,
1079
+ natural,
1043
1080
  never,
1044
1081
  nonEmptyArray,
1045
1082
  nonEmptyString,
1083
+ nonNegativeNumber,
1046
1084
  null_,
1047
1085
  nullable,
1048
1086
  nullish,
@@ -1075,4 +1113,5 @@ export {
1075
1113
  uuidv1,
1076
1114
  uuidv4
1077
1115
  };
1116
+ // istanbul ignore next -- @preserve
1078
1117
  // istanbul ignore else -- @preserve
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,17 +35,17 @@
35
35
  ],
36
36
  "scripts": {
37
37
  "build": "tsup",
38
- "docs": "npm run build && cp -r dist/ docs/decoders-latest-snapshot/ && cd docs && rm -rf .next && npm run dev",
38
+ "docs": "pnpm run build && cp -r dist/ docs/decoders-latest-snapshot/ && cd docs && rm -rf .next && pnpm run dev",
39
39
  "docs:update-sources": "node bin/update-source-lines.js",
40
40
  "docs:update-redirects": "node bin/update-decoder-redirects.js",
41
41
  "lint": "oxlint src/ test/ && prettier --list-different src/ test/ test-d/ && node bin/lint-side-effects.js",
42
42
  "lint:package": "publint --strict && attw --pack",
43
- "format": "oxlint --fix src/ test/ ; prettier --write src/ test/ test-d/ docs/content/ ; npm run docs:update-sources ; npm run docs:update-redirects",
43
+ "format": "oxlint --fix src/ test/ ; prettier --write src/ test/ test-d/ docs/content/ ; pnpm run docs:update-sources ; pnpm run docs:update-redirects",
44
44
  "test": "vitest run --coverage",
45
- "test:treeshake": "npm run build && node bin/test-treeshake.js",
45
+ "test:treeshake": "pnpm run build && node bin/test-treeshake.js",
46
46
  "test:completeness": "./bin/check.sh",
47
47
  "test:typescript": "tsc --noEmit",
48
- "test:types": "npm run build && tsd --typings ./dist/index.d.ts",
48
+ "test:types": "pnpm run build && tsd --typings ./dist/index.d.ts",
49
49
  "test:watch": "vitest --coverage",
50
50
  "release": "release-it"
51
51
  },
@@ -80,5 +80,6 @@
80
80
  "vitest": "^4.1.11"
81
81
  },
82
82
  "githubUrl": "https://github.com/nvie/decoders",
83
- "sideEffects": false
83
+ "sideEffects": false,
84
+ "packageManager": "pnpm@11.24.0"
84
85
  }