From d7877359d4b5756b51599ba1b033a0db5da165fd Mon Sep 17 00:00:00 2001 From: Felix-Ayush <67006255+Ayush7614@users.noreply.github.com> Date: Wed, 9 Sep 2026 11:02:53 +0530 Subject: [PATCH] fix(puter-js): return key_undefined for missing kv keys (#3803) get/del/incr/decr/add/expire/expireAt only checked size, so undefined keys threw TypeError. Add assertKeyPresent first, matching set/update/remove. --- src/puter-js/src/modules/kv/add.js | 3 ++- src/puter-js/src/modules/kv/decr.js | 3 ++- src/puter-js/src/modules/kv/del.js | 3 ++- src/puter-js/src/modules/kv/expire.js | 3 ++- src/puter-js/src/modules/kv/expireAt.js | 3 ++- src/puter-js/src/modules/kv/get.js | 3 ++- src/puter-js/src/modules/kv/incr.js | 3 ++- src/puter-js/src/modules/kv/kv.test.js | 16 ++++++++++++++++ 8 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/puter-js/src/modules/kv/add.js b/src/puter-js/src/modules/kv/add.js index 49907592d..0daaa7ee2 100644 --- a/src/puter-js/src/modules/kv/add.js +++ b/src/puter-js/src/modules/kv/add.js @@ -1,6 +1,6 @@ import * as utils from '../../lib/utils.js'; import { isObject, isOptConfigShorthand } from './lib/args.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVAddPath} KVAddPath */ /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -55,6 +55,7 @@ export async function add (keyOrOptions, valueOrMap, optConfig) { }; } + assertKeyPresent(options.key); assertKeySize(options.key); return await utils.makeDriverMethod({ iface: 'puter-kvstore', method: 'add', argNames: ['key'], puter: this.puter })(options); } diff --git a/src/puter-js/src/modules/kv/decr.js b/src/puter-js/src/modules/kv/decr.js index a2f1da50c..304274fba 100644 --- a/src/puter-js/src/modules/kv/decr.js +++ b/src/puter-js/src/modules/kv/decr.js @@ -1,6 +1,6 @@ import * as utils from '../../lib/utils.js'; import { parseCounterArgs } from './lib/args.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVIncrementPath} KVIncrementPath */ /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -33,6 +33,7 @@ import { assertKeySize } from './lib/validate.js'; */ export async function decr (keyOrOptions, amountOrMap, optConfig) { const options = parseCounterArgs(keyOrOptions, amountOrMap, optConfig); + assertKeyPresent(options.key); assertKeySize(options.key); return await utils.makeDriverMethod({ iface: 'puter-kvstore', method: 'decr', argNames: ['key'], puter: this.puter })(options); } diff --git a/src/puter-js/src/modules/kv/del.js b/src/puter-js/src/modules/kv/del.js index b2c03f6a9..8fec289f8 100644 --- a/src/puter-js/src/modules/kv/del.js +++ b/src/puter-js/src/modules/kv/del.js @@ -1,6 +1,6 @@ import * as utils from '../../lib/utils.js'; import { isObject, parseOptConfigThenCallbacks } from './lib/args.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -11,6 +11,7 @@ const delDriverCall = (puter, args) => argNames: ['key'], puter, preprocess: (driverArgs) => { + assertKeyPresent(driverArgs.key); assertKeySize(driverArgs.key); return driverArgs; }, diff --git a/src/puter-js/src/modules/kv/expire.js b/src/puter-js/src/modules/kv/expire.js index 70df42dc1..f48536da1 100644 --- a/src/puter-js/src/modules/kv/expire.js +++ b/src/puter-js/src/modules/kv/expire.js @@ -1,5 +1,5 @@ import * as utils from '../../lib/utils.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -22,6 +22,7 @@ import { assertKeySize } from './lib/validate.js'; * @returns {Promise} */ export async function expire (key, ttl, optConfig) { + assertKeyPresent(key); assertKeySize(key); return await utils.makeDriverMethod({ iface: 'puter-kvstore', method: 'expire', argNames: ['key', 'ttl'], puter: this.puter })({ key, ttl, optConfig }); } diff --git a/src/puter-js/src/modules/kv/expireAt.js b/src/puter-js/src/modules/kv/expireAt.js index 07418c578..a55574840 100644 --- a/src/puter-js/src/modules/kv/expireAt.js +++ b/src/puter-js/src/modules/kv/expireAt.js @@ -1,5 +1,5 @@ import * as utils from '../../lib/utils.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -23,6 +23,7 @@ import { assertKeySize } from './lib/validate.js'; * @returns {Promise} */ export async function expireAt (key, timestamp, optConfig) { + assertKeyPresent(key); assertKeySize(key); return await utils.makeDriverMethod({ iface: 'puter-kvstore', method: 'expireAt', argNames: ['key', 'timestamp'], puter: this.puter })({ key, timestamp, optConfig }); } diff --git a/src/puter-js/src/modules/kv/get.js b/src/puter-js/src/modules/kv/get.js index 796ed1b0d..b712a281e 100644 --- a/src/puter-js/src/modules/kv/get.js +++ b/src/puter-js/src/modules/kv/get.js @@ -1,6 +1,6 @@ import * as utils from '../../lib/utils.js'; import { isObject, parseOptConfigThenCallbacks } from './lib/args.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -12,6 +12,7 @@ const getDriverCall = (puter, args) => puter, readonly: true, preprocess: (driverArgs) => { + assertKeyPresent(driverArgs.key); assertKeySize(driverArgs.key); return driverArgs; }, diff --git a/src/puter-js/src/modules/kv/incr.js b/src/puter-js/src/modules/kv/incr.js index 6411a7a28..056bdadad 100644 --- a/src/puter-js/src/modules/kv/incr.js +++ b/src/puter-js/src/modules/kv/incr.js @@ -1,6 +1,6 @@ import * as utils from '../../lib/utils.js'; import { parseCounterArgs } from './lib/args.js'; -import { assertKeySize } from './lib/validate.js'; +import { assertKeyPresent, assertKeySize } from './lib/validate.js'; /** @typedef {import('./types.js').KVIncrementPath} KVIncrementPath */ /** @typedef {import('./types.js').KVOptConfig} KVOptConfig */ @@ -33,6 +33,7 @@ import { assertKeySize } from './lib/validate.js'; */ export async function incr (keyOrOptions, amountOrMap, optConfig) { const options = parseCounterArgs(keyOrOptions, amountOrMap, optConfig); + assertKeyPresent(options.key); assertKeySize(options.key); return await utils.makeDriverMethod({ iface: 'puter-kvstore', method: 'incr', argNames: ['key'], puter: this.puter })(options); } diff --git a/src/puter-js/src/modules/kv/kv.test.js b/src/puter-js/src/modules/kv/kv.test.js index 059d7e065..b0b4feda1 100644 --- a/src/puter-js/src/modules/kv/kv.test.js +++ b/src/puter-js/src/modules/kv/kv.test.js @@ -232,6 +232,11 @@ describe('kv.get driver payloads', () => { await expect(kv.get('k'.repeat(1025))).rejects.toMatchObject({ code: 'key_too_large' }); expect(FakeXHR.requests).toHaveLength(0); }); + + it('rejects an undefined key without a request', async () => { + await expect(kv.get(undefined)).rejects.toMatchObject({ code: 'key_undefined' }); + expect(FakeXHR.requests).toHaveLength(0); + }); }); describe('kv.get GUI boot cache', () => { @@ -487,6 +492,12 @@ describe('kv.expire / kv.expireAt driver payloads', () => { await expect(kv.expireAt(bigKey, 1)).rejects.toMatchObject({ code: 'key_too_large' }); expect(FakeXHR.requests).toHaveLength(0); }); + + it('both reject an undefined key without a request', async () => { + await expect(kv.expire(undefined, 60)).rejects.toMatchObject({ code: 'key_undefined' }); + await expect(kv.expireAt(undefined, 1)).rejects.toMatchObject({ code: 'key_undefined' }); + expect(FakeXHR.requests).toHaveLength(0); + }); }); describe('kv.del driver payloads', () => { @@ -511,6 +522,11 @@ describe('kv.del driver payloads', () => { await expect(kv.del('k'.repeat(1025))).rejects.toMatchObject({ code: 'key_too_large' }); expect(FakeXHR.requests).toHaveLength(0); }); + + it('rejects an undefined key without a request', async () => { + await expect(kv.del(undefined)).rejects.toMatchObject({ code: 'key_undefined' }); + expect(FakeXHR.requests).toHaveLength(0); + }); }); describe('kv.list driver payloads', () => {