Skip to content

Commit b8bc7c0

Browse files
authored
Remap: duplicate targets (#3025)
#2878 (comment) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Remap now detects and rejects duplicate target keys in mappings to prevent key collisions. * **Tests** * Added a runtime test asserting an error is thrown for duplicate mapping targets. * **Documentation** * CHANGELOG updated to document the remap duplicate-target fix (v2.1.0). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 2777e0b commit b8bc7c0

File tree

3 files changed

+21
-3
lines changed

3 files changed

+21
-3
lines changed

zod-plugin/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Version 2
44

5+
### v2.1.0
6+
7+
- `ZodObject::remap()` now throws an `Error` if duplicate target keys found in its argument.
8+
59
### v2.0.0
610

711
- Restricting the supported Node.js versions: `^20.19.0 || ^22.12.0 || ^24.0.0`.

zod-plugin/src/remap.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,20 @@ type Mapper = <T extends Record<string, unknown>>(
2727
subject: T,
2828
) => { [P in string | keyof T]: T[keyof T] };
2929

30+
const makeTransformer = (mapping: Partial<Record<string, string>>) => {
31+
const clean = R.reject(R.isNil, mapping); // rejecting undefined
32+
const targets = Object.values(clean);
33+
if (new Set(targets).size !== targets.length)
34+
throw new Error("remap(): duplicate target keys", { cause: mapping });
35+
return R.renameKeys(clean);
36+
};
37+
3038
/** Used by runtime (bound) */
3139
export const remap = function (
3240
this: z.ZodObject,
33-
tool: Record<string, string> | Mapper,
41+
tool: Parameters<typeof makeTransformer>[0] | Mapper,
3442
) {
35-
const transformer =
36-
typeof tool === "function" ? tool : R.renameKeys(R.reject(R.isNil, tool)); // rejecting undefined
43+
const transformer = typeof tool === "function" ? tool : makeTransformer(tool);
3744
const nextShape = transformer(
3845
R.map(R.invoker(0, "clone"), this._zod.def.shape), // immutable, changed from R.clone due to failure
3946
);

zod-plugin/tests/runtime.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ describe("Zod Runtime Plugin", () => {
117117
},
118118
);
119119

120+
test("throws when multiple source keys map to the same target", () => {
121+
const schema = z.object({ a: z.number(), b: z.string() });
122+
expect(() => schema.remap({ a: "x", b: "x" })).toThrowError(
123+
/duplicate target keys/,
124+
);
125+
});
126+
120127
test("should support a mapping function", () => {
121128
const schema = z.object({ user_id: z.string(), name: z.string() });
122129
const mappedSchema = schema.remap((shape) => camelize(shape, true));

0 commit comments

Comments
 (0)