UNPKG

modern-file-saver

Version:

Modern file saving library for browsers with File System Access API support and fallback

157 lines (152 loc) 4.93 kB
'use strict'; // src/utils/blob.ts function isBase64DataUrl(str) { return str.startsWith("data:") && str.includes(";base64,"); } async function base64ToBlob(base64, mimeType) { const dataUrl = isBase64DataUrl(base64) ? base64 : `data:${mimeType || "application/octet-stream"};base64,${base64}`; const response = await fetch(dataUrl); const blob = await response.blob(); if (mimeType && blob.type !== mimeType) { return new Blob([await blob.arrayBuffer()], { type: mimeType }); } return blob; } async function convertToBlob(input, options = {}) { if (isObjectType(input)) { return new Blob([JSON.stringify(input, null, 2)], { type: options.mimeType || "application/json" }); } if (typeof input === "string") { if (options.isBase64) { return base64ToBlob(input, options.mimeType); } if (isBase64DataUrl(input)) { return base64ToBlob(input, options.mimeType); } return new Blob([input], { type: options.mimeType || "text/plain" }); } if (input instanceof Blob) { if (options.mimeType && options.mimeType !== input.type) { return new Blob([await input.arrayBuffer()], { type: options.mimeType }); } return input; } if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { return new Blob([input], { type: options.mimeType || "application/octet-stream" }); } if (input instanceof URLSearchParams) { return new Blob([input.toString()], { type: "application/x-www-form-urlencoded" }); } if (input instanceof FormData) { const pairs = []; input.forEach((value, key) => { pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.toString())}`); }); return new Blob([pairs.join("&")], { type: "multipart/form-data" }); } throw new Error("Unsupported input type"); } function isObjectType(input) { return typeof input === "object" && !(input instanceof Blob) && !(input instanceof ArrayBuffer) && !ArrayBuffer.isView(input) && !(input instanceof URLSearchParams) && !(input instanceof FormData); } // src/utils/logger.ts var ConsoleLogger = class { constructor(level = "none") { this.level = level; } debug(message, ...args) { if (this.level === "debug") { console.log(`[modern-file-saver] ${message}`, ...args); } } }; function createLogger(level = "none") { return new ConsoleLogger(level); } // src/utils/file-picker.ts function getFilePickerOptions(blob, fileName) { let mimeType = blob.type || "application/octet-stream"; if (!mimeType.includes("/")) { mimeType = "application/octet-stream"; } let extension = fileName.split(".").pop() || "bin"; extension = extension.startsWith(".") ? extension : `.${extension}`; const accept = { [mimeType]: [extension] }; return { suggestedName: fileName, types: [ { description: "File", accept } ] }; } // src/index.ts async function saveFile(input, options = {}) { const { fileName = input instanceof File ? input.name : "download", promptSaveAs = true, logLevel = "none" } = options; const logger = createLogger(logLevel); try { logger.debug("Converting input to blob", { type: input.constructor.name }); const blob = await convertToBlob(input, options); logger.debug("Blob created", { type: blob.type, size: blob.size }); if (promptSaveAs && "showSaveFilePicker" in window) { try { logger.debug("Attempting to use File System Access API"); const handle = await window.showSaveFilePicker( getFilePickerOptions(blob, fileName) ); logger.debug("File handle obtained, creating writable"); const writable = await handle.createWritable(); await writable.write(blob); await writable.close(); logger.debug("File saved successfully using File System Access API"); return; } catch (err) { if (err.name === "AbortError") { logger.debug("User aborted File System Access API save dialog"); throw err; } logger.debug("File System Access API failed, falling back to legacy method", err); } } else { logger.debug("Using legacy download method", { reason: promptSaveAs ? "API not available" : "promptSaveAs is false" }); } const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.style.display = "none"; link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); setTimeout(() => { URL.revokeObjectURL(url); document.body.removeChild(link); logger.debug("File saved successfully using legacy method"); }, 100); } catch (error) { logger.debug("Error saving file:", error); throw error; } } exports.saveFile = saveFile; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map