decoders 2.5.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -18,7 +18,8 @@ function isPojo(value) {
18
18
  }
19
19
 
20
20
  // src/core/annotate.ts
21
- var _register = /* @__PURE__ */ new WeakSet();
21
+ var kAnnotationRegistry = Symbol.for("decoders.kAnnotationRegistry");
22
+ var _register = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
22
23
  function brand(ann) {
23
24
  _register.add(ann);
24
25
  return ann;
@@ -102,7 +103,7 @@ function public_annotateObject(obj, text) {
102
103
  // src/lib/text.ts
103
104
  var INDENT = " ";
104
105
  function isMultiline(s) {
105
- return s.indexOf("\n") >= 0;
106
+ return s.includes("\n");
106
107
  }
107
108
  function indent(s, prefix = INDENT) {
108
109
  if (isMultiline(s)) {
@@ -166,7 +167,7 @@ function serializeArray(annotation, prefix) {
166
167
  const result = [];
167
168
  for (const item of items) {
168
169
  const [ser, ann] = serializeAnnotation(item, `${prefix}${INDENT}`);
169
- result.push(`${prefix}${INDENT}${ser}${","}`);
170
+ result.push(`${prefix}${INDENT}${ser},`);
170
171
  if (ann !== void 0) {
171
172
  result.push(indent(ann, `${prefix}${INDENT}`));
172
173
  }
@@ -242,6 +243,41 @@ ${annotation}`;
242
243
  function formatShort(ann) {
243
244
  return summarize(ann, []).join("\n");
244
245
  }
246
+ function* iterAnnotation(ann, stack) {
247
+ if (ann.text) {
248
+ if (stack.length > 0) {
249
+ yield { message: ann.text, path: [...stack] };
250
+ } else {
251
+ yield { message: ann.text };
252
+ }
253
+ }
254
+ switch (ann.type) {
255
+ case "array": {
256
+ let index = 0;
257
+ for (const item of ann.items) {
258
+ stack.push(index++);
259
+ yield* iterAnnotation(item, stack);
260
+ stack.pop();
261
+ }
262
+ break;
263
+ }
264
+ case "object": {
265
+ for (const [key, value] of ann.fields) {
266
+ stack.push(key);
267
+ yield* iterAnnotation(value, stack);
268
+ stack.pop();
269
+ }
270
+ break;
271
+ }
272
+ case "scalar":
273
+ case "opaque": {
274
+ break;
275
+ }
276
+ }
277
+ }
278
+ function formatAsIssues(ann) {
279
+ return Array.from(iterAnnotation(ann, []));
280
+ }
245
281
 
246
282
  // src/core/Result.ts
247
283
  function ok(value) {
@@ -342,10 +378,24 @@ function define(fn) {
342
378
  reject,
343
379
  describe,
344
380
  then,
345
- pipe
381
+ pipe,
382
+ "~standard": {
383
+ version: 1,
384
+ vendor: "decoders",
385
+ validate: (blob) => {
386
+ const result = decode(blob);
387
+ if (result.ok) {
388
+ return { value: result.value };
389
+ } else {
390
+ const issues = formatAsIssues(result.error);
391
+ return { issues };
392
+ }
393
+ }
394
+ }
346
395
  });
347
396
  }
348
- var _register2 = /* @__PURE__ */ new WeakSet();
397
+ var kDecoderRegistry = Symbol.for("decoders.kDecoderRegistry");
398
+ var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
349
399
  function brand2(decoder) {
350
400
  _register2.add(decoder);
351
401
  return decoder;
@@ -418,7 +468,10 @@ function tuple(...decoders) {
418
468
  // src/misc.ts
419
469
  function instanceOf(klass) {
420
470
  return define(
421
- (blob, ok2, err2) => blob instanceof klass ? ok2(blob) : err2(`Must be ${klass.name} instance`)
471
+ (blob, ok2, err2) => (
472
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
473
+ blob instanceof klass ? ok2(blob) : err2(`Must be ${klass.name} instance`)
474
+ )
422
475
  );
423
476
  }
424
477
  function lazy(decoderFn) {
@@ -547,8 +600,8 @@ function either(...decoders) {
547
600
  }
548
601
  return define((blob, _, err2) => {
549
602
  const errors = [];
550
- for (let i = 0; i < decoders.length; i++) {
551
- const result = decoders[i].decode(blob);
603
+ for (const decoder of decoders) {
604
+ const result = decoder.decode(blob);
552
605
  if (result.ok) {
553
606
  return result;
554
607
  } else {
@@ -649,7 +702,7 @@ var truthy = define((blob, ok2, _) => ok2(!!blob));
649
702
  // src/collections.ts
650
703
  function record(fst, snd) {
651
704
  const keyDecoder = snd !== void 0 ? fst : void 0;
652
- const valueDecoder = snd !== void 0 ? snd : fst;
705
+ const valueDecoder = _nullishCoalesce(snd, () => ( fst));
653
706
  return pojo.then((input, ok2, err2) => {
654
707
  let rv = {};
655
708
  const errors = /* @__PURE__ */ new Map();
@@ -690,9 +743,9 @@ function mapping(decoder) {
690
743
 
691
744
  // src/lib/size-options.ts
692
745
  function bySizeOptions(options) {
693
- const size = _optionalChain([options, 'optionalAccess', _6 => _6.size]);
694
- const min = _nullishCoalesce(size, () => ( _optionalChain([options, 'optionalAccess', _7 => _7.min])));
695
- const max = _nullishCoalesce(size, () => ( _optionalChain([options, 'optionalAccess', _8 => _8.max])));
746
+ const size = options.size;
747
+ const min = _nullishCoalesce(size, () => ( options.min));
748
+ const max = _nullishCoalesce(size, () => ( options.max));
696
749
  const atLeast = min === max ? "" : "at least ";
697
750
  const atMost = min === max ? "" : "at most ";
698
751
  const tooShort = min !== void 0 && `Too short, must be ${atLeast}${min} chars`;
package/dist/index.d.cts CHANGED
@@ -45,6 +45,62 @@ declare function ok<T>(value: T): Ok<T>;
45
45
  */
46
46
  declare function err<E>(error: E): Err<E>;
47
47
 
48
+ /** The Standard Schema interface. */
49
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
50
+ /** The Standard Schema properties. */
51
+ readonly '~standard': StandardSchemaV1.Props<Input, Output>;
52
+ }
53
+ declare namespace StandardSchemaV1 {
54
+ /** The Standard Schema properties interface. */
55
+ interface Props<Input = unknown, Output = Input> {
56
+ /** The version number of the standard. */
57
+ readonly version: 1;
58
+ /** The vendor name of the schema library. */
59
+ readonly vendor: string;
60
+ /** Validates unknown input values. */
61
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
62
+ /** Inferred types associated with the schema. */
63
+ readonly types?: Types<Input, Output> | undefined;
64
+ }
65
+ /** The result interface of the validate function. */
66
+ type Result<Output> = SuccessResult<Output> | FailureResult;
67
+ /** The result interface if validation succeeds. */
68
+ interface SuccessResult<Output> {
69
+ /** The typed output value. */
70
+ readonly value: Output;
71
+ /** The non-existent issues. */
72
+ readonly issues?: undefined;
73
+ }
74
+ /** The result interface if validation fails. */
75
+ interface FailureResult {
76
+ /** The issues of failed validation. */
77
+ readonly issues: ReadonlyArray<Issue>;
78
+ }
79
+ /** The issue interface of the failure output. */
80
+ interface Issue {
81
+ /** The error message of the issue. */
82
+ readonly message: string;
83
+ /** The path of the issue, if any. */
84
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
85
+ }
86
+ /** The path segment interface of the issue. */
87
+ interface PathSegment {
88
+ /** The key representing a path segment. */
89
+ readonly key: PropertyKey;
90
+ }
91
+ /** The Standard Schema types interface. */
92
+ interface Types<Input = unknown, Output = Input> {
93
+ /** The input type of the schema. */
94
+ readonly input: Input;
95
+ /** The output type of the schema. */
96
+ readonly output: Output;
97
+ }
98
+ /** Infers the input type of a Standard Schema. */
99
+ type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
100
+ /** Infers the output type of a Standard Schema. */
101
+ type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
102
+ }
103
+
48
104
  type DecodeResult<T> = Result<T, Annotation>;
49
105
  /**
50
106
  * A function taking a untrusted input, and returning a DecodeResult<T>. The
@@ -112,8 +168,11 @@ interface Decoder<T> {
112
168
  *
113
169
  * string.pipe((s) => s.startsWith('@') ? username : email)
114
170
  */
115
- pipe<V, D extends Decoder<V>>(next: D): Decoder<DecoderType<D>>;
116
- pipe<V, D extends Decoder<V>>(next: (blob: T) => D): Decoder<DecoderType<D>>;
171
+ pipe<V, D extends Decoder<V>>(next: D | ((blob: T) => D)): Decoder<DecoderType<D>>;
172
+ /**
173
+ * The Standard Schema interface for this decoder.
174
+ */
175
+ '~standard': StandardSchemaV1.Props<unknown, T>;
117
176
  }
118
177
  /**
119
178
  * Helper type to return the output type of a Decoder.
package/dist/index.d.ts CHANGED
@@ -45,6 +45,62 @@ declare function ok<T>(value: T): Ok<T>;
45
45
  */
46
46
  declare function err<E>(error: E): Err<E>;
47
47
 
48
+ /** The Standard Schema interface. */
49
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
50
+ /** The Standard Schema properties. */
51
+ readonly '~standard': StandardSchemaV1.Props<Input, Output>;
52
+ }
53
+ declare namespace StandardSchemaV1 {
54
+ /** The Standard Schema properties interface. */
55
+ interface Props<Input = unknown, Output = Input> {
56
+ /** The version number of the standard. */
57
+ readonly version: 1;
58
+ /** The vendor name of the schema library. */
59
+ readonly vendor: string;
60
+ /** Validates unknown input values. */
61
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
62
+ /** Inferred types associated with the schema. */
63
+ readonly types?: Types<Input, Output> | undefined;
64
+ }
65
+ /** The result interface of the validate function. */
66
+ type Result<Output> = SuccessResult<Output> | FailureResult;
67
+ /** The result interface if validation succeeds. */
68
+ interface SuccessResult<Output> {
69
+ /** The typed output value. */
70
+ readonly value: Output;
71
+ /** The non-existent issues. */
72
+ readonly issues?: undefined;
73
+ }
74
+ /** The result interface if validation fails. */
75
+ interface FailureResult {
76
+ /** The issues of failed validation. */
77
+ readonly issues: ReadonlyArray<Issue>;
78
+ }
79
+ /** The issue interface of the failure output. */
80
+ interface Issue {
81
+ /** The error message of the issue. */
82
+ readonly message: string;
83
+ /** The path of the issue, if any. */
84
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
85
+ }
86
+ /** The path segment interface of the issue. */
87
+ interface PathSegment {
88
+ /** The key representing a path segment. */
89
+ readonly key: PropertyKey;
90
+ }
91
+ /** The Standard Schema types interface. */
92
+ interface Types<Input = unknown, Output = Input> {
93
+ /** The input type of the schema. */
94
+ readonly input: Input;
95
+ /** The output type of the schema. */
96
+ readonly output: Output;
97
+ }
98
+ /** Infers the input type of a Standard Schema. */
99
+ type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
100
+ /** Infers the output type of a Standard Schema. */
101
+ type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
102
+ }
103
+
48
104
  type DecodeResult<T> = Result<T, Annotation>;
49
105
  /**
50
106
  * A function taking a untrusted input, and returning a DecodeResult<T>. The
@@ -112,8 +168,11 @@ interface Decoder<T> {
112
168
  *
113
169
  * string.pipe((s) => s.startsWith('@') ? username : email)
114
170
  */
115
- pipe<V, D extends Decoder<V>>(next: D): Decoder<DecoderType<D>>;
116
- pipe<V, D extends Decoder<V>>(next: (blob: T) => D): Decoder<DecoderType<D>>;
171
+ pipe<V, D extends Decoder<V>>(next: D | ((blob: T) => D)): Decoder<DecoderType<D>>;
172
+ /**
173
+ * The Standard Schema interface for this decoder.
174
+ */
175
+ '~standard': StandardSchemaV1.Props<unknown, T>;
117
176
  }
118
177
  /**
119
178
  * Helper type to return the output type of a Decoder.
package/dist/index.js CHANGED
@@ -18,7 +18,8 @@ function isPojo(value) {
18
18
  }
19
19
 
20
20
  // src/core/annotate.ts
21
- var _register = /* @__PURE__ */ new WeakSet();
21
+ var kAnnotationRegistry = Symbol.for("decoders.kAnnotationRegistry");
22
+ var _register = globalThis[kAnnotationRegistry] ??= /* @__PURE__ */ new WeakSet();
22
23
  function brand(ann) {
23
24
  _register.add(ann);
24
25
  return ann;
@@ -102,7 +103,7 @@ function public_annotateObject(obj, text) {
102
103
  // src/lib/text.ts
103
104
  var INDENT = " ";
104
105
  function isMultiline(s) {
105
- return s.indexOf("\n") >= 0;
106
+ return s.includes("\n");
106
107
  }
107
108
  function indent(s, prefix = INDENT) {
108
109
  if (isMultiline(s)) {
@@ -166,7 +167,7 @@ function serializeArray(annotation, prefix) {
166
167
  const result = [];
167
168
  for (const item of items) {
168
169
  const [ser, ann] = serializeAnnotation(item, `${prefix}${INDENT}`);
169
- result.push(`${prefix}${INDENT}${ser}${","}`);
170
+ result.push(`${prefix}${INDENT}${ser},`);
170
171
  if (ann !== void 0) {
171
172
  result.push(indent(ann, `${prefix}${INDENT}`));
172
173
  }
@@ -242,6 +243,41 @@ ${annotation}`;
242
243
  function formatShort(ann) {
243
244
  return summarize(ann, []).join("\n");
244
245
  }
246
+ function* iterAnnotation(ann, stack) {
247
+ if (ann.text) {
248
+ if (stack.length > 0) {
249
+ yield { message: ann.text, path: [...stack] };
250
+ } else {
251
+ yield { message: ann.text };
252
+ }
253
+ }
254
+ switch (ann.type) {
255
+ case "array": {
256
+ let index = 0;
257
+ for (const item of ann.items) {
258
+ stack.push(index++);
259
+ yield* iterAnnotation(item, stack);
260
+ stack.pop();
261
+ }
262
+ break;
263
+ }
264
+ case "object": {
265
+ for (const [key, value] of ann.fields) {
266
+ stack.push(key);
267
+ yield* iterAnnotation(value, stack);
268
+ stack.pop();
269
+ }
270
+ break;
271
+ }
272
+ case "scalar":
273
+ case "opaque": {
274
+ break;
275
+ }
276
+ }
277
+ }
278
+ function formatAsIssues(ann) {
279
+ return Array.from(iterAnnotation(ann, []));
280
+ }
245
281
 
246
282
  // src/core/Result.ts
247
283
  function ok(value) {
@@ -342,10 +378,24 @@ function define(fn) {
342
378
  reject,
343
379
  describe,
344
380
  then,
345
- pipe
381
+ pipe,
382
+ "~standard": {
383
+ version: 1,
384
+ vendor: "decoders",
385
+ validate: (blob) => {
386
+ const result = decode(blob);
387
+ if (result.ok) {
388
+ return { value: result.value };
389
+ } else {
390
+ const issues = formatAsIssues(result.error);
391
+ return { issues };
392
+ }
393
+ }
394
+ }
346
395
  });
347
396
  }
348
- var _register2 = /* @__PURE__ */ new WeakSet();
397
+ var kDecoderRegistry = Symbol.for("decoders.kDecoderRegistry");
398
+ var _register2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
349
399
  function brand2(decoder) {
350
400
  _register2.add(decoder);
351
401
  return decoder;
@@ -418,7 +468,10 @@ function tuple(...decoders) {
418
468
  // src/misc.ts
419
469
  function instanceOf(klass) {
420
470
  return define(
421
- (blob, ok2, err2) => blob instanceof klass ? ok2(blob) : err2(`Must be ${klass.name} instance`)
471
+ (blob, ok2, err2) => (
472
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
473
+ blob instanceof klass ? ok2(blob) : err2(`Must be ${klass.name} instance`)
474
+ )
422
475
  );
423
476
  }
424
477
  function lazy(decoderFn) {
@@ -547,8 +600,8 @@ function either(...decoders) {
547
600
  }
548
601
  return define((blob, _, err2) => {
549
602
  const errors = [];
550
- for (let i = 0; i < decoders.length; i++) {
551
- const result = decoders[i].decode(blob);
603
+ for (const decoder of decoders) {
604
+ const result = decoder.decode(blob);
552
605
  if (result.ok) {
553
606
  return result;
554
607
  } else {
@@ -649,7 +702,7 @@ var truthy = define((blob, ok2, _) => ok2(!!blob));
649
702
  // src/collections.ts
650
703
  function record(fst, snd) {
651
704
  const keyDecoder = snd !== void 0 ? fst : void 0;
652
- const valueDecoder = snd !== void 0 ? snd : fst;
705
+ const valueDecoder = snd ?? fst;
653
706
  return pojo.then((input, ok2, err2) => {
654
707
  let rv = {};
655
708
  const errors = /* @__PURE__ */ new Map();
@@ -690,9 +743,9 @@ function mapping(decoder) {
690
743
 
691
744
  // src/lib/size-options.ts
692
745
  function bySizeOptions(options) {
693
- const size = options?.size;
694
- const min = size ?? options?.min;
695
- const max = size ?? options?.max;
746
+ const size = options.size;
747
+ const min = size ?? options.min;
748
+ const max = size ?? options.max;
696
749
  const atLeast = min === max ? "" : "at least ";
697
750
  const atMost = min === max ? "" : "at most ";
698
751
  const tooShort = min !== void 0 && `Too short, must be ${atLeast}${min} chars`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.5.0",
3
+ "version": "2.6.1",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,7 +14,6 @@
14
14
  },
15
15
  "type": "module",
16
16
  "main": "./dist/index.cjs",
17
- "module": "./dist/index.js",
18
17
  "types": "./dist/index.d.cts",
19
18
  "exports": {
20
19
  ".": {
@@ -61,27 +60,27 @@
61
60
  "verify"
62
61
  ],
63
62
  "devDependencies": {
64
- "@arethetypeswrong/cli": "^0.16.4",
65
- "@release-it/keep-a-changelog": "^5.0.0",
66
- "@types/eslint": "^8.56.10",
67
- "@typescript-eslint/eslint-plugin": "^7.14.1",
68
- "@typescript-eslint/parser": "^7.14.1",
69
- "@vitest/coverage-istanbul": "^1.6.0",
70
- "eslint": "^8.57.0",
71
- "eslint-plugin-import": "^2.29.1",
72
- "eslint-plugin-simple-import-sort": "^12.1.0",
73
- "fast-check": "^3.22.0",
74
- "itertools": "^2.3.2",
75
- "pkg-pr-new": "^0.0.24",
76
- "prettier": "^3.3.3",
77
- "publint": "^0.2.11",
78
- "release-it": "^17.6.0",
79
- "ts-morph": "^23.0.0",
63
+ "@arethetypeswrong/cli": "^0.17.4",
64
+ "@eslint/js": "^9.21.0",
65
+ "@release-it/keep-a-changelog": "^6.0.0",
66
+ "@standard-schema/spec": "^1.0.0",
67
+ "@vitest/coverage-istanbul": "^3.0.7",
68
+ "eslint": "^9.21.0",
69
+ "eslint-plugin-import": "^2.31.0",
70
+ "eslint-plugin-simple-import-sort": "^12.1.1",
71
+ "fast-check": "^3.23.2",
72
+ "itertools": "^2.4.1",
73
+ "pkg-pr-new": "^0.0.39",
74
+ "prettier": "^3.5.2",
75
+ "publint": "^0.3.6",
76
+ "release-it": "^18.1.2",
77
+ "ts-morph": "^25.0.1",
80
78
  "tsd": "^0.31.2",
81
- "tsup": "^8.3.0",
82
- "typescript": "^5.5.2",
83
- "vite-tsconfig-paths": "^4.3.2",
84
- "vitest": "^1.6.0"
79
+ "tsup": "^8.3.6",
80
+ "typescript": "^5.7.3",
81
+ "typescript-eslint": "^8.25.0",
82
+ "vite-tsconfig-paths": "^5.1.4",
83
+ "vitest": "^3.0.7"
85
84
  },
86
85
  "githubUrl": "https://github.com/nvie/decoders",
87
86
  "sideEffects": false