UNPKG

@mjackson/file-storage

Version:
897 lines (892 loc) 27.6 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/local.ts var local_exports = {}; __export(local_exports, { LocalFileStorage: () => LocalFileStorage }); module.exports = __toCommonJS(local_exports); // src/lib/local-file-storage.ts var fs2 = __toESM(require("node:fs"), 1); var fsp = __toESM(require("node:fs/promises"), 1); var path2 = __toESM(require("node:path"), 1); // ../lazy-file/dist/fs.js var fs = __toESM(require("node:fs"), 1); var path = __toESM(require("node:path"), 1); var mimes = { "3g2": "video/3gpp2", "3gp": "video/3gpp", "3gpp": "video/3gpp", "3mf": "model/3mf", "aac": "audio/aac", "ac": "application/pkix-attr-cert", "adp": "audio/adpcm", "adts": "audio/aac", "ai": "application/postscript", "aml": "application/automationml-aml+xml", "amlx": "application/automationml-amlx+zip", "amr": "audio/amr", "apng": "image/apng", "appcache": "text/cache-manifest", "appinstaller": "application/appinstaller", "appx": "application/appx", "appxbundle": "application/appxbundle", "asc": "application/pgp-keys", "atom": "application/atom+xml", "atomcat": "application/atomcat+xml", "atomdeleted": "application/atomdeleted+xml", "atomsvc": "application/atomsvc+xml", "au": "audio/basic", "avci": "image/avci", "avcs": "image/avcs", "avif": "image/avif", "aw": "application/applixware", "bdoc": "application/bdoc", "bin": "application/octet-stream", "bmp": "image/bmp", "bpk": "application/octet-stream", "btf": "image/prs.btif", "btif": "image/prs.btif", "buffer": "application/octet-stream", "ccxml": "application/ccxml+xml", "cdfx": "application/cdfx+xml", "cdmia": "application/cdmi-capability", "cdmic": "application/cdmi-container", "cdmid": "application/cdmi-domain", "cdmio": "application/cdmi-object", "cdmiq": "application/cdmi-queue", "cer": "application/pkix-cert", "cgm": "image/cgm", "cjs": "application/node", "class": "application/java-vm", "coffee": "text/coffeescript", "conf": "text/plain", "cpl": "application/cpl+xml", "cpt": "application/mac-compactpro", "crl": "application/pkix-crl", "css": "text/css", "csv": "text/csv", "cu": "application/cu-seeme", "cwl": "application/cwl", "cww": "application/prs.cww", "davmount": "application/davmount+xml", "dbk": "application/docbook+xml", "deb": "application/octet-stream", "def": "text/plain", "deploy": "application/octet-stream", "dib": "image/bmp", "disposition-notification": "message/disposition-notification", "dist": "application/octet-stream", "distz": "application/octet-stream", "dll": "application/octet-stream", "dmg": "application/octet-stream", "dms": "application/octet-stream", "doc": "application/msword", "dot": "application/msword", "dpx": "image/dpx", "drle": "image/dicom-rle", "dsc": "text/prs.lines.tag", "dssc": "application/dssc+der", "dtd": "application/xml-dtd", "dump": "application/octet-stream", "dwd": "application/atsc-dwd+xml", "ear": "application/java-archive", "ecma": "application/ecmascript", "elc": "application/octet-stream", "emf": "image/emf", "eml": "message/rfc822", "emma": "application/emma+xml", "emotionml": "application/emotionml+xml", "eps": "application/postscript", "epub": "application/epub+zip", "exe": "application/octet-stream", "exi": "application/exi", "exp": "application/express", "exr": "image/aces", "ez": "application/andrew-inset", "fdf": "application/fdf", "fdt": "application/fdt+xml", "fits": "image/fits", "g3": "image/g3fax", "gbr": "application/rpki-ghostbusters", "geojson": "application/geo+json", "gif": "image/gif", "glb": "model/gltf-binary", "gltf": "model/gltf+json", "gml": "application/gml+xml", "gpx": "application/gpx+xml", "gram": "application/srgs", "grxml": "application/srgs+xml", "gxf": "application/gxf", "gz": "application/gzip", "h261": "video/h261", "h263": "video/h263", "h264": "video/h264", "heic": "image/heic", "heics": "image/heic-sequence", "heif": "image/heif", "heifs": "image/heif-sequence", "hej2": "image/hej2k", "held": "application/atsc-held+xml", "hjson": "application/hjson", "hlp": "application/winhlp", "hqx": "application/mac-binhex40", "hsj2": "image/hsj2", "htm": "text/html", "html": "text/html", "ics": "text/calendar", "ief": "image/ief", "ifb": "text/calendar", "iges": "model/iges", "igs": "model/iges", "img": "application/octet-stream", "in": "text/plain", "ini": "text/plain", "ink": "application/inkml+xml", "inkml": "application/inkml+xml", "ipfix": "application/ipfix", "iso": "application/octet-stream", "its": "application/its+xml", "jade": "text/jade", "jar": "application/java-archive", "jhc": "image/jphc", "jls": "image/jls", "jp2": "image/jp2", "jpe": "image/jpeg", "jpeg": "image/jpeg", "jpf": "image/jpx", "jpg": "image/jpeg", "jpg2": "image/jp2", "jpgm": "image/jpm", "jpgv": "video/jpeg", "jph": "image/jph", "jpm": "image/jpm", "jpx": "image/jpx", "js": "text/javascript", "json": "application/json", "json5": "application/json5", "jsonld": "application/ld+json", "jsonml": "application/jsonml+json", "jsx": "text/jsx", "jt": "model/jt", "jxr": "image/jxr", "jxra": "image/jxra", "jxrs": "image/jxrs", "jxs": "image/jxs", "jxsc": "image/jxsc", "jxsi": "image/jxsi", "jxss": "image/jxss", "kar": "audio/midi", "ktx": "image/ktx", "ktx2": "image/ktx2", "less": "text/less", "lgr": "application/lgr+xml", "list": "text/plain", "litcoffee": "text/coffeescript", "log": "text/plain", "lostxml": "application/lost+xml", "lrf": "application/octet-stream", "m1v": "video/mpeg", "m21": "application/mp21", "m2a": "audio/mpeg", "m2v": "video/mpeg", "m3a": "audio/mpeg", "m4a": "audio/mp4", "m4p": "application/mp4", "m4s": "video/iso.segment", "ma": "application/mathematica", "mads": "application/mads+xml", "maei": "application/mmt-aei+xml", "man": "text/troff", "manifest": "text/cache-manifest", "map": "application/json", "mar": "application/octet-stream", "markdown": "text/markdown", "mathml": "application/mathml+xml", "mb": "application/mathematica", "mbox": "application/mbox", "md": "text/markdown", "mdx": "text/mdx", "me": "text/troff", "mesh": "model/mesh", "meta4": "application/metalink4+xml", "metalink": "application/metalink+xml", "mets": "application/mets+xml", "mft": "application/rpki-manifest", "mid": "audio/midi", "midi": "audio/midi", "mime": "message/rfc822", "mj2": "video/mj2", "mjp2": "video/mj2", "mjs": "text/javascript", "mml": "text/mathml", "mods": "application/mods+xml", "mov": "video/quicktime", "mp2": "audio/mpeg", "mp21": "application/mp21", "mp2a": "audio/mpeg", "mp3": "audio/mpeg", "mp4": "video/mp4", "mp4a": "audio/mp4", "mp4s": "application/mp4", "mp4v": "video/mp4", "mpd": "application/dash+xml", "mpe": "video/mpeg", "mpeg": "video/mpeg", "mpf": "application/media-policy-dataset+xml", "mpg": "video/mpeg", "mpg4": "video/mp4", "mpga": "audio/mpeg", "mpp": "application/dash-patch+xml", "mrc": "application/marc", "mrcx": "application/marcxml+xml", "ms": "text/troff", "mscml": "application/mediaservercontrol+xml", "msh": "model/mesh", "msi": "application/octet-stream", "msix": "application/msix", "msixbundle": "application/msixbundle", "msm": "application/octet-stream", "msp": "application/octet-stream", "mtl": "model/mtl", "musd": "application/mmt-usd+xml", "mxf": "application/mxf", "mxmf": "audio/mobile-xmf", "mxml": "application/xv+xml", "n3": "text/n3", "nb": "application/mathematica", "nq": "application/n-quads", "nt": "application/n-triples", "obj": "model/obj", "oda": "application/oda", "oga": "audio/ogg", "ogg": "audio/ogg", "ogv": "video/ogg", "ogx": "application/ogg", "omdoc": "application/omdoc+xml", "onepkg": "application/onenote", "onetmp": "application/onenote", "onetoc": "application/onenote", "onetoc2": "application/onenote", "opf": "application/oebps-package+xml", "opus": "audio/ogg", "otf": "font/otf", "owl": "application/rdf+xml", "oxps": "application/oxps", "p10": "application/pkcs10", "p7c": "application/pkcs7-mime", "p7m": "application/pkcs7-mime", "p7s": "application/pkcs7-signature", "p8": "application/pkcs8", "pdf": "application/pdf", "pfr": "application/font-tdpfr", "pgp": "application/pgp-encrypted", "pkg": "application/octet-stream", "pki": "application/pkixcmp", "pkipath": "application/pkix-pkipath", "pls": "application/pls+xml", "png": "image/png", "prc": "model/prc", "prf": "application/pics-rules", "provx": "application/provenance+xml", "ps": "application/postscript", "pskcxml": "application/pskc+xml", "pti": "image/prs.pti", "qt": "video/quicktime", "raml": "application/raml+yaml", "rapd": "application/route-apd+xml", "rdf": "application/rdf+xml", "relo": "application/p2p-overlay+xml", "rif": "application/reginfo+xml", "rl": "application/resource-lists+xml", "rld": "application/resource-lists-diff+xml", "rmi": "audio/midi", "rnc": "application/relax-ng-compact-syntax", "rng": "application/xml", "roa": "application/rpki-roa", "roff": "text/troff", "rq": "application/sparql-query", "rs": "application/rls-services+xml", "rsat": "application/atsc-rsat+xml", "rsd": "application/rsd+xml", "rsheet": "application/urc-ressheet+xml", "rss": "application/rss+xml", "rtf": "text/rtf", "rtx": "text/richtext", "rusd": "application/route-usd+xml", "s3m": "audio/s3m", "sbml": "application/sbml+xml", "scq": "application/scvp-cv-request", "scs": "application/scvp-cv-response", "sdp": "application/sdp", "senmlx": "application/senml+xml", "sensmlx": "application/sensml+xml", "ser": "application/java-serialized-object", "setpay": "application/set-payment-initiation", "setreg": "application/set-registration-initiation", "sgi": "image/sgi", "sgm": "text/sgml", "sgml": "text/sgml", "shex": "text/shex", "shf": "application/shf+xml", "shtml": "text/html", "sieve": "application/sieve", "sig": "application/pgp-signature", "sil": "audio/silk", "silo": "model/mesh", "siv": "application/sieve", "slim": "text/slim", "slm": "text/slim", "sls": "application/route-s-tsid+xml", "smi": "application/smil+xml", "smil": "application/smil+xml", "snd": "audio/basic", "so": "application/octet-stream", "spdx": "text/spdx", "spp": "application/scvp-vp-response", "spq": "application/scvp-vp-request", "spx": "audio/ogg", "sql": "application/sql", "sru": "application/sru+xml", "srx": "application/sparql-results+xml", "ssdl": "application/ssdl+xml", "ssml": "application/ssml+xml", "stk": "application/hyperstudio", "stl": "model/stl", "stpx": "model/step+xml", "stpxz": "model/step-xml+zip", "stpz": "model/step+zip", "styl": "text/stylus", "stylus": "text/stylus", "svg": "image/svg+xml", "svgz": "image/svg+xml", "swidtag": "application/swid+xml", "t": "text/troff", "t38": "image/t38", "td": "application/urc-targetdesc+xml", "tei": "application/tei+xml", "teicorpus": "application/tei+xml", "text": "text/plain", "tfi": "application/thraud+xml", "tfx": "image/tiff-fx", "tif": "image/tiff", "tiff": "image/tiff", "toml": "application/toml", "tr": "text/troff", "trig": "application/trig", "ts": "video/mp2t", "tsd": "application/timestamped-data", "tsv": "text/tab-separated-values", "ttc": "font/collection", "ttf": "font/ttf", "ttl": "text/turtle", "ttml": "application/ttml+xml", "txt": "text/plain", "u3d": "model/u3d", "u8dsn": "message/global-delivery-status", "u8hdr": "message/global-headers", "u8mdn": "message/global-disposition-notification", "u8msg": "message/global", "ubj": "application/ubjson", "uri": "text/uri-list", "uris": "text/uri-list", "urls": "text/uri-list", "vcard": "text/vcard", "vrml": "model/vrml", "vtt": "text/vtt", "vxml": "application/voicexml+xml", "war": "application/java-archive", "wasm": "application/wasm", "wav": "audio/wav", "weba": "audio/webm", "webm": "video/webm", "webmanifest": "application/manifest+json", "webp": "image/webp", "wgsl": "text/wgsl", "wgt": "application/widget", "wif": "application/watcherinfo+xml", "wmf": "image/wmf", "woff": "font/woff", "woff2": "font/woff2", "wrl": "model/vrml", "wsdl": "application/wsdl+xml", "wspolicy": "application/wspolicy+xml", "x3d": "model/x3d+xml", "x3db": "model/x3d+fastinfoset", "x3dbz": "model/x3d+binary", "x3dv": "model/x3d-vrml", "x3dvz": "model/x3d+vrml", "x3dz": "model/x3d+xml", "xaml": "application/xaml+xml", "xav": "application/xcap-att+xml", "xca": "application/xcap-caps+xml", "xcs": "application/calendar+xml", "xdf": "application/xcap-diff+xml", "xdssc": "application/dssc+xml", "xel": "application/xcap-el+xml", "xenc": "application/xenc+xml", "xer": "application/patch-ops-error+xml", "xfdf": "application/xfdf", "xht": "application/xhtml+xml", "xhtml": "application/xhtml+xml", "xhvml": "application/xv+xml", "xlf": "application/xliff+xml", "xm": "audio/xm", "xml": "text/xml", "xns": "application/xcap-ns+xml", "xop": "application/xop+xml", "xpl": "application/xproc+xml", "xsd": "application/xml", "xsf": "application/prs.xsf+xml", "xsl": "application/xml", "xslt": "application/xml", "xspf": "application/xspf+xml", "xvm": "application/xv+xml", "xvml": "application/xv+xml", "yaml": "text/yaml", "yang": "application/yang", "yin": "application/yin+xml", "yml": "text/yaml", "zip": "application/zip" }; function lookup(extn) { let tmp = ("" + extn).trim().toLowerCase(); let idx = tmp.lastIndexOf("."); return mimes[!~idx ? tmp : tmp.substring(++idx)]; } function getByteLength(range, size) { let [start, end] = getIndexes(range, size); return end - start; } function getIndexes(range, size) { let start = Math.min(Math.max(0, range.start < 0 ? size + range.start : range.start), size); let end = Math.min(Math.max(start, range.end < 0 ? size + range.end : range.end), size); return [start, end]; } var LazyBlob = class extends Blob { #content; constructor(parts, options) { super([], options); this.#content = new BlobContent(parts, options); } /** * Returns the blob's contents as an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer). * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer) */ async arrayBuffer() { return this.#content.arrayBuffer(); } /** * Returns the blob's contents as a byte array. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/bytes) */ async bytes() { return this.#content.bytes(); } /** * The size of the blob in bytes. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/size) */ get size() { return this.#content.size; } /** * Returns a new [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that contains the data in the specified range. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) */ slice(start, end, contentType) { return this.#content.slice(start, end, contentType); } /** * Returns a stream that can be used to read the blob's contents. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/stream) */ stream() { return this.#content.stream(); } /** * Returns the blob's contents as a string. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/text) */ async text() { return this.#content.text(); } }; var LazyFile = class extends File { #content; constructor(parts, name, options) { super([], name, options); this.#content = new BlobContent(parts, options); } /** * Returns the file's content as an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer). * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer) */ async arrayBuffer() { return this.#content.arrayBuffer(); } /** * Returns the file's contents as a byte array. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/bytes) */ async bytes() { return this.#content.bytes(); } /** * The size of the file in bytes. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/size) */ get size() { return this.#content.size; } /** * Returns a new [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that contains the data in the specified range. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) */ slice(start, end, contentType) { return this.#content.slice(start, end, contentType); } /** * Returns a stream that can be used to read the file's contents. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/stream) */ stream() { return this.#content.stream(); } /** * Returns the file's contents as a string. * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/text) */ async text() { return this.#content.text(); } }; var BlobContent = class { source; totalSize; range; type; constructor(parts, options) { if (Array.isArray(parts)) { this.source = []; this.totalSize = 0; for (let part of parts) { if (part instanceof Blob) { this.source.push(part); this.totalSize += part.size; } else { let array; if (typeof part === "string") { array = new TextEncoder().encode(part); } else if (ArrayBuffer.isView(part)) { array = new Uint8Array(part.buffer, part.byteOffset, part.byteLength); } else { array = new Uint8Array(part); } this.source.push(array); this.totalSize += array.byteLength; } } } else { this.source = parts; this.totalSize = parts.byteLength; } this.range = options?.range; this.type = options?.type ?? ""; } async arrayBuffer() { return (await this.bytes()).buffer; } async bytes() { let result = new Uint8Array(this.size); let offset = 0; for await (let chunk of this.stream()) { result.set(chunk, offset); offset += chunk.length; } return result; } get size() { return this.range != null ? getByteLength(this.range, this.totalSize) : this.totalSize; } slice(start = 0, end, contentType = "") { let range = this.range != null ? ( // file.slice().slice() is additive { start: this.range.start + start, end: this.range.end + (end ?? 0) } ) : { start, end: end ?? this.size }; return new LazyBlob(this.source, { range, type: contentType }); } stream() { if (this.range != null) { let [start, end] = getIndexes(this.range, this.totalSize); return Array.isArray(this.source) ? streamContentArray(this.source, start, end) : this.source.stream(start, end); } return Array.isArray(this.source) ? streamContentArray(this.source) : this.source.stream(); } async text() { return new TextDecoder("utf-8").decode(await this.bytes()); } }; function streamContentArray(content, start = 0, end = Infinity) { let index = 0; let bytesRead = 0; return new ReadableStream({ async pull(controller) { if (index >= content.length) { controller.close(); return; } let hasPushed = false; function pushChunk(chunk) { let chunkLength = chunk.byteLength; if (!(bytesRead + chunkLength < start || bytesRead >= end)) { let startIndex = Math.max(start - bytesRead, 0); let endIndex = Math.min(end - bytesRead, chunkLength); controller.enqueue(chunk.subarray(startIndex, endIndex)); hasPushed = true; } bytesRead += chunkLength; } async function pushPart(part) { if (part instanceof Blob) { if (bytesRead + part.size <= start) { bytesRead += part.size; return; } for await (let chunk of part.stream()) { pushChunk(chunk); if (bytesRead >= end) { break; } } } else { pushChunk(part); } } while (!hasPushed && index < content.length) { await pushPart(content[index++]); if (bytesRead >= end) { controller.close(); break; } } } }); } function openFile(filename, options) { let stats = fs.statSync(filename); if (!stats.isFile()) { throw new Error(`Path "${filename}" is not a file`); } let content = { byteLength: stats.size, stream(start, end) { return streamFile(filename, start, end); } }; return new LazyFile(content, options?.name ?? path.basename(filename), { type: options?.type ?? lookup(filename), lastModified: options?.lastModified ?? stats.mtimeMs }); } function streamFile(filename, start = 0, end = Infinity) { let read = fs.createReadStream(filename, { start, end: end - 1 }).iterator(); return new ReadableStream({ async pull(controller) { let { done, value } = await read.next(); if (done) { controller.close(); } else { controller.enqueue(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)); } } }); } function writeFile(to, file) { return new Promise(async (resolve2, reject) => { let writeStream = typeof to === "string" ? fs.createWriteStream(to) : fs.createWriteStream("ignored", { fd: to }); try { for await (let chunk of file.stream()) { writeStream.write(chunk); } writeStream.end(() => { resolve2(); }); } catch (error) { writeStream.end(() => { reject(error); }); } }); } // src/lib/local-file-storage.ts var LocalFileStorage = class { #dirname; /** * @param directory The directory where files are stored */ constructor(directory) { this.#dirname = path2.resolve(directory); try { let stats = fs2.statSync(this.#dirname); if (!stats.isDirectory()) { throw new Error(`Path "${this.#dirname}" is not a directory`); } } catch (error) { if (!isNoEntityError(error)) { throw error; } fs2.mkdirSync(this.#dirname, { recursive: true }); } } async get(key) { let { filePath, metaPath } = await this.#getPaths(key); try { let meta = await readMetadata(metaPath); return openFile(filePath, { lastModified: meta.lastModified, name: meta.name, type: meta.type }); } catch (error) { if (!isNoEntityError(error)) { throw error; } return null; } } async has(key) { let { metaPath } = await this.#getPaths(key); try { await fsp.access(metaPath); return true; } catch { return false; } } async list(options) { let { cursor, includeMetadata = false, limit = 32, prefix } = options ?? {}; let files = []; let foundCursor = cursor === void 0; let nextCursor; let lastHash; outerLoop: for await (let subdir of await fsp.opendir(this.#dirname)) { if (!subdir.isDirectory()) continue; for await (let file of await fsp.opendir(path2.join(this.#dirname, subdir.name))) { if (!file.isFile() || !file.name.endsWith(".meta.json")) continue; let hash = file.name.slice(0, -10); if (foundCursor) { let meta = await readMetadata(path2.join(this.#dirname, subdir.name, file.name)); if (prefix != null && !meta.key.startsWith(prefix)) { continue; } if (files.length >= limit) { nextCursor = lastHash; break outerLoop; } if (includeMetadata) { let size = (await fsp.stat(path2.join(this.#dirname, subdir.name, `${hash}.dat`))).size; files.push({ ...meta, size }); } else { files.push({ key: meta.key }); } } else if (hash === cursor) { foundCursor = true; } lastHash = hash; } } return { cursor: nextCursor, files }; } async put(key, file) { await this.set(key, file); return await this.get(key); } async remove(key) { let { filePath, metaPath } = await this.#getPaths(key); try { await Promise.all([fsp.unlink(filePath), fsp.unlink(metaPath)]); } catch (error) { if (!isNoEntityError(error)) { throw error; } } } async set(key, file) { let { directory, filePath, metaPath } = await this.#getPaths(key); await fsp.mkdir(directory, { recursive: true }); await writeFile(filePath, file); let meta = { key, lastModified: file.lastModified, name: file.name, type: file.type }; await fsp.writeFile(metaPath, JSON.stringify(meta)); } async #getPaths(key) { let hash = await computeHash(key); let directory = path2.join(this.#dirname, hash.slice(0, 2)); return { directory, filePath: path2.join(directory, `${hash}.dat`), metaPath: path2.join(directory, `${hash}.meta.json`) }; } }; async function readMetadata(metaPath) { return JSON.parse(await fsp.readFile(metaPath, "utf-8")); } async function computeHash(key, algorithm = "SHA-256") { let digest = await crypto.subtle.digest(algorithm, new TextEncoder().encode(key)); return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join(""); } function isNoEntityError(obj) { return obj instanceof Error && "code" in obj && obj.code === "ENOENT"; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { LocalFileStorage }); //# sourceMappingURL=local.cjs.map