decoders 2.9.3 → 2.10.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
@@ -574,11 +574,26 @@ function difference(xs, ys) {
574
574
  }
575
575
 
576
576
  // src/objects.ts
577
+ // @__NO_SIDE_EFFECTS__
578
+ function rejectUnsafeKey(decoders) {
579
+ if (Object.prototype.hasOwnProperty.call(decoders, "__proto__")) {
580
+ throw new Error(
581
+ 'Unsafe key: "__proto__" cannot be used in an object(), exact(), or inexact() definition'
582
+ );
583
+ }
584
+ }
585
+ // @__NO_SIDE_EFFECTS__
586
+ function annotateUnsafeKey(plainObj) {
587
+ return merge(
588
+ public_annotateObject(plainObj),
589
+ /* @__PURE__ */ new Map([["__proto__", public_annotate(plainObj["__proto__"], "Unsafe key")]])
590
+ );
591
+ }
577
592
  var pojo = define(
578
593
  (blob, ok2, err2) => isPlainObject(blob) ? ok2(blob) : err2("Must be an object")
579
594
  );
580
595
  // @__NO_SIDE_EFFECTS__
581
- function object(decoders) {
596
+ function buildObject(decoders) {
582
597
  const knownKeys = new Set(Object.keys(decoders));
583
598
  return pojo.chain((plainObj, ok2, err2) => {
584
599
  const actualKeys = new Set(Object.keys(plainObj));
@@ -621,36 +636,51 @@ function object(decoders) {
621
636
  });
622
637
  }
623
638
  // @__NO_SIDE_EFFECTS__
639
+ function object(decoders) {
640
+ /* @__PURE__ */ rejectUnsafeKey(decoders);
641
+ return /* @__PURE__ */ buildObject(decoders);
642
+ }
643
+ // @__NO_SIDE_EFFECTS__
624
644
  function exact(decoders) {
645
+ /* @__PURE__ */ rejectUnsafeKey(decoders);
625
646
  const allowedKeys = new Set(Object.keys(decoders));
626
- const checked = pojo.reject((plainObj) => {
647
+ const checked = pojo.chain((plainObj, ok2, err2) => {
627
648
  const actualKeys = new Set(Object.keys(plainObj));
649
+ if (actualKeys.has("__proto__")) {
650
+ return err2(/* @__PURE__ */ annotateUnsafeKey(plainObj));
651
+ }
628
652
  const extraKeys = difference(actualKeys, allowedKeys);
629
- return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}` : null;
653
+ return extraKeys.size > 0 ? err2(`Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}`) : ok2(plainObj);
630
654
  });
631
- return checked.pipe(/* @__PURE__ */ object(decoders));
655
+ return checked.pipe(/* @__PURE__ */ buildObject(decoders));
632
656
  }
633
657
  // @__NO_SIDE_EFFECTS__
634
658
  function inexact(decoders) {
635
- return pojo.pipe((plainObj) => {
636
- const allkeys = new Set(Object.keys(plainObj));
637
- return (/* @__PURE__ */ object(decoders)).transform((safepart) => {
638
- const safekeys = new Set(Object.keys(decoders));
639
- for (const k of safekeys) allkeys.add(k);
640
- const rv = {};
641
- for (const k of allkeys) {
642
- if (safekeys.has(k)) {
643
- const value = safepart[k];
644
- if (value !== void 0) {
645
- rv[k] = value;
659
+ /* @__PURE__ */ rejectUnsafeKey(decoders);
660
+ return pojo.chain(
661
+ (plainObj, _ok, err2) => {
662
+ const allkeys = new Set(Object.keys(plainObj));
663
+ if (allkeys.has("__proto__")) {
664
+ return err2(/* @__PURE__ */ annotateUnsafeKey(plainObj));
665
+ }
666
+ return (/* @__PURE__ */ buildObject(decoders)).transform((safepart) => {
667
+ const safekeys = new Set(Object.keys(decoders));
668
+ for (const k of safekeys) allkeys.add(k);
669
+ const rv = {};
670
+ for (const k of allkeys) {
671
+ if (safekeys.has(k)) {
672
+ const value = safepart[k];
673
+ if (value !== void 0) {
674
+ rv[k] = value;
675
+ }
676
+ } else {
677
+ rv[k] = plainObj[k];
646
678
  }
647
- } else {
648
- rv[k] = plainObj[k];
649
679
  }
650
- }
651
- return rv;
652
- });
653
- });
680
+ return rv;
681
+ });
682
+ }
683
+ );
654
684
  }
655
685
 
656
686
  // src/unions.ts
@@ -785,6 +815,11 @@ function record(fst, snd) {
785
815
  const errors = /* @__PURE__ */ new Map();
786
816
  for (const key of Object.keys(input)) {
787
817
  const value = input[key];
818
+ if (key === "__proto__") {
819
+ errors.set(key, public_annotate(value, "Unsafe key"));
820
+ rv = {};
821
+ continue;
822
+ }
788
823
  const keyResult = _optionalChain([keyDecoder, 'optionalAccess', _4 => _4.decode, 'call', _5 => _5(key)]);
789
824
  if (_optionalChain([keyResult, 'optionalAccess', _6 => _6.ok]) === false) {
790
825
  return err2(
@@ -819,7 +854,7 @@ function mapping(decoder) {
819
854
  }
820
855
 
821
856
  // src/strings.ts
822
- var url_re = /^([A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?):\/\/(?:([^@:]*:?(?:[^@]+)?)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,!$&'()*:;=@\w]*)?(?:\?[-+~%/.,!$&'()*:;=@?\w]*)?)?(?:#[^\s#]*)?$/;
857
+ var url_re = /^[A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?:\/\/(?:[^@]*@)?[A-Za-z0-9.-]+(?::[0-9]{2,5})?(?:\/[-+~%/.,!$&'()*:;=@\w]*(?:\?[-+~%/.,!$&'()*:;=@?\w]*)?)?(?:#[^\s#]*)?$/;
823
858
  var string = define(
824
859
  (blob, ok2, err2) => isString(blob) ? ok2(blob) : err2("Must be string")
825
860
  );
@@ -844,7 +879,7 @@ function endsWith(suffix) {
844
879
  }
845
880
  var email = /* @__PURE__ */ regex(
846
881
  // The almost perfect email regex, taken from https://emailregex.com/
847
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
882
+ /^(?:[^<>()[\]\\.,;:\s@"]+(?:\.[^<>()[\]\\.,;:\s@"]+)*|".+")@(?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}]|(?:[a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})$/,
848
883
  "Must be email"
849
884
  );
850
885
  var urlString = /* @__PURE__ */ regex(url_re, "Must be URL");
package/dist/index.d.cts CHANGED
@@ -550,10 +550,6 @@ type ObjectDecoderType<Ds extends Record<string, Decoder<unknown>>> = UndefinedT
550
550
  * values further.
551
551
  */
552
552
  declare const pojo: Decoder<Record<string, unknown>>;
553
- /**
554
- * Accepts objects with fields matching the given decoders. Extra fields that
555
- * exist on the input object are ignored and will not be returned.
556
- */
557
553
  declare function object(decoders: Record<any, never>): Decoder<Record<string, never>>;
558
554
  declare function object<Ds extends Record<string, Decoder<unknown>>>(decoders: Ds): Decoder<ObjectDecoderType<Ds>>;
559
555
  /**
@@ -565,6 +561,10 @@ declare function exact<Ds extends Record<string, Decoder<unknown>>>(decoders: Ds
565
561
  /**
566
562
  * Like `object()`, but will pass through any extra fields on the input object
567
563
  * unvalidated that will thus be of `unknown` type statically.
564
+ *
565
+ * A `__proto__` key is the one exception: it cannot be passed through without
566
+ * reassigning the prototype of the returned object, so inputs containing one
567
+ * are rejected.
568
568
  */
569
569
  declare function inexact(decoders: Record<any, never>): Decoder<Record<string, unknown>>;
570
570
  declare function inexact<Ds extends Record<string, Decoder<unknown>>>(decoders: Ds): Decoder<ObjectDecoderType<Ds> & Record<string, unknown>>;
package/dist/index.d.ts CHANGED
@@ -550,10 +550,6 @@ type ObjectDecoderType<Ds extends Record<string, Decoder<unknown>>> = UndefinedT
550
550
  * values further.
551
551
  */
552
552
  declare const pojo: Decoder<Record<string, unknown>>;
553
- /**
554
- * Accepts objects with fields matching the given decoders. Extra fields that
555
- * exist on the input object are ignored and will not be returned.
556
- */
557
553
  declare function object(decoders: Record<any, never>): Decoder<Record<string, never>>;
558
554
  declare function object<Ds extends Record<string, Decoder<unknown>>>(decoders: Ds): Decoder<ObjectDecoderType<Ds>>;
559
555
  /**
@@ -565,6 +561,10 @@ declare function exact<Ds extends Record<string, Decoder<unknown>>>(decoders: Ds
565
561
  /**
566
562
  * Like `object()`, but will pass through any extra fields on the input object
567
563
  * unvalidated that will thus be of `unknown` type statically.
564
+ *
565
+ * A `__proto__` key is the one exception: it cannot be passed through without
566
+ * reassigning the prototype of the returned object, so inputs containing one
567
+ * are rejected.
568
568
  */
569
569
  declare function inexact(decoders: Record<any, never>): Decoder<Record<string, unknown>>;
570
570
  declare function inexact<Ds extends Record<string, Decoder<unknown>>>(decoders: Ds): Decoder<ObjectDecoderType<Ds> & Record<string, unknown>>;
package/dist/index.js CHANGED
@@ -574,11 +574,26 @@ function difference(xs, ys) {
574
574
  }
575
575
 
576
576
  // src/objects.ts
577
+ // @__NO_SIDE_EFFECTS__
578
+ function rejectUnsafeKey(decoders) {
579
+ if (Object.prototype.hasOwnProperty.call(decoders, "__proto__")) {
580
+ throw new Error(
581
+ 'Unsafe key: "__proto__" cannot be used in an object(), exact(), or inexact() definition'
582
+ );
583
+ }
584
+ }
585
+ // @__NO_SIDE_EFFECTS__
586
+ function annotateUnsafeKey(plainObj) {
587
+ return merge(
588
+ public_annotateObject(plainObj),
589
+ /* @__PURE__ */ new Map([["__proto__", public_annotate(plainObj["__proto__"], "Unsafe key")]])
590
+ );
591
+ }
577
592
  var pojo = define(
578
593
  (blob, ok2, err2) => isPlainObject(blob) ? ok2(blob) : err2("Must be an object")
579
594
  );
580
595
  // @__NO_SIDE_EFFECTS__
581
- function object(decoders) {
596
+ function buildObject(decoders) {
582
597
  const knownKeys = new Set(Object.keys(decoders));
583
598
  return pojo.chain((plainObj, ok2, err2) => {
584
599
  const actualKeys = new Set(Object.keys(plainObj));
@@ -621,36 +636,51 @@ function object(decoders) {
621
636
  });
622
637
  }
623
638
  // @__NO_SIDE_EFFECTS__
639
+ function object(decoders) {
640
+ /* @__PURE__ */ rejectUnsafeKey(decoders);
641
+ return /* @__PURE__ */ buildObject(decoders);
642
+ }
643
+ // @__NO_SIDE_EFFECTS__
624
644
  function exact(decoders) {
645
+ /* @__PURE__ */ rejectUnsafeKey(decoders);
625
646
  const allowedKeys = new Set(Object.keys(decoders));
626
- const checked = pojo.reject((plainObj) => {
647
+ const checked = pojo.chain((plainObj, ok2, err2) => {
627
648
  const actualKeys = new Set(Object.keys(plainObj));
649
+ if (actualKeys.has("__proto__")) {
650
+ return err2(/* @__PURE__ */ annotateUnsafeKey(plainObj));
651
+ }
628
652
  const extraKeys = difference(actualKeys, allowedKeys);
629
- return extraKeys.size > 0 ? `Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}` : null;
653
+ return extraKeys.size > 0 ? err2(`Unexpected extra keys: ${Array.from(extraKeys).map(quote).join(", ")}`) : ok2(plainObj);
630
654
  });
631
- return checked.pipe(/* @__PURE__ */ object(decoders));
655
+ return checked.pipe(/* @__PURE__ */ buildObject(decoders));
632
656
  }
633
657
  // @__NO_SIDE_EFFECTS__
634
658
  function inexact(decoders) {
635
- return pojo.pipe((plainObj) => {
636
- const allkeys = new Set(Object.keys(plainObj));
637
- return (/* @__PURE__ */ object(decoders)).transform((safepart) => {
638
- const safekeys = new Set(Object.keys(decoders));
639
- for (const k of safekeys) allkeys.add(k);
640
- const rv = {};
641
- for (const k of allkeys) {
642
- if (safekeys.has(k)) {
643
- const value = safepart[k];
644
- if (value !== void 0) {
645
- rv[k] = value;
659
+ /* @__PURE__ */ rejectUnsafeKey(decoders);
660
+ return pojo.chain(
661
+ (plainObj, _ok, err2) => {
662
+ const allkeys = new Set(Object.keys(plainObj));
663
+ if (allkeys.has("__proto__")) {
664
+ return err2(/* @__PURE__ */ annotateUnsafeKey(plainObj));
665
+ }
666
+ return (/* @__PURE__ */ buildObject(decoders)).transform((safepart) => {
667
+ const safekeys = new Set(Object.keys(decoders));
668
+ for (const k of safekeys) allkeys.add(k);
669
+ const rv = {};
670
+ for (const k of allkeys) {
671
+ if (safekeys.has(k)) {
672
+ const value = safepart[k];
673
+ if (value !== void 0) {
674
+ rv[k] = value;
675
+ }
676
+ } else {
677
+ rv[k] = plainObj[k];
646
678
  }
647
- } else {
648
- rv[k] = plainObj[k];
649
679
  }
650
- }
651
- return rv;
652
- });
653
- });
680
+ return rv;
681
+ });
682
+ }
683
+ );
654
684
  }
655
685
 
656
686
  // src/unions.ts
@@ -785,6 +815,11 @@ function record(fst, snd) {
785
815
  const errors = /* @__PURE__ */ new Map();
786
816
  for (const key of Object.keys(input)) {
787
817
  const value = input[key];
818
+ if (key === "__proto__") {
819
+ errors.set(key, public_annotate(value, "Unsafe key"));
820
+ rv = {};
821
+ continue;
822
+ }
788
823
  const keyResult = keyDecoder?.decode(key);
789
824
  if (keyResult?.ok === false) {
790
825
  return err2(
@@ -819,7 +854,7 @@ function mapping(decoder) {
819
854
  }
820
855
 
821
856
  // src/strings.ts
822
- var url_re = /^([A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?):\/\/(?:([^@:]*:?(?:[^@]+)?)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,!$&'()*:;=@\w]*)?(?:\?[-+~%/.,!$&'()*:;=@?\w]*)?)?(?:#[^\s#]*)?$/;
857
+ var url_re = /^[A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?:\/\/(?:[^@]*@)?[A-Za-z0-9.-]+(?::[0-9]{2,5})?(?:\/[-+~%/.,!$&'()*:;=@\w]*(?:\?[-+~%/.,!$&'()*:;=@?\w]*)?)?(?:#[^\s#]*)?$/;
823
858
  var string = define(
824
859
  (blob, ok2, err2) => isString(blob) ? ok2(blob) : err2("Must be string")
825
860
  );
@@ -844,7 +879,7 @@ function endsWith(suffix) {
844
879
  }
845
880
  var email = /* @__PURE__ */ regex(
846
881
  // The almost perfect email regex, taken from https://emailregex.com/
847
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
882
+ /^(?:[^<>()[\]\\.,;:\s@"]+(?:\.[^<>()[\]\\.,;:\s@"]+)*|".+")@(?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}]|(?:[a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})$/,
848
883
  "Must be email"
849
884
  );
850
885
  var urlString = /* @__PURE__ */ regex(url_re, "Must be URL");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.9.3",
3
+ "version": "2.10.0",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data for TypeScript",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -62,22 +62,22 @@
62
62
  "verify"
63
63
  ],
64
64
  "devDependencies": {
65
- "@arethetypeswrong/cli": "^0.18.2",
66
- "@release-it/keep-a-changelog": "^7.0.1",
65
+ "@arethetypeswrong/cli": "^0.18.5",
66
+ "@release-it/keep-a-changelog": "^8.0.1",
67
67
  "@standard-schema/spec": "^1.1.0",
68
- "@vitest/coverage-istanbul": "^4.1.3",
69
- "fast-check": "^4.6.0",
70
- "itertools": "^2.6.0",
71
- "oxlint": "^1.59.0",
72
- "pkg-pr-new": "^0.0.66",
73
- "prettier": "^3.8.1",
74
- "publint": "^0.3.18",
75
- "release-it": "^19.2.4",
76
- "ts-morph": "^27.0.2",
68
+ "@vitest/coverage-istanbul": "^4.1.11",
69
+ "fast-check": "^4.10.0",
70
+ "itertools": "^2.7.1",
71
+ "oxlint": "^1.83.0",
72
+ "pkg-pr-new": "^0.0.88",
73
+ "prettier": "^3.9.6",
74
+ "publint": "^0.3.24",
75
+ "release-it": "^21.0.3",
76
+ "ts-morph": "^28.0.0",
77
77
  "tsd": "^0.33.0",
78
78
  "tsup": "^8.5.1",
79
79
  "typescript": "^5.9.3",
80
- "vitest": "^4.1.3"
80
+ "vitest": "^4.1.11"
81
81
  },
82
82
  "githubUrl": "https://github.com/nvie/decoders",
83
83
  "sideEffects": false