taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit ff821e23caaa26e685eedce17504785f005c3b58
parent 159fa81d9dd96c992a66f3ce216b7647f8505908
Author: Florian Dold <dold@taler.net>
Date:   Thu, 20 Aug 2026 14:22:52 +0200

idb-bridge: support cursor updates with compound keys

Diffstat:
Mpackages/idb-bridge/src/bridge-idb.ts | 12+++---------
Apackages/idb-bridge/src/idbcursor-update.test.ts | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+), 9 deletions(-)

diff --git a/packages/idb-bridge/src/bridge-idb.ts b/packages/idb-bridge/src/bridge-idb.ts @@ -46,7 +46,6 @@ import { IDBTransactionMode, IDBValidKey, } from "./idbtypes.js"; -import { canInjectKey } from "./util/canInjectKey.js"; import { compareKeys } from "./util/cmp.js"; import { extractKey } from "./util/extractKey.js"; import { enforceRange } from "./util/enforceRange.js"; @@ -395,6 +394,9 @@ export class BridgeIDBCursor implements IDBCursor { } if (os._objectStoreMeta.keyPath !== null) { + // A cursor update keeps the existing primary key; it never generates + // or injects one. Extracting and comparing the inline key is therefore + // the complete validation, including for compound key paths. const key2 = extractKey(os._objectStoreMeta.keyPath, value); if (compareKeys(key, key2) !== 0) { throw new DataError( @@ -402,14 +404,6 @@ export class BridgeIDBCursor implements IDBCursor { ); } } - - if (os.keyPath !== null && os.keyPath !== undefined) { - if (!canInjectKey(os.keyPath, value)) { - throw new DataError( - "The value cannot accept a key at the object store's in-line key path.", - ); - } - } } catch (err) { throw addErrorContext(err, "IDBCursor.update", this); } diff --git a/packages/idb-bridge/src/idbcursor-update.test.ts b/packages/idb-bridge/src/idbcursor-update.test.ts @@ -0,0 +1,79 @@ +/* + Copyright 2026 Taler Systems SA + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import assert from "node:assert"; +import { before, test } from "node:test"; +import { + createDatabase, + initTestIndexedDB, + promiseForRequest, + promiseForTransaction, +} from "./idb-wpt-ported/wptsupport.js"; + +before(initTestIndexedDB); + +async function createCompoundKeyDatabase(t: any) { + return createDatabase(t, (db) => { + const store = db.createObjectStore("records", { + keyPath: ["scope", "id"], + }); + store.add({ scope: "exchange", id: 1, value: "before" }); + }); +} + +test("cursor update accepts an unchanged inline compound key", async (t) => { + const db = await createCompoundKeyDatabase(t); + const tx = db.transaction("records", "readwrite"); + const txDone = promiseForTransaction(t, tx); + const cursor = await promiseForRequest( + t, + tx.objectStore("records").openCursor(), + ); + assert(cursor); + + await promiseForRequest( + t, + cursor.update({ ...cursor.value, value: "after" }), + ); + await txDone; + + const readTx = db.transaction("records", "readonly"); + const record = await promiseForRequest( + t, + readTx.objectStore("records").get(["exchange", 1]), + ); + assert.deepStrictEqual(record, { + scope: "exchange", + id: 1, + value: "after", + }); +}); + +test("cursor update rejects a changed inline compound key", async (t) => { + const db = await createCompoundKeyDatabase(t); + const tx = db.transaction("records", "readwrite"); + const txDone = promiseForTransaction(t, tx); + const cursor = await promiseForRequest( + t, + tx.objectStore("records").openCursor(), + ); + assert(cursor); + + assert.throws(() => cursor.update({ ...cursor.value, id: 2 }), { + name: "DataError", + }); + await txDone; +});