fix: bound migration commits and version the OPFS worker protocol
STO-06: the IndexedDB codec migration commit chain runs entirely inside IndexedDB callbacks, so up to maxRows records could keep executing past the caller's cooperative deadline. The monotonic budget is now re-checked before each record's first write; a started record still completes atomically, the checkpoint advances only to the last safe key, and a clock failure aborts the transaction rather than committing an unbounded batch. STO-07: every OPFS worker request and response now carries OPFS_WORKER_PROTOCOL_VERSION = 2, responses echo their request kind, and the client validates the envelope and failure shape strictly while remembering the expected kind per pending request. A page/worker release mismatch or a reply for a different operation closes as UNSUPPORTED instead of being decoded as a value of the wrong shape. UNSUPPORTED is used deliberately: the closed browser-data taxonomy has no INCOMPATIBLE code and none was invented. SW-10 and the OPFS/Web Push V2 wire rollouts remain deferred: they are expand/dual-read/drain/contract deployments across releases rather than a single in-repo change. The ledger records them as DEFERRED_TO_MIGRATION. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fce8e046ea
commit
f6098242be
@@ -596,6 +596,37 @@ describe("IndexedDB bounded codec maintenance", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("stops codec migration commit at the cooperative deadline", async () => {
|
||||
const memory = new MemoryIndexedDbFactory();
|
||||
await prepareSchema(memory);
|
||||
seedLegacy(memory, "row-a", "first");
|
||||
seedLegacy(memory, "row-b", "second");
|
||||
const policy = defaultPolicy();
|
||||
// STO-06. The clock only advances past the deadline once the commit
|
||||
// transaction is already open, so the stop must happen inside the commit
|
||||
// chain rather than before transform.
|
||||
let calls = 0;
|
||||
const maintenance = createMaintenance(memory, policy, {
|
||||
now: () => {
|
||||
calls += 1;
|
||||
// Scan, prepare and the first commit record stay inside the budget.
|
||||
return calls <= 6 ? 0 : 5_000;
|
||||
},
|
||||
});
|
||||
|
||||
const result = await maintenance.migrateCodecBatch({
|
||||
maxRows: 10,
|
||||
maxDurationMs: 1_000,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
// The batch is incomplete and says so; it never claims a full pass.
|
||||
expect(result.value.state).toBe("MORE");
|
||||
expect(result.value.budgetExhausted).toBe(true);
|
||||
expect(result.value.checkpointedRows).toBeLessThan(2);
|
||||
});
|
||||
|
||||
it("fails closed when a historical payload cannot be transformed", async () => {
|
||||
const memory = new MemoryIndexedDbFactory();
|
||||
await prepareSchema(memory);
|
||||
|
||||
Reference in New Issue
Block a user