export async function openInputStream(path, init = {}) { const mergedInit = { ...readableStreamInit, ...init } return new Promise((resolve, reject) => { const chunks = [] let aborted = false const onAbort = () => { aborted = true cleanup() reject(new DOMException("The operation was aborted.", "AbortError")) } if (mergedInit.signal) { if (mergedInit.signal.aborted) { onAbort() return } mergedInit.signal.addEventListener("abort", onAbort) } function cleanup() { if (mergedInit.signal) { mergedInit.signal.removeEventListener("abort", onAbort) } window.FsInputStream.onmessage = null } window.FsInputStream.addEventListener("message", (event) => { if (aborted) return const msg = event.data if (msg instanceof ArrayBuffer) { chunks.push(new Uint8Array(msg)) } else if (typeof msg === "string") { cleanup() reject(new Error(msg)) return } // Once we have the full file (or a single chunk for now), create a stream const stream = new ReadableStream({ start(controller) { try { for (const chunk of chunks) { controller.enqueue(chunk) } controller.close() cleanup() } catch (e) { cleanup() controller.error(e) } }, cancel(reason) { console.warn("Stream canceled:", reason) cleanup() } }) resolve(new Response(stream, mergedInit)) }) // Send the request to Android window.FsInputStream.postMessage(path) }) } export const readableStreamInit = { headers: { "Content-Type": "application/octet-stream" } }