Skip to content

Commit ba12dbe

Browse files
grypezclaude
andcommitted
fix(sheaves): use Object.hasOwn in decomposeMetadata to avoid prototype chain
Replace `key in obj` with `Object.hasOwn(obj, key)` in two places inside decomposeMetadata: - Sharing check (line 127): `key in meta` would pass for prototype-inherited names (e.g. 'constructor'), causing a key to be treated as present in a candidate that does not own it. If the inherited value also happens to match via Object.is, the key is wrongly promoted to a shared constraint. - Stripping step (line 137): `key in constraints` is true for prototype property names on an empty {} object, so any metadata key that shadows a prototype name gets silently dropped from stripped candidates even when it was never added to constraints. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 98d4c58 commit ba12dbe

1 file changed

Lines changed: 2 additions & 2 deletions

File tree

packages/sheaves/src/sheafify.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const decomposeMetadata = <MetaData extends Record<string, unknown>>(
124124
const val = first[key];
125125
const shared = candidates.every((entry) => {
126126
const meta = entry.metadata;
127-
return key in meta && Object.is(meta[key], val);
127+
return Object.hasOwn(meta, key) && Object.is(meta[key], val);
128128
});
129129
if (shared) {
130130
constraints[key] = val;
@@ -134,7 +134,7 @@ const decomposeMetadata = <MetaData extends Record<string, unknown>>(
134134
const stripped = candidates.map((entry) => {
135135
const remaining: Record<string, unknown> = {};
136136
for (const [key, val] of Object.entries(entry.metadata)) {
137-
if (!(key in constraints)) {
137+
if (!Object.hasOwn(constraints, key)) {
138138
remaining[key] = val;
139139
}
140140
}

0 commit comments

Comments
 (0)