decoders 2.11.0 → 2.12.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
@@ -371,29 +371,63 @@ ${formatted}`);
371
371
  return formatted;
372
372
  }
373
373
  }
374
- // @__NO_SIDE_EFFECTS__
375
- function define(fn) {
376
- function decode(blob) {
377
- const makeFlexErr = (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg));
378
- return fn(blob, ok, makeFlexErr);
379
- }
380
- function verify(blob, formatter = formatInline) {
381
- const result = decode(blob);
382
- if (result.ok) {
383
- return result.value;
384
- } else {
385
- throw format(result.error, formatter);
386
- }
387
- }
388
- function value(blob) {
389
- return decode(blob).value;
374
+ var DecoderImpl = class {
375
+ /**
376
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
377
+ *
378
+ * Contrasted with `.verify()`, calls to `.decode()` will never fail and
379
+ * instead return a result type.
380
+ */
381
+
382
+ /**
383
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
384
+ * When accepted, returns a value of type `T`. Otherwise fail with
385
+ * a runtime error.
386
+ */
387
+
388
+ /**
389
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
390
+ * When accepted, returns the decoded `T` value directly. Otherwise returns
391
+ * `undefined`.
392
+ *
393
+ * Use this when you're not interested in programmatically handling the
394
+ * error message.
395
+ */
396
+
397
+ /**
398
+ * Memoized `~standard` props, built on first access.
399
+ */
400
+ #standard;
401
+ constructor(fn) {
402
+ const decode = (blob) => {
403
+ const makeFlexErr = (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg));
404
+ return fn(blob, ok, makeFlexErr);
405
+ };
406
+ const verify = (blob, formatter = formatInline) => {
407
+ const result = decode(blob);
408
+ if (result.ok) {
409
+ return result.value;
410
+ } else {
411
+ throw format(result.error, formatter);
412
+ }
413
+ };
414
+ const value = (blob) => decode(blob).value;
415
+ this.decode = decode;
416
+ this.verify = verify;
417
+ this.value = value;
390
418
  }
391
- function transform(transformFn) {
392
- return chain(noThrow(transformFn));
419
+ /**
420
+ * Accepts any value the given decoder accepts, and on success, will call
421
+ * the given function **on the decoded result**. If the transformation
422
+ * function throws an error, the whole decoder will fail using the error
423
+ * message as the failure reason.
424
+ */
425
+ transform(transformFn) {
426
+ return this.chain(noThrow(transformFn));
393
427
  }
394
- function refine(predicateFn, errmsg) {
395
- return reject(
396
- (value2) => predicateFn(value2) ? (
428
+ refine(predicateFn, errmsg) {
429
+ return this.reject(
430
+ (value) => predicateFn(value) ? (
397
431
  // Don't reject
398
432
  null
399
433
  ) : (
@@ -402,10 +436,25 @@ function define(fn) {
402
436
  )
403
437
  );
404
438
  }
405
- function refineType() {
406
- return self;
439
+ /**
440
+ * Cast the return type of this read-only decoder to a narrower type. This is
441
+ * useful to return "branded" types. This method has no runtime effect.
442
+ */
443
+ refineType() {
444
+ return this;
407
445
  }
408
- function chain(next) {
446
+ /**
447
+ * Send the output of the current decoder into another decoder or acceptance
448
+ * function. The given acceptance function will receive the output of the
449
+ * current decoder as its input.
450
+ *
451
+ * > _**NOTE:** This is an advanced, low-level, API. It's not recommended
452
+ * > to reach for this construct unless there is no other way. Most cases can
453
+ * > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
454
+ * > instead._
455
+ */
456
+ chain(next) {
457
+ const decode = this.decode;
409
458
  return /* @__PURE__ */ define((blob, ok2, err2) => {
410
459
  const r1 = decode(blob);
411
460
  if (!r1.ok) return r1;
@@ -413,16 +462,46 @@ function define(fn) {
413
462
  return /* @__PURE__ */ isDecoder(r2) ? r2.decode(r1.value) : r2;
414
463
  });
415
464
  }
416
- function pipe(next) {
417
- return chain(next);
465
+ /**
466
+ * Send the output of this decoder as input to another decoder.
467
+ *
468
+ * This can be useful to validate the results of a transform, i.e.:
469
+ *
470
+ * string
471
+ * .transform((s) => s.split(','))
472
+ * .pipe(array(nonEmptyString))
473
+ *
474
+ * You can also conditionally pipe:
475
+ *
476
+ * string.pipe((s) => s.startsWith('@') ? username : email)
477
+ */
478
+ pipe(next) {
479
+ return this.chain(next);
418
480
  }
419
- function reject(rejectFn) {
420
- return chain((blob, ok2, err2) => {
481
+ /**
482
+ * Adds an extra predicate to a decoder. The new decoder is like the
483
+ * original decoder, but only accepts values that aren't rejected by the
484
+ * given function.
485
+ *
486
+ * The given function can return `null` to accept the decoded value, or
487
+ * return a specific error message to reject.
488
+ *
489
+ * Unlike `.refine()`, you can use this function to return a dynamic error
490
+ * message.
491
+ */
492
+ reject(rejectFn) {
493
+ return this.chain((blob, ok2, err2) => {
421
494
  const errmsg = rejectFn(blob);
422
495
  return errmsg === null ? ok2(blob) : err2(typeof errmsg === "string" ? public_annotate(blob, errmsg) : errmsg);
423
496
  });
424
497
  }
425
- function describe(message) {
498
+ /**
499
+ * Uses the given decoder, but will use an alternative error message in
500
+ * case it rejects. This can be used to simplify or shorten otherwise
501
+ * long or low-level/technical errors.
502
+ */
503
+ describe(message) {
504
+ const decode = this.decode;
426
505
  return /* @__PURE__ */ define((blob, _, err2) => {
427
506
  const result = decode(blob);
428
507
  if (result.ok) {
@@ -432,18 +511,12 @@ function define(fn) {
432
511
  }
433
512
  });
434
513
  }
435
- const newDecoder = {
436
- verify,
437
- value,
438
- decode,
439
- transform,
440
- refine,
441
- refineType,
442
- reject,
443
- describe,
444
- chain,
445
- pipe,
446
- "~standard": {
514
+ /**
515
+ * The Standard Schema interface for this decoder.
516
+ */
517
+ get "~standard"() {
518
+ const decode = this.decode;
519
+ return this.#standard ??= {
447
520
  version: 1,
448
521
  vendor: "decoders",
449
522
  validate: (blob) => {
@@ -455,10 +528,14 @@ function define(fn) {
455
528
  return { issues };
456
529
  }
457
530
  }
458
- }
459
- };
460
- const self = stamp2(newDecoder);
461
- return self;
531
+ };
532
+ }
533
+ };
534
+ Object.defineProperty(DecoderImpl, "name", { value: "Decoder" });
535
+ // @__NO_SIDE_EFFECTS__
536
+ function define(fn) {
537
+ const decoder = new DecoderImpl(fn);
538
+ return stamp2(decoder);
462
539
  }
463
540
  var kDecoderRegistry = /* @__PURE__ */ Symbol.for("decoders.kDecoderRegistry");
464
541
  var _stamped2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
package/dist/index.d.cts CHANGED
@@ -179,7 +179,7 @@ interface Decoder<T> {
179
179
  /**
180
180
  * The Standard Schema interface for this decoder.
181
181
  */
182
- '~standard': StandardSchemaV1.Props<unknown, T>;
182
+ readonly '~standard': StandardSchemaV1.Props<unknown, T>;
183
183
  }
184
184
  /**
185
185
  * Helper type to return the output type of a Decoder.
@@ -673,9 +673,10 @@ declare function either<TDecoders extends readonly Decoder<unknown>[]>(...decode
673
673
  * Accepts any value that is strictly-equal (using `===`) to one of the
674
674
  * specified values.
675
675
  */
676
- declare function oneOf<C extends Scalar>(constants: readonly C[]): Decoder<C>;
676
+ declare function oneOf<const C extends Scalar>(constants: readonly C[]): Decoder<C>;
677
677
  /**
678
- * Accepts and return an enum value.
678
+ * Accepts and return an enum value. Works with TypeScript enums, as well as
679
+ * with `as const` objects.
679
680
  */
680
681
  declare function enum_<TEnum extends Record<string, string | number>>(enumObj: TEnum): Decoder<TEnum[keyof TEnum]>;
681
682
  /**
package/dist/index.d.ts CHANGED
@@ -179,7 +179,7 @@ interface Decoder<T> {
179
179
  /**
180
180
  * The Standard Schema interface for this decoder.
181
181
  */
182
- '~standard': StandardSchemaV1.Props<unknown, T>;
182
+ readonly '~standard': StandardSchemaV1.Props<unknown, T>;
183
183
  }
184
184
  /**
185
185
  * Helper type to return the output type of a Decoder.
@@ -673,9 +673,10 @@ declare function either<TDecoders extends readonly Decoder<unknown>[]>(...decode
673
673
  * Accepts any value that is strictly-equal (using `===`) to one of the
674
674
  * specified values.
675
675
  */
676
- declare function oneOf<C extends Scalar>(constants: readonly C[]): Decoder<C>;
676
+ declare function oneOf<const C extends Scalar>(constants: readonly C[]): Decoder<C>;
677
677
  /**
678
- * Accepts and return an enum value.
678
+ * Accepts and return an enum value. Works with TypeScript enums, as well as
679
+ * with `as const` objects.
679
680
  */
680
681
  declare function enum_<TEnum extends Record<string, string | number>>(enumObj: TEnum): Decoder<TEnum[keyof TEnum]>;
681
682
  /**
package/dist/index.js CHANGED
@@ -371,29 +371,63 @@ ${formatted}`);
371
371
  return formatted;
372
372
  }
373
373
  }
374
- // @__NO_SIDE_EFFECTS__
375
- function define(fn) {
376
- function decode(blob) {
377
- const makeFlexErr = (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg));
378
- return fn(blob, ok, makeFlexErr);
379
- }
380
- function verify(blob, formatter = formatInline) {
381
- const result = decode(blob);
382
- if (result.ok) {
383
- return result.value;
384
- } else {
385
- throw format(result.error, formatter);
386
- }
387
- }
388
- function value(blob) {
389
- return decode(blob).value;
374
+ var DecoderImpl = class {
375
+ /**
376
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
377
+ *
378
+ * Contrasted with `.verify()`, calls to `.decode()` will never fail and
379
+ * instead return a result type.
380
+ */
381
+ decode;
382
+ /**
383
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
384
+ * When accepted, returns a value of type `T`. Otherwise fail with
385
+ * a runtime error.
386
+ */
387
+ verify;
388
+ /**
389
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
390
+ * When accepted, returns the decoded `T` value directly. Otherwise returns
391
+ * `undefined`.
392
+ *
393
+ * Use this when you're not interested in programmatically handling the
394
+ * error message.
395
+ */
396
+ value;
397
+ /**
398
+ * Memoized `~standard` props, built on first access.
399
+ */
400
+ #standard;
401
+ constructor(fn) {
402
+ const decode = (blob) => {
403
+ const makeFlexErr = (msg) => err(isAnnotation(msg) ? msg : public_annotate(blob, msg));
404
+ return fn(blob, ok, makeFlexErr);
405
+ };
406
+ const verify = (blob, formatter = formatInline) => {
407
+ const result = decode(blob);
408
+ if (result.ok) {
409
+ return result.value;
410
+ } else {
411
+ throw format(result.error, formatter);
412
+ }
413
+ };
414
+ const value = (blob) => decode(blob).value;
415
+ this.decode = decode;
416
+ this.verify = verify;
417
+ this.value = value;
390
418
  }
391
- function transform(transformFn) {
392
- return chain(noThrow(transformFn));
419
+ /**
420
+ * Accepts any value the given decoder accepts, and on success, will call
421
+ * the given function **on the decoded result**. If the transformation
422
+ * function throws an error, the whole decoder will fail using the error
423
+ * message as the failure reason.
424
+ */
425
+ transform(transformFn) {
426
+ return this.chain(noThrow(transformFn));
393
427
  }
394
- function refine(predicateFn, errmsg) {
395
- return reject(
396
- (value2) => predicateFn(value2) ? (
428
+ refine(predicateFn, errmsg) {
429
+ return this.reject(
430
+ (value) => predicateFn(value) ? (
397
431
  // Don't reject
398
432
  null
399
433
  ) : (
@@ -402,10 +436,25 @@ function define(fn) {
402
436
  )
403
437
  );
404
438
  }
405
- function refineType() {
406
- return self;
439
+ /**
440
+ * Cast the return type of this read-only decoder to a narrower type. This is
441
+ * useful to return "branded" types. This method has no runtime effect.
442
+ */
443
+ refineType() {
444
+ return this;
407
445
  }
408
- function chain(next) {
446
+ /**
447
+ * Send the output of the current decoder into another decoder or acceptance
448
+ * function. The given acceptance function will receive the output of the
449
+ * current decoder as its input.
450
+ *
451
+ * > _**NOTE:** This is an advanced, low-level, API. It's not recommended
452
+ * > to reach for this construct unless there is no other way. Most cases can
453
+ * > be covered more elegantly by `.transform()`, `.refine()`, or `.pipe()`
454
+ * > instead._
455
+ */
456
+ chain(next) {
457
+ const decode = this.decode;
409
458
  return /* @__PURE__ */ define((blob, ok2, err2) => {
410
459
  const r1 = decode(blob);
411
460
  if (!r1.ok) return r1;
@@ -413,16 +462,46 @@ function define(fn) {
413
462
  return /* @__PURE__ */ isDecoder(r2) ? r2.decode(r1.value) : r2;
414
463
  });
415
464
  }
416
- function pipe(next) {
417
- return chain(next);
465
+ /**
466
+ * Send the output of this decoder as input to another decoder.
467
+ *
468
+ * This can be useful to validate the results of a transform, i.e.:
469
+ *
470
+ * string
471
+ * .transform((s) => s.split(','))
472
+ * .pipe(array(nonEmptyString))
473
+ *
474
+ * You can also conditionally pipe:
475
+ *
476
+ * string.pipe((s) => s.startsWith('@') ? username : email)
477
+ */
478
+ pipe(next) {
479
+ return this.chain(next);
418
480
  }
419
- function reject(rejectFn) {
420
- return chain((blob, ok2, err2) => {
481
+ /**
482
+ * Adds an extra predicate to a decoder. The new decoder is like the
483
+ * original decoder, but only accepts values that aren't rejected by the
484
+ * given function.
485
+ *
486
+ * The given function can return `null` to accept the decoded value, or
487
+ * return a specific error message to reject.
488
+ *
489
+ * Unlike `.refine()`, you can use this function to return a dynamic error
490
+ * message.
491
+ */
492
+ reject(rejectFn) {
493
+ return this.chain((blob, ok2, err2) => {
421
494
  const errmsg = rejectFn(blob);
422
495
  return errmsg === null ? ok2(blob) : err2(typeof errmsg === "string" ? public_annotate(blob, errmsg) : errmsg);
423
496
  });
424
497
  }
425
- function describe(message) {
498
+ /**
499
+ * Uses the given decoder, but will use an alternative error message in
500
+ * case it rejects. This can be used to simplify or shorten otherwise
501
+ * long or low-level/technical errors.
502
+ */
503
+ describe(message) {
504
+ const decode = this.decode;
426
505
  return /* @__PURE__ */ define((blob, _, err2) => {
427
506
  const result = decode(blob);
428
507
  if (result.ok) {
@@ -432,18 +511,12 @@ function define(fn) {
432
511
  }
433
512
  });
434
513
  }
435
- const newDecoder = {
436
- verify,
437
- value,
438
- decode,
439
- transform,
440
- refine,
441
- refineType,
442
- reject,
443
- describe,
444
- chain,
445
- pipe,
446
- "~standard": {
514
+ /**
515
+ * The Standard Schema interface for this decoder.
516
+ */
517
+ get "~standard"() {
518
+ const decode = this.decode;
519
+ return this.#standard ??= {
447
520
  version: 1,
448
521
  vendor: "decoders",
449
522
  validate: (blob) => {
@@ -455,10 +528,14 @@ function define(fn) {
455
528
  return { issues };
456
529
  }
457
530
  }
458
- }
459
- };
460
- const self = stamp2(newDecoder);
461
- return self;
531
+ };
532
+ }
533
+ };
534
+ Object.defineProperty(DecoderImpl, "name", { value: "Decoder" });
535
+ // @__NO_SIDE_EFFECTS__
536
+ function define(fn) {
537
+ const decoder = new DecoderImpl(fn);
538
+ return stamp2(decoder);
462
539
  }
463
540
  var kDecoderRegistry = /* @__PURE__ */ Symbol.for("decoders.kDecoderRegistry");
464
541
  var _stamped2 = globalThis[kDecoderRegistry] ??= /* @__PURE__ */ new WeakSet();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.11.0",
3
+ "version": "2.12.1",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -66,11 +66,11 @@
66
66
  "@release-it/keep-a-changelog": "^8.0.1",
67
67
  "@standard-schema/spec": "^1.1.0",
68
68
  "@vitest/coverage-istanbul": "^4.1.11",
69
- "fast-check": "^4.10.0",
69
+ "fast-check": "^4.10.2",
70
70
  "itertools": "^2.7.1",
71
71
  "oxlint": "^1.83.0",
72
72
  "pkg-pr-new": "^0.0.88",
73
- "prettier": "^3.9.6",
73
+ "prettier": "^3.9.9",
74
74
  "publint": "^0.3.24",
75
75
  "release-it": "^21.0.3",
76
76
  "ts-morph": "^28.0.0",