UNPKG

native-file-system-adapter-ts

Version:
163 lines 5.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileHandleDownloader = void 0; const errors_1 = require("../errors"); const { GONE } = errors_1.errors; const isSafari = /constructor/i.test(window.HTMLElement) || window.safari || window.WebKitPoint; class FileHandleDownloader { constructor(name = 'unkown') { this.name = name; this.kind = 'file'; this.writable = true; this.readable = false; this._deleted = false; } async getFile() { throw new Error(...GONE); } async isSameEntry(other) { return this === other; } async remove() { this._deleted = true; } async createWritable(options = {}) { var _a; if (this._deleted) throw new Error(...GONE); const sw = await navigator.serviceWorker.getRegistration(); const link = document.createElement('a'); const ts = new TransformStream(); const sink = ts.writable; link.download = this.name; if (isSafari || !sw) { /** @type {Blob[]} */ let chunks = []; ts.readable.pipeTo(new WritableStream({ write(chunk) { chunks.push(new Blob([chunk])); }, close() { const blob = new Blob(chunks, { type: 'application/octet-stream; charset=utf-8', }); chunks = []; link.href = URL.createObjectURL(blob); link.click(); setTimeout(() => URL.revokeObjectURL(link.href), 10000); }, })); } else { const { writable, readablePort } = new RemoteWritableStream(); // Make filename RFC5987 compatible const fileName = encodeURIComponent(this.name).replace(/['()]/g, escape).replace(/\*/g, '%2A'); const headers = { 'content-disposition': "attachment; filename*=UTF-8''" + fileName, 'content-type': 'application/octet-stream; charset=utf-8', ...(options.size ? { 'content-length': options.size } : {}), }; const keepAlive = setTimeout(() => { var _a; return (_a = sw.active) === null || _a === void 0 ? void 0 : _a.postMessage(0); }, 10000); ts.readable .pipeThrough(new TransformStream({ transform(chunk, ctrl) { var _a; if (chunk instanceof Uint8Array) return ctrl.enqueue(chunk); const reader = (_a = new Response(chunk).body) === null || _a === void 0 ? void 0 : _a.getReader(); const pump = () => reader === null || reader === void 0 ? void 0 : reader.read().then((e) => (e.done ? 0 : pump(ctrl.enqueue(e.value)))); return pump(); }, })) .pipeTo(writable) .finally(() => { clearInterval(keepAlive); }); // Transfer the stream to service worker (_a = sw.active) === null || _a === void 0 ? void 0 : _a.postMessage({ url: sw.scope + fileName, headers, readablePort, }, [readablePort]); // Trigger the download with a hidden iframe const iframe = document.createElement('iframe'); iframe.hidden = true; iframe.src = sw.scope + fileName; document.body.appendChild(iframe); } return sink.getWriter(); } } exports.FileHandleDownloader = FileHandleDownloader; const WRITE = 0; const PULL = 0; const ERROR = 1; const ABORT = 1; const CLOSE = 2; class MessagePortSink { constructor(port) { port.onmessage = (event) => this._onMessage(event.data); this._port = port; this._resetReady(); } start(controller) { this._controller = controller; // Apply initial backpressure return this._readyPromise; } write(chunk) { const message = { type: WRITE, chunk }; // Send chunk this._port.postMessage(message, [chunk.buffer]); // Assume backpressure after every write, until sender pulls this._resetReady(); // Apply backpressure return this._readyPromise; } close() { this._port.postMessage({ type: CLOSE }); this._port.close(); } abort(reason) { this._port.postMessage({ type: ABORT, reason }); this._port.close(); } _onMessage(message) { if (message.type === PULL) this._resolveReady(); if (message.type === ERROR) this._onError(message.reason); } _onError(reason) { this._controller.error(reason); this._rejectReady(reason); this._port.close(); } _resetReady() { this._readyPromise = new Promise((resolve, reject) => { this._readyResolve = resolve; this._readyReject = reject; }); this._readyPending = true; } _resolveReady() { this._readyResolve(); this._readyPending = false; } _rejectReady(reason) { if (!this._readyPending) this._resetReady(); // eslint-disable-next-line @typescript-eslint/no-empty-function this._readyPromise.catch(() => { }); this._readyReject(reason); this._readyPending = false; } } class RemoteWritableStream { constructor() { const channel = new MessageChannel(); this.readablePort = channel.port1; this.writable = new WritableStream(new MessagePortSink(channel.port2)); } } //# sourceMappingURL=downloader.js.map