Skip to content

Commit 2fbfeb1

Browse files
kriszypclaude
andcommitted
fix(resources): detect RequestTarget in transactional arg normalizer (two-object form)
When a static resource method override called super.put(target, body) where both arguments are objects, the transactional argument normalizer misidentified the form as (data, context) rather than (id/query, data), causing the RequestTarget descriptor to be stored as the record and the actual body to be discarded silently. Fix: exclude RequestTarget instances from the (data, context) path — a RequestTarget is always an id/query, never record data. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 88c94e6 commit 2fbfeb1

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

resources/Resource.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ function transactional(
524524
if (
525525
typeof idOrQuery === 'object' &&
526526
idOrQuery &&
527+
!(idOrQuery instanceof RequestTarget) &&
527528
(!Array.isArray(idOrQuery) || typeof idOrQuery[0] === 'object')
528529
) {
529530
// (data, context) form

unitTests/resources/crud.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,43 @@ describe('CRUD operations with the Resource API', () => {
307307
analytics.setAnalyticsEnabled(false); // restore to normal unit test behavior
308308
});
309309
});
310+
311+
describe('transactional argument normalization with RequestTarget', () => {
312+
let BaseTable, SubTable;
313+
before(async function () {
314+
setupTestDBPath();
315+
setMainIsWorker(true);
316+
BaseTable = table({
317+
table: 'NormTable',
318+
database: 'test',
319+
attributes: [{ name: 'id', isPrimaryKey: true }, { name: 'title' }, { name: 'stamped' }],
320+
});
321+
// Subclass that overrides static put and calls super.put(RequestTarget, body) —
322+
// the form that previously misidentified (RequestTarget, data) as (data, context).
323+
SubTable = class extends BaseTable {
324+
static async put(target, data) {
325+
const body = await data;
326+
body.stamped = true;
327+
return super.put(target, body);
328+
}
329+
};
330+
Object.defineProperty(SubTable, 'name', { value: 'SubTable' });
331+
});
332+
333+
it('super.put(RequestTarget, body) stores body data, not the RequestTarget', async function () {
334+
const target = new RequestTarget('/rt-test-1');
335+
await SubTable.put(target, { title: 'hello' });
336+
const record = await SubTable.get('rt-test-1');
337+
assert.equal(record.title, 'hello', 'body data should be stored');
338+
assert.equal(record.stamped, true, 'override logic should have run');
339+
assert.equal(record.id, 'rt-test-1', 'id should come from the RequestTarget path');
340+
assert.ok(!record.pathname, 'RequestTarget descriptor fields must not be stored as record data');
341+
});
342+
343+
it('super.put(string_id, body) continues to work', async function () {
344+
await SubTable.put('rt-test-2', { title: 'world' });
345+
const record = await SubTable.get('rt-test-2');
346+
assert.equal(record.title, 'world');
347+
assert.equal(record.stamped, true);
348+
});
349+
});

0 commit comments

Comments
 (0)