From 048817bcf4ed8d6cc903f26be28dcd90c43f5a2e Mon Sep 17 00:00:00 2001 From: Jonathan Sykes Date: Fri, 3 Jul 2026 05:16:05 +0800 Subject: [PATCH] fix: fall back to copy when OPFS move() throws on WebKit and retry failed worker downloads on the main thread --- frontend/opfs-worker.js | 12 ++++++++---- frontend/opfs.js | 17 ++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/frontend/opfs-worker.js b/frontend/opfs-worker.js index dc92780..99162d4 100644 --- a/frontend/opfs-worker.js +++ b/frontend/opfs-worker.js @@ -76,12 +76,16 @@ self.onmessage = async (e) => { access.close(); } - // Finalize: .part → permanent name. Prefer the native rename; fall back - // to a chunked copy (still fully inside the worker, small fixed buffers). + // Finalize: .part → permanent name. Prefer the native rename, but treat + // ANY move() failure as "unavailable" and fall back to a chunked copy — + // WebKit's move() has a different signature/behavior than Chrome's and + // throws TypeError ("Not enough arguments") rather than being absent. try { await dir.removeEntry(filename); } catch { /* no previous copy */ } + let renamed = false; if (typeof partHandle.move === 'function') { - await partHandle.move(filename); - } else { + try { await partHandle.move(filename); renamed = true; } catch { /* copy below */ } + } + if (!renamed) { const finalHandle = await dir.getFileHandle(filename, { create: true }); const out = await finalHandle.createSyncAccessHandle(); try { diff --git a/frontend/opfs.js b/frontend/opfs.js index dcc997a..9c910e0 100644 --- a/frontend/opfs.js +++ b/frontend/opfs.js @@ -154,18 +154,21 @@ throw err; } } else { - // Fallback: buffer entirely (older browsers) - const buf = await response.arrayBuffer(); - const writable = await tmpHandle.createWritable(); - await writable.write(buf); - await writable.close(); + // No createWritable on this browser — main-thread OPFS writes are + // impossible (sync access handles are worker-only). Surface a real + // error instead of the old branch that called the missing API. + throw new Error('Offline saving is not supported in this browser'); } // Rename tmp → final. Prefer the native rename; else stream-copy so // the whole file is never buffered in main-thread memory at once. + // move() failures (WebKit's signature differs from Chrome's and + // throws TypeError) fall back to the copy path too. + let renamed = false; if (typeof tmpHandle.move === 'function') { - await tmpHandle.move(filename); - } else { + try { await tmpHandle.move(filename); renamed = true; } catch { /* copy below */ } + } + if (!renamed) { const finalHandle = await dir.getFileHandle(filename, { create: true }); const finalWritable = await finalHandle.createWritable(); const tmpFile = await tmpHandle.getFile();