decoders 2.6.1 → 2.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  <img alt="Decoders logo" src="./docs/assets/logo@2x.png" style="width: 100%; max-width: 830px; max-height: 248px" width="830" /><br />
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/decoders.svg)](https://www.npmjs.com/package/decoders)
4
- [![Build Status](https://github.com/nvie/decoders/workflows/test/badge.svg)](https://github.com/nvie/decoders/actions)
4
+ [![Test Status](https://github.com/nvie/decoders/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/nvie/decoders/actions)
5
5
  [![Bundle size for decoders](https://pkg-size.dev/badge/bundle/4200)](https://pkg-size.dev/decoders)
6
6
 
7
7
  Elegant and battle-tested validation library for type-safe input data for
package/dist/index.cjs CHANGED
@@ -311,11 +311,8 @@ ${formatted}`);
311
311
  }
312
312
  function define(fn) {
313
313
  function decode(blob) {
314
- return fn(
315
- blob,
316
- ok,
317
- (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg))
318
- );
314
+ const makeFlexErr = (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg));
315
+ return fn(blob, ok, makeFlexErr);
319
316
  }
320
317
  function verify(blob, formatter = formatInline) {
321
318
  const result = decode(blob);
@@ -342,6 +339,9 @@ function define(fn) {
342
339
  )
343
340
  );
344
341
  }
342
+ function refineType() {
343
+ return self;
344
+ }
345
345
  function then(next) {
346
346
  return define((blob, ok2, err2) => {
347
347
  const r1 = decode(blob);
@@ -369,12 +369,13 @@ function define(fn) {
369
369
  }
370
370
  });
371
371
  }
372
- return brand2({
372
+ const unregistered = {
373
373
  verify,
374
374
  value,
375
375
  decode,
376
376
  transform,
377
377
  refine,
378
+ refineType,
378
379
  reject,
379
380
  describe,
380
381
  then,
@@ -392,7 +393,9 @@ function define(fn) {
392
393
  }
393
394
  }
394
395
  }
395
- });
396
+ };
397
+ const self = brand2(unregistered);
398
+ return self;
396
399
  }
397
400
  var kDecoderRegistry = Symbol.for("decoders.kDecoderRegistry");
398
401
  var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
@@ -411,30 +414,28 @@ var poja = define((blob, ok2, err2) => {
411
414
  }
412
415
  return ok2(blob);
413
416
  });
414
- function all(items, blobs, ok2, err2) {
415
- const results = [];
416
- for (let index = 0; index < items.length; ++index) {
417
- const result = items[index];
418
- if (result.ok) {
419
- results.push(result.value);
420
- } else {
421
- const ann = result.error;
422
- const clone = blobs.slice();
423
- clone.splice(
424
- index,
425
- 1,
426
- public_annotate(ann, ann.text ? `${ann.text} (at index ${index})` : `index ${index}`)
427
- );
428
- return err2(public_annotate(clone));
429
- }
430
- }
431
- return ok2(results);
432
- }
433
417
  function array(decoder) {
434
418
  const decodeFn = decoder.decode;
435
- return poja.then((blobs, ok2, err2) => {
436
- const results = blobs.map(decodeFn);
437
- return all(results, blobs, ok2, err2);
419
+ return poja.then((inputs, ok2, err2) => {
420
+ const results = [];
421
+ for (let i = 0; i < inputs.length; ++i) {
422
+ const blob = inputs[i];
423
+ const result = decodeFn(blob);
424
+ if (result.ok) {
425
+ results.push(result.value);
426
+ } else {
427
+ results.length = 0;
428
+ const ann = result.error;
429
+ const clone = inputs.slice();
430
+ clone.splice(
431
+ i,
432
+ 1,
433
+ public_annotate(ann, ann.text ? `${ann.text} (at index ${i})` : `index ${i}`)
434
+ );
435
+ return err2(public_annotate(clone));
436
+ }
437
+ }
438
+ return ok2(results);
438
439
  });
439
440
  }
440
441
  function isNonEmpty(arr) {
@@ -533,9 +534,7 @@ function object(decoders) {
533
534
  if (rawValue === void 0) {
534
535
  missingKeys.add(key);
535
536
  } else {
536
- if (errors === null) {
537
- errors = /* @__PURE__ */ new Map();
538
- }
537
+ errors ??= /* @__PURE__ */ new Map();
539
538
  errors.set(key, ann);
540
539
  }
541
540
  }
@@ -820,19 +819,14 @@ var iso8601_re = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.]\d+)?(?:Z|[+-]\d{2}:
820
819
  var date = define((blob, ok2, err2) => {
821
820
  return isDate(blob) ? ok2(blob) : err2("Must be a Date");
822
821
  });
823
- var iso8601 = (
824
- // Input itself needs to match the ISO8601 regex...
825
- regex(iso8601_re, "Must be ISO8601 format").transform(
826
- // Make sure it is a _valid_ date
827
- (value) => {
828
- const date2 = new Date(value);
829
- if (isNaN(date2.getTime())) {
830
- throw new Error("Must be valid date/time value");
831
- }
832
- return date2;
833
- }
834
- )
822
+ var dateString = regex(
823
+ iso8601_re,
824
+ "Must be ISO8601 format"
825
+ ).refine(
826
+ (value) => !isNaN(new Date(value).getTime()),
827
+ "Must be valid date/time value"
835
828
  );
829
+ var iso8601 = dateString.transform((value) => new Date(value));
836
830
  var datelike = either(date, iso8601).describe("Must be a Date or date string");
837
831
 
838
832
  // src/numbers.ts
package/dist/index.d.cts CHANGED
@@ -130,6 +130,11 @@ interface Decoder<T> {
130
130
  */
131
131
  refine<N extends T>(predicate: (value: T) => value is N, msg: string): Decoder<N>;
132
132
  refine(predicate: (value: T) => boolean, msg: string): Decoder<T>;
133
+ /**
134
+ * Cast the return type of this read-only decoder to a narrower type. This is
135
+ * useful to return "branded" types. This method has no runtime effect.
136
+ */
137
+ refineType<SubT extends T>(): Decoder<SubT>;
133
138
  /**
134
139
  * Build a new decoder from the current one, with an extra rejection
135
140
  * criterium.
package/dist/index.d.ts CHANGED
@@ -130,6 +130,11 @@ interface Decoder<T> {
130
130
  */
131
131
  refine<N extends T>(predicate: (value: T) => value is N, msg: string): Decoder<N>;
132
132
  refine(predicate: (value: T) => boolean, msg: string): Decoder<T>;
133
+ /**
134
+ * Cast the return type of this read-only decoder to a narrower type. This is
135
+ * useful to return "branded" types. This method has no runtime effect.
136
+ */
137
+ refineType<SubT extends T>(): Decoder<SubT>;
133
138
  /**
134
139
  * Build a new decoder from the current one, with an extra rejection
135
140
  * criterium.
package/dist/index.js CHANGED
@@ -311,11 +311,8 @@ ${formatted}`);
311
311
  }
312
312
  function define(fn) {
313
313
  function decode(blob) {
314
- return fn(
315
- blob,
316
- ok,
317
- (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg))
318
- );
314
+ const makeFlexErr = (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg));
315
+ return fn(blob, ok, makeFlexErr);
319
316
  }
320
317
  function verify(blob, formatter = formatInline) {
321
318
  const result = decode(blob);
@@ -342,6 +339,9 @@ function define(fn) {
342
339
  )
343
340
  );
344
341
  }
342
+ function refineType() {
343
+ return self;
344
+ }
345
345
  function then(next) {
346
346
  return define((blob, ok2, err2) => {
347
347
  const r1 = decode(blob);
@@ -369,12 +369,13 @@ function define(fn) {
369
369
  }
370
370
  });
371
371
  }
372
- return brand2({
372
+ const unregistered = {
373
373
  verify,
374
374
  value,
375
375
  decode,
376
376
  transform,
377
377
  refine,
378
+ refineType,
378
379
  reject,
379
380
  describe,
380
381
  then,
@@ -392,7 +393,9 @@ function define(fn) {
392
393
  }
393
394
  }
394
395
  }
395
- });
396
+ };
397
+ const self = brand2(unregistered);
398
+ return self;
396
399
  }
397
400
  var kDecoderRegistry = Symbol.for("decoders.kDecoderRegistry");
398
401
  var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
@@ -411,30 +414,28 @@ var poja = define((blob, ok2, err2) => {
411
414
  }
412
415
  return ok2(blob);
413
416
  });
414
- function all(items, blobs, ok2, err2) {
415
- const results = [];
416
- for (let index = 0; index < items.length; ++index) {
417
- const result = items[index];
418
- if (result.ok) {
419
- results.push(result.value);
420
- } else {
421
- const ann = result.error;
422
- const clone = blobs.slice();
423
- clone.splice(
424
- index,
425
- 1,
426
- public_annotate(ann, ann.text ? `${ann.text} (at index ${index})` : `index ${index}`)
427
- );
428
- return err2(public_annotate(clone));
429
- }
430
- }
431
- return ok2(results);
432
- }
433
417
  function array(decoder) {
434
418
  const decodeFn = decoder.decode;
435
- return poja.then((blobs, ok2, err2) => {
436
- const results = blobs.map(decodeFn);
437
- return all(results, blobs, ok2, err2);
419
+ return poja.then((inputs, ok2, err2) => {
420
+ const results = [];
421
+ for (let i = 0; i < inputs.length; ++i) {
422
+ const blob = inputs[i];
423
+ const result = decodeFn(blob);
424
+ if (result.ok) {
425
+ results.push(result.value);
426
+ } else {
427
+ results.length = 0;
428
+ const ann = result.error;
429
+ const clone = inputs.slice();
430
+ clone.splice(
431
+ i,
432
+ 1,
433
+ public_annotate(ann, ann.text ? `${ann.text} (at index ${i})` : `index ${i}`)
434
+ );
435
+ return err2(public_annotate(clone));
436
+ }
437
+ }
438
+ return ok2(results);
438
439
  });
439
440
  }
440
441
  function isNonEmpty(arr) {
@@ -533,9 +534,7 @@ function object(decoders) {
533
534
  if (rawValue === void 0) {
534
535
  missingKeys.add(key);
535
536
  } else {
536
- if (errors === null) {
537
- errors = /* @__PURE__ */ new Map();
538
- }
537
+ errors ??= /* @__PURE__ */ new Map();
539
538
  errors.set(key, ann);
540
539
  }
541
540
  }
@@ -820,19 +819,14 @@ var iso8601_re = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.]\d+)?(?:Z|[+-]\d{2}:
820
819
  var date = define((blob, ok2, err2) => {
821
820
  return isDate(blob) ? ok2(blob) : err2("Must be a Date");
822
821
  });
823
- var iso8601 = (
824
- // Input itself needs to match the ISO8601 regex...
825
- regex(iso8601_re, "Must be ISO8601 format").transform(
826
- // Make sure it is a _valid_ date
827
- (value) => {
828
- const date2 = new Date(value);
829
- if (isNaN(date2.getTime())) {
830
- throw new Error("Must be valid date/time value");
831
- }
832
- return date2;
833
- }
834
- )
822
+ var dateString = regex(
823
+ iso8601_re,
824
+ "Must be ISO8601 format"
825
+ ).refine(
826
+ (value) => !isNaN(new Date(value).getTime()),
827
+ "Must be valid date/time value"
835
828
  );
829
+ var iso8601 = dateString.transform((value) => new Date(value));
836
830
  var datelike = either(date, iso8601).describe("Must be a Date or date string");
837
831
 
838
832
  // src/numbers.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -61,26 +61,26 @@
61
61
  ],
62
62
  "devDependencies": {
63
63
  "@arethetypeswrong/cli": "^0.17.4",
64
- "@eslint/js": "^9.21.0",
64
+ "@eslint/js": "^9.23.0",
65
65
  "@release-it/keep-a-changelog": "^6.0.0",
66
66
  "@standard-schema/spec": "^1.0.0",
67
- "@vitest/coverage-istanbul": "^3.0.7",
68
- "eslint": "^9.21.0",
67
+ "@vitest/coverage-istanbul": "^3.0.9",
68
+ "eslint": "^9.23.0",
69
69
  "eslint-plugin-import": "^2.31.0",
70
70
  "eslint-plugin-simple-import-sort": "^12.1.1",
71
- "fast-check": "^3.23.2",
71
+ "fast-check": "^4.0.0",
72
72
  "itertools": "^2.4.1",
73
- "pkg-pr-new": "^0.0.39",
74
- "prettier": "^3.5.2",
75
- "publint": "^0.3.6",
73
+ "pkg-pr-new": "^0.0.41",
74
+ "prettier": "^3.5.3",
75
+ "publint": "^0.3.9",
76
76
  "release-it": "^18.1.2",
77
77
  "ts-morph": "^25.0.1",
78
78
  "tsd": "^0.31.2",
79
- "tsup": "^8.3.6",
80
- "typescript": "^5.7.3",
81
- "typescript-eslint": "^8.25.0",
79
+ "tsup": "^8.4.0",
80
+ "typescript": "^5.8.2",
81
+ "typescript-eslint": "^8.28.0",
82
82
  "vite-tsconfig-paths": "^5.1.4",
83
- "vitest": "^3.0.7"
83
+ "vitest": "^3.0.9"
84
84
  },
85
85
  "githubUrl": "https://github.com/nvie/decoders",
86
86
  "sideEffects": false