fix: fall back to copy when OPFS move() throws on WebKit and retry failed worker downloads on the main thread

This commit is contained in:
Jonathan Sykes
2026-07-03 05:16:05 +08:00
parent 76f8ec525d
commit 048817bcf4
2 changed files with 18 additions and 11 deletions

View File

@@ -76,12 +76,16 @@ self.onmessage = async (e) => {
access.close(); access.close();
} }
// Finalize: .part → permanent name. Prefer the native rename; fall back // Finalize: .part → permanent name. Prefer the native rename, but treat
// to a chunked copy (still fully inside the worker, small fixed buffers). // 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 */ } try { await dir.removeEntry(filename); } catch { /* no previous copy */ }
let renamed = false;
if (typeof partHandle.move === 'function') { if (typeof partHandle.move === 'function') {
await partHandle.move(filename); try { await partHandle.move(filename); renamed = true; } catch { /* copy below */ }
} else { }
if (!renamed) {
const finalHandle = await dir.getFileHandle(filename, { create: true }); const finalHandle = await dir.getFileHandle(filename, { create: true });
const out = await finalHandle.createSyncAccessHandle(); const out = await finalHandle.createSyncAccessHandle();
try { try {

View File

@@ -154,18 +154,21 @@
throw err; throw err;
} }
} else { } else {
// Fallback: buffer entirely (older browsers) // No createWritable on this browser — main-thread OPFS writes are
const buf = await response.arrayBuffer(); // impossible (sync access handles are worker-only). Surface a real
const writable = await tmpHandle.createWritable(); // error instead of the old branch that called the missing API.
await writable.write(buf); throw new Error('Offline saving is not supported in this browser');
await writable.close();
} }
// Rename tmp → final. Prefer the native rename; else stream-copy so // Rename tmp → final. Prefer the native rename; else stream-copy so
// the whole file is never buffered in main-thread memory at once. // 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') { if (typeof tmpHandle.move === 'function') {
await tmpHandle.move(filename); try { await tmpHandle.move(filename); renamed = true; } catch { /* copy below */ }
} else { }
if (!renamed) {
const finalHandle = await dir.getFileHandle(filename, { create: true }); const finalHandle = await dir.getFileHandle(filename, { create: true });
const finalWritable = await finalHandle.createWritable(); const finalWritable = await finalHandle.createWritable();
const tmpFile = await tmpHandle.getFile(); const tmpFile = await tmpHandle.getFile();