decoders 2.8.0 → 2.9.0-pre.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,31 +1,22 @@
1
- <img alt="Decoders logo" src="./docs/assets/logo@2x.png" style="width: 100%; max-width: 830px; max-height: 248px" width="830" /><br />
1
+ <img alt="Decoders logo" src="./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
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
- Elegant and battle-tested validation library for type-safe input data for
8
- [TypeScript](https://www.typescriptlang.org/).
9
-
10
- ## Introduction
11
-
12
- Data entering your application from the outside world should not be trusted without
13
- validation and often is of the `any` type, effectively disabling your type checker around
14
- input values. It's an industry good practice to validate your expectations right at your
15
- program's boundaries. This has two benefits: (1) your inputs are getting validated, and
16
- (2) you can now statically know for sure the shape of the incoming data. **Decoders help
17
- solve both of these problems at once.**
7
+ Elegant and battle-tested validation library for type-safe input data for TypeScript.
18
8
 
19
9
  ## Basic example
20
10
 
21
11
  ```typescript
22
12
  import { array, iso8601, number, object, optional, string } from 'decoders';
23
13
 
24
- // Incoming data at runtime
14
+ // Incoming data at runtime, e.g. the request body
15
+ // The point is that this data is untrusted and its type unknown
25
16
  const externalData = {
26
17
  id: 123,
27
18
  name: 'Alison Roberts',
28
- createdAt: '1994-01-11T12:26:37.024Z',
19
+ createdAt: '2026-01-11T12:26:37.024Z',
29
20
  tags: ['foo', 'bar'],
30
21
  };
31
22
 
@@ -40,7 +31,7 @@ const userDecoder = object({
40
31
  // Call .verify() on the incoming data
41
32
  const user = userDecoder.verify(externalData);
42
33
  // ^^^^
43
- // TypeScript automatically infers this type as:
34
+ // TypeScript will infer this type as:
44
35
  // {
45
36
  // id: number;
46
37
  // name: string;
@@ -71,82 +62,7 @@ correctly!
71
62
 
72
63
  ## Documentation
73
64
 
74
- <div id="$DecoderType"></div>
75
- <div id="DecodeResult"></div>
76
- <div id="Decoder"></div>
77
- <div id="DecoderType"></div>
78
- <div id="Guard"></div>
79
- <div id="JSONArray"></div>
80
- <div id="JSONObject"></div>
81
- <div id="JSONValue"></div>
82
- <div id="Scalar"></div>
83
- <div id="adding-predicates"></div>
84
- <div id="always"></div>
85
- <div id="anyNumber"></div>
86
- <div id="array"></div>
87
- <div id="boolean"></div>
88
- <div id="building-custom-decoders"></div>
89
- <div id="compose"></div>
90
- <div id="compositions"></div>
91
- <div id="constant"></div>
92
- <div id="date"></div>
93
- <div id="define"></div>
94
- <div id="describe"></div>
95
- <div id="dict"></div>
96
- <div id="either"></div>
97
- <div id="email"></div>
98
- <div id="exact"></div>
99
- <div id="fail"></div>
100
- <div id="guard"></div>
101
- <div id="hardcoded"></div>
102
- <div id="httpsUrl"></div>
103
- <div id="inexact"></div>
104
- <div id="instanceOf"></div>
105
- <div id="integer"></div>
106
- <div id="iso8601"></div>
107
- <div id="json"></div>
108
- <div id="jsonArray"></div>
109
- <div id="jsonObject"></div>
110
- <div id="lazy"></div>
111
- <div id="mapping"></div>
112
- <div id="maybe"></div>
113
- <div id="mixed"></div>
114
- <div id="never"></div>
115
- <div id="nonEmptyArray"></div>
116
- <div id="nonEmptyString"></div>
117
- <div id="null_"></div>
118
- <div id="nullable"></div>
119
- <div id="number"></div>
120
- <div id="numericBoolean"></div>
121
- <div id="object"></div>
122
- <div id="oneOf"></div>
123
- <div id="optional"></div>
124
- <div id="poja"></div>
125
- <div id="pojo"></div>
126
- <div id="positiveInteger"></div>
127
- <div id="positiveNumber"></div>
128
- <div id="predicate"></div>
129
- <div id="prep"></div>
130
- <div id="primitives"></div>
131
- <div id="regex"></div>
132
- <div id="set"></div>
133
- <div id="string"></div>
134
- <div id="taggedUnion"></div>
135
- <div id="the-difference-between-object-exact-and-inexact"></div>
136
- <div id="transform"></div>
137
- <div id="transformation"></div>
138
- <div id="truthy"></div>
139
- <div id="tuple"></div>
140
- <div id="undefined_"></div>
141
- <div id="unknown"></div>
142
- <div id="url"></div>
143
- <div id="uuid"></div>
144
- <div id="uuidv1"></div>
145
- <div id="uuidv4"></div>
146
-
147
- Documentation can be found on [https://decoders.cc](https://decoders.cc).
148
-
149
- ## Building your own decoders
65
+ Documentation can be found on [decoders.cc](https://decoders.cc).
150
66
 
151
- There is a dedicated page in the docs that explains how to
152
- [build your own decoders](https://decoders.cc/building-your-own.html) — it’s fun!
67
+ There is a dedicated page that explains how to
68
+ [build your own decoders](https://decoders.cc/docs/building-your-own).
package/dist/index.cjs CHANGED
@@ -21,7 +21,7 @@ function isPlainObject(value) {
21
21
  }
22
22
 
23
23
  // src/core/annotate.ts
24
- var kAnnotationRegistry = Symbol.for("decoders.kAnnotationRegistry");
24
+ var kAnnotationRegistry = /* @__PURE__ */ Symbol.for("decoders.kAnnotationRegistry");
25
25
  var _register = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
26
26
  function brand(ann) {
27
27
  _register.add(ann);
@@ -57,7 +57,7 @@ function annotateArray(arr, text, seen) {
57
57
  seen.add(arr);
58
58
  const items = [];
59
59
  for (const value of arr) {
60
- items.push(annotate(value, void 0, seen));
60
+ items.push(__annotate(value, void 0, seen));
61
61
  }
62
62
  return makeArrayAnn(items, text);
63
63
  }
@@ -66,11 +66,11 @@ function annotateObject(obj, text, seen) {
66
66
  const fields = /* @__PURE__ */ new Map();
67
67
  for (const key of Object.keys(obj)) {
68
68
  const value = obj[key];
69
- fields.set(key, annotate(value, void 0, seen));
69
+ fields.set(key, __annotate(value, void 0, seen));
70
70
  }
71
71
  return makeObjectAnn(fields, text);
72
72
  }
73
- function annotate(value, text, seen) {
73
+ function __annotate(value, text, seen) {
74
74
  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") {
75
75
  return makeScalarAnn(value, text);
76
76
  }
@@ -104,7 +104,7 @@ function annotate(value, text, seen) {
104
104
  }
105
105
  }
106
106
  function public_annotate(value, text) {
107
- return annotate(value, text, /* @__PURE__ */ new WeakSet());
107
+ return __annotate(value, text, /* @__PURE__ */ new WeakSet());
108
108
  }
109
109
  function public_annotateObject(obj, text) {
110
110
  return annotateObject(obj, text, /* @__PURE__ */ new WeakSet());
@@ -338,7 +338,7 @@ function define(fn) {
338
338
  return decode(blob).value;
339
339
  }
340
340
  function transform(transformFn) {
341
- return then(noThrow(transformFn));
341
+ return chain(noThrow(transformFn));
342
342
  }
343
343
  function refine(predicateFn, errmsg) {
344
344
  return reject(
@@ -354,7 +354,7 @@ function define(fn) {
354
354
  function refineType() {
355
355
  return self;
356
356
  }
357
- function then(next) {
357
+ function chain(next) {
358
358
  return define((blob, ok2, err2) => {
359
359
  const r1 = decode(blob);
360
360
  if (!r1.ok) return r1;
@@ -363,10 +363,10 @@ function define(fn) {
363
363
  });
364
364
  }
365
365
  function pipe(next) {
366
- return then(next);
366
+ return chain(next);
367
367
  }
368
368
  function reject(rejectFn) {
369
- return then((blob, ok2, err2) => {
369
+ return chain((blob, ok2, err2) => {
370
370
  const errmsg = rejectFn(blob);
371
371
  return errmsg === null ? ok2(blob) : err2(typeof errmsg === "string" ? public_annotate(blob, errmsg) : errmsg);
372
372
  });
@@ -390,7 +390,7 @@ function define(fn) {
390
390
  refineType,
391
391
  reject,
392
392
  describe,
393
- then,
393
+ chain,
394
394
  pipe,
395
395
  "~standard": {
396
396
  version: 1,
@@ -409,7 +409,7 @@ function define(fn) {
409
409
  const self = brand2(unregistered);
410
410
  return self;
411
411
  }
412
- var kDecoderRegistry = Symbol.for("decoders.kDecoderRegistry");
412
+ var kDecoderRegistry = /* @__PURE__ */ Symbol.for("decoders.kDecoderRegistry");
413
413
  var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
414
414
  function brand2(decoder) {
415
415
  _register2.add(decoder);
@@ -428,7 +428,7 @@ var poja = define((blob, ok2, err2) => {
428
428
  });
429
429
  function array(decoder) {
430
430
  const decodeFn = decoder.decode;
431
- return poja.then((inputs, ok2, err2) => {
431
+ return poja.chain((inputs, ok2, err2) => {
432
432
  const results = [];
433
433
  for (let i = 0; i < inputs.length; ++i) {
434
434
  const blob = inputs[i];
@@ -458,7 +458,7 @@ function nonEmptyArray(decoder) {
458
458
  }
459
459
  var ntuple = (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
460
460
  function tuple(...decoders) {
461
- return ntuple(decoders.length).then((blobs, ok2, err2) => {
461
+ return ntuple(decoders.length).chain((blobs, ok2, err2) => {
462
462
  let allOk = true;
463
463
  const rvs = decoders.map((decoder, i) => {
464
464
  const blob = blobs[i];
@@ -526,7 +526,7 @@ var pojo = define(
526
526
  );
527
527
  function object(decoders) {
528
528
  const knownKeys = new Set(Object.keys(decoders));
529
- return pojo.then((plainObj, ok2, err2) => {
529
+ return pojo.chain((plainObj, ok2, err2) => {
530
530
  const actualKeys = new Set(Object.keys(plainObj));
531
531
  const missingKeys = difference(knownKeys, actualKeys);
532
532
  const record2 = {};
@@ -712,7 +712,7 @@ var truthy = define((blob, ok2, _) => ok2(!!blob));
712
712
  function record(fst, snd) {
713
713
  const keyDecoder = snd !== void 0 ? fst : void 0;
714
714
  const valueDecoder = _nullishCoalesce(snd, () => ( fst));
715
- return pojo.then((input, ok2, err2) => {
715
+ return pojo.chain((input, ok2, err2) => {
716
716
  let rv = {};
717
717
  const errors = /* @__PURE__ */ new Map();
718
718
  for (const key of Object.keys(input)) {
package/dist/index.d.cts CHANGED
@@ -160,7 +160,7 @@ interface Decoder<T> {
160
160
  * > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
161
161
  * > instead._
162
162
  */
163
- then<V>(next: Next<V, T>): Decoder<V>;
163
+ chain<V>(next: Next<V, T>): Decoder<V>;
164
164
  /**
165
165
  * Send the output of this decoder as input to another decoder.
166
166
  *
package/dist/index.d.ts CHANGED
@@ -160,7 +160,7 @@ interface Decoder<T> {
160
160
  * > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
161
161
  * > instead._
162
162
  */
163
- then<V>(next: Next<V, T>): Decoder<V>;
163
+ chain<V>(next: Next<V, T>): Decoder<V>;
164
164
  /**
165
165
  * Send the output of this decoder as input to another decoder.
166
166
  *
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ function isPlainObject(value) {
21
21
  }
22
22
 
23
23
  // src/core/annotate.ts
24
- var kAnnotationRegistry = Symbol.for("decoders.kAnnotationRegistry");
24
+ var kAnnotationRegistry = /* @__PURE__ */ Symbol.for("decoders.kAnnotationRegistry");
25
25
  var _register = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
26
26
  function brand(ann) {
27
27
  _register.add(ann);
@@ -57,7 +57,7 @@ function annotateArray(arr, text, seen) {
57
57
  seen.add(arr);
58
58
  const items = [];
59
59
  for (const value of arr) {
60
- items.push(annotate(value, void 0, seen));
60
+ items.push(__annotate(value, void 0, seen));
61
61
  }
62
62
  return makeArrayAnn(items, text);
63
63
  }
@@ -66,11 +66,11 @@ function annotateObject(obj, text, seen) {
66
66
  const fields = /* @__PURE__ */ new Map();
67
67
  for (const key of Object.keys(obj)) {
68
68
  const value = obj[key];
69
- fields.set(key, annotate(value, void 0, seen));
69
+ fields.set(key, __annotate(value, void 0, seen));
70
70
  }
71
71
  return makeObjectAnn(fields, text);
72
72
  }
73
- function annotate(value, text, seen) {
73
+ function __annotate(value, text, seen) {
74
74
  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") {
75
75
  return makeScalarAnn(value, text);
76
76
  }
@@ -104,7 +104,7 @@ function annotate(value, text, seen) {
104
104
  }
105
105
  }
106
106
  function public_annotate(value, text) {
107
- return annotate(value, text, /* @__PURE__ */ new WeakSet());
107
+ return __annotate(value, text, /* @__PURE__ */ new WeakSet());
108
108
  }
109
109
  function public_annotateObject(obj, text) {
110
110
  return annotateObject(obj, text, /* @__PURE__ */ new WeakSet());
@@ -338,7 +338,7 @@ function define(fn) {
338
338
  return decode(blob).value;
339
339
  }
340
340
  function transform(transformFn) {
341
- return then(noThrow(transformFn));
341
+ return chain(noThrow(transformFn));
342
342
  }
343
343
  function refine(predicateFn, errmsg) {
344
344
  return reject(
@@ -354,7 +354,7 @@ function define(fn) {
354
354
  function refineType() {
355
355
  return self;
356
356
  }
357
- function then(next) {
357
+ function chain(next) {
358
358
  return define((blob, ok2, err2) => {
359
359
  const r1 = decode(blob);
360
360
  if (!r1.ok) return r1;
@@ -363,10 +363,10 @@ function define(fn) {
363
363
  });
364
364
  }
365
365
  function pipe(next) {
366
- return then(next);
366
+ return chain(next);
367
367
  }
368
368
  function reject(rejectFn) {
369
- return then((blob, ok2, err2) => {
369
+ return chain((blob, ok2, err2) => {
370
370
  const errmsg = rejectFn(blob);
371
371
  return errmsg === null ? ok2(blob) : err2(typeof errmsg === "string" ? public_annotate(blob, errmsg) : errmsg);
372
372
  });
@@ -390,7 +390,7 @@ function define(fn) {
390
390
  refineType,
391
391
  reject,
392
392
  describe,
393
- then,
393
+ chain,
394
394
  pipe,
395
395
  "~standard": {
396
396
  version: 1,
@@ -409,7 +409,7 @@ function define(fn) {
409
409
  const self = brand2(unregistered);
410
410
  return self;
411
411
  }
412
- var kDecoderRegistry = Symbol.for("decoders.kDecoderRegistry");
412
+ var kDecoderRegistry = /* @__PURE__ */ Symbol.for("decoders.kDecoderRegistry");
413
413
  var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
414
414
  function brand2(decoder) {
415
415
  _register2.add(decoder);
@@ -428,7 +428,7 @@ var poja = define((blob, ok2, err2) => {
428
428
  });
429
429
  function array(decoder) {
430
430
  const decodeFn = decoder.decode;
431
- return poja.then((inputs, ok2, err2) => {
431
+ return poja.chain((inputs, ok2, err2) => {
432
432
  const results = [];
433
433
  for (let i = 0; i < inputs.length; ++i) {
434
434
  const blob = inputs[i];
@@ -458,7 +458,7 @@ function nonEmptyArray(decoder) {
458
458
  }
459
459
  var ntuple = (n) => poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
460
460
  function tuple(...decoders) {
461
- return ntuple(decoders.length).then((blobs, ok2, err2) => {
461
+ return ntuple(decoders.length).chain((blobs, ok2, err2) => {
462
462
  let allOk = true;
463
463
  const rvs = decoders.map((decoder, i) => {
464
464
  const blob = blobs[i];
@@ -526,7 +526,7 @@ var pojo = define(
526
526
  );
527
527
  function object(decoders) {
528
528
  const knownKeys = new Set(Object.keys(decoders));
529
- return pojo.then((plainObj, ok2, err2) => {
529
+ return pojo.chain((plainObj, ok2, err2) => {
530
530
  const actualKeys = new Set(Object.keys(plainObj));
531
531
  const missingKeys = difference(knownKeys, actualKeys);
532
532
  const record2 = {};
@@ -712,7 +712,7 @@ var truthy = define((blob, ok2, _) => ok2(!!blob));
712
712
  function record(fst, snd) {
713
713
  const keyDecoder = snd !== void 0 ? fst : void 0;
714
714
  const valueDecoder = snd ?? fst;
715
- return pojo.then((input, ok2, err2) => {
715
+ return pojo.chain((input, ok2, err2) => {
716
716
  let rv = {};
717
717
  const errors = /* @__PURE__ */ new Map();
718
718
  for (const key of Object.keys(input)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.8.0",
3
+ "version": "2.9.0-pre.0",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,19 +34,19 @@
34
34
  "README.md"
35
35
  ],
36
36
  "scripts": {
37
- "actually-prepublish": "echo \"\\n\\nPlease run \\`bin/publish.sh <options>\\` instead.\\n\\n\" && exit 2",
38
37
  "build": "tsup",
39
- "docs": "cog -cr docs/*.md",
40
- "lint": "eslint --color --report-unused-disable-directives src/ test/ && prettier --list-different src/ test/ test-d/",
41
- "lint:docs": "cog -c --check docs/*.md || (npm run docs; git diff; echo 'Error: docs not up-to-date, please re-run \"npm docs\" to update them.' && exit 1)",
38
+ "docs:dev": "cd docs && npm run dev",
39
+ "docs:build": "cd docs && npm run build",
40
+ "docs:update-sources": "node bin/update-source-lines.js",
41
+ "lint": "oxlint src/ test/ && prettier --list-different src/ test/ test-d/",
42
42
  "lint:package": "publint --strict && attw --pack",
43
- "format": "eslint --color --report-unused-disable-directives --fix src/ test/ ; prettier --write src/ test/ test-d/",
43
+ "format": "oxlint --fix src/ test/ ; prettier --write src/ test/ test-d/",
44
44
  "test": "vitest run --coverage",
45
45
  "test:completeness": "./bin/check.sh",
46
46
  "test:typescript": "tsc --noEmit",
47
47
  "test:types": "npm run build && tsd --typings ./dist/index.d.ts",
48
48
  "test:watch": "vitest --coverage",
49
- "release": "npm run test && npm run lint && npm run lint:docs && npm run build && npm run lint:package && release-it"
49
+ "release": "release-it"
50
50
  },
51
51
  "keywords": [
52
52
  "Decoder",
@@ -62,26 +62,22 @@
62
62
  ],
63
63
  "devDependencies": {
64
64
  "@arethetypeswrong/cli": "^0.18.2",
65
- "@eslint/js": "^9.35.0",
66
- "@release-it/keep-a-changelog": "^7.0.0",
67
- "@standard-schema/spec": "^1.0.0",
68
- "@vitest/coverage-istanbul": "^3.2.4",
69
- "eslint": "^9.35.0",
70
- "eslint-plugin-import": "^2.32.0",
71
- "eslint-plugin-simple-import-sort": "^12.1.1",
72
- "fast-check": "^4.3.0",
73
- "itertools": "^2.5.0",
74
- "pkg-pr-new": "^0.0.60",
75
- "prettier": "^3.6.2",
76
- "publint": "^0.3.13",
77
- "release-it": "^19.0.5",
78
- "ts-morph": "^27.0.0",
65
+ "@release-it/keep-a-changelog": "^7.0.1",
66
+ "@standard-schema/spec": "^1.1.0",
67
+ "@vitest/coverage-istanbul": "^4.0.18",
68
+ "fast-check": "^4.5.3",
69
+ "itertools": "^2.6.0",
70
+ "oxlint": "^1.50.0",
71
+ "pkg-pr-new": "^0.0.65",
72
+ "prettier": "^3.8.1",
73
+ "publint": "^0.3.18",
74
+ "release-it": "^19.2.4",
75
+ "ts-morph": "^27.0.2",
79
76
  "tsd": "^0.33.0",
80
- "tsup": "^8.5.0",
81
- "typescript": "^5.9.2",
82
- "typescript-eslint": "^8.44.0",
83
- "vite-tsconfig-paths": "^5.1.4",
84
- "vitest": "^3.2.4"
77
+ "tsup": "^8.5.1",
78
+ "typescript": "^5.9.3",
79
+ "vite-tsconfig-paths": "^6.1.1",
80
+ "vitest": "^4.0.18"
85
81
  },
86
82
  "githubUrl": "https://github.com/nvie/decoders",
87
83
  "sideEffects": false