From ef65552a2bbd02f46fb8d12ea4438d80ddea9384 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Sun, 26 Jul 2026 17:44:46 -0500 Subject: [PATCH] fix(wasm): tolerate blocked browser storage --- examples/browser/browser-local.js | 26 +++++++++++++++++++------- examples/browser/browser-local.test.js | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/examples/browser/browser-local.js b/examples/browser/browser-local.js index a07bbb82..f87c69d2 100644 --- a/examples/browser/browser-local.js +++ b/examples/browser/browser-local.js @@ -40,12 +40,20 @@ function snapshotDirectory(fs, directory, files) { } /** Create a localStorage-backed persistence adapter for BashKit's browser VFS. */ -export function browserLocal({ - storage = globalThis.localStorage, - key = DEFAULT_KEY, - root = DEFAULT_ROOT, -} = {}) { - if (!storage) throw new Error("browserLocal requires a Storage implementation"); +export function browserLocal({ storage: configuredStorage, key = DEFAULT_KEY, root = DEFAULT_ROOT } = {}) { + let storage = configuredStorage; + let storageBlocked = false; + if (storage === undefined) { + try { + storage = globalThis.localStorage; + } catch { + // Access itself can throw in sandboxed or storage-blocked browser contexts. + storageBlocked = true; + } + } + if (!storage && !storageBlocked) { + throw new Error("browserLocal requires a Storage implementation"); + } return { load(defaultFiles = {}) { @@ -70,7 +78,11 @@ export function browserLocal({ }, clear() { - storage.removeItem(key); + try { + storage.removeItem(key); + } catch { + // Unavailable storage must not prevent the browser shell from running. + } }, }; } diff --git a/examples/browser/browser-local.test.js b/examples/browser/browser-local.test.js index 1472c0f7..914bfbea 100644 --- a/examples/browser/browser-local.test.js +++ b/examples/browser/browser-local.test.js @@ -115,6 +115,26 @@ test("reports localStorage write failures without throwing", () => { })), false); }); +test("tolerates blocked access to the global localStorage", () => { + const descriptor = Object.getOwnPropertyDescriptor(globalThis, "localStorage"); + Object.defineProperty(globalThis, "localStorage", { + configurable: true, + get() { throw new DOMException("blocked", "SecurityError"); }, + }); + + try { + const backend = browserLocal(); + assert.deepEqual(backend.load({ "/home/user/welcome.txt": "hello\n" }), { + "/home/user/welcome.txt": "hello\n", + }); + assert.equal(backend.save(fakeFs({})), false); + assert.doesNotThrow(() => backend.clear()); + } finally { + if (descriptor) Object.defineProperty(globalThis, "localStorage", descriptor); + else delete globalThis.localStorage; + } +}); + test("clear removes the persisted filesystem", () => { const storage = new MemoryStorage(); storage.setItem("bashkit:fs", "saved");