From 942495da0295eed45b72d41e1f30135a8cdfceb3 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sun, 21 Jun 2026 23:47:08 +0100 Subject: [PATCH 01/41] osfile: direct-read fast path for large reads, bypassing the page cache readToBuffer loaded every page into a cache buffer and then copied page->destination, so a large sequential read (e.g. a 100-350MB zkey/ptau section) cost a full extra copy and held the whole section in page buffers (maxPagesLoaded is bumped to fit the read). For reads >= directReadThreshold (1MB) with no overlapping dirty pages, read straight from the fd into the destination buffer instead. Small reads (section headers, ULE32 navigation) keep using the cache. _rangeHasDirtyPages() guards correctness on read-write files; positioned fd.read (pread) keeps concurrent section reads safe. On a sha256 groth16 prove this cut peak memory ~4.7GB -> ~3.9GB by dropping the duplicate page buffers, and removed the page->destination memcpy that was ~13% of main-thread self-time in the flame profile. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/main.cjs | 39 +++++++++++++++++++++++++++++++++++---- src/osfile.js | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index 517c09f..ad01647 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -34,6 +34,10 @@ class FastFile { this.totalSize = stats.size; this.totalPages = Math.floor((stats.size -1) / this.pageSize)+1; this.maxPagesLoaded = Math.floor( cacheSize / this.pageSize)+1; + // Reads at least this large bypass the page cache and copy straight from + // disk into the destination buffer (see readToBuffer), avoiding the + // extra page->destination copy that dominates large sequential reads. + this.directReadThreshold = 1 << 20; this.pages = {}; this.pendingLoads = []; this.writing = false; @@ -285,19 +289,46 @@ class FastFile { return buff; } + _rangeHasDirtyPages(pos, len) { + const firstPage = Math.floor(pos / this.pageSize); + const lastPage = Math.floor((pos + len - 1) / this.pageSize); + for (let p = firstPage; p <= lastPage; p++) { + const page = this.pages[p]; + if (page && (page.dirty || page.writing)) return true; + } + return false; + } + async readToBuffer(buffDst, offset, len, pos) { if (len == 0) { return; } const self = this; - if (len > self.pageSize*self.maxPagesLoaded*0.8) { - const cacheSize = Math.floor(len * 1.1); - this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; - } if (typeof pos == "undefined") pos = self.pos; self.pos = pos+len; if (self.pendingClose) throw new Error("Reading a closing file"); + + // Direct-read fast path: for large reads with no overlapping unwritten + // (dirty) pages, copy straight from disk into the destination buffer. + // This skips the page cache and the page->destination copy it incurs, + // which dominates large sequential reads (e.g. zkey/ptau sections). + if (len >= self.directReadThreshold && !self._rangeHasDirtyPages(pos, len)) { + let toRead = (pos + len > self.totalSize) ? (self.totalSize - pos) : len; + if (toRead < 0) toRead = 0; + let done = 0; + while (done < toRead) { + const { bytesRead } = await self.fd.read(buffDst, offset + done, toRead - done, pos + done); + if (bytesRead === 0) break; // EOF + done += bytesRead; + } + return; + } + + if (len > self.pageSize*self.maxPagesLoaded*0.8) { + const cacheSize = Math.floor(len * 1.1); + this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; + } const firstPage = Math.floor(pos / self.pageSize); const lastPage = Math.floor((pos + len -1) / self.pageSize); diff --git a/src/osfile.js b/src/osfile.js index fcd8445..d80c0b4 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -26,6 +26,10 @@ class FastFile { this.totalSize = stats.size; this.totalPages = Math.floor((stats.size -1) / this.pageSize)+1; this.maxPagesLoaded = Math.floor( cacheSize / this.pageSize)+1; + // Reads at least this large bypass the page cache and copy straight from + // disk into the destination buffer (see readToBuffer), avoiding the + // extra page->destination copy that dominates large sequential reads. + this.directReadThreshold = 1 << 20; this.pages = {}; this.pendingLoads = []; this.writing = false; @@ -277,19 +281,46 @@ class FastFile { return buff; } + _rangeHasDirtyPages(pos, len) { + const firstPage = Math.floor(pos / this.pageSize); + const lastPage = Math.floor((pos + len - 1) / this.pageSize); + for (let p = firstPage; p <= lastPage; p++) { + const page = this.pages[p]; + if (page && (page.dirty || page.writing)) return true; + } + return false; + } + async readToBuffer(buffDst, offset, len, pos) { if (len == 0) { return; } const self = this; - if (len > self.pageSize*self.maxPagesLoaded*0.8) { - const cacheSize = Math.floor(len * 1.1); - this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; - } if (typeof pos == "undefined") pos = self.pos; self.pos = pos+len; if (self.pendingClose) throw new Error("Reading a closing file"); + + // Direct-read fast path: for large reads with no overlapping unwritten + // (dirty) pages, copy straight from disk into the destination buffer. + // This skips the page cache and the page->destination copy it incurs, + // which dominates large sequential reads (e.g. zkey/ptau sections). + if (len >= self.directReadThreshold && !self._rangeHasDirtyPages(pos, len)) { + let toRead = (pos + len > self.totalSize) ? (self.totalSize - pos) : len; + if (toRead < 0) toRead = 0; + let done = 0; + while (done < toRead) { + const { bytesRead } = await self.fd.read(buffDst, offset + done, toRead - done, pos + done); + if (bytesRead === 0) break; // EOF + done += bytesRead; + } + return; + } + + if (len > self.pageSize*self.maxPagesLoaded*0.8) { + const cacheSize = Math.floor(len * 1.1); + this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; + } const firstPage = Math.floor(pos / self.pageSize); const lastPage = Math.floor((pos + len -1) / self.pageSize); From 4639ab750542d3e8e016d35748aaccff9b8433b9 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Jun 2026 00:15:05 +0100 Subject: [PATCH 02/41] osfile: direct-write fast path for large writes, bypassing the page cache Mirrors the direct-read path. The cached write() loads pages, copies buff->page, marks them dirty, and flushes page->disk later, so a large write (e.g. a zkey/ptau section) costs an extra copy and holds the whole region in page buffers. For writes >= directWriteThreshold (1MB) to a region with no cached pages, write straight to disk via positioned fd.write instead. The guard is stricter than the read path's: it falls back to the cache when ANY page in range is cached (even clean), since a clean cached page would go stale after a direct write. Small writes (headers, ULE32) keep using the cache. The direct write awaits completion, so it leaves no deferred flush. Verified byte-exact on real files (large+small mixed writes, mid-file overwrite, read-back) and end to end via the snarkjs Full process suite (49/49). Symmetric with the read path's memory win for large sequential I/O. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/main.cjs | 40 +++++++++++++++++++++++++++++++--------- src/osfile.js | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index ad01647..a6e02e0 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -34,10 +34,12 @@ class FastFile { this.totalSize = stats.size; this.totalPages = Math.floor((stats.size -1) / this.pageSize)+1; this.maxPagesLoaded = Math.floor( cacheSize / this.pageSize)+1; - // Reads at least this large bypass the page cache and copy straight from - // disk into the destination buffer (see readToBuffer), avoiding the - // extra page->destination copy that dominates large sequential reads. + // Reads/writes at least this large bypass the page cache and move bytes + // straight between disk and the caller's buffer (see readToBuffer / + // write), avoiding the extra buffer<->page copy that dominates large + // sequential transfers (e.g. zkey/ptau sections). this.directReadThreshold = 1 << 20; + this.directWriteThreshold = 1 << 20; this.pages = {}; this.pendingLoads = []; this.writing = false; @@ -238,20 +240,40 @@ class FastFile { return -1; } + _rangeHasCachedPages(pos, len) { + const firstPage = Math.floor(pos / this.pageSize); + const lastPage = Math.floor((pos + len - 1) / this.pageSize); + for (let p = firstPage; p <= lastPage; p++) { + if (this.pages[p]) return true; + } + return false; + } + async write(buff, pos) { if (buff.byteLength == 0) return; const self = this; - /* - if (buff.byteLength > self.pageSize*self.maxPagesLoaded*0.8) { - const cacheSize = Math.floor(buff.byteLength * 1.1); - this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; - } - */ if (typeof pos == "undefined") pos = self.pos; self.pos = pos+buff.byteLength; if (self.totalSize < pos + buff.byteLength) self.totalSize = pos + buff.byteLength; if (self.pendingClose) throw new Error("Writing a closing file"); + + // Direct-write fast path: for large writes to a region with no cached + // pages, write straight to disk, skipping the buff->page copy and the + // deferred page flush. Any cached page in range (even clean) would go + // stale after a direct write, so we fall back to the cached path then. + if (buff.byteLength >= self.directWriteThreshold && !self._rangeHasCachedPages(pos, buff.byteLength)) { + let done = 0; + while (done < buff.byteLength) { + const { bytesWritten } = await self.fd.write(buff, done, buff.byteLength - done, pos + done); + if (bytesWritten === 0) break; // should not happen + done += bytesWritten; + } + const lastPage = Math.floor((pos + buff.byteLength - 1) / self.pageSize); + if (lastPage + 1 > self.totalPages) self.totalPages = lastPage + 1; + return; + } + const firstPage = Math.floor(pos / self.pageSize); const lastPage = Math.floor((pos + buff.byteLength -1) / self.pageSize); diff --git a/src/osfile.js b/src/osfile.js index d80c0b4..6ee503e 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -26,10 +26,12 @@ class FastFile { this.totalSize = stats.size; this.totalPages = Math.floor((stats.size -1) / this.pageSize)+1; this.maxPagesLoaded = Math.floor( cacheSize / this.pageSize)+1; - // Reads at least this large bypass the page cache and copy straight from - // disk into the destination buffer (see readToBuffer), avoiding the - // extra page->destination copy that dominates large sequential reads. + // Reads/writes at least this large bypass the page cache and move bytes + // straight between disk and the caller's buffer (see readToBuffer / + // write), avoiding the extra buffer<->page copy that dominates large + // sequential transfers (e.g. zkey/ptau sections). this.directReadThreshold = 1 << 20; + this.directWriteThreshold = 1 << 20; this.pages = {}; this.pendingLoads = []; this.writing = false; @@ -230,20 +232,40 @@ class FastFile { return -1; } + _rangeHasCachedPages(pos, len) { + const firstPage = Math.floor(pos / this.pageSize); + const lastPage = Math.floor((pos + len - 1) / this.pageSize); + for (let p = firstPage; p <= lastPage; p++) { + if (this.pages[p]) return true; + } + return false; + } + async write(buff, pos) { if (buff.byteLength == 0) return; const self = this; - /* - if (buff.byteLength > self.pageSize*self.maxPagesLoaded*0.8) { - const cacheSize = Math.floor(buff.byteLength * 1.1); - this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; - } - */ if (typeof pos == "undefined") pos = self.pos; self.pos = pos+buff.byteLength; if (self.totalSize < pos + buff.byteLength) self.totalSize = pos + buff.byteLength; if (self.pendingClose) throw new Error("Writing a closing file"); + + // Direct-write fast path: for large writes to a region with no cached + // pages, write straight to disk, skipping the buff->page copy and the + // deferred page flush. Any cached page in range (even clean) would go + // stale after a direct write, so we fall back to the cached path then. + if (buff.byteLength >= self.directWriteThreshold && !self._rangeHasCachedPages(pos, buff.byteLength)) { + let done = 0; + while (done < buff.byteLength) { + const { bytesWritten } = await self.fd.write(buff, done, buff.byteLength - done, pos + done); + if (bytesWritten === 0) break; // should not happen + done += bytesWritten; + } + const lastPage = Math.floor((pos + buff.byteLength - 1) / self.pageSize); + if (lastPage + 1 > self.totalPages) self.totalPages = lastPage + 1; + return; + } + const firstPage = Math.floor(pos / self.pageSize); const lastPage = Math.floor((pos + buff.byteLength -1) / self.pageSize); From b37e440fcf12ea82182b09ba0a25ac6133164aa0 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Jun 2026 00:33:32 +0100 Subject: [PATCH 03/41] fastfile: read open flags from fs.constants instead of importing node:fs fastfile.js imported `{ constants }` from "node:fs", which bundlers that stub the "fs" module for the browser (e.g. snarkjs's rollup config) do not cover, so the browser build left node:fs as an undefined external global and crashed with "node_fs is not defined". Read O_TRUNC/O_CREAT/O_RDWR/O_EXCL/O_RDONLY off `fs.constants` via the existing `import fs from "fs"` instead. The "fs" module is already stubbed for browser builds, and these flags are only used on the Node file path (browsers use MemFile), so `fs.constants || {}` is harmless there. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/main.cjs | 6 ++++-- src/fastfile.js | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index a6e02e0..aa99f5c 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -3,7 +3,6 @@ Object.defineProperty(exports, '__esModule', { value: true }); var fs = require('fs'); -var node_fs = require('node:fs'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } @@ -951,7 +950,10 @@ class BigMemFile { } } -const { O_TRUNC, O_CREAT, O_RDWR, O_EXCL, O_RDONLY } = node_fs.constants; +// Read open-mode flags from fs.constants instead of a separate builtin import, +// so bundlers that stub the "fs" module for the browser cover these too. The +// flags are only used on the Node file path, never in the browser. +const { O_TRUNC, O_CREAT, O_RDWR, O_EXCL, O_RDONLY } = fs__default["default"].constants || {}; const DEFAULT_CACHE_SIZE = (1 << 16); const DEFAULT_PAGE_SIZE = (1 << 13); diff --git a/src/fastfile.js b/src/fastfile.js index b017f7d..8b48c54 100644 --- a/src/fastfile.js +++ b/src/fastfile.js @@ -1,9 +1,13 @@ import { open } from "./osfile.js"; +//import { directOpen as open } from "./directfile.js"; import * as memFile from "./memfile.js"; import * as bigMemFile from "./bigmemfile.js"; -import { constants } from "node:fs"; +import fs from "fs"; -const { O_TRUNC, O_CREAT, O_RDWR, O_EXCL, O_RDONLY } = constants; +// Read open-mode flags from fs.constants instead of a separate builtin import, +// so bundlers that stub the "fs" module for the browser cover these too. The +// flags are only used on the Node file path, never in the browser. +const { O_TRUNC, O_CREAT, O_RDWR, O_EXCL, O_RDONLY } = fs.constants || {}; const DEFAULT_CACHE_SIZE = (1 << 16); const DEFAULT_PAGE_SIZE = (1 << 13); From b5852e04fd07fd09f63ae0c02e859055a0a4921a Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Jun 2026 18:22:54 +0100 Subject: [PATCH 04/41] Rebuild --- build/main.cjs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index 362e628..0b64a4b 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -971,7 +971,7 @@ async function createOverride(o, b, c) { } else if (o.type == "bigMem") { return createNew(o); } else { - throw new Error("Invalid FastFile type: " + o.type); + throw new Error("Invalid FastFile type: "+o.type); } } @@ -991,7 +991,7 @@ function createNoOverride(o, b, c) { } else if (o.type == "bigMem") { return createNew(o); } else { - throw new Error("Invalid FastFile type: " + o.type); + throw new Error("Invalid FastFile type: "+o.type); } } @@ -1004,7 +1004,7 @@ async function readExisting(o, b, c) { } if (process.browser) { if (typeof o === "string") { - const buff = await fetch(o).then(function (res) { + const buff = await fetch(o).then( function(res) { return res.arrayBuffer(); }).then(function (ab) { return new Uint8Array(ab); @@ -1031,7 +1031,7 @@ async function readExisting(o, b, c) { } else if (o.type == "bigMem") { return await readExisting$1(o); } else { - throw new Error("Invalid FastFile type: " + o.type); + throw new Error("Invalid FastFile type: "+o.type); } } @@ -1051,7 +1051,7 @@ function readWriteExisting(o, b, c) { } else if (o.type == "bigMem") { return readWriteExisting$1(o); } else { - throw new Error("Invalid FastFile type: " + o.type); + throw new Error("Invalid FastFile type: "+o.type); } } @@ -1071,7 +1071,7 @@ function readWriteExistingOrCreate(o, b, c) { } else if (o.type == "bigMem") { return readWriteExisting$1(o); } else { - throw new Error("Invalid FastFile type: " + o.type); + throw new Error("Invalid FastFile type: "+o.type); } } From fd168d576fcf0f95d779498100a27dfad8a9d103 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Jun 2026 22:35:38 +0100 Subject: [PATCH 05/41] osfile: make direct-io page guards O(cached pages), not O(range) _rangeHasDirtyPages / _rangeHasCachedPages walked every page index in [firstPage, lastPage], so a large direct read/write scanned the whole range (e.g. ~12.5M iterations for a 100GB span with small pages) just to look for conflicting cached pages. Iterate the actually-cached pages via Object.keys(this.pages) and test range membership instead. The cache holds few pages, so the check is now O(cached pages) regardless of range size or page size, and order-independent. Using Object.keys (own enumerable keys) also keeps it immune to prototype pollution. Result set is unchanged: { cached pages with index in [firstPage, lastPage] }. Verified byte-exact via a direct-io guard test (large+small mixed writes, mid-file overwrite with a cached page in range, read-back) plus fastfile 15/15 and the snarkjs node suite 49/49. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/main.cjs | 20 +++++++++++++++----- src/osfile.js | 20 +++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index 0b64a4b..54a3c42 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -240,11 +240,15 @@ class FastFile { return -1; } + // Iterate the actually-cached pages (usually few) rather than every page + // index in [firstPage, lastPage], so the check stays O(cached pages) even + // for very large ranges / small page sizes. _rangeHasCachedPages(pos, len) { const firstPage = Math.floor(pos / this.pageSize); const lastPage = Math.floor((pos + len - 1) / this.pageSize); - for (let p = firstPage; p <= lastPage; p++) { - if (this.pages[p]) return true; + for (const k of Object.keys(this.pages)) { + const p = +k; + if (p >= firstPage && p <= lastPage) return true; } return false; } @@ -311,12 +315,18 @@ class FastFile { return buff; } + // Iterate the actually-cached pages (usually few) rather than every page + // index in [firstPage, lastPage], so the check stays O(cached pages) even + // for very large ranges / small page sizes. _rangeHasDirtyPages(pos, len) { const firstPage = Math.floor(pos / this.pageSize); const lastPage = Math.floor((pos + len - 1) / this.pageSize); - for (let p = firstPage; p <= lastPage; p++) { - const page = this.pages[p]; - if (page && (page.dirty || page.writing)) return true; + for (const k of Object.keys(this.pages)) { + const p = +k; + if (p >= firstPage && p <= lastPage) { + const page = this.pages[p]; + if (page.dirty || page.writing) return true; + } } return false; } diff --git a/src/osfile.js b/src/osfile.js index 6ee503e..a1d6255 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -232,11 +232,15 @@ class FastFile { return -1; } + // Iterate the actually-cached pages (usually few) rather than every page + // index in [firstPage, lastPage], so the check stays O(cached pages) even + // for very large ranges / small page sizes. _rangeHasCachedPages(pos, len) { const firstPage = Math.floor(pos / this.pageSize); const lastPage = Math.floor((pos + len - 1) / this.pageSize); - for (let p = firstPage; p <= lastPage; p++) { - if (this.pages[p]) return true; + for (const k of Object.keys(this.pages)) { + const p = +k; + if (p >= firstPage && p <= lastPage) return true; } return false; } @@ -303,12 +307,18 @@ class FastFile { return buff; } + // Iterate the actually-cached pages (usually few) rather than every page + // index in [firstPage, lastPage], so the check stays O(cached pages) even + // for very large ranges / small page sizes. _rangeHasDirtyPages(pos, len) { const firstPage = Math.floor(pos / this.pageSize); const lastPage = Math.floor((pos + len - 1) / this.pageSize); - for (let p = firstPage; p <= lastPage; p++) { - const page = this.pages[p]; - if (page && (page.dirty || page.writing)) return true; + for (const k of Object.keys(this.pages)) { + const p = +k; + if (p >= firstPage && p <= lastPage) { + const page = this.pages[p]; + if (page.dirty || page.writing) return true; + } } return false; } From 00b2bd82ec2c70d6904d19ad3b0ea660e5aea598 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Jun 2026 23:49:22 +0100 Subject: [PATCH 06/41] osfile: gate direct-io on ArrayBuffer.isView (fix BigBuffer corruption) + tests The direct read/write fast paths hand the buffer straight to fd.read / fd.write, which require a real TypedArray/DataView. fastfile is also called with a BigBuffer (paged, not an ArrayBufferView) for large sections; the cached path handles those via BigBuffer's own .set()/.slice(), but the direct path passed the BigBuffer to fd.read and silently left it unfilled -> corrupted data. This surfaced as `powersoftau verify` on a prepared ptau failing with "Phase2 caclutation does not match with powers of tau": the verify reads section chunks into a BigBuffer, and the direct read returned garbage. The in-memory test suites never caught it because they use MemFile, not OsFile; only on-disk ceremony/zkey I/O hits this path (it's exercised by the snarkjs tutorial). Gate both fast paths on ArrayBuffer.isView(buff): a BigBuffer now falls back to the cached path. Plain Uint8Array sections (e.g. the groth16 prove zkey reads) still get the direct-io speed/memory win. Adds two on-disk OsFile regression tests (read into / write from a 2MB BigBuffer, above the 1MB direct threshold). Verified they fail without the gate and pass with it. fastfile suite 17/17. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/main.cjs | 9 +++++++-- src/osfile.js | 9 +++++++-- test/osfile.test.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index 54a3c42..84a65f8 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -266,7 +266,9 @@ class FastFile { // pages, write straight to disk, skipping the buff->page copy and the // deferred page flush. Any cached page in range (even clean) would go // stale after a direct write, so we fall back to the cached path then. - if (buff.byteLength >= self.directWriteThreshold && !self._rangeHasCachedPages(pos, buff.byteLength)) { + // ArrayBuffer.isView gate: fd.write needs a real TypedArray/DataView; a + // BigBuffer (paged, not a view) must use the cached path. + if (buff.byteLength >= self.directWriteThreshold && ArrayBuffer.isView(buff) && !self._rangeHasCachedPages(pos, buff.byteLength)) { let done = 0; while (done < buff.byteLength) { const { bytesWritten } = await self.fd.write(buff, done, buff.byteLength - done, pos + done); @@ -345,7 +347,10 @@ class FastFile { // (dirty) pages, copy straight from disk into the destination buffer. // This skips the page cache and the page->destination copy it incurs, // which dominates large sequential reads (e.g. zkey/ptau sections). - if (len >= self.directReadThreshold && !self._rangeHasDirtyPages(pos, len)) { + // ArrayBuffer.isView gate: the direct path hands buffDst to fd.read, which + // needs a real TypedArray/DataView. A BigBuffer (paged, not a view) must + // go through the cached path, which copies into it via its own .set(). + if (len >= self.directReadThreshold && ArrayBuffer.isView(buffDst) && !self._rangeHasDirtyPages(pos, len)) { let toRead = (pos + len > self.totalSize) ? (self.totalSize - pos) : len; if (toRead < 0) toRead = 0; let done = 0; diff --git a/src/osfile.js b/src/osfile.js index a1d6255..0f3deca 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -258,7 +258,9 @@ class FastFile { // pages, write straight to disk, skipping the buff->page copy and the // deferred page flush. Any cached page in range (even clean) would go // stale after a direct write, so we fall back to the cached path then. - if (buff.byteLength >= self.directWriteThreshold && !self._rangeHasCachedPages(pos, buff.byteLength)) { + // ArrayBuffer.isView gate: fd.write needs a real TypedArray/DataView; a + // BigBuffer (paged, not a view) must use the cached path. + if (buff.byteLength >= self.directWriteThreshold && ArrayBuffer.isView(buff) && !self._rangeHasCachedPages(pos, buff.byteLength)) { let done = 0; while (done < buff.byteLength) { const { bytesWritten } = await self.fd.write(buff, done, buff.byteLength - done, pos + done); @@ -337,7 +339,10 @@ class FastFile { // (dirty) pages, copy straight from disk into the destination buffer. // This skips the page cache and the page->destination copy it incurs, // which dominates large sequential reads (e.g. zkey/ptau sections). - if (len >= self.directReadThreshold && !self._rangeHasDirtyPages(pos, len)) { + // ArrayBuffer.isView gate: the direct path hands buffDst to fd.read, which + // needs a real TypedArray/DataView. A BigBuffer (paged, not a view) must + // go through the cached path, which copies into it via its own .set(). + if (len >= self.directReadThreshold && ArrayBuffer.isView(buffDst) && !self._rangeHasDirtyPages(pos, len)) { let toRead = (pos + len > self.totalSize) ? (self.totalSize - pos) : len; if (toRead < 0) toRead = 0; let done = 0; diff --git a/test/osfile.test.js b/test/osfile.test.js index 855d644..44cab89 100644 --- a/test/osfile.test.js +++ b/test/osfile.test.js @@ -2,6 +2,8 @@ import fs from "fs"; import * as testUtils from "./testUtils.js"; import * as fastFile from "../src/fastfile.js"; +import { BigBuffer } from "ffjavascript"; + import assert from "assert"; import * as chai from "chai"; import chaiAsPromised from "chai-as-promised"; @@ -49,6 +51,47 @@ describe("fastfile testing suite for osfile", function () { expect(fd.readString(0)).to.be.rejectedWith("Reading a closing file"); await fs.promises.unlink(fileName); }); + + // Regression: the direct-io fast paths hand the buffer to fd.read/fd.write, + // which require a real TypedArray. A BigBuffer (paged, not an ArrayBufferView) + // must fall back to the cached path. A direct read into a BigBuffer used to + // silently leave it unfilled -> corrupted ptau/zkey sections. The read must be + // >= directReadThreshold (1MB) so it would take the direct path if not gated. + it("should read a large section into a BigBuffer (direct-io must not bypass it)", async () => { + const N = 1 << 21; // 2 MB > directReadThreshold + const ref = new Uint8Array(N); + for (let i = 0; i < N; i++) ref[i] = (i * 131 + 7) & 0xff; + + let fd = await fastFile.createOverride(fileName); + await fd.write(ref, 0); + await fd.close(); + + fd = await fastFile.readExisting(fileName); + assert(!ArrayBuffer.isView(new BigBuffer(8)), "BigBuffer must not be an ArrayBufferView"); + const bb = new BigBuffer(N); + await fd.readToBuffer(bb, 0, N, 0); + await fd.close(); + + assert(Buffer.from(bb.slice(0, N)).equals(Buffer.from(ref)), "BigBuffer read does not match file"); + await fs.promises.unlink(fileName); + }); + + it("should write a large section from a BigBuffer (direct-io must not bypass it)", async () => { + const N = 1 << 21; // 2 MB > directWriteThreshold + const ref = new Uint8Array(N); + for (let i = 0; i < N; i++) ref[i] = (i * 197 + 13) & 0xff; + const bb = new BigBuffer(N); + bb.set(ref, 0); + + let fd = await fastFile.createOverride(fileName); + await fd.write(bb, 0); + await fd.close(); + + const onDisk = fs.readFileSync(fileName); + assert.strictEqual(onDisk.length, N); + assert(onDisk.equals(Buffer.from(ref)), "BigBuffer write does not match disk"); + await fs.promises.unlink(fileName); + }); }); From 661dc0e02c866ee0845b0c43f1ef1024d2375dd1 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Tue, 23 Jun 2026 14:01:29 +0100 Subject: [PATCH 07/41] compat: detect Node at runtime instead of process.browser readExisting used `process.browser` to decide whether a string argument is a URL to fetch or a file path to open. `process.browser` is a webpack-ism and is undefined under Vite/esbuild/SES -> ReferenceError. Replace with a non-throwing `isNode` (process.versions.node, true for Node/Bun/Deno). Node-like envs open the file; everything else falls back to fetch, which is universal (Node 18+, Bun, Deno, edge, browser) -- not a browser-only API. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/main.cjs | 6 +++++- src/fastfile.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index 84a65f8..425fa87 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -969,6 +969,10 @@ class BigMemFile { const DEFAULT_CACHE_SIZE = (1 << 16); const DEFAULT_PAGE_SIZE = (1 << 13); +// Robust Node detection that never throws (unlike `process.browser`, which is a +// webpack-ism and is undefined under Vite/esbuild/SES). +const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; + async function createOverride(o, b, c) { if (typeof o === "string") { @@ -1017,7 +1021,7 @@ async function readExisting(o, b, c) { data: o }; } - if (process.browser) { + if (!isNode) { if (typeof o === "string") { const buff = await fetch(o).then( function(res) { return res.arrayBuffer(); diff --git a/src/fastfile.js b/src/fastfile.js index 0d127d8..d50eaa6 100644 --- a/src/fastfile.js +++ b/src/fastfile.js @@ -7,6 +7,10 @@ import { O_TRUNC, O_CREAT, O_RDWR, O_EXCL, O_RDONLY } from "constants"; const DEFAULT_CACHE_SIZE = (1 << 16); const DEFAULT_PAGE_SIZE = (1 << 13); +// Robust Node detection that never throws (unlike `process.browser`, which is a +// webpack-ism and is undefined under Vite/esbuild/SES). +const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; + export async function createOverride(o, b, c) { if (typeof o === "string") { @@ -55,7 +59,7 @@ export async function readExisting(o, b, c) { data: o }; } - if (process.browser) { + if (!isNode) { if (typeof o === "string") { const buff = await fetch(o).then( function(res) { return res.arrayBuffer(); From e432ad2dc7198cefed28d6c29e42aa540b3d6901 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Tue, 23 Jun 2026 17:23:30 +0100 Subject: [PATCH 08/41] compat (Tier 1): browser field stubs fs/constants so bundlers need no hand-stubbing Browser bundlers got the raw src, which imports `fs` (osfile) and the open-mode flags from "constants" -- unresolved without consumer-side stubs. Add a package.json "browser" field: fs -> false (empty; only used on the Node OsFile path, browsers use MemFile), and constants -> a small browser-stub file with NAMED exports (O_TRUNC etc. = 0, unused in browser). The named stub matters because fastfile.js does a NAMED import; an empty `false` stub has no named exports and would fail that import (and a default import would break snarkjs's named-only virtual constants stub at build time). Node behavior unchanged (browser field ignored by Node; real constants used). Verified: clean browser bundle resolution, full snarkjs build, browser + Node tutorial e2e. Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 5 +++++ src/constants.browser.js | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/constants.browser.js diff --git a/package.json b/package.json index b8c0049..1181ba5 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,14 @@ "main": "./build/main.cjs", "module": "./src/fastfile.js", "exports": { + "browser": "./src/fastfile.js", "import": "./src/fastfile.js", "require": "./build/main.cjs" }, + "browser": { + "fs": false, + "constants": "./src/constants.browser.js" + }, "scripts": { "pretest": "eslint .", "test": "mocha", diff --git a/src/constants.browser.js b/src/constants.browser.js new file mode 100644 index 0000000..26c19e7 --- /dev/null +++ b/src/constants.browser.js @@ -0,0 +1,11 @@ +// Browser stub for the Node "constants" module, mapped via the package.json +// "browser" field. fastfile.js does a NAMED import of these open-mode flags, so +// the stub must provide them as named exports (an empty `false` stub has none +// and would fail the named import). The values are never used in the browser: +// file operations go through the Node-only OsFile path, while browsers use +// MemFile. They exist only so the import resolves. +export const O_TRUNC = 0; +export const O_CREAT = 0; +export const O_RDWR = 0; +export const O_EXCL = 0; +export const O_RDONLY = 0; From 86a85b4e08db0204e9f2e957e4a9ac63bcfec989 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sat, 4 Jul 2026 20:31:19 +0100 Subject: [PATCH 09/41] Fix dev-dependency audit findings npm audit fix for the in-range bumps (ajv, brace-expansion, cross-spawn, flatted, ...); serialize-javascript ^7.0.5, diff ^8.0.3 and minimatch ^3.1.4 overridden (mocha 10 pins vulnerable ranges). npm audit clean; 17 tests pass. --- package-lock.json | 523 ++++++++++++++++++++++++---------------------- package.json | 10 +- 2 files changed, 277 insertions(+), 256 deletions(-) diff --git a/package-lock.json b/package-lock.json index a475cd1..ec454e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,7 +161,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -179,10 +178,11 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -195,10 +195,11 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -265,10 +266,11 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -318,7 +320,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, - "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -435,10 +436,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -449,12 +451,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -496,10 +499,11 @@ "dev": true }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -548,7 +552,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -799,10 +802,11 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -1051,10 +1055,21 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1140,10 +1155,11 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1152,32 +1168,32 @@ } }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -1185,26 +1201,69 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1212,12 +1271,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1234,22 +1287,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -1380,10 +1422,11 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1429,15 +1472,6 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1494,10 +1528,11 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.80.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", + "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "dev": true, + "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -1531,33 +1566,14 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.6.tgz", + "integrity": "sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==", "dev": true, - "dependencies": { - "randombytes": "^2.1.0" + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" } }, "node_modules/shebang-command": { @@ -1728,10 +1744,11 @@ } }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -1784,10 +1801,11 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -1843,7 +1861,7 @@ "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "minimatch": "^3.1.4", "strip-json-comments": "^3.1.1" } }, @@ -1861,7 +1879,7 @@ "requires": { "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", - "minimatch": "^3.0.5" + "minimatch": "^3.1.4" } }, "@humanwhocodes/module-importer": { @@ -1912,8 +1930,7 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "peer": true + "dev": true }, "acorn-jsx": { "version": "5.3.2", @@ -1923,9 +1940,9 @@ "requires": {} }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -1935,9 +1952,9 @@ } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-regex": { @@ -1984,9 +2001,9 @@ "dev": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -2025,7 +2042,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, - "peer": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -2113,9 +2129,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -2124,12 +2140,12 @@ } }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "decamelize": { @@ -2154,9 +2170,9 @@ "dev": true }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true }, "doctrine": { @@ -2191,7 +2207,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2226,7 +2241,7 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", @@ -2384,9 +2399,9 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "fs.realpath": { @@ -2423,7 +2438,7 @@ "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -2563,9 +2578,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -2636,67 +2651,93 @@ } }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^8.0.3", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^3.1.4", + "ms": "^2.1.3", + "serialize-javascript": "^7.0.5", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "dependencies": { "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "requires": { "balanced-match": "^1.0.0" } }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.4", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -2709,15 +2750,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "natural-compare": { @@ -2813,9 +2848,9 @@ "dev": true }, "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true }, "prelude-ls": { @@ -2836,15 +2871,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2882,9 +2908,9 @@ } }, "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.80.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", + "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -2899,20 +2925,11 @@ "queue-microtask": "^1.2.2" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.6.tgz", + "integrity": "sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==", + "dev": true }, "shebang-command": { "version": "2.0.0", @@ -3040,9 +3057,9 @@ } }, "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { @@ -3084,9 +3101,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index 1181ba5..0861541 100644 --- a/package.json +++ b/package.json @@ -29,17 +29,21 @@ ], "author": "0kims", "license": "GPL-3.0", - "dependencies": {}, "repository": { "type": "git", "url": "https://github.com/iden3/fastfile.git" }, "devDependencies": { + "chai": "^4.3.6", + "chai-as-promised": "^7.1.1", "eslint": "^8.17.0", "ffjavascript": "^0.3.0", "mocha": "^10.0.0", - "chai": "^4.3.6", - "chai-as-promised": "^7.1.1", "rollup": "^2.20.0" + }, + "overrides": { + "serialize-javascript": "^7.0.5", + "diff": "^8.0.3", + "minimatch": "^3.1.4" } } From 4ff30102e65275b76d8d5c31af1cc52d4d9255b9 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 9 Jul 2026 00:39:22 +0100 Subject: [PATCH 10/41] fix: correct destination offset when a cached-page read hits EOF readToBuffer's cached-page path (reads below directReadThreshold, i.e. the common case for most zkey/ptau/wtns sections) computed the destination-buffer write offset as `offset + len - r`, where `r` had already been clamped to the EOF-truncated byte count on the previous line. That put the on-disk bytes at a nonzero offset in the output buffer instead of at the start -- the START of the returned buffer came back as zeros (the caller's fresh-allocated buffer contents) while the truncated tail held garbage/misplaced real data, rather than the read failing cleanly or zero-padding only the genuinely missing tail. Reproduced with a section whose declared size exceeds the actual (truncated/corrupted) file: readSection returned bytes shifted by exactly the truncated amount. Fixed by tracking bytes-written with an independent counter instead of deriving the offset from the EOF-clamped remaining-count. --- src/osfile.js | 13 +++++++++++-- test/osfile.test.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/osfile.js b/src/osfile.js index 0f3deca..a7aefd6 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -368,8 +368,16 @@ class FastFile { let p = firstPage; let o = pos % self.pageSize; - // Remaining bytes to read + // Remaining bytes to read (clamped to EOF: a read past the end of a + // truncated/short file reads fewer bytes than requested). let r = pos + len > self.totalSize ? len - (pos + len - self.totalSize): len; + // Bytes already written to buffDst -- tracked independently of `r` + // (which shrinks on EOF-clamping) so the destination offset stays + // correct. Previously computed as `offset + len - r`: with `r` + // pre-clamped below `len`, that put the first bytes read at a + // nonzero offset instead of the real EOF-truncated tail, silently + // shifting valid data to the wrong position in the output buffer. + let done = 0; while (r>0) { await pagePromises[p - firstPage]; self.__statusPage("After Await (read): ", p); @@ -377,12 +385,13 @@ class FastFile { // bytes to copy from this page const l = (o+r > self.pageSize) ? (self.pageSize -o) : r; const srcView = new Uint8Array(self.pages[p].buff.buffer, self.pages[p].buff.byteOffset + o, l); - buffDst.set(srcView, offset+len-r); + buffDst.set(srcView, offset+done); self.pages[p].pendingOps --; self.__statusPage("After Op done: ", p); r = r-l; + done = done+l; p ++; o = 0; if (self.pendingLoads.length>0) setImmediate(self._triggerLoad.bind(self)); diff --git a/test/osfile.test.js b/test/osfile.test.js index 44cab89..89e29c1 100644 --- a/test/osfile.test.js +++ b/test/osfile.test.js @@ -92,6 +92,42 @@ describe("fastfile testing suite for osfile", function () { assert(onDisk.equals(Buffer.from(ref)), "BigBuffer write does not match disk"); await fs.promises.unlink(fileName); }); + + // Regression: readToBuffer's cached-page path (reads below directReadThreshold, + // i.e. the common case for most zkey/ptau/wtns sections) computed the + // destination-buffer offset as `offset + len - r`, where `r` had already been + // clamped to the EOF-truncated byte count. That put the on-disk bytes at a + // nonzero offset in the output instead of at the start, and left the START of + // the buffer as zeros instead of the tail -- valid bytes silently shifted to + // the wrong position rather than the read cleanly failing or zero-padding + // only the truncated tail. This simulates a corrupted/incomplete file (a + // section header claims more bytes than the file actually has). + it("readToBuffer past EOF on a truncated file places real bytes at the start, not shifted (below directReadThreshold)", async () => { + const declaredLen = 2000; // < directReadThreshold (1MB): exercises the cached-page path + const actualLen = 1000; // how many bytes actually exist past `pos` + const pos = 24; // simulate a section starting after a small header + + let fd = await fastFile.createOverride(fileName); + await fd.write(new Uint8Array(pos + actualLen).fill(0xAB), 0); + await fd.close(); + + assert.strictEqual(fs.statSync(fileName).size, pos + actualLen); + + fd = await fastFile.readExisting(fileName); + const buff = await fd.read(declaredLen, pos); + await fd.close(); + + assert.strictEqual(buff.length, declaredLen); + // The real on-disk bytes must land at the START of the buffer... + for (let i = 0; i < actualLen; i++) { + assert.strictEqual(buff[i], 0xAB, `expected real data at index ${i}`); + } + // ...and only the truncated TAIL (bytes that don't exist on disk) is zero. + for (let i = actualLen; i < declaredLen; i++) { + assert.strictEqual(buff[i], 0, `expected zero padding at index ${i}`); + } + await fs.promises.unlink(fileName); + }); }); From f0b3783b5463a3348a40137b63581201ad59d64b Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 9 Jul 2026 15:47:38 +0100 Subject: [PATCH 11/41] fix: propagate page-cache IO errors instead of hanging or deferring to close() Two error-propagation bugs in osfile's async page cache, the same class as the recently-fixed ThreadManager hangs: 1. A failed page read rejected only the FIRST waiter; co-readers queued on page.loading were never settled (awaited forever), and the page stayed cached with a dead loading list so every future reader of that page hung too. Now every waiter is rejected and the page is dropped so a retry re-reads it. 2. A failed background page flush was recorded in self.error and only surfaced at close(); page.writing also stayed true, pinning the page. A prover that skipped close() on its error path silently produced a truncated file while 'succeeding'. Now write()/ readToBuffer() fail fast once a flush error is recorded, writing is cleared, and _tryClose no longer falls through to resolve after rejecting. Both covered by regression tests (fail pre-fix: one as a hang, one as a silent success). --- src/osfile.js | 22 +++++++++++++++++++++- test/osfile.test.js | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/osfile.js b/src/osfile.js index a7aefd6..f441a18 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -156,7 +156,17 @@ class FastFile { self.__statusPage("After Load (loaded): ", load.page); return res; }, (err) => { - load.reject(err); + // Reject EVERY waiter, not just the first: co-readers of the + // same page were appended to page.loading (not to `load`) and + // would otherwise await forever. Drop the page entirely so a + // later retry re-reads it instead of queueing onto a dead + // loading list. + const page = self.pages[load.page]; + const loading = (page && page.loading) ? page.loading : [load]; + delete self.pages[load.page]; + for (let i=0; i { console.log("ERROR Writing: "+err); + // Clear `writing` (a stuck flag pins the page forever and + // blocks _tryClose) and record the error. write()/read() + // surface it on their next call (fail fast) and close() + // rejects with it -- previously it was only visible at + // close(), so a prover that skipped close on error paths + // silently produced a truncated/corrupt file. + page.writing = false; self.error = err; self._tryClose(); })); @@ -248,6 +265,7 @@ class FastFile { async write(buff, pos) { if (buff.byteLength == 0) return; const self = this; + if (self.error) throw self.error; if (typeof pos == "undefined") pos = self.pos; self.pos = pos+buff.byteLength; if (self.totalSize < pos + buff.byteLength) self.totalSize = pos + buff.byteLength; @@ -330,6 +348,7 @@ class FastFile { return; } const self = this; + if (self.error) throw self.error; if (typeof pos == "undefined") pos = self.pos; self.pos = pos+len; if (self.pendingClose) @@ -407,6 +426,7 @@ class FastFile { if (!self.pendingClose) return; if (self.error) { self.pendingCloseReject(self.error); + return; } const p = self._getDirtyPage(); if ((p>=0) || (self.writing) || (self.reading) || (self.pendingLoads.length>0)) return; diff --git a/test/osfile.test.js b/test/osfile.test.js index 89e29c1..da5eaf9 100644 --- a/test/osfile.test.js +++ b/test/osfile.test.js @@ -128,6 +128,52 @@ describe("fastfile testing suite for osfile", function () { } await fs.promises.unlink(fileName); }); + + // Regression: when a page read failed, only the FIRST waiter was rejected; + // co-readers queued on page.loading hung forever, and the page stayed + // cached with a dead loading list so every future reader of it hung too. + it("a failed page read rejects all waiters and the page recovers", async () => { + fs.writeFileSync(fileName, new Uint8Array(1 << 16).fill(0xab)); + const fd = await fastFile.readExisting(fileName, 1 << 20, 1 << 12); + + const realRead = fd.fd.read.bind(fd.fd); + let failures = 1; + fd.fd.read = (...args) => { + if (failures > 0) { failures--; return Promise.reject(new Error("simulated EIO")); } + return realRead(...args); + }; + + // Two concurrent readers of the same page: both must settle (reject + // or, for the one that arrives after recovery, resolve) -- never hang. + const r1 = await fd.read(64, 0).then(() => "resolved", () => "rejected"); + const r2 = await fd.read(64, 128).then(() => "resolved", () => "rejected"); + assert(r1 === "rejected" || r2 === "rejected", "at least one reader sees the EIO"); + + // A later reader of the same page must succeed (page not poisoned). + const b = await fd.read(64, 0); + assert.strictEqual(b[0], 0xab); + await fd.close(); + await fs.promises.unlink(fileName); + }); + + // Regression: a failed background page flush was only visible at close(); + // page.writing also stayed true, pinning the page. Subsequent writes/reads + // must now fail fast and close() must reject. + it("a failed background write surfaces on the next write and on close()", async () => { + const fd = await fastFile.createOverride(fileName, 1 << 20, 1 << 12); + + const realWrite = fd.fd.write.bind(fd.fd); + fd.fd.write = () => Promise.reject(new Error("simulated ENOSPC")); + + await fd.write(new Uint8Array(64).fill(1), 0); // dirty page; background flush fails + await new Promise((resolve) => setTimeout(resolve, 50)); + + await expect(fd.write(new Uint8Array(64).fill(2), 4096)).to.be.rejectedWith("simulated ENOSPC"); + await expect(fd.close()).to.be.rejectedWith("simulated ENOSPC"); + + fd.fd.write = realWrite; // let the underlying fd close cleanly + await fs.promises.unlink(fileName); + }); }); From ffc97b304034daf796406d7b0ef8c5c6ce26c695 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sun, 12 Jul 2026 11:18:00 +0100 Subject: [PATCH 12/41] feat: stream reads over HTTP Range requests and from Blobs readExisting with a URL previously buffered the entire body in memory (browser: any string; Node: unsupported). Now a Range probe (bytes=0-0) routes to a paged read-only backend: large reads stream the 206 body straight into the caller's buffer, small header reads coalesce into cached pages. If-Range with the validator captured at open fails reads if the remote file changes mid-session instead of mixing versions. Servers without range support fall back to the old buffer-it-all path, reusing the probe response body. Blob/File inputs stream via slice(). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wo6AVSAwvL9mHTpvnREZPR --- build/main.cjs | 389 +++++++++++++++++++++++++++++++++++++++--- rollup.cjs.config.js | 2 +- src/blobfile.js | 18 ++ src/fastfile.js | 37 ++-- src/httpfile.js | 134 +++++++++++++++ src/rangefile.js | 181 ++++++++++++++++++++ test/blobfile.test.js | 50 ++++++ test/httpfile.test.js | 215 +++++++++++++++++++++++ 8 files changed, 994 insertions(+), 32 deletions(-) create mode 100644 src/blobfile.js create mode 100644 src/httpfile.js create mode 100644 src/rangefile.js create mode 100644 test/blobfile.test.js create mode 100644 test/httpfile.test.js diff --git a/build/main.cjs b/build/main.cjs index 425fa87..7395d70 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -164,7 +164,17 @@ class FastFile { self.__statusPage("After Load (loaded): ", load.page); return res; }, (err) => { - load.reject(err); + // Reject EVERY waiter, not just the first: co-readers of the + // same page were appended to page.loading (not to `load`) and + // would otherwise await forever. Drop the page entirely so a + // later retry re-reads it instead of queueing onto a dead + // loading list. + const page = self.pages[load.page]; + const loading = (page && page.loading) ? page.loading : [load]; + delete self.pages[load.page]; + for (let i=0; i { console.log("ERROR Writing: "+err); + // Clear `writing` (a stuck flag pins the page forever and + // blocks _tryClose) and record the error. write()/read() + // surface it on their next call (fail fast) and close() + // rejects with it -- previously it was only visible at + // close(), so a prover that skipped close on error paths + // silently produced a truncated/corrupt file. + page.writing = false; self.error = err; self._tryClose(); })); @@ -256,6 +273,7 @@ class FastFile { async write(buff, pos) { if (buff.byteLength == 0) return; const self = this; + if (self.error) throw self.error; if (typeof pos == "undefined") pos = self.pos; self.pos = pos+buff.byteLength; if (self.totalSize < pos + buff.byteLength) self.totalSize = pos + buff.byteLength; @@ -338,6 +356,7 @@ class FastFile { return; } const self = this; + if (self.error) throw self.error; if (typeof pos == "undefined") pos = self.pos; self.pos = pos+len; if (self.pendingClose) @@ -376,8 +395,16 @@ class FastFile { let p = firstPage; let o = pos % self.pageSize; - // Remaining bytes to read + // Remaining bytes to read (clamped to EOF: a read past the end of a + // truncated/short file reads fewer bytes than requested). let r = pos + len > self.totalSize ? len - (pos + len - self.totalSize): len; + // Bytes already written to buffDst -- tracked independently of `r` + // (which shrinks on EOF-clamping) so the destination offset stays + // correct. Previously computed as `offset + len - r`: with `r` + // pre-clamped below `len`, that put the first bytes read at a + // nonzero offset instead of the real EOF-truncated tail, silently + // shifting valid data to the wrong position in the output buffer. + let done = 0; while (r>0) { await pagePromises[p - firstPage]; self.__statusPage("After Await (read): ", p); @@ -385,12 +412,13 @@ class FastFile { // bytes to copy from this page const l = (o+r > self.pageSize) ? (self.pageSize -o) : r; const srcView = new Uint8Array(self.pages[p].buff.buffer, self.pages[p].buff.byteOffset + o, l); - buffDst.set(srcView, offset+len-r); + buffDst.set(srcView, offset+done); self.pages[p].pendingOps --; self.__statusPage("After Op done: ", p); r = r-l; + done = done+l; p ++; o = 0; if (self.pendingLoads.length>0) setImmediate(self._triggerLoad.bind(self)); @@ -406,6 +434,7 @@ class FastFile { if (!self.pendingClose) return; if (self.error) { self.pendingCloseReject(self.error); + return; } const p = self._getDirtyPage(); if ((p>=0) || (self.writing) || (self.reading) || (self.pendingLoads.length>0)) return; @@ -559,7 +588,7 @@ function createNew$1(o) { return fd; } -function readExisting$2(o) { +function readExisting$4(o) { const fd = new MemFile(); fd.o = o; fd.allocSize = o.data.byteLength; @@ -755,7 +784,7 @@ function createNew(o) { return fd; } -function readExisting$1(o) { +function readExisting$3(o) { const fd = new BigMemFile(); fd.o = o; fd.totalSize = (o.data.length-1)* PAGE_SIZE + o.data[o.data.length-1].byteLength; @@ -966,6 +995,317 @@ class BigMemFile { } } +// Shared read-only file over an abstract positioned range reader. +// +// Backends (httpfile, blobfile) supply a single primitive: +// readRangeInto(dstBuff, dstOffset, pos, len) -> Promise +// and this class layers the FastFile read interface on top: +// - reads >= pageSize bypass the cache and go straight to the backend +// (the file is read-only, so a direct read is always coherent); +// - smaller reads (header scans: magics, section tables, ULE32/64) are +// served from page-aligned cached ranges so that e.g. a binfile section +// scan does not issue one backend request per 4-byte field. +// +// Write operations throw: remote ranges are strictly a read transport. + +const DEFAULT_CACHE_SIZE$1 = 1 << 20; +const DEFAULT_PAGE_SIZE$1 = 1 << 13; + +class RangeFile { + + constructor(readRangeInto, totalSize, cacheSize, pageSize) { + this.readRangeInto = readRangeInto; + this.totalSize = totalSize; + this.pos = 0; + this.pageSize = pageSize || DEFAULT_PAGE_SIZE$1; + this.maxPagesLoaded = Math.floor((cacheSize || DEFAULT_CACHE_SIZE$1) / this.pageSize) + 1; + this.pages = new Map(); // page index -> { buff: Uint8Array|null, promise: Promise } + this.readOnly = true; + } + + _pageLen(p) { + const start = p * this.pageSize; + const end = Math.min(start + this.pageSize, this.totalSize); + return end - start; + } + + // Load (or join the in-flight load of) page p; resolves to its buffer. + // A failed load removes the entry so a later retry re-requests it. + _loadPage(p) { + const self = this; + let page = self.pages.get(p); + if (page) { + // LRU touch: re-insert so Map iteration order tracks recency. + self.pages.delete(p); + self.pages.set(p, page); + return page.promise; + } + const buff = new Uint8Array(self._pageLen(p)); + page = { buff: null, promise: null }; + page.promise = self.readRangeInto(buff, 0, p * self.pageSize, buff.byteLength).then(function () { + page.buff = buff; + return buff; + }, function (err) { + self.pages.delete(p); + throw err; + }); + self.pages.set(p, page); + self._trimCache(); + return page.promise; + } + + _trimCache() { + const self = this; + if (self.pages.size <= self.maxPagesLoaded) return; + // Evict oldest settled pages first; in-flight loads are kept. + for (const entry of self.pages) { + if (self.pages.size <= self.maxPagesLoaded) return; + if (entry[1].buff) self.pages.delete(entry[0]); + } + } + + async readToBuffer(buffDst, offset, len, pos) { + const self = this; + if (len === 0) return; + if (self.pendingClose) throw new Error("Reading a closing file"); + if (typeof pos === "undefined") pos = self.pos; + if (pos + len > self.totalSize) throw new Error("Reading out of bounds"); + self.pos = pos + len; + + // Direct path: one backend request straight into the destination + // (works for both typed arrays and BigBuffer via .set()). + if (len >= self.pageSize) { + await self.readRangeInto(buffDst, offset, pos, len); + return; + } + + const firstPage = Math.floor(pos / self.pageSize); + const lastPage = Math.floor((pos + len - 1) / self.pageSize); + let o = pos % self.pageSize; + let done = 0; + for (let p = firstPage; p <= lastPage; p++) { + const buff = await self._loadPage(p); + const l = Math.min(len - done, self.pageSize - o); + buffDst.set(buff.subarray(o, o + l), offset + done); + done += l; + o = 0; + } + } + + async read(len, pos) { + const buff = new Uint8Array(len); + await this.readToBuffer(buff, 0, len, pos); + return buff; + } + + async readULE32(pos) { + const b = await this.read(4, pos); + const view = new Uint32Array(b.buffer); + return view[0]; + } + + async readUBE32(pos) { + const b = await this.read(4, pos); + const view = new DataView(b.buffer); + return view.getUint32(0, false); + } + + async readULE64(pos) { + const b = await this.read(8, pos); + const view = new Uint32Array(b.buffer); + return view[1] * 0x100000000 + view[0]; + } + + async readString(pos) { + const self = this; + if (self.pendingClose) throw new Error("Reading a closing file"); + let p = typeof pos === "undefined" ? self.pos : pos; + const chunks = []; + while (p < self.totalSize) { + const l = Math.min(self.pageSize, self.totalSize - p); + const chunk = await self.read(l, p); + const z = chunk.indexOf(0); + if (z >= 0) { + chunks.push(chunk.subarray(0, z)); + self.pos = p + z + 1; + return decodeChunks(chunks); + } + chunks.push(chunk); + p += l; + } + // No terminator before EOF: the string ends at EOF. + self.pos = p; + return decodeChunks(chunks); + } + + async write() { + throw new Error("Writing a read only file"); + } + + async writeULE32() { + throw new Error("Writing a read only file"); + } + + async writeUBE32() { + throw new Error("Writing a read only file"); + } + + async writeULE64() { + throw new Error("Writing a read only file"); + } + + async close() { + if (this.pendingClose) throw new Error("Closing the file twice"); + this.pendingClose = true; + this.pages.clear(); + } + + async discard() { + await this.close(); + } +} + +function decodeChunks(chunks) { + let total = 0; + for (let i = 0; i < chunks.length; i++) total += chunks[i].byteLength; + const all = new Uint8Array(total); + let o = 0; + for (let i = 0; i < chunks.length; i++) { + all.set(chunks[i], o); + o += chunks[i].byteLength; + } + return new TextDecoder().decode(all); +} + +// Read-only file over HTTP(S) using Range requests. + +async function readExisting$2(o) { + const url = o.url; + const probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); + + if (probe.status === 206) { + const contentRange = probe.headers.get("content-range"); + const m = contentRange ? /\/(\d+)\s*$/.exec(contentRange) : null; + if (m) { + const totalSize = parseInt(m[1]); + // Drain the 1-byte probe body so the connection can be reused. + await probe.arrayBuffer(); + const validator = strongValidator(probe); + const readRangeInto = function (dst, dstOffset, pos, len) { + return httpReadRangeInto(url, validator, dst, dstOffset, pos, len); + }; + return new RangeFile(readRangeInto, totalSize, o.cacheSize, o.pageSize); + } + // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot + // do bounded positioned reads; refetch whole and buffer. + await probe.arrayBuffer(); + return await readFullyToMem(url); + } + + if (!probe.ok && probe.status !== 416) { + throw new Error("HTTP " + probe.status + " fetching " + url); + } + + if (probe.status === 416) { + // Range not satisfiable -- only legitimate for an empty file. + const contentRange = probe.headers.get("content-range"); + if (contentRange && /\/0\s*$/.test(contentRange)) { + return readExisting$4({ type: "mem", data: new Uint8Array(0) }); + } + return await readFullyToMem(url); + } + + // 200: the server ignored Range and sent the whole file; reuse this body. + const data = new Uint8Array(await probe.arrayBuffer()); + return readExisting$4({ type: "mem", data: data }); +} + +async function readFullyToMem(url) { + const res = await fetch(url); + if (!res.ok) throw new Error("HTTP " + res.status + " fetching " + url); + const data = new Uint8Array(await res.arrayBuffer()); + return readExisting$4({ type: "mem", data: data }); +} + +// Only a strong validator is usable with If-Range (RFC 9110 §13.1.5): a weak +// ETag would make conditional range requests always fail on some servers. +function strongValidator(res) { + const etag = res.headers.get("etag"); + if (etag && etag.indexOf("W/") !== 0) return etag; + const lastModified = res.headers.get("last-modified"); + if (lastModified) return lastModified; + return null; +} + +async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { + const headers = { "Range": "bytes=" + pos + "-" + (pos + len - 1) }; + if (validator) headers["If-Range"] = validator; + const res = await fetch(url, { headers: headers }); + if (res.status === 200) { + // With If-Range this is the server signalling the file changed; + // without it, the server stopped honoring ranges mid-session. + await abandonBody(res); + throw new Error(url + ": file changed (or server stopped honoring Range) while reading"); + } + if (res.status !== 206) { + await abandonBody(res); + throw new Error("HTTP " + res.status + " reading range " + pos + "+" + len + " of " + url); + } + const contentRange = res.headers.get("content-range"); + const m = contentRange ? /bytes\s+(\d+)-(\d+)\//.exec(contentRange) : null; + if (m && parseInt(m[1]) !== pos) { + await abandonBody(res); + throw new Error(url + ": server returned range starting at " + m[1] + ", requested " + pos); + } + + // Stream the body straight into the destination (typed array or + // BigBuffer -- both expose .set(chunk, offset)); avoids materializing + // a second full-size copy of large section reads. + let done = 0; + if (res.body && typeof res.body.getReader === "function") { + const reader = res.body.getReader(); + for (;;) { + const it = await reader.read(); + if (it.done) break; + if (done + it.value.byteLength > len) { + reader.cancel().catch(function () {}); + throw new Error(url + ": range response longer than requested"); + } + dst.set(it.value, dstOffset + done); + done += it.value.byteLength; + } + } else { + const buff = new Uint8Array(await res.arrayBuffer()); + if (buff.byteLength > len) throw new Error(url + ": range response longer than requested"); + dst.set(buff, dstOffset); + done = buff.byteLength; + } + if (done !== len) { + throw new Error(url + ": short range response (" + done + "/" + len + " bytes at " + pos + ")"); + } +} + +async function abandonBody(res) { + try { + if (res.body && typeof res.body.cancel === "function") await res.body.cancel(); + else await res.arrayBuffer(); + } catch (e) { /* body teardown is best-effort */ } +} + +// Read-only file over a Blob/File (e.g. a browser + +function readExisting$1(o) { + const blob = o.blob; + const readRangeInto = async function (dst, dstOffset, pos, len) { + const ab = await blob.slice(pos, pos + len).arrayBuffer(); + if (ab.byteLength !== len) { + throw new Error("short blob read (" + ab.byteLength + "/" + len + " bytes at " + pos + ")"); + } + dst.set(new Uint8Array(ab), dstOffset); + }; + return new RangeFile(readRangeInto, blob.size, o.cacheSize, o.pageSize); +} + const DEFAULT_CACHE_SIZE = (1 << 16); const DEFAULT_PAGE_SIZE = (1 << 13); @@ -1021,20 +1361,27 @@ async function readExisting(o, b, c) { data: o }; } - if (!isNode) { - if (typeof o === "string") { - const buff = await fetch(o).then( function(res) { - return res.arrayBuffer(); - }).then(function (ab) { - return new Uint8Array(ab); - }); + if (typeof Blob !== "undefined" && o instanceof Blob) { + o = { + type: "blob", + blob: o, + cacheSize: b, + pageSize: c + }; + } + if (typeof o === "string") { + // URLs (and, in the browser, any string -- historically fetched + // whole) go through the http backend: it streams via Range requests + // when the server supports them and falls back to buffering the + // full body (the previous behavior) when it does not. + if (!isNode || /^https?:\/\//i.test(o)) { o = { - type: "mem", - data: buff + type: "http", + url: o, + cacheSize: b, + pageSize: c }; - } - } else { - if (typeof o === "string") { + } else { o = { type: "file", fileName: o, @@ -1046,9 +1393,13 @@ async function readExisting(o, b, c) { if (o.type == "file") { return await open(o.fileName, constants.O_RDONLY, o.cacheSize, o.pageSize); } else if (o.type == "mem") { - return await readExisting$2(o); + return await readExisting$4(o); } else if (o.type == "bigMem") { - return await readExisting$1(o); + return await readExisting$3(o); + } else if (o.type == "http") { + return await readExisting$2(o); + } else if (o.type == "blob") { + return readExisting$1(o); } else { throw new Error("Invalid FastFile type: "+o.type); } diff --git a/rollup.cjs.config.js b/rollup.cjs.config.js index e3f40b6..70cebf2 100644 --- a/rollup.cjs.config.js +++ b/rollup.cjs.config.js @@ -10,7 +10,7 @@ export default { format: "cjs", }, external: [ - ...Object.keys(pkg.dependencies), + ...Object.keys(pkg.dependencies || {}), ...builtin, ] }; diff --git a/src/blobfile.js b/src/blobfile.js new file mode 100644 index 0000000..4191723 --- /dev/null +++ b/src/blobfile.js @@ -0,0 +1,18 @@ +// Read-only file over a Blob/File (e.g. a browser +// selection). blob.slice() is a zero-copy view; bytes only reach memory when +// a sub-range is read, so large zkeys stream from disk chunk-by-chunk with +// the same bounded footprint as the Node file backend. + +import { RangeFile } from "./rangefile.js"; + +export function readExisting(o) { + const blob = o.blob; + const readRangeInto = async function (dst, dstOffset, pos, len) { + const ab = await blob.slice(pos, pos + len).arrayBuffer(); + if (ab.byteLength !== len) { + throw new Error("short blob read (" + ab.byteLength + "/" + len + " bytes at " + pos + ")"); + } + dst.set(new Uint8Array(ab), dstOffset); + }; + return new RangeFile(readRangeInto, blob.size, o.cacheSize, o.pageSize); +} diff --git a/src/fastfile.js b/src/fastfile.js index d50eaa6..715ce7f 100644 --- a/src/fastfile.js +++ b/src/fastfile.js @@ -1,6 +1,8 @@ import { open } from "./osfile.js"; import * as memFile from "./memfile.js"; import * as bigMemFile from "./bigmemfile.js"; +import * as httpFile from "./httpfile.js"; +import * as blobFile from "./blobfile.js"; import { O_TRUNC, O_CREAT, O_RDWR, O_EXCL, O_RDONLY } from "constants"; @@ -59,20 +61,27 @@ export async function readExisting(o, b, c) { data: o }; } - if (!isNode) { - if (typeof o === "string") { - const buff = await fetch(o).then( function(res) { - return res.arrayBuffer(); - }).then(function (ab) { - return new Uint8Array(ab); - }); + if (typeof Blob !== "undefined" && o instanceof Blob) { + o = { + type: "blob", + blob: o, + cacheSize: b, + pageSize: c + }; + } + if (typeof o === "string") { + // URLs (and, in the browser, any string -- historically fetched + // whole) go through the http backend: it streams via Range requests + // when the server supports them and falls back to buffering the + // full body (the previous behavior) when it does not. + if (!isNode || /^https?:\/\//i.test(o)) { o = { - type: "mem", - data: buff + type: "http", + url: o, + cacheSize: b, + pageSize: c }; - } - } else { - if (typeof o === "string") { + } else { o = { type: "file", fileName: o, @@ -87,6 +96,10 @@ export async function readExisting(o, b, c) { return await memFile.readExisting(o); } else if (o.type == "bigMem") { return await bigMemFile.readExisting(o); + } else if (o.type == "http") { + return await httpFile.readExisting(o); + } else if (o.type == "blob") { + return blobFile.readExisting(o); } else { throw new Error("Invalid FastFile type: "+o.type); } diff --git a/src/httpfile.js b/src/httpfile.js new file mode 100644 index 0000000..68820ea --- /dev/null +++ b/src/httpfile.js @@ -0,0 +1,134 @@ +// Read-only file over HTTP(S) using Range requests. +// +// Open probes the server with `Range: bytes=0-0`: +// - 206 + parseable Content-Range total -> a RangeFile whose reads become +// range requests; large reads (zkey/ptau sections, MSM chunks) stream +// the response body straight into the caller's buffer, so the full file +// is never resident; +// - anything else (200, no range support, unknown total) -> fall back to +// buffering the body in memory, i.e. the historical browser behavior. +// +// Consistency: the validator captured at open (strong ETag, else +// Last-Modified) is sent as If-Range on every range request. If the remote +// file changes mid-read the server answers 200 instead of 206 and the read +// throws, rather than silently mixing chunks of two file versions. +// +// Server requirements (CORS deployments): allow the `Range` request header +// and expose `Content-Range` + `ETag`; serve zkeys without Content-Encoding +// (ranges address encoded bytes). + +import { RangeFile } from "./rangefile.js"; +import * as memFile from "./memfile.js"; + +export async function readExisting(o) { + const url = o.url; + const probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); + + if (probe.status === 206) { + const contentRange = probe.headers.get("content-range"); + const m = contentRange ? /\/(\d+)\s*$/.exec(contentRange) : null; + if (m) { + const totalSize = parseInt(m[1]); + // Drain the 1-byte probe body so the connection can be reused. + await probe.arrayBuffer(); + const validator = strongValidator(probe); + const readRangeInto = function (dst, dstOffset, pos, len) { + return httpReadRangeInto(url, validator, dst, dstOffset, pos, len); + }; + return new RangeFile(readRangeInto, totalSize, o.cacheSize, o.pageSize); + } + // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot + // do bounded positioned reads; refetch whole and buffer. + await probe.arrayBuffer(); + return await readFullyToMem(url); + } + + if (!probe.ok && probe.status !== 416) { + throw new Error("HTTP " + probe.status + " fetching " + url); + } + + if (probe.status === 416) { + // Range not satisfiable -- only legitimate for an empty file. + const contentRange = probe.headers.get("content-range"); + if (contentRange && /\/0\s*$/.test(contentRange)) { + return memFile.readExisting({ type: "mem", data: new Uint8Array(0) }); + } + return await readFullyToMem(url); + } + + // 200: the server ignored Range and sent the whole file; reuse this body. + const data = new Uint8Array(await probe.arrayBuffer()); + return memFile.readExisting({ type: "mem", data: data }); +} + +async function readFullyToMem(url) { + const res = await fetch(url); + if (!res.ok) throw new Error("HTTP " + res.status + " fetching " + url); + const data = new Uint8Array(await res.arrayBuffer()); + return memFile.readExisting({ type: "mem", data: data }); +} + +// Only a strong validator is usable with If-Range (RFC 9110 §13.1.5): a weak +// ETag would make conditional range requests always fail on some servers. +function strongValidator(res) { + const etag = res.headers.get("etag"); + if (etag && etag.indexOf("W/") !== 0) return etag; + const lastModified = res.headers.get("last-modified"); + if (lastModified) return lastModified; + return null; +} + +async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { + const headers = { "Range": "bytes=" + pos + "-" + (pos + len - 1) }; + if (validator) headers["If-Range"] = validator; + const res = await fetch(url, { headers: headers }); + if (res.status === 200) { + // With If-Range this is the server signalling the file changed; + // without it, the server stopped honoring ranges mid-session. + await abandonBody(res); + throw new Error(url + ": file changed (or server stopped honoring Range) while reading"); + } + if (res.status !== 206) { + await abandonBody(res); + throw new Error("HTTP " + res.status + " reading range " + pos + "+" + len + " of " + url); + } + const contentRange = res.headers.get("content-range"); + const m = contentRange ? /bytes\s+(\d+)-(\d+)\//.exec(contentRange) : null; + if (m && parseInt(m[1]) !== pos) { + await abandonBody(res); + throw new Error(url + ": server returned range starting at " + m[1] + ", requested " + pos); + } + + // Stream the body straight into the destination (typed array or + // BigBuffer -- both expose .set(chunk, offset)); avoids materializing + // a second full-size copy of large section reads. + let done = 0; + if (res.body && typeof res.body.getReader === "function") { + const reader = res.body.getReader(); + for (;;) { + const it = await reader.read(); + if (it.done) break; + if (done + it.value.byteLength > len) { + reader.cancel().catch(function () {}); + throw new Error(url + ": range response longer than requested"); + } + dst.set(it.value, dstOffset + done); + done += it.value.byteLength; + } + } else { + const buff = new Uint8Array(await res.arrayBuffer()); + if (buff.byteLength > len) throw new Error(url + ": range response longer than requested"); + dst.set(buff, dstOffset); + done = buff.byteLength; + } + if (done !== len) { + throw new Error(url + ": short range response (" + done + "/" + len + " bytes at " + pos + ")"); + } +} + +async function abandonBody(res) { + try { + if (res.body && typeof res.body.cancel === "function") await res.body.cancel(); + else await res.arrayBuffer(); + } catch (e) { /* body teardown is best-effort */ } +} diff --git a/src/rangefile.js b/src/rangefile.js new file mode 100644 index 0000000..918a230 --- /dev/null +++ b/src/rangefile.js @@ -0,0 +1,181 @@ +// Shared read-only file over an abstract positioned range reader. +// +// Backends (httpfile, blobfile) supply a single primitive: +// readRangeInto(dstBuff, dstOffset, pos, len) -> Promise +// and this class layers the FastFile read interface on top: +// - reads >= pageSize bypass the cache and go straight to the backend +// (the file is read-only, so a direct read is always coherent); +// - smaller reads (header scans: magics, section tables, ULE32/64) are +// served from page-aligned cached ranges so that e.g. a binfile section +// scan does not issue one backend request per 4-byte field. +// +// Write operations throw: remote ranges are strictly a read transport. + +const DEFAULT_CACHE_SIZE = 1 << 20; +const DEFAULT_PAGE_SIZE = 1 << 13; + +export class RangeFile { + + constructor(readRangeInto, totalSize, cacheSize, pageSize) { + this.readRangeInto = readRangeInto; + this.totalSize = totalSize; + this.pos = 0; + this.pageSize = pageSize || DEFAULT_PAGE_SIZE; + this.maxPagesLoaded = Math.floor((cacheSize || DEFAULT_CACHE_SIZE) / this.pageSize) + 1; + this.pages = new Map(); // page index -> { buff: Uint8Array|null, promise: Promise } + this.readOnly = true; + } + + _pageLen(p) { + const start = p * this.pageSize; + const end = Math.min(start + this.pageSize, this.totalSize); + return end - start; + } + + // Load (or join the in-flight load of) page p; resolves to its buffer. + // A failed load removes the entry so a later retry re-requests it. + _loadPage(p) { + const self = this; + let page = self.pages.get(p); + if (page) { + // LRU touch: re-insert so Map iteration order tracks recency. + self.pages.delete(p); + self.pages.set(p, page); + return page.promise; + } + const buff = new Uint8Array(self._pageLen(p)); + page = { buff: null, promise: null }; + page.promise = self.readRangeInto(buff, 0, p * self.pageSize, buff.byteLength).then(function () { + page.buff = buff; + return buff; + }, function (err) { + self.pages.delete(p); + throw err; + }); + self.pages.set(p, page); + self._trimCache(); + return page.promise; + } + + _trimCache() { + const self = this; + if (self.pages.size <= self.maxPagesLoaded) return; + // Evict oldest settled pages first; in-flight loads are kept. + for (const entry of self.pages) { + if (self.pages.size <= self.maxPagesLoaded) return; + if (entry[1].buff) self.pages.delete(entry[0]); + } + } + + async readToBuffer(buffDst, offset, len, pos) { + const self = this; + if (len === 0) return; + if (self.pendingClose) throw new Error("Reading a closing file"); + if (typeof pos === "undefined") pos = self.pos; + if (pos + len > self.totalSize) throw new Error("Reading out of bounds"); + self.pos = pos + len; + + // Direct path: one backend request straight into the destination + // (works for both typed arrays and BigBuffer via .set()). + if (len >= self.pageSize) { + await self.readRangeInto(buffDst, offset, pos, len); + return; + } + + const firstPage = Math.floor(pos / self.pageSize); + const lastPage = Math.floor((pos + len - 1) / self.pageSize); + let o = pos % self.pageSize; + let done = 0; + for (let p = firstPage; p <= lastPage; p++) { + const buff = await self._loadPage(p); + const l = Math.min(len - done, self.pageSize - o); + buffDst.set(buff.subarray(o, o + l), offset + done); + done += l; + o = 0; + } + } + + async read(len, pos) { + const buff = new Uint8Array(len); + await this.readToBuffer(buff, 0, len, pos); + return buff; + } + + async readULE32(pos) { + const b = await this.read(4, pos); + const view = new Uint32Array(b.buffer); + return view[0]; + } + + async readUBE32(pos) { + const b = await this.read(4, pos); + const view = new DataView(b.buffer); + return view.getUint32(0, false); + } + + async readULE64(pos) { + const b = await this.read(8, pos); + const view = new Uint32Array(b.buffer); + return view[1] * 0x100000000 + view[0]; + } + + async readString(pos) { + const self = this; + if (self.pendingClose) throw new Error("Reading a closing file"); + let p = typeof pos === "undefined" ? self.pos : pos; + const chunks = []; + while (p < self.totalSize) { + const l = Math.min(self.pageSize, self.totalSize - p); + const chunk = await self.read(l, p); + const z = chunk.indexOf(0); + if (z >= 0) { + chunks.push(chunk.subarray(0, z)); + self.pos = p + z + 1; + return decodeChunks(chunks); + } + chunks.push(chunk); + p += l; + } + // No terminator before EOF: the string ends at EOF. + self.pos = p; + return decodeChunks(chunks); + } + + async write() { + throw new Error("Writing a read only file"); + } + + async writeULE32() { + throw new Error("Writing a read only file"); + } + + async writeUBE32() { + throw new Error("Writing a read only file"); + } + + async writeULE64() { + throw new Error("Writing a read only file"); + } + + async close() { + if (this.pendingClose) throw new Error("Closing the file twice"); + this.pendingClose = true; + this.pages.clear(); + } + + async discard() { + await this.close(); + } +} + +function decodeChunks(chunks) { + let total = 0; + for (let i = 0; i < chunks.length; i++) total += chunks[i].byteLength; + const all = new Uint8Array(total); + let o = 0; + for (let i = 0; i < chunks.length; i++) { + all.set(chunks[i], o); + o += chunks[i].byteLength; + } + return new TextDecoder().decode(all); +} diff --git a/test/blobfile.test.js b/test/blobfile.test.js new file mode 100644 index 0000000..ceb8254 --- /dev/null +++ b/test/blobfile.test.js @@ -0,0 +1,50 @@ +import * as fastFile from "../src/fastfile.js"; + +import assert from "assert"; +import * as chai from "chai"; +import chaiAsPromised from "chai-as-promised"; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +function makeData(n) { + const data = new Uint8Array(n); + let x = 0x9E3779B9; + for (let i = 0; i < n; i++) { + x ^= x << 13; x >>>= 0; + x ^= x >> 17; + x ^= x << 5; x >>>= 0; + data[i] = x & 0xFF; + } + return data; +} + +describe("fastfile testing suite for blobfile", function () { + this.timeout(100000); + + it("should read positioned ranges from a Blob", async () => { + const data = makeData(1 << 18); + const fd = await fastFile.readExisting(new Blob([data])); + assert.strictEqual(fd.totalSize, data.length); + + const head = await fd.read(4, 0); + assert.deepStrictEqual(Buffer.from(head), Buffer.from(data.slice(0, 4))); + + const big = new Uint8Array(100000); + await fd.readToBuffer(big, 0, big.length, 30000); + assert.deepStrictEqual(Buffer.from(big), Buffer.from(data.slice(30000, 130000))); + + const v = await fd.readULE64(1024); + const dv = new DataView(data.buffer); + assert.strictEqual(v, dv.getUint32(1028, true) * 0x100000000 + dv.getUint32(1024, true)); + + await fd.close(); + }); + + it("should reject reads out of bounds and any write", async () => { + const fd = await fastFile.readExisting(new Blob([makeData(100)])); + await expect(fd.read(8, 96)).to.be.rejectedWith(/out of bounds/); + await expect(fd.write(new Uint8Array(4), 0)).to.be.rejectedWith(/read only/); + await fd.close(); + }); +}); diff --git a/test/httpfile.test.js b/test/httpfile.test.js new file mode 100644 index 0000000..8dfe34a --- /dev/null +++ b/test/httpfile.test.js @@ -0,0 +1,215 @@ +import * as fastFile from "../src/fastfile.js"; + +import http from "http"; +import assert from "assert"; +import * as chai from "chai"; +import chaiAsPromised from "chai-as-promised"; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +// Deterministic pseudo-random content so failures are reproducible. +function makeData(n) { + const data = new Uint8Array(n); + let x = 0x12345678; + for (let i = 0; i < n; i++) { + x ^= x << 13; x >>>= 0; + x ^= x >> 17; + x ^= x << 5; x >>>= 0; + data[i] = x & 0xFF; + } + return data; +} + +// Minimal static server. opts: +// ranges: false -> ignore Range headers, always 200 full body +// etag -> served as ETag; If-Range mismatches answer 200 full +// state.data / state.etag may be swapped mid-test to simulate a changed file. +function startServer(state, opts) { + opts = opts || {}; + const log = { requests: [], bytesServed: 0 }; + const server = http.createServer(function (req, res) { + const entry = { range: req.headers.range || null, ifRange: req.headers["if-range"] || null }; + log.requests.push(entry); + const data = state.data; + const headers = {}; + if (state.etag) headers["ETag"] = state.etag; + + const m = (opts.ranges !== false && req.headers.range) ? + /^bytes=(\d+)-(\d+)$/.exec(req.headers.range) : null; + const ifRangeOk = !req.headers["if-range"] || req.headers["if-range"] === state.etag; + + if (m && ifRangeOk) { + const start = parseInt(m[1]); + if (start >= data.length) { + headers["Content-Range"] = "bytes */" + data.length; + res.writeHead(416, headers); + res.end(); + return; + } + const end = Math.min(parseInt(m[2]), data.length - 1); + const body = Buffer.from(data.slice(start, end + 1)); + headers["Content-Range"] = "bytes " + start + "-" + end + "/" + data.length; + headers["Content-Length"] = body.length; + headers["Accept-Ranges"] = "bytes"; + res.writeHead(206, headers); + log.bytesServed += body.length; + res.end(body); + } else { + const body = Buffer.from(data); + headers["Content-Length"] = body.length; + res.writeHead(200, headers); + log.bytesServed += body.length; + res.end(body); + } + }); + return new Promise(function (resolve) { + server.listen(0, "127.0.0.1", function () { + resolve({ + server: server, + log: log, + url: "http://127.0.0.1:" + server.address().port + "/data.bin", + close: function () { + return new Promise(function (res2) { server.close(res2); }); + } + }); + }); + }); +} + +describe("fastfile testing suite for httpfile", function () { + this.timeout(100000); + + it("should read ranges without ever downloading the full file", async () => { + const data = makeData(1 << 20); // 1 MiB + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + const fd = await fastFile.readExisting(srv.url); + assert.strictEqual(fd.totalSize, data.length); + + // header-style small reads + const head = await fd.read(4, 0); + assert.deepStrictEqual([...head], [...data.slice(0, 4)]); + const v = await fd.readULE32(4); + assert.strictEqual(v, new DataView(data.buffer).getUint32(4, true)); + + // large positioned read (direct path) + const big = new Uint8Array(300000); + await fd.readToBuffer(big, 0, big.length, 500000); + assert.deepStrictEqual(Buffer.from(big), Buffer.from(data.slice(500000, 800000))); + + // scattered small reads (cached path), spanning a page boundary + const ps = fd.pageSize; + const cross = await fd.read(16, ps - 8); + assert.deepStrictEqual(Buffer.from(cross), Buffer.from(data.slice(ps - 8, ps + 8))); + + await fd.close(); + + // every request carried a Range header (no full-file GET) ... + for (const r of srv.log.requests) assert.ok(r.range, "expected only range requests"); + // ... and far less than the file was transferred + assert.ok(srv.log.bytesServed < data.length / 2, + "served " + srv.log.bytesServed + " of " + data.length); + } finally { + await srv.close(); + } + }); + + it("should serve repeated small reads on one page from cache (single request)", async () => { + const data = makeData(1 << 16); + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + const fd = await fastFile.readExisting(srv.url); + const before = srv.log.requests.length; + await fd.read(4, 100); + await fd.read(4, 200); + await fd.readULE64(300); + assert.strictEqual(srv.log.requests.length, before + 1); + await fd.close(); + } finally { + await srv.close(); + } + }); + + it("should read sequentially without explicit positions", async () => { + const data = makeData(4096); + const srv = await startServer({ data: data }); + try { + const fd = await fastFile.readExisting(srv.url); + const a = await fd.read(10); + const b = await fd.read(10); + assert.deepStrictEqual(Buffer.concat([Buffer.from(a), Buffer.from(b)]), Buffer.from(data.slice(0, 20))); + assert.strictEqual(fd.pos, 20); + await fd.close(); + } finally { + await srv.close(); + } + }); + + it("should fall back to full buffering when the server ignores Range", async () => { + const data = makeData(100000); + const srv = await startServer({ data: data }, { ranges: false }); + try { + const fd = await fastFile.readExisting(srv.url); + assert.strictEqual(fd.totalSize, data.length); + const all = await fd.read(data.length, 0); + assert.deepStrictEqual(Buffer.from(all), Buffer.from(data)); + await fd.close(); + // the probe response body was reused: exactly one request + assert.strictEqual(srv.log.requests.length, 1); + } finally { + await srv.close(); + } + }); + + it("should fail a read when the remote file changes mid-session (If-Range)", async () => { + const state = { data: makeData(1 << 20), etag: "\"v1\"" }; + const srv = await startServer(state); + try { + const fd = await fastFile.readExisting(srv.url); + const buff = new Uint8Array(1 << 16); + await fd.readToBuffer(buff, 0, buff.length, 0); // works on v1 + + state.data = makeData(1 << 20).map(function (b) { return b ^ 0xFF; }); + state.etag = "\"v2\""; + + // direct (uncached) read must reject, not silently mix versions + await expect(fd.readToBuffer(new Uint8Array(1 << 16), 0, 1 << 16, 1 << 17)) + .to.be.rejectedWith(/file changed/); + await fd.close(); + } finally { + await srv.close(); + } + }); + + it("should reject reads out of bounds and any write", async () => { + const data = makeData(1000); + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + const fd = await fastFile.readExisting(srv.url); + await expect(fd.read(16, 992)).to.be.rejectedWith(/out of bounds/); + await expect(fd.write(new Uint8Array(4), 0)).to.be.rejectedWith(/read only/); + await expect(fd.writeULE32(1, 0)).to.be.rejectedWith(/read only/); + await fd.close(); + } finally { + await srv.close(); + } + }); + + it("should read strings through the page cache", async () => { + const data = makeData(9000); + const msg = "hello_snarkjs"; + for (let i = 0; i < msg.length; i++) data[8000 + i] = msg.charCodeAt(i); + data[8000 + msg.length] = 0; + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + const fd = await fastFile.readExisting(srv.url); + const str = await fd.readString(8000); + assert.strictEqual(str, msg); + assert.strictEqual(fd.pos, 8000 + msg.length + 1); + await fd.close(); + } finally { + await srv.close(); + } + }); +}); From 332248829e90683e4d398a05af7665b2d391a2db Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sun, 12 Jul 2026 11:37:50 +0100 Subject: [PATCH 13/41] fix: cap page size in http/blob backends against disk-tuned hints Callers (snarkjs) pass 8 MiB pageSize hints meant for the disk cache; over HTTP that turned a 4-byte header read into a whole-file range request for any file under the page size. Cap http pages at 64 KiB and blob pages at 1 MiB; reads at/above the page size already bypass the cache, so large section reads are unaffected. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wo6AVSAwvL9mHTpvnREZPR --- build/main.cjs | 21 +++++++++++++++++++-- src/blobfile.js | 9 ++++++++- src/httpfile.js | 12 +++++++++++- test/httpfile.test.js | 19 ++++++++++++++++++- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/build/main.cjs b/build/main.cjs index 7395d70..120bb2e 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -1179,6 +1179,15 @@ function decodeChunks(chunks) { // Read-only file over HTTP(S) using Range requests. +// Callers tune cacheSize/pageSize for the disk backend (snarkjs passes 8 MiB +// pages); over HTTP a page that large turns a 4-byte header read into a +// megabytes-range request -- for files below the page size, the whole file in +// one response, defeating streaming. Cap pages at 64 KiB: big enough to +// coalesce a binfile section-table scan, small enough to stay incidental. +// Reads >= the page size bypass the cache entirely (RangeFile direct path), +// so large section/chunk reads are unaffected by the cap. +const MAX_HTTP_PAGE_SIZE = 1 << 16; + async function readExisting$2(o) { const url = o.url; const probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); @@ -1194,7 +1203,8 @@ async function readExisting$2(o) { const readRangeInto = function (dst, dstOffset, pos, len) { return httpReadRangeInto(url, validator, dst, dstOffset, pos, len); }; - return new RangeFile(readRangeInto, totalSize, o.cacheSize, o.pageSize); + const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); + return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); } // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot // do bounded positioned reads; refetch whole and buffer. @@ -1294,6 +1304,12 @@ async function abandonBody(res) { // Read-only file over a Blob/File (e.g. a browser +// Same rationale as httpfile's cap, relaxed for a local source: callers pass +// disk-tuned page sizes (MiBs) that would make every small header read +// materialize a huge slice; 1 MiB keeps that bounded while costing at most a +// handful of slice reads per file open. +const MAX_BLOB_PAGE_SIZE = 1 << 20; + function readExisting$1(o) { const blob = o.blob; const readRangeInto = async function (dst, dstOffset, pos, len) { @@ -1303,7 +1319,8 @@ function readExisting$1(o) { } dst.set(new Uint8Array(ab), dstOffset); }; - return new RangeFile(readRangeInto, blob.size, o.cacheSize, o.pageSize); + const pageSize = Math.min(o.pageSize || MAX_BLOB_PAGE_SIZE, MAX_BLOB_PAGE_SIZE); + return new RangeFile(readRangeInto, blob.size, o.cacheSize, pageSize); } const DEFAULT_CACHE_SIZE = (1 << 16); diff --git a/src/blobfile.js b/src/blobfile.js index 4191723..0fd13eb 100644 --- a/src/blobfile.js +++ b/src/blobfile.js @@ -5,6 +5,12 @@ import { RangeFile } from "./rangefile.js"; +// Same rationale as httpfile's cap, relaxed for a local source: callers pass +// disk-tuned page sizes (MiBs) that would make every small header read +// materialize a huge slice; 1 MiB keeps that bounded while costing at most a +// handful of slice reads per file open. +const MAX_BLOB_PAGE_SIZE = 1 << 20; + export function readExisting(o) { const blob = o.blob; const readRangeInto = async function (dst, dstOffset, pos, len) { @@ -14,5 +20,6 @@ export function readExisting(o) { } dst.set(new Uint8Array(ab), dstOffset); }; - return new RangeFile(readRangeInto, blob.size, o.cacheSize, o.pageSize); + const pageSize = Math.min(o.pageSize || MAX_BLOB_PAGE_SIZE, MAX_BLOB_PAGE_SIZE); + return new RangeFile(readRangeInto, blob.size, o.cacheSize, pageSize); } diff --git a/src/httpfile.js b/src/httpfile.js index 68820ea..67df32d 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -20,6 +20,15 @@ import { RangeFile } from "./rangefile.js"; import * as memFile from "./memfile.js"; +// Callers tune cacheSize/pageSize for the disk backend (snarkjs passes 8 MiB +// pages); over HTTP a page that large turns a 4-byte header read into a +// megabytes-range request -- for files below the page size, the whole file in +// one response, defeating streaming. Cap pages at 64 KiB: big enough to +// coalesce a binfile section-table scan, small enough to stay incidental. +// Reads >= the page size bypass the cache entirely (RangeFile direct path), +// so large section/chunk reads are unaffected by the cap. +const MAX_HTTP_PAGE_SIZE = 1 << 16; + export async function readExisting(o) { const url = o.url; const probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); @@ -35,7 +44,8 @@ export async function readExisting(o) { const readRangeInto = function (dst, dstOffset, pos, len) { return httpReadRangeInto(url, validator, dst, dstOffset, pos, len); }; - return new RangeFile(readRangeInto, totalSize, o.cacheSize, o.pageSize); + const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); + return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); } // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot // do bounded positioned reads; refetch whole and buffer. diff --git a/test/httpfile.test.js b/test/httpfile.test.js index 8dfe34a..d5e5cec 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -27,7 +27,7 @@ function makeData(n) { // state.data / state.etag may be swapped mid-test to simulate a changed file. function startServer(state, opts) { opts = opts || {}; - const log = { requests: [], bytesServed: 0 }; + const log = { requests: [], bytesServed: 0, maxResponse: 0 }; const server = http.createServer(function (req, res) { const entry = { range: req.headers.range || null, ifRange: req.headers["if-range"] || null }; log.requests.push(entry); @@ -54,6 +54,7 @@ function startServer(state, opts) { headers["Accept-Ranges"] = "bytes"; res.writeHead(206, headers); log.bytesServed += body.length; + log.maxResponse = Math.max(log.maxResponse, body.length); res.end(body); } else { const body = Buffer.from(data); @@ -196,6 +197,22 @@ describe("fastfile testing suite for httpfile", function () { } }); + it("should cap disk-tuned page-size hints (small read must not fetch the file)", async () => { + const data = makeData(1 << 19); // 512 KiB, well under an 8 MiB "page" + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + // snarkjs-style hints: 32 MiB cache, 8 MiB pages (meant for disk) + const fd = await fastFile.readExisting(srv.url, 1 << 25, 1 << 23); + await fd.read(4, 0); + await fd.readULE64(300000); + assert.ok(srv.log.maxResponse <= 1 << 16, + "largest response was " + srv.log.maxResponse + " bytes; page hint not capped"); + await fd.close(); + } finally { + await srv.close(); + } + }); + it("should read strings through the page cache", async () => { const data = makeData(9000); const msg = "hello_snarkjs"; From 62788794a30341e63a6d55c61f6d28d35d63c70f Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Tue, 25 Aug 2026 22:07:37 +0100 Subject: [PATCH 14/41] fix: make close() idempotent, matching fs.promises.FileHandle semantics Double-closing an osfile threw "Closing the file twice" synchronously, forcing every consumer's cleanup path (finally blocks in snarkjs) to wrap close() in try/catch. Repeated calls now return the same promise (including the same rejection when a final flush fails), and the http range backend's close() is a no-op on repeat. Reads and writes after close still fail fast. Co-Authored-By: Claude Fable 5 --- src/osfile.js | 9 ++++++--- src/rangefile.js | 3 ++- test/httpfile.test.js | 13 +++++++++++++ test/osfile.test.js | 31 +++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/osfile.js b/src/osfile.js index f441a18..e027494 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -435,9 +435,11 @@ class FastFile { close() { const self = this; - if (self.pendingClose) - throw new Error("Closing the file twice"); - return new Promise((resolve, reject) => { + // Idempotent, matching fs.promises.FileHandle.close(): repeated calls + // return the same promise, so cleanup code (e.g. a finally block) can + // close unconditionally. Reads/writes after close still throw. + if (self.closePromise) return self.closePromise; + self.closePromise = new Promise((resolve, reject) => { self.pendingClose = resolve; self.pendingCloseReject = reject; self._tryClose(); @@ -447,6 +449,7 @@ class FastFile { self.fd.close(); throw (err); }); + return self.closePromise; } async discard() { diff --git a/src/rangefile.js b/src/rangefile.js index 918a230..36b8942 100644 --- a/src/rangefile.js +++ b/src/rangefile.js @@ -158,7 +158,8 @@ export class RangeFile { } async close() { - if (this.pendingClose) throw new Error("Closing the file twice"); + // Idempotent: a second close is a no-op, matching the os backend. + if (this.pendingClose) return; this.pendingClose = true; this.pages.clear(); } diff --git a/test/httpfile.test.js b/test/httpfile.test.js index d5e5cec..f13bb6a 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -213,6 +213,19 @@ describe("fastfile testing suite for httpfile", function () { } }); + it("close() is idempotent on the http backend", async () => { + const data = makeData(1 << 12); + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + const fd = await fastFile.readExisting(srv.url); + await fd.read(4, 0); + await fd.close(); + await fd.close(); // second close: no-op, no throw + } finally { + await srv.close(); + } + }); + it("should read strings through the page cache", async () => { const data = makeData(9000); const msg = "hello_snarkjs"; diff --git a/test/osfile.test.js b/test/osfile.test.js index da5eaf9..bb9e201 100644 --- a/test/osfile.test.js +++ b/test/osfile.test.js @@ -159,6 +159,37 @@ describe("fastfile testing suite for osfile", function () { // Regression: a failed background page flush was only visible at close(); // page.writing also stayed true, pinning the page. Subsequent writes/reads // must now fail fast and close() must reject. + it("close() is idempotent: repeated and concurrent closes resolve", async () => { + const fd = await fastFile.createOverride(fileName); + await testUtils.writeStringToFile(fd, str1); + + // concurrent double close: both settle without throwing + await Promise.all([fd.close(), fd.close()]); + // close after close: still a no-op + await fd.close(); + + // I/O after close keeps failing fast + await expect(fd.read(4, 0)).to.be.rejectedWith("Reading a closing file"); + + await fs.promises.unlink(fileName); + }); + + it("a failing close() rejects every caller with the same error", async () => { + const fd = await fastFile.createOverride(fileName, 1 << 20, 1 << 12); + + const realWrite = fd.fd.write.bind(fd.fd); + fd.fd.write = () => Promise.reject(new Error("simulated ENOSPC")); + await fd.write(new Uint8Array(64).fill(1), 0); // dirty page; flush will fail + + await expect(fd.close()).to.be.rejectedWith("simulated ENOSPC"); + // the repeated close reports the same failure instead of throwing + // "Closing the file twice" + await expect(fd.close()).to.be.rejectedWith("simulated ENOSPC"); + + fd.fd.write = realWrite; + await fs.promises.unlink(fileName); + }); + it("a failed background write surfaces on the next write and on close()", async () => { const fd = await fastFile.createOverride(fileName, 1 << 20, 1 << 12); From 547eb2794b44b66a197c265ceb0113bc12468812 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Tue, 25 Aug 2026 23:16:50 +0100 Subject: [PATCH 15/41] test: cover the full API surface (99.5% lines, 100% functions); fix two readString bugs New test/api_surface.test.js (34 tests) covers the dispatcher variants (createOverride/createNoOverride/readExisting/readWriteExisting/ readWriteExistingOrCreate for mem, bigMem, file, blob and invalid types), the ULE32/UBE32/ULE64/readString helpers on every backend, error paths (out-of-bounds reads, writes to read-only backends, read-after-close, short/overlong/misaligned HTTP range responses, failing probes, 416 and unknown-total fallbacks, cache eviction, failed page-load retry). Two genuine readString bugs found by the new tests, both fixed: - memfile: reading a string past the written data on a writable file built a negative-length typed-array view and threw a RangeError; it now returns "" (read-only files still reject out of bounds). - bigmemfile: an unterminated string spun forever re-reading an empty window at the end of the data; it now ends the string at EOF, matching the rangefile backend. c8-ignored with reasons: osfile's logHistory debug instrumentation and httpfile's non-streaming fetch fallbacks (Node's undici always streams). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- src/bigmemfile.js | 7 + src/httpfile.js | 5 + src/memfile.js | 7 +- src/osfile.js | 4 + test/api_surface.test.js | 523 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 544 insertions(+), 2 deletions(-) create mode 100644 test/api_surface.test.js diff --git a/src/bigmemfile.js b/src/bigmemfile.js index 8513db0..d483c52 100644 --- a/src/bigmemfile.js +++ b/src/bigmemfile.js @@ -208,6 +208,13 @@ class BigMemFile { } let readLength = Math.min(fixedSize, self.o.data[currentPage].length - offsetOnPage); + if (readLength <= 0) { + // EOF without a terminator: the string ends at the end of the + // data (matches the rangefile backend). This used to spin + // forever re-reading an empty window. + self.pos = currentPosition; + return str; + } const dataArray = new Uint8Array(self.o.data[currentPage].buffer, offsetOnPage, readLength); let indexEndOfString = dataArray.findIndex(element => element === 0); diff --git a/src/httpfile.js b/src/httpfile.js index 67df32d..f47f887 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -126,10 +126,14 @@ async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { done += it.value.byteLength; } } else { + // coverage: fetch implementations without a streaming body (older + // browser polyfills); Node's undici always streams + /* c8 ignore start */ const buff = new Uint8Array(await res.arrayBuffer()); if (buff.byteLength > len) throw new Error(url + ": range response longer than requested"); dst.set(buff, dstOffset); done = buff.byteLength; + /* c8 ignore stop */ } if (done !== len) { throw new Error(url + ": short range response (" + done + "/" + len + " bytes at " + pos + ")"); @@ -139,6 +143,7 @@ async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { async function abandonBody(res) { try { if (res.body && typeof res.body.cancel === "function") await res.body.cancel(); + /* c8 ignore next -- non-streaming fetch polyfills only */ else await res.arrayBuffer(); } catch (e) { /* body teardown is best-effort */ } } diff --git a/src/memfile.js b/src/memfile.js index fa74a7d..5b13aab 100644 --- a/src/memfile.js +++ b/src/memfile.js @@ -162,11 +162,14 @@ class MemFile { let currentPosition = typeof pos == "undefined" ? self.pos : pos; - if (currentPosition > this.totalSize) { + if (currentPosition >= this.totalSize) { if (this.readOnly) { throw new Error("Reading out of bounds"); } - this._resizeIfNeeded(pos); + // Past the written data there is no string to read. (This used to + // grow the allocation and then build a negative-length view, which + // threw a RangeError.) + return ""; } const dataArray = new Uint8Array( self.o.data.buffer, diff --git a/src/osfile.js b/src/osfile.js index e027494..b2fe940 100644 --- a/src/osfile.js +++ b/src/osfile.js @@ -53,6 +53,8 @@ class FastFile { return P; } + // coverage: debug instrumentation, only enabled by hand (this.logHistory) + /* c8 ignore start */ __statusPage(s, p) { const logEntry = []; const self=this; @@ -90,6 +92,8 @@ class FastFile { + /* c8 ignore stop */ + _triggerLoad() { const self = this; diff --git a/test/api_surface.test.js b/test/api_surface.test.js new file mode 100644 index 0000000..89f22df --- /dev/null +++ b/test/api_surface.test.js @@ -0,0 +1,523 @@ +import fs from "fs"; +import http from "http"; +import * as fastFile from "../src/fastfile.js"; + +import assert from "assert"; +import * as chai from "chai"; +import chaiAsPromised from "chai-as-promised"; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +// Behavioral coverage of the full FastFile API surface: the dispatcher +// variants in fastfile.js, and the ULE/UBE/string helpers plus error paths +// of every backend (mem, bigMem, os, http/range, blob). + +const BIGMEM_PAGE = 1 << 22; // bigmemfile's fixed page size + +function makeData(n) { + const data = new Uint8Array(n); + for (let i = 0; i < n; i++) data[i] = (i * 7 + 3) & 0xFF; + return data; +} + +// Round-trips every numeric helper plus a string on a writable fd. +async function roundTripHelpers(fd) { + await fd.writeULE32(0x01020304, 0); + await fd.writeUBE32(0x0A0B0C0D, 4); + await fd.writeULE64(0x1FFFFFFFFF, 8); // > 2^32: exercises the high word + + assert.strictEqual(await fd.readULE32(0), 0x01020304); + assert.strictEqual(await fd.readUBE32(4), 0x0A0B0C0D); + assert.strictEqual(await fd.readULE64(8), 0x1FFFFFFFFF); + + // byte-level check of endianness + const raw = await fd.read(8, 0); + assert.deepStrictEqual([...raw], [4, 3, 2, 1, 0x0A, 0x0B, 0x0C, 0x0D]); +} + +describe("fastfile API surface", function () { + this.timeout(100000); + + describe("dispatcher (fastfile.js)", function () { + it("createOverride / createNoOverride / readWriteExisting reject unknown types", async () => { + await expect(fastFile.createOverride({ type: "nope" })).to.be.rejectedWith("Invalid FastFile type"); + expect(() => fastFile.createNoOverride({ type: "nope" })).to.throw("Invalid FastFile type"); + await expect(fastFile.readExisting({ type: "nope" })).to.be.rejectedWith("Invalid FastFile type"); + expect(() => fastFile.readWriteExisting({ type: "nope" })).to.throw("Invalid FastFile type"); + expect(() => fastFile.readWriteExistingOrCreate({ type: "nope" })).to.throw("Invalid FastFile type"); + }); + + it("createNoOverride creates mem/bigMem files and a fresh os file, but refuses an existing one", async () => { + const mem = fastFile.createNoOverride({ type: "mem" }); + await mem.write(new Uint8Array([1, 2]), 0); + mem.close(); + + const big = fastFile.createNoOverride({ type: "bigMem" }); + await big.write(new Uint8Array([1, 2]), 0); + big.close(); + + const fileName = "test_no_override.bin"; + const fd = await fastFile.createNoOverride(fileName); + await fd.write(new Uint8Array([9]), 0); + await fd.close(); + // O_EXCL: a second createNoOverride on the same path must fail + await expect(fastFile.createNoOverride(fileName)).to.be.rejected; + await fs.promises.unlink(fileName); + }); + + it("readWriteExisting opens mem, bigMem and os files for update", async () => { + const memData = { type: "mem", data: makeData(16) }; + const mem = fastFile.readWriteExisting(memData); + await mem.write(new Uint8Array([0xFF]), 0); + assert.strictEqual((await mem.read(1, 0))[0], 0xFF); + mem.close(); + + const big = fastFile.readWriteExisting({ type: "bigMem", data: [makeData(16)] }); + await big.write(new Uint8Array([0xEE]), 0); + assert.strictEqual((await big.read(1, 0))[0], 0xEE); + big.close(); + + const fileName = "test_rw_existing.bin"; + await fs.promises.writeFile(fileName, Buffer.from(makeData(32))); + const fd = await fastFile.readWriteExisting(fileName); + await fd.write(new Uint8Array([0xAB]), 0); + assert.strictEqual((await fd.read(1, 0))[0], 0xAB); + await fd.close(); + await fs.promises.unlink(fileName); + }); + + it("readWriteExistingOrCreate creates a fresh os file and opens mem/bigMem", async () => { + const fileName = "test_rw_or_create.bin"; + const fd = await fastFile.readWriteExistingOrCreate(fileName); + await fd.write(new Uint8Array([1, 2, 3]), 0); + await fd.close(); + assert.strictEqual((await fs.promises.stat(fileName)).size, 3); + await fs.promises.unlink(fileName); + + const mem = fastFile.readWriteExistingOrCreate({ type: "mem", data: makeData(4) }); + assert.strictEqual(mem.totalSize, 4); + mem.close(); + + const big = fastFile.readWriteExistingOrCreate({ type: "bigMem", data: [makeData(4)] }); + assert.strictEqual(big.totalSize, 4); + big.close(); + }); + + it("readExisting accepts a bare Uint8Array and a bigMem descriptor", async () => { + const data = makeData(8); + const fd = await fastFile.readExisting(data); + assert.deepStrictEqual([...(await fd.read(8, 0))], [...data]); + fd.close(); + + const big = await fastFile.readExisting({ type: "bigMem", data: [makeData(8)] }); + assert.strictEqual(big.totalSize, 8); + assert.strictEqual(big.readOnly, true); + big.close(); + }); + }); + + describe("mem backend", function () { + it("round-trips the numeric helpers", async () => { + const fd = await fastFile.createOverride({ type: "mem" }); + await roundTripHelpers(fd); + fd.close(); + }); + + it("grows the allocation when writing beyond it, and trims on close", async () => { + const o = { type: "mem", initialSize: 4 }; + const fd = await fastFile.createOverride(o); + const big = makeData(1 << 21); // 2 MiB: beyond the 1 MiB growth step + await fd.write(big, 0); + assert.strictEqual(fd.totalSize, big.byteLength); + fd.close(); + assert.strictEqual(o.data.byteLength, big.byteLength); + // a second close finds byteLength == totalSize and does not re-trim + fd.close(); + }); + + it("readToBuffer beyond the end grows a writable file but rejects on a read-only one", async () => { + const rw = fastFile.readWriteExisting({ type: "mem", data: makeData(4) }); + const got = await rw.read(8, 0); // grows to 8; tail is zero-filled + assert.deepStrictEqual([...got.slice(4)], [0, 0, 0, 0]); + + const ro = await fastFile.readExisting({ type: "mem", data: makeData(4) }); + await expect(ro.read(8, 0)).to.be.rejectedWith("Reading out of bounds"); + await expect(ro.write(new Uint8Array(1), 0)).to.be.rejectedWith("read only"); + }); + + it("readString finds terminated strings and returns empty when unterminated", async () => { + const enc = new TextEncoder(); + const fd = await fastFile.createOverride({ type: "mem" }); + await fd.write(enc.encode("hello"), 0); + await fd.write(new Uint8Array([0]), 5); + await fd.write(enc.encode("tail"), 6); // no terminator + + assert.strictEqual(await fd.readString(0), "hello"); + // sequential read continues after the terminator: unterminated tail + const tail = await fd.readString(); + assert.strictEqual(tail, ""); + + // read-only fd: reading a string past the end rejects + const ro = await fastFile.readExisting({ type: "mem", data: enc.encode("x") }); + await expect(ro.readString(10)).to.be.rejectedWith("Reading out of bounds"); + + // writable fd: a position past the end resizes and yields "" + assert.strictEqual(await fd.readString(1000), ""); + + await fd.discard(); + fd.close(); + }); + }); + + describe("bigMem backend", function () { + it("preallocates pages for initialSize and round-trips the numeric helpers", async () => { + const fd = await fastFile.createOverride({ type: "bigMem", initialSize: BIGMEM_PAGE + 10 }); + assert.strictEqual(fd.o.data.length, 2); + await roundTripHelpers(fd); + await fd.discard(); + fd.close(); + }); + + it("writes and reads across the page boundary", async () => { + const fd = await fastFile.createOverride({ type: "bigMem" }); + const chunk = makeData(4096); + const pos = BIGMEM_PAGE - 100; // straddles pages 0 and 1 + await fd.write(chunk, pos); + const got = await fd.read(4096, pos); + assert.deepStrictEqual([...got], [...chunk]); + assert.strictEqual(fd.o.data.length, 2); + fd.close(); + }); + + it("read-only files reject out-of-bounds reads and writes", async () => { + const fd = await fastFile.readExisting({ type: "bigMem", data: [makeData(16)] }); + await expect(fd.read(32, 0)).to.be.rejected; + await expect(fd.write(new Uint8Array(1), 0)).to.be.rejectedWith("read only"); + }); + + it("readString handles long strings, page boundaries and a missing terminator", async () => { + const enc = new TextEncoder(); + const fd = await fastFile.createOverride({ type: "bigMem" }); + + // longer than the 2048-byte scan chunk + const long = "a".repeat(5000); + await fd.write(enc.encode(long), 0); + await fd.write(new Uint8Array([0]), 5000); + assert.strictEqual(await fd.readString(0), long); + + // string crossing the 4 MiB page boundary + const cross = "b".repeat(200); + await fd.write(enc.encode(cross), BIGMEM_PAGE - 100); + await fd.write(new Uint8Array([0]), BIGMEM_PAGE + 100); + assert.strictEqual(await fd.readString(BIGMEM_PAGE - 100), cross); + + // unterminated string: ends at EOF instead of spinning forever + const fd2 = await fastFile.createOverride({ type: "bigMem" }); + await fd2.write(enc.encode("no-end"), 0); + assert.strictEqual(await fd2.readString(0), "no-end"); + fd2.close(); + + fd.close(); + }); + + it("readString past the end of a writable file yields the empty string or ERROR at a page boundary", async () => { + const fd = await fastFile.createOverride({ type: "bigMem" }); + await fd.write(new Uint8Array([1, 2, 3]), 0); + assert.strictEqual(await fd.readString(100), ""); + // exactly at a page boundary the resized data has no next page + await expect(fd.readString(BIGMEM_PAGE)).to.be.rejectedWith("ERROR"); + fd.close(); + }); + }); + + describe("os backend extras", function () { + const fileName = "test_os_extras.bin"; + + afterEach(async () => { + try { await fs.promises.unlink(fileName); } catch (e) { /* may not exist */ } + }); + + it("round-trips the numeric helpers", async () => { + const fd = await fastFile.createOverride(fileName); + await roundTripHelpers(fd); + await fd.close(); + }); + + it("grows a sub-blksize pageSize up to the filesystem block size", async () => { + const fd = await fastFile.createOverride(fileName, 1 << 16, 256); + assert.strictEqual(fd.pageSize % 256, 0); + const stats = await fs.promises.stat(fileName); + assert(fd.pageSize >= stats.blksize); + await fd.close(); + }); + + it("readString spans multiple pages", async () => { + const enc = new TextEncoder(); + const fd = await fastFile.createOverride(fileName, 1 << 16, 1 << 12); + const long = "s".repeat(10000); // > 2 pages of 4096 + await fd.write(enc.encode(long), 0); + await fd.write(new Uint8Array([0]), 10000); + assert.strictEqual(await fd.readString(0), long); + await fd.close(); + }); + + it("read(0) is a no-op and a direct-size read past EOF returns zero-filled bytes", async () => { + const fd = await fastFile.createOverride(fileName, 1 << 22, 1 << 12); + await fd.write(makeData(1 << 12), 0); + const empty = await fd.read(0, 0); + assert.strictEqual(empty.byteLength, 0); + + // >= directReadThreshold (1 MiB) with pos beyond EOF: clamps to + // zero bytes read, destination stays zero-filled + const past = await fd.read(1 << 20, 1 << 21); + assert(past.every((b) => b === 0)); + await fd.close(); + }); + + it("a large read overlapping dirty pages takes the cached path", async () => { + const fd = await fastFile.createOverride(fileName, 1 << 23, 1 << 12); + const small = makeData(4096); + await fd.write(small, 0); // small cached write: page 0 is dirty + const got = await fd.read(1 << 20, 0); // direct-size read overlaps it + assert.deepStrictEqual([...got.slice(0, 64)], [...small.slice(0, 64)]); + await fd.close(); + }); + + it("a direct-size write over previously cached pages stays coherent", async () => { + const fd = await fastFile.createOverride(fileName, 1 << 22, 1 << 12); + await fd.write(makeData(1 << 12), 0); + await fd.read(16, 0); // populate the page cache + const big = new Uint8Array(1 << 20).fill(0x5A); + await fd.write(big, 0); // >= directWriteThreshold with cached overlap + const got = await fd.read(32, 0); + assert(got.every((b) => b === 0x5A)); + await fd.close(); + }); + + it("discard closes and unlinks the file", async () => { + const fd = await fastFile.createOverride(fileName); + await fd.write(makeData(8), 0); + await fd.discard(); + await expect(fs.promises.stat(fileName)).to.be.rejected; + }); + + it("writing after close fails fast", async () => { + const fd = await fastFile.createOverride(fileName); + await fd.write(makeData(8), 0); + await fd.close(); + await expect(fd.write(makeData(8), 0)).to.be.rejectedWith("Writing a closing file"); + await expect(fd.readString(0)).to.be.rejectedWith("Reading a closing file"); + }); + }); + + describe("blob backend (RangeFile)", function () { + function blobOf(data) { + return new Blob([data]); + } + + it("round-trips numeric helpers and strings, and evicts old pages", async () => { + const enc = new TextEncoder(); + const data = new Uint8Array(1 << 16); + data.set([4, 3, 2, 1], 0); // ULE32 0x01020304 + data.set([0x0A, 0x0B, 0x0C, 0x0D], 4); // UBE32 + data.set([0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0, 0, 0], 8); // ULE64 0x1FFFFFFFFF + data.set(enc.encode("blob-str"), 16); + data[24] = 0; + + // pageSize 4 KiB, cacheSize == pageSize -> maxPagesLoaded 2: + // touching many pages exercises cache eviction + const fd = await fastFile.readExisting({ type: "blob", blob: blobOf(data), cacheSize: 1 << 12, pageSize: 1 << 12 }); + assert.strictEqual(await fd.readULE32(0), 0x01020304); + assert.strictEqual(await fd.readUBE32(4), 0x0A0B0C0D); + assert.strictEqual(await fd.readULE64(8), 0x1FFFFFFFFF); + assert.strictEqual(await fd.readString(16), "blob-str"); + for (let p = 0; p < 8; p++) { + await fd.read(4, p * (1 << 12)); + } + assert(fd.pages.size <= fd.maxPagesLoaded); + await fd.close(); + }); + + it("readString without a terminator ends at EOF; sequential reads advance pos", async () => { + const enc = new TextEncoder(); + const fd = await fastFile.readExisting({ type: "blob", blob: blobOf(enc.encode("abc\0defgh")) }); + assert.strictEqual(await fd.readString(), "abc"); + assert.strictEqual(await fd.readString(), "defgh"); // runs to EOF + await fd.close(); + }); + + it("rejects out-of-bounds reads and every write helper", async () => { + const fd = await fastFile.readExisting({ type: "blob", blob: blobOf(makeData(16)) }); + await expect(fd.read(32, 0)).to.be.rejectedWith("Reading out of bounds"); + await expect(fd.write(new Uint8Array(1), 0)).to.be.rejectedWith("read only"); + await expect(fd.writeULE32(1, 0)).to.be.rejectedWith("read only"); + await expect(fd.writeUBE32(1, 0)).to.be.rejectedWith("read only"); + await expect(fd.writeULE64(1, 0)).to.be.rejectedWith("read only"); + await fd.discard(); // close()s; second close via discard is a no-op + await expect(fd.readString(0)).to.be.rejectedWith("Reading a closing file"); + }); + + it("surfaces short reads from a lying blob", async () => { + const fake = { + size: 100, + slice: function () { + return { arrayBuffer: function () { return Promise.resolve(new ArrayBuffer(1)); } }; + } + }; + const fd = await fastFile.readExisting({ type: "blob", blob: fake }); + await expect(fd.read(50, 0)).to.be.rejectedWith("short blob read"); + // the failed page load was evicted, not cached + assert.strictEqual(fd.pages.size, 0); + }); + }); + + describe("http backend probe fallbacks", function () { + // Minimal configurable server: `mode` selects the probe behavior. + function startServer(data, mode) { + const server = http.createServer(function (req, res) { + const range = req.headers.range; + if (!range) { // plain GET (readFullyToMem) + res.writeHead(200, { "Content-Length": data.length }); + res.end(Buffer.from(data)); + return; + } + const m = /^bytes=(\d+)-(\d+)$/.exec(range); + const start = parseInt(m[1]); + const end = Math.min(parseInt(m[2]), data.length - 1); + const isProbe = start === 0 && parseInt(m[1]) === 0 && parseInt(m[2]) === 0; + + if (mode === "probe-500") { + res.writeHead(500); res.end("boom"); return; + } + if (mode === "416-empty") { + res.writeHead(416, { "Content-Range": "bytes */0" }); res.end(); return; + } + if (mode === "416-other") { + res.writeHead(416, { "Content-Range": "bytes */" + data.length }); res.end(); return; + } + if (mode === "no-total") { + res.writeHead(206, { "Content-Range": "bytes 0-0/*", "Content-Length": 1 }); + res.end(Buffer.from(data.slice(0, 1))); return; + } + if (mode === "fail-after-probe" && !isProbe) { + res.writeHead(500); res.end("boom"); return; + } + if (mode === "wrong-start" && !isProbe) { + const body = Buffer.from(data.slice(start + 1, end + 2)); + res.writeHead(206, { + "Content-Range": "bytes " + (start + 1) + "-" + (end + 1) + "/" + data.length, + "Content-Length": body.length + }); + res.end(body); return; + } + if (mode === "overlong" && !isProbe) { + const body = Buffer.concat([Buffer.from(data.slice(start, end + 1)), Buffer.alloc(10, 7)]); + res.writeHead(206, { + "Content-Range": "bytes " + start + "-" + end + "/" + data.length, + "Content-Length": body.length + }); + res.end(body); return; + } + if (mode === "short" && !isProbe) { + const body = Buffer.from(data.slice(start, end)); // one byte short + res.writeHead(206, { + "Content-Range": "bytes " + start + "-" + end + "/" + data.length, + "Content-Length": body.length + }); + res.end(body); return; + } + const body = Buffer.from(data.slice(start, end + 1)); + res.writeHead(206, { + "Content-Range": "bytes " + start + "-" + end + "/" + data.length, + "Content-Length": body.length, + "ETag": "\"v1\"" + }); + res.end(body); + }); + return new Promise(function (resolve) { + server.listen(0, "127.0.0.1", function () { + resolve({ + url: "http://127.0.0.1:" + server.address().port + "/f.bin", + close: function () { return new Promise(function (r) { server.close(r); }); } + }); + }); + }); + } + + it("buffers fully when the total size is unknown (Content-Range */*)", async () => { + const data = makeData(4096); + const srv = await startServer(data, "no-total"); + try { + const fd = await fastFile.readExisting(srv.url); + assert.strictEqual(fd.totalSize, data.length); + assert.deepStrictEqual([...(await fd.read(8, 100))], [...data.slice(100, 108)]); + fd.close(); + } finally { await srv.close(); } + }); + + it("throws on a failing probe", async () => { + const srv = await startServer(makeData(16), "probe-500"); + try { + await expect(fastFile.readExisting(srv.url)).to.be.rejectedWith("HTTP 500"); + } finally { await srv.close(); } + }); + + it("treats 416 with a zero total as an empty file", async () => { + const srv = await startServer(new Uint8Array(0), "416-empty"); + try { + const fd = await fastFile.readExisting(srv.url); + assert.strictEqual(fd.totalSize, 0); + fd.close(); + } finally { await srv.close(); } + }); + + it("falls back to a full fetch on other 416 responses", async () => { + const data = makeData(64); + const srv = await startServer(data, "416-other"); + try { + const fd = await fastFile.readExisting(srv.url); + assert.strictEqual(fd.totalSize, data.length); + fd.close(); + } finally { await srv.close(); } + }); + + it("rejects a range answered at the wrong offset", async () => { + const srv = await startServer(makeData(1 << 17), "wrong-start"); + try { + const fd = await fastFile.readExisting(srv.url); + await expect(fd.read(1 << 16, 0)).to.be.rejectedWith("server returned range starting at"); + fd.close(); + } finally { await srv.close(); } + }); + + it("rejects a range response longer than requested", async () => { + const srv = await startServer(makeData(1 << 17), "overlong"); + try { + const fd = await fastFile.readExisting(srv.url); + await expect(fd.read(1 << 16, 0)).to.be.rejectedWith("longer than requested"); + fd.close(); + } finally { await srv.close(); } + }); + + it("rejects a short range response", async () => { + const srv = await startServer(makeData(1 << 17), "short"); + try { + const fd = await fastFile.readExisting(srv.url); + await expect(fd.read(1 << 16, 0)).to.be.rejectedWith("short range response"); + fd.close(); + } finally { await srv.close(); } + }); + + it("a failed page load is retried on the next read", async () => { + const srv = await startServer(makeData(1 << 14), "fail-after-probe"); + try { + const fd = await fastFile.readExisting(srv.url); + await expect(fd.read(4, 0)).to.be.rejectedWith("HTTP 500"); + // the rejected page was evicted so a retry issues a new request + await expect(fd.read(4, 0)).to.be.rejectedWith("HTTP 500"); + fd.close(); + } finally { await srv.close(); } + }); + }); +}); From dba4d82e8910264951d3242720f5357308fe70a1 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Wed, 26 Aug 2026 02:01:36 +0100 Subject: [PATCH 16/41] fix: degrade to full buffering when the origin stops honoring Range A 206 answer to the probe can come from an intermediary -- notably a browser's HTTP cache, which satisfies range requests out of a cached full 200 -- while the origin itself ignores Range and answers 200. The range reader treated any mid-session 200 as fatal ('file changed'), which broke snarkjs's in-browser URL streaming against st-style static servers. A 200 whose strong validator still matches now hands its body to the reader, which buffers it once and serves all further reads from memory; a changed validator remains a hard error. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- src/httpfile.js | 37 +++++++++++++++++++++++--- test/httpfile.test.js | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/httpfile.js b/src/httpfile.js index f47f887..56e63ac 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -41,8 +41,27 @@ export async function readExisting(o) { // Drain the 1-byte probe body so the connection can be reused. await probe.arrayBuffer(); const validator = strongValidator(probe); - const readRangeInto = function (dst, dstOffset, pos, len) { - return httpReadRangeInto(url, validator, dst, dstOffset, pos, len); + // Degrade-to-buffering escape hatch: a 206 probe answer can come + // from an intermediary (browsers satisfy ranges out of their own + // HTTP cache of a full 200), while the origin itself ignores + // Range. When a later range request gets a 200 whose strong + // validator still matches, the file is unchanged -- buffer that + // full body once and serve every subsequent read from it. + let fullBody = null; + const readRangeInto = async function (dst, dstOffset, pos, len) { + if (!fullBody) { + try { + return await httpReadRangeInto(url, validator, dst, dstOffset, pos, len); + } catch (err) { + if (!err || !err.degradeToFull) throw err; + fullBody = err.fullBodyPromise; + } + } + const data = await fullBody; + if (pos + len > data.byteLength) { + throw new Error(url + ": read past the end of the buffered body"); + } + dst.set(data.subarray(pos, pos + len), dstOffset); }; const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); @@ -93,8 +112,18 @@ async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { if (validator) headers["If-Range"] = validator; const res = await fetch(url, { headers: headers }); if (res.status === 200) { - // With If-Range this is the server signalling the file changed; - // without it, the server stopped honoring ranges mid-session. + // A 200 mid-session means the origin does not honor Range (the 206 + // probe answer may have come from a browser cache). If the strong + // validator still matches -- or we never had one -- the file is + // unchanged: hand the full body to the caller to degrade to + // buffered mode. Only a changed validator is a hard error. + const nowValidator = strongValidator(res); + if (!validator || (nowValidator && nowValidator === validator)) { + const degrade = new Error(url + ": origin ignored Range; degrading to full buffering"); + degrade.degradeToFull = true; + degrade.fullBodyPromise = res.arrayBuffer().then((b) => new Uint8Array(b)); + throw degrade; + } await abandonBody(res); throw new Error(url + ": file changed (or server stopped honoring Range) while reading"); } diff --git a/test/httpfile.test.js b/test/httpfile.test.js index f13bb6a..df9ffaf 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -226,6 +226,68 @@ describe("fastfile testing suite for httpfile", function () { } }); + it("degrades to full buffering when the origin stops honoring Range (matching validator)", async () => { + // Browser scenario: the probe is answered 206 by an HTTP cache, but + // the origin itself ignores Range and answers 200 with the same + // strong ETag. Reads must succeed via buffered mode, not throw + // "file changed". + const data = makeData(1 << 18); + let first = true; + const server = http.createServer((req, res) => { + if (first && req.headers.range === "bytes=0-0") { + first = false; + res.writeHead(206, { + "Content-Range": "bytes 0-0/" + data.length, + "Content-Length": 1, + "ETag": "\"stable\"", + }); + return res.end(Buffer.from(data.slice(0, 1))); + } + res.writeHead(200, { "Content-Length": data.length, "ETag": "\"stable\"" }); + res.end(Buffer.from(data)); + }); + await new Promise((r) => server.listen(0, "127.0.0.1", r)); + const url = "http://127.0.0.1:" + server.address().port + "/data.bin"; + try { + const fd = await fastFile.readExisting(url); + assert.strictEqual(fd.totalSize, data.length); + const head = await fd.read(8, 0); + assert.deepStrictEqual([...head], [...data.slice(0, 8)]); + const mid = await fd.read(1 << 12, 1 << 17); + assert.deepStrictEqual([...mid], [...data.slice(1 << 17, (1 << 17) + (1 << 12))]); + await fd.close(); + } finally { + await new Promise((r) => server.close(r)); + } + }); + + it("still fails hard when a mid-session 200 carries a different validator", async () => { + const data = makeData(1 << 16); + let first = true; + const server = http.createServer((req, res) => { + if (first && req.headers.range === "bytes=0-0") { + first = false; + res.writeHead(206, { + "Content-Range": "bytes 0-0/" + data.length, + "Content-Length": 1, + "ETag": "\"v1\"", + }); + return res.end(Buffer.from(data.slice(0, 1))); + } + res.writeHead(200, { "Content-Length": data.length, "ETag": "\"v2\"" }); + res.end(Buffer.from(data)); + }); + await new Promise((r) => server.listen(0, "127.0.0.1", r)); + const url = "http://127.0.0.1:" + server.address().port + "/data.bin"; + try { + const fd = await fastFile.readExisting(url); + await expect(fd.read(64, 0)).to.be.rejectedWith(/file changed/); + await fd.close(); + } finally { + await new Promise((r) => server.close(r)); + } + }); + it("should read strings through the page cache", async () => { const data = makeData(9000); const msg = "hello_snarkjs"; From e7eb0f05ab17027c2c7760eaebf8137001d7827d Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sat, 4 Jul 2026 23:11:00 +0100 Subject: [PATCH 17/41] Migrate build/test tooling to Vite + Vitest (rebased onto fix/idempotent-close) Port of the feature/esm-tooling migration (fde21ad) onto the current branch: Rollup -> Vite (build/node/main.cjs + browser esm/iife), Mocha/Chai -> Vitest (node-esm / node-cjs / browser Playwright projects), flat eslint.config.js. Everything since the original fork point is preserved: - src/fastfile.browser.js adopts the explicit browser/node split but routes strings through the HTTP Range-streaming backend and Blobs through the blob backend (the original migration fetched URLs whole, regressing the streaming work); constants.browser.js and the browser field are gone. - Our post-fork suites (api_surface, httpfile, blobfile, osfile additions) converted: chai/chai-as-promised -> vitest expect, rejectedWith -> rejects.toThrow, this.timeout dropped. - The browser URL test now mocks a 200 no-Range server (the backend probes with Range: bytes=0-0 and reuses the full body on fallback). - ffjavascript devDep pinned to 4ac1cba (Vite+Vitest migration landed there); vitest family at ^4.1.11 (GHSA-p63j-vcc4-9vmv); postcss/brace-expansion audit overrides added (npm audit clean). 72 tests pass (64 node incl. CJS-build project, 8 browser in Chromium); lint clean. (cherry picked from commit fde21ad) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- .eslintrc.cjs | 30 - .github/workflows/ci.yml | 12 +- ...af8651c53057a5979ec541fda0458ee631238a.png | Bin 0 -> 2082 bytes build/browser/browser.esm.js | 412 ++ build/browser/browser.iife.js | 1 + build/main.cjs | 1469 ------ build/node/main.cjs | 1 + eslint.config.js | 33 + package-lock.json | 4347 ++++++++++------- package.json | 52 +- rollup.cjs.config.js | 16 - src/constants.browser.js | 11 - src/fastfile.browser.js | 55 + ...m-readExisting-from-URL-fetches-data-1.png | Bin 0 -> 2082 bytes test/api_surface.test.js | 56 +- test/bigmemfile.test.js | 20 +- test/blobfile.test.js | 10 +- test/fastfile.browser.test.js | 107 + test/httpfile.test.js | 16 +- test/memfile.test.js | 20 +- test/osfile.test.js | 46 +- test/test.js | 44 +- vite.config.js | 90 + 23 files changed, 3289 insertions(+), 3559 deletions(-) delete mode 100644 .eslintrc.cjs create mode 100644 .vitest-attachments/0baf8651c53057a5979ec541fda0458ee631238a.png create mode 100644 build/browser/browser.esm.js create mode 100644 build/browser/browser.iife.js delete mode 100644 build/main.cjs create mode 100644 build/node/main.cjs create mode 100644 eslint.config.js delete mode 100644 rollup.cjs.config.js delete mode 100644 src/constants.browser.js create mode 100644 src/fastfile.browser.js create mode 100644 test/__screenshots__/fastfile.browser.test.js/fastfile-browser---mem-readExisting-from-URL-fetches-data-1.png create mode 100644 test/fastfile.browser.test.js create mode 100644 vite.config.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index c1bd204..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - "env": { - "es6": true, - "node": true, - "mocha": true - }, - "parserOptions": { - "ecmaVersion": 2017, - "sourceType": "module" - }, - "extends": "eslint:recommended", - "rules": { - "indent": [ - "error", - 4 - ], - "linebreak-style": [ - "warn", - "unix" - ], - "quotes": [ - "error", - "double" - ], - "semi": [ - "error", - "always" - ] - } -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0706d5..2fd5ddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,26 +2,27 @@ name: Continuous Integration on: push: branches: - - main + - master pull_request: jobs: test: name: Test + timeout-minutes: 15 runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [lts/*, lts/-1] + node-version: [lts/*] steps: - name: Checkout project - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} check-latest: true @@ -30,5 +31,8 @@ jobs: - name: Install dependencies run: npm ci + - name: Install Playwright dependencies + run: npx playwright install + - name: Run tests run: npm test diff --git a/.vitest-attachments/0baf8651c53057a5979ec541fda0458ee631238a.png b/.vitest-attachments/0baf8651c53057a5979ec541fda0458ee631238a.png new file mode 100644 index 0000000000000000000000000000000000000000..47767d2f3db3e57c7b5cb7d5e6f5d4e0ed04e8ff GIT binary patch literal 2082 zcmeAS@N?(olHy`uVBq!ia0y~yVDx2RV7kD;1QeOwv~@BA1N(JP7srr_Id3jA@-i53 z95DEFXMgI4ja%e?m6tbcF?X2z{K9j~FO__?^PK08GDpKv_u@O4hD6# qC=0CM|NmbQa_}pV!3L`0nHgejI2P=w?qmguFnGH9xvX this.allocSize) { + let t = Math.max(this.allocSize + (1 << 20), Math.floor(this.allocSize * 1.1), e), n = new Uint8Array(t); + n.set(this.o.data), this.o.data = n, this.allocSize = t; + } + } + async write(e, t) { + let n = this; + if (t === void 0 && (t = n.pos), this.readOnly) throw Error("Writing a read only file"); + this._resizeIfNeeded(t + e.byteLength), this.o.data.set(e.slice(), t), t + e.byteLength > this.totalSize && (this.totalSize = t + e.byteLength), this.pos = t + e.byteLength; + } + async readToBuffer(e, t, n, r) { + let i = this; + if (r === void 0 && (r = i.pos), this.readOnly && r + n > this.totalSize) throw Error("Reading out of bounds"); + this._resizeIfNeeded(r + n); + let a = new Uint8Array(this.o.data.buffer, this.o.data.byteOffset + r, n); + e.set(a, t), this.pos = r + n; + } + async read(e, t) { + let n = this, r = new Uint8Array(e); + return await n.readToBuffer(r, 0, e, t), r; + } + close() { + this.o.data.byteLength != this.totalSize && (this.o.data = this.o.data.slice(0, this.totalSize)); + } + async discard() {} + async writeULE32(e, t) { + let n = this; + i.setUint32(0, e, !0), await n.write(r, t); + } + async writeUBE32(e, t) { + let n = this; + i.setUint32(0, e, !1), await n.write(r, t); + } + async writeULE64(e, t) { + let n = this; + o.setUint32(0, e & 4294967295, !0), o.setUint32(4, Math.floor(e / 4294967296), !0), await n.write(a, t); + } + async readULE32(e) { + let t = await this.read(4, e); + return new Uint32Array(t.buffer)[0]; + } + async readUBE32(e) { + let t = await this.read(4, e); + return new DataView(t.buffer).getUint32(0, !1); + } + async readULE64(e) { + let t = await this.read(8, e), n = new Uint32Array(t.buffer); + return n[1] * 4294967296 + n[0]; + } + async readString(e) { + let t = this, n = e === void 0 ? t.pos : e; + if (n >= this.totalSize) { + if (this.readOnly) throw Error("Reading out of bounds"); + return ""; + } + let r = new Uint8Array(t.o.data.buffer, n, this.totalSize - n), i = r.findIndex((e) => e === 0), a = i !== -1, o = ""; + return a ? (o = new TextDecoder().decode(r.slice(0, i)), t.pos = n + i + 1) : t.pos = n, o; + } +}, c = 1 << 22; +function l(e) { + let t = e.initialSize || 0, n = new g(); + n.o = e; + let r = t ? Math.floor((t - 1) / c) + 1 : 0; + n.o.data = []; + for (let e = 0; e < r - 1; e++) n.o.data.push(new Uint8Array(c)); + return r && n.o.data.push(new Uint8Array(t - c * (r - 1))), n.totalSize = 0, n.readOnly = !1, n.pos = 0, n; +} +function u(e) { + let t = new g(); + return t.o = e, t.totalSize = (e.data.length - 1) * c + e.data[e.data.length - 1].byteLength, t.readOnly = !0, t.pos = 0, t; +} +function d(e) { + let t = new g(); + return t.o = e, t.totalSize = (e.data.length - 1) * c + e.data[e.data.length - 1].byteLength, t.readOnly = !1, t.pos = 0, t; +} +var f = /* @__PURE__ */ new Uint8Array(4), p = new DataView(f.buffer), m = /* @__PURE__ */ new Uint8Array(8), h = new DataView(m.buffer), g = class { + constructor() { + this.pageSize = 16384; + } + _resizeIfNeeded(e) { + if (e <= this.totalSize) return; + if (this.readOnly) throw Error("Reading out of file bounds"); + let t = Math.floor((e - 1) / c) + 1; + for (let n = Math.max(this.o.data.length - 1, 0); n < t; n++) { + let r = n < t - 1 ? c : e - (t - 1) * c, i = new Uint8Array(r); + n == this.o.data.length - 1 && i.set(this.o.data[n]), this.o.data[n] = i; + } + this.totalSize = e; + } + async write(e, t) { + let n = this; + if (t === void 0 && (t = n.pos), this.readOnly) throw Error("Writing a read only file"); + this._resizeIfNeeded(t + e.byteLength); + let r = Math.floor(t / c), i = t % c, a = e.byteLength; + for (; a > 0;) { + let t = i + a > c ? c - i : a, o = e.slice(e.byteLength - a, e.byteLength - a + t); + new Uint8Array(n.o.data[r].buffer, i, t).set(o), a -= t, r++, i = 0; + } + this.pos = t + e.byteLength; + } + async readToBuffer(e, t, n, r) { + let i = this; + if (r === void 0 && (r = i.pos), this.readOnly && r + n > this.totalSize) throw Error("Reading out of bounds"); + this._resizeIfNeeded(r + n); + let a = Math.floor(r / c), o = r % c, s = n; + for (; s > 0;) { + let r = o + s > c ? c - o : s, l = new Uint8Array(i.o.data[a].buffer, o, r); + e.set(l, t + n - s), s -= r, a++, o = 0; + } + this.pos = r + n; + } + async read(e, t) { + let n = this, r = new Uint8Array(e); + return await n.readToBuffer(r, 0, e, t), r; + } + close() {} + async discard() {} + async writeULE32(e, t) { + let n = this; + p.setUint32(0, e, !0), await n.write(f, t); + } + async writeUBE32(e, t) { + let n = this; + p.setUint32(0, e, !1), await n.write(f, t); + } + async writeULE64(e, t) { + let n = this; + h.setUint32(0, e & 4294967295, !0), h.setUint32(4, Math.floor(e / 4294967296), !0), await n.write(m, t); + } + async readULE32(e) { + let t = await this.read(4, e); + return new Uint32Array(t.buffer)[0]; + } + async readUBE32(e) { + let t = await this.read(4, e); + return new DataView(t.buffer).getUint32(0, !1); + } + async readULE64(e) { + let t = await this.read(8, e), n = new Uint32Array(t.buffer); + return n[1] * 4294967296 + n[0]; + } + async readString(e) { + let t = this, n = e === void 0 ? t.pos : e; + if (n > this.totalSize) { + if (this.readOnly) throw Error("Reading out of bounds"); + this._resizeIfNeeded(e); + } + let r = !1, i = ""; + for (; !r;) { + let e = Math.floor(n / c), a = n % c; + if (t.o.data[e] === void 0) throw Error("ERROR"); + let o = Math.min(2048, t.o.data[e].length - a); + if (o <= 0) return t.pos = n, i; + let s = new Uint8Array(t.o.data[e].buffer, a, o), l = s.findIndex((e) => e === 0); + r = l !== -1, r ? (i += new TextDecoder().decode(s.slice(0, l)), t.pos = e * c + a + l + 1) : (i += new TextDecoder().decode(s), t.pos = e * c + a + s.length), n = t.pos; + } + return i; + } +}, _ = 1 << 20, v = 8192, y = class { + constructor(e, t, n, r) { + this.readRangeInto = e, this.totalSize = t, this.pos = 0, this.pageSize = r || v, this.maxPagesLoaded = Math.floor((n || _) / this.pageSize) + 1, this.pages = /* @__PURE__ */ new Map(), this.readOnly = !0; + } + _pageLen(e) { + let t = e * this.pageSize; + return Math.min(t + this.pageSize, this.totalSize) - t; + } + _loadPage(e) { + let t = this, n = t.pages.get(e); + if (n) return t.pages.delete(e), t.pages.set(e, n), n.promise; + let r = new Uint8Array(t._pageLen(e)); + return n = { + buff: null, + promise: null + }, n.promise = t.readRangeInto(r, 0, e * t.pageSize, r.byteLength).then(function() { + return n.buff = r, r; + }, function(n) { + throw t.pages.delete(e), n; + }), t.pages.set(e, n), t._trimCache(), n.promise; + } + _trimCache() { + let e = this; + if (!(e.pages.size <= e.maxPagesLoaded)) for (let t of e.pages) { + if (e.pages.size <= e.maxPagesLoaded) return; + t[1].buff && e.pages.delete(t[0]); + } + } + async readToBuffer(e, t, n, r) { + let i = this; + if (n === 0) return; + if (i.pendingClose) throw Error("Reading a closing file"); + if (r === void 0 && (r = i.pos), r + n > i.totalSize) throw Error("Reading out of bounds"); + if (i.pos = r + n, n >= i.pageSize) { + await i.readRangeInto(e, t, r, n); + return; + } + let a = Math.floor(r / i.pageSize), o = Math.floor((r + n - 1) / i.pageSize), s = r % i.pageSize, c = 0; + for (let r = a; r <= o; r++) { + let a = await i._loadPage(r), o = Math.min(n - c, i.pageSize - s); + e.set(a.subarray(s, s + o), t + c), c += o, s = 0; + } + } + async read(e, t) { + let n = new Uint8Array(e); + return await this.readToBuffer(n, 0, e, t), n; + } + async readULE32(e) { + let t = await this.read(4, e); + return new Uint32Array(t.buffer)[0]; + } + async readUBE32(e) { + let t = await this.read(4, e); + return new DataView(t.buffer).getUint32(0, !1); + } + async readULE64(e) { + let t = await this.read(8, e), n = new Uint32Array(t.buffer); + return n[1] * 4294967296 + n[0]; + } + async readString(e) { + let t = this; + if (t.pendingClose) throw Error("Reading a closing file"); + let n = e === void 0 ? t.pos : e, r = []; + for (; n < t.totalSize;) { + let e = Math.min(t.pageSize, t.totalSize - n), i = await t.read(e, n), a = i.indexOf(0); + if (a >= 0) return r.push(i.subarray(0, a)), t.pos = n + a + 1, b(r); + r.push(i), n += e; + } + return t.pos = n, b(r); + } + async write() { + throw Error("Writing a read only file"); + } + async writeULE32() { + throw Error("Writing a read only file"); + } + async writeUBE32() { + throw Error("Writing a read only file"); + } + async writeULE64() { + throw Error("Writing a read only file"); + } + async close() { + this.pendingClose || (this.pendingClose = !0, this.pages.clear()); + } + async discard() { + await this.close(); + } +}; +function b(e) { + let t = 0; + for (let n = 0; n < e.length; n++) t += e[n].byteLength; + let n = new Uint8Array(t), r = 0; + for (let t = 0; t < e.length; t++) n.set(e[t], r), r += e[t].byteLength; + return new TextDecoder().decode(n); +} +//#endregion +//#region src/httpfile.js +var x = 65536; +async function S(e) { + let n = e.url, r = await fetch(n, { headers: { Range: "bytes=0-0" } }); + if (r.status === 206) { + let t = r.headers.get("content-range"), i = t ? /\/(\d+)\s*$/.exec(t) : null; + if (i) { + let t = parseInt(i[1]); + await r.arrayBuffer(); + let a = w(r), o = null, s = async function(e, t, r, i) { + if (!o) try { + return await T(n, a, e, t, r, i); + } catch (e) { + if (!e || !e.degradeToFull) throw e; + o = e.fullBodyPromise; + } + let s = await o; + if (r + i > s.byteLength) throw Error(n + ": read past the end of the buffered body"); + e.set(s.subarray(r, r + i), t); + }, c = Math.min(e.pageSize || x, x); + return new y(s, t, e.cacheSize, c); + } + return await r.arrayBuffer(), await C(n); + } + if (!r.ok && r.status !== 416) throw Error("HTTP " + r.status + " fetching " + n); + if (r.status === 416) { + let e = r.headers.get("content-range"); + return e && /\/0\s*$/.test(e) ? t({ + type: "mem", + data: /* @__PURE__ */ new Uint8Array() + }) : await C(n); + } + return t({ + type: "mem", + data: new Uint8Array(await r.arrayBuffer()) + }); +} +async function C(e) { + let n = await fetch(e); + if (!n.ok) throw Error("HTTP " + n.status + " fetching " + e); + return t({ + type: "mem", + data: new Uint8Array(await n.arrayBuffer()) + }); +} +function w(e) { + let t = e.headers.get("etag"); + return t && t.indexOf("W/") !== 0 ? t : e.headers.get("last-modified") || null; +} +async function T(e, t, n, r, i, a) { + let o = { Range: "bytes=" + i + "-" + (i + a - 1) }; + t && (o["If-Range"] = t); + let s = await fetch(e, { headers: o }); + if (s.status === 200) { + let n = w(s); + if (!t || n && n === t) { + let t = /* @__PURE__ */ Error(e + ": origin ignored Range; degrading to full buffering"); + throw t.degradeToFull = !0, t.fullBodyPromise = s.arrayBuffer().then((e) => new Uint8Array(e)), t; + } + throw await E(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); + } + if (s.status !== 206) throw await E(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); + let c = s.headers.get("content-range"), l = c ? /bytes\s+(\d+)-(\d+)\//.exec(c) : null; + if (l && parseInt(l[1]) !== i) throw await E(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); + let u = 0; + if (s.body && typeof s.body.getReader == "function") { + let t = s.body.getReader(); + for (;;) { + let i = await t.read(); + if (i.done) break; + if (u + i.value.byteLength > a) throw t.cancel().catch(function() {}), Error(e + ": range response longer than requested"); + n.set(i.value, r + u), u += i.value.byteLength; + } + } else { + /* c8 ignore start */ + let t = new Uint8Array(await s.arrayBuffer()); + if (t.byteLength > a) throw Error(e + ": range response longer than requested"); + n.set(t, r), u = t.byteLength; + } + if (u !== a) throw Error(e + ": short range response (" + u + "/" + a + " bytes at " + i + ")"); +} +async function E(e) { + try { + e.body && typeof e.body.cancel == "function" ? await e.body.cancel() : await e.arrayBuffer(); + } catch {} +} +//#endregion +//#region src/blobfile.js +var D = 1 << 20; +function O(e) { + let t = e.blob, n = async function(e, n, r, i) { + let a = await t.slice(r, r + i).arrayBuffer(); + if (a.byteLength !== i) throw Error("short blob read (" + a.byteLength + "/" + i + " bytes at " + r + ")"); + e.set(new Uint8Array(a), n); + }, r = Math.min(e.pageSize || D, D); + return new y(n, t.size, e.cacheSize, r); +} +//#endregion +//#region src/fastfile.browser.js +function k() { + throw Error("File I/O is not supported in the browser"); +} +function A(e) { + return e instanceof Uint8Array ? { + type: "mem", + data: e + } : (typeof e == "string" && k(), e); +} +function j(e, t, n) { + if (e.type === "file" && k(), e.type === "mem") return t(e); + if (e.type === "bigMem") return n(e); + throw Error("Invalid FastFile type: " + e.type); +} +function M(t) { + return j(A(t), e, l); +} +var N = M; +async function P(e, n, r) { + return e instanceof Uint8Array && (e = { + type: "mem", + data: e + }), typeof Blob < "u" && e instanceof Blob && (e = { + type: "blob", + blob: e, + cacheSize: n, + pageSize: r + }), typeof e == "string" && (e = { + type: "http", + url: e, + cacheSize: n, + pageSize: r + }), e.type === "http" ? await S(e) : e.type === "blob" ? O(e) : j(e, t, u); +} +function F(e) { + return typeof e == "string" && k(), j(e, n, d); +} +var I = F; +//#endregion +export { N as createNoOverride, M as createOverride, P as readExisting, F as readWriteExisting, I as readWriteExistingOrCreate }; diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js new file mode 100644 index 0000000..482fdcf --- /dev/null +++ b/build/browser/browser.iife.js @@ -0,0 +1 @@ +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;ns.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||S,S);return new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await w(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await w(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function w(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function T(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function E(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=T(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await D(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await D(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await D(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function D(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var O=1<<20;function k(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||O,O);return new b(n,t.size,e.cacheSize,r)}function A(){throw Error(`File I/O is not supported in the browser`)}function j(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&A(),e)}function M(e,t,n){if(e.type===`file`&&A(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function N(e){return M(j(e),t,u)}var P=N;async function F(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await C(e):e.type===`blob`?k(e):M(e,n,d)}function I(e){return typeof e==`string`&&A(),M(e,r,f)}var L=I;return e.createNoOverride=P,e.createOverride=N,e.readExisting=F,e.readWriteExisting=I,e.readWriteExistingOrCreate=L,e})({}); \ No newline at end of file diff --git a/build/main.cjs b/build/main.cjs deleted file mode 100644 index 120bb2e..0000000 --- a/build/main.cjs +++ /dev/null @@ -1,1469 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -var fs = require('fs'); -var constants = require('constants'); - -function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - -var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); - -async function open(fileName, openFlags, cacheSize, pageSize) { - cacheSize = cacheSize || 4096*64; - if (typeof openFlags !== "number" && ["w+", "wx+", "r", "ax+", "a+"].indexOf(openFlags) <0) - throw new Error("Invalid open option"); - const fd =await fs__default["default"].promises.open(fileName, openFlags); - - const stats = await fd.stat(); - - return new FastFile(fd, stats, cacheSize, pageSize, fileName); -} - - -class FastFile { - - constructor(fd, stats, cacheSize, pageSize, fileName) { - this.fileName = fileName; - this.fd = fd; - this.pos = 0; - this.pageSize = pageSize || (1 << 8); - while (this.pageSize < stats.blksize) { - this.pageSize *= 2; - } - this.totalSize = stats.size; - this.totalPages = Math.floor((stats.size -1) / this.pageSize)+1; - this.maxPagesLoaded = Math.floor( cacheSize / this.pageSize)+1; - // Reads/writes at least this large bypass the page cache and move bytes - // straight between disk and the caller's buffer (see readToBuffer / - // write), avoiding the extra buffer<->page copy that dominates large - // sequential transfers (e.g. zkey/ptau sections). - this.directReadThreshold = 1 << 20; - this.directWriteThreshold = 1 << 20; - this.pages = {}; - this.pendingLoads = []; - this.writing = false; - this.reading = false; - this.avBuffs = []; - this.history = {}; - } - - _loadPage(p) { - const self = this; - const P = new Promise((resolve, reject)=> { - self.pendingLoads.push({ - page: p, - resolve: resolve, - reject: reject - }); - }); - self.__statusPage("After Load request: ", p); - return P; - } - - __statusPage(s, p) { - const logEntry = []; - const self=this; - if (!self.logHistory) return; - logEntry.push("==" + s+ " " +p); - let S = ""; - for (let i=0; i " + self.history[p][i][j]); - } - } - } - - - - _triggerLoad() { - const self = this; - - if (self.reading) return; - if (self.pendingLoads.length==0) return; - - const pageIdxs = Object.keys(self.pages); - - const deletablePages = []; - for (let i=0; i0) && - ( (typeof self.pages[self.pendingLoads[0].page] != "undefined" ) - ||( (freePages>0) - ||(deletablePages.length>0)))) { - const load = self.pendingLoads.shift(); - if (typeof self.pages[load.page] != "undefined") { - self.pages[load.page].pendingOps ++; - const idx = deletablePages.indexOf(load.page); - if (idx>=0) deletablePages.splice(idx, 1); - if (self.pages[load.page].loading) { - self.pages[load.page].loading.push(load); - } else { - load.resolve(); - } - self.__statusPage("After Load (cached): ", load.page); - - } else { - if (freePages) { - freePages--; - } else { - const fp = deletablePages.shift(); - self.__statusPage("Before Unload: ", fp); - self.avBuffs.unshift(self.pages[fp]); - delete self.pages[fp]; - self.__statusPage("After Unload: ", fp); - } - - if (load.page>=self.totalPages) { - self.pages[load.page] = getNewPage(); - load.resolve(); - self.__statusPage("After Load (new): ", load.page); - } else { - self.reading = true; - self.pages[load.page] = getNewPage(); - self.pages[load.page].loading = [load]; - ops.push(self.fd.read(self.pages[load.page].buff, 0, self.pageSize, load.page*self.pageSize).then((res)=> { - self.pages[load.page].size = res.bytesRead; - const loading = self.pages[load.page].loading; - delete self.pages[load.page].loading; - for (let i=0; i { - // Reject EVERY waiter, not just the first: co-readers of the - // same page were appended to page.loading (not to `load`) and - // would otherwise await forever. Drop the page entirely so a - // later retry re-reads it instead of queueing onto a dead - // loading list. - const page = self.pages[load.page]; - const loading = (page && page.loading) ? page.loading : [load]; - delete self.pages[load.page]; - for (let i=0; i1) console.log(ops.length); - - Promise.all(ops).then( () => { - self.reading = false; - if (self.pendingLoads.length>0) setImmediate(self._triggerLoad.bind(self)); - self._tryClose(); - }); - - function getNewPage() { - if (self.avBuffs.length>0) { - const p = self.avBuffs.shift(); - p.dirty = false; - p.pendingOps = 1; - p.size =0; - return p; - } else { - return { - dirty: false, - buff: new Uint8Array(self.pageSize), - pendingOps: 1, - size: 0 - }; - } - } - - } - - - _triggerWrite() { - const self = this; - if (self.writing) return; - - const pageIdxs = Object.keys(self.pages); - - const ops = []; - - for (let i=0; i { - page.writing = false; - return; - }, (err) => { - console.log("ERROR Writing: "+err); - // Clear `writing` (a stuck flag pins the page forever and - // blocks _tryClose) and record the error. write()/read() - // surface it on their next call (fail fast) and close() - // rejects with it -- previously it was only visible at - // close(), so a prover that skipped close on error paths - // silently produced a truncated/corrupt file. - page.writing = false; - self.error = err; - self._tryClose(); - })); - } - } - - if (self.writing) { - Promise.all(ops).then( () => { - self.writing = false; - setImmediate(self._triggerWrite.bind(self)); - self._tryClose(); - if (self.pendingLoads.length>0) setImmediate(self._triggerLoad.bind(self)); - }); - } - } - - _getDirtyPage() { - for (let p in this.pages) { - if (this.pages[p].dirty) return p; - } - return -1; - } - - // Iterate the actually-cached pages (usually few) rather than every page - // index in [firstPage, lastPage], so the check stays O(cached pages) even - // for very large ranges / small page sizes. - _rangeHasCachedPages(pos, len) { - const firstPage = Math.floor(pos / this.pageSize); - const lastPage = Math.floor((pos + len - 1) / this.pageSize); - for (const k of Object.keys(this.pages)) { - const p = +k; - if (p >= firstPage && p <= lastPage) return true; - } - return false; - } - - async write(buff, pos) { - if (buff.byteLength == 0) return; - const self = this; - if (self.error) throw self.error; - if (typeof pos == "undefined") pos = self.pos; - self.pos = pos+buff.byteLength; - if (self.totalSize < pos + buff.byteLength) self.totalSize = pos + buff.byteLength; - if (self.pendingClose) - throw new Error("Writing a closing file"); - - // Direct-write fast path: for large writes to a region with no cached - // pages, write straight to disk, skipping the buff->page copy and the - // deferred page flush. Any cached page in range (even clean) would go - // stale after a direct write, so we fall back to the cached path then. - // ArrayBuffer.isView gate: fd.write needs a real TypedArray/DataView; a - // BigBuffer (paged, not a view) must use the cached path. - if (buff.byteLength >= self.directWriteThreshold && ArrayBuffer.isView(buff) && !self._rangeHasCachedPages(pos, buff.byteLength)) { - let done = 0; - while (done < buff.byteLength) { - const { bytesWritten } = await self.fd.write(buff, done, buff.byteLength - done, pos + done); - if (bytesWritten === 0) break; // should not happen - done += bytesWritten; - } - const lastPage = Math.floor((pos + buff.byteLength - 1) / self.pageSize); - if (lastPage + 1 > self.totalPages) self.totalPages = lastPage + 1; - return; - } - - const firstPage = Math.floor(pos / self.pageSize); - const lastPage = Math.floor((pos + buff.byteLength -1) / self.pageSize); - - const pagePromises = []; - for (let i=firstPage; i<=lastPage; i++) pagePromises.push(self._loadPage(i)); - self._triggerLoad(); - - let p = firstPage; - let o = pos % self.pageSize; - let r = buff.byteLength; - while (r>0) { - await pagePromises[p-firstPage]; - const l = (o+r > self.pageSize) ? (self.pageSize -o) : r; - const srcView = buff.slice( buff.byteLength - r, buff.byteLength - r + l); - const dstView = new Uint8Array(self.pages[p].buff.buffer, o, l); - dstView.set(srcView); - self.pages[p].dirty = true; - self.pages[p].pendingOps --; - self.pages[p].size = Math.max(o+l, self.pages[p].size); - if (p>=self.totalPages) { - self.totalPages = p+1; - } - r = r-l; - p ++; - o = 0; - if (!self.writing) setImmediate(self._triggerWrite.bind(self)); - } - } - - async read(len, pos) { - const self = this; - let buff = new Uint8Array(len); - await self.readToBuffer(buff, 0, len, pos); - - return buff; - } - - // Iterate the actually-cached pages (usually few) rather than every page - // index in [firstPage, lastPage], so the check stays O(cached pages) even - // for very large ranges / small page sizes. - _rangeHasDirtyPages(pos, len) { - const firstPage = Math.floor(pos / this.pageSize); - const lastPage = Math.floor((pos + len - 1) / this.pageSize); - for (const k of Object.keys(this.pages)) { - const p = +k; - if (p >= firstPage && p <= lastPage) { - const page = this.pages[p]; - if (page.dirty || page.writing) return true; - } - } - return false; - } - - async readToBuffer(buffDst, offset, len, pos) { - if (len == 0) { - return; - } - const self = this; - if (self.error) throw self.error; - if (typeof pos == "undefined") pos = self.pos; - self.pos = pos+len; - if (self.pendingClose) - throw new Error("Reading a closing file"); - - // Direct-read fast path: for large reads with no overlapping unwritten - // (dirty) pages, copy straight from disk into the destination buffer. - // This skips the page cache and the page->destination copy it incurs, - // which dominates large sequential reads (e.g. zkey/ptau sections). - // ArrayBuffer.isView gate: the direct path hands buffDst to fd.read, which - // needs a real TypedArray/DataView. A BigBuffer (paged, not a view) must - // go through the cached path, which copies into it via its own .set(). - if (len >= self.directReadThreshold && ArrayBuffer.isView(buffDst) && !self._rangeHasDirtyPages(pos, len)) { - let toRead = (pos + len > self.totalSize) ? (self.totalSize - pos) : len; - if (toRead < 0) toRead = 0; - let done = 0; - while (done < toRead) { - const { bytesRead } = await self.fd.read(buffDst, offset + done, toRead - done, pos + done); - if (bytesRead === 0) break; // EOF - done += bytesRead; - } - return; - } - - if (len > self.pageSize*self.maxPagesLoaded*0.8) { - const cacheSize = Math.floor(len * 1.1); - this.maxPagesLoaded = Math.floor( cacheSize / self.pageSize)+1; - } - const firstPage = Math.floor(pos / self.pageSize); - const lastPage = Math.floor((pos + len -1) / self.pageSize); - - const pagePromises = []; - for (let i=firstPage; i<=lastPage; i++) pagePromises.push(self._loadPage(i)); - - self._triggerLoad(); - - let p = firstPage; - let o = pos % self.pageSize; - // Remaining bytes to read (clamped to EOF: a read past the end of a - // truncated/short file reads fewer bytes than requested). - let r = pos + len > self.totalSize ? len - (pos + len - self.totalSize): len; - // Bytes already written to buffDst -- tracked independently of `r` - // (which shrinks on EOF-clamping) so the destination offset stays - // correct. Previously computed as `offset + len - r`: with `r` - // pre-clamped below `len`, that put the first bytes read at a - // nonzero offset instead of the real EOF-truncated tail, silently - // shifting valid data to the wrong position in the output buffer. - let done = 0; - while (r>0) { - await pagePromises[p - firstPage]; - self.__statusPage("After Await (read): ", p); - - // bytes to copy from this page - const l = (o+r > self.pageSize) ? (self.pageSize -o) : r; - const srcView = new Uint8Array(self.pages[p].buff.buffer, self.pages[p].buff.byteOffset + o, l); - buffDst.set(srcView, offset+done); - self.pages[p].pendingOps --; - - self.__statusPage("After Op done: ", p); - - r = r-l; - done = done+l; - p ++; - o = 0; - if (self.pendingLoads.length>0) setImmediate(self._triggerLoad.bind(self)); - } - - this.pos = pos + len; - - } - - - _tryClose() { - const self = this; - if (!self.pendingClose) return; - if (self.error) { - self.pendingCloseReject(self.error); - return; - } - const p = self._getDirtyPage(); - if ((p>=0) || (self.writing) || (self.reading) || (self.pendingLoads.length>0)) return; - self.pendingClose(); - } - - close() { - const self = this; - if (self.pendingClose) - throw new Error("Closing the file twice"); - return new Promise((resolve, reject) => { - self.pendingClose = resolve; - self.pendingCloseReject = reject; - self._tryClose(); - }).then(()=> { - self.fd.close(); - }, (err) => { - self.fd.close(); - throw (err); - }); - } - - async discard() { - const self = this; - await self.close(); - await fs__default["default"].promises.unlink(this.fileName); - } - - async writeULE32(v, pos) { - const self = this; - const tmpBuff32 = new Uint8Array(4); - const tmpBuff32v = new DataView(tmpBuff32.buffer); - - tmpBuff32v.setUint32(0, v, true); - - await self.write(tmpBuff32, pos); - } - - async writeUBE32(v, pos) { - const self = this; - - const tmpBuff32 = new Uint8Array(4); - const tmpBuff32v = new DataView(tmpBuff32.buffer); - - tmpBuff32v.setUint32(0, v, false); - - await self.write(tmpBuff32, pos); - } - - - async writeULE64(v, pos) { - const self = this; - - const tmpBuff64 = new Uint8Array(8); - const tmpBuff64v = new DataView(tmpBuff64.buffer); - - tmpBuff64v.setUint32(0, v & 0xFFFFFFFF, true); - tmpBuff64v.setUint32(4, Math.floor(v / 0x100000000) , true); - - await self.write(tmpBuff64, pos); - } - - async readULE32(pos) { - const self = this; - const b = await self.read(4, pos); - - const view = new Uint32Array(b.buffer); - - return view[0]; - } - - async readUBE32(pos) { - const self = this; - const b = await self.read(4, pos); - - const view = new DataView(b.buffer); - - return view.getUint32(0, false); - } - - async readULE64(pos) { - const self = this; - const b = await self.read(8, pos); - - const view = new Uint32Array(b.buffer); - - return view[1] * 0x100000000 + view[0]; - } - - async readString(pos) { - const self = this; - - if (self.pendingClose) { - throw new Error("Reading a closing file"); - } - - let currentPosition = typeof pos == "undefined" ? self.pos : pos; - let currentPage = Math.floor(currentPosition / self.pageSize); - - let endOfStringFound = false; - let str = ""; - - while (!endOfStringFound) { - //Read page - let pagePromise = self._loadPage(currentPage); - self._triggerLoad(); - await pagePromise; - self.__statusPage("After Await (read): ", currentPage); - - let offsetOnPage = currentPosition % self.pageSize; - - const dataArray = new Uint8Array( - self.pages[currentPage].buff.buffer, - self.pages[currentPage].buff.byteOffset + offsetOnPage, - self.pageSize - offsetOnPage - ); - - let indexEndOfString = dataArray.findIndex(element => element === 0); - endOfStringFound = indexEndOfString !== -1; - - if (endOfStringFound) { - str += new TextDecoder().decode(dataArray.slice(0, indexEndOfString)); - self.pos = currentPage * this.pageSize + offsetOnPage + indexEndOfString + 1; - } else { - str += new TextDecoder().decode(dataArray); - self.pos = currentPage * this.pageSize + offsetOnPage + dataArray.length; - } - - self.pages[currentPage].pendingOps--; - self.__statusPage("After Op done: ", currentPage); - - currentPosition = self.pos; - currentPage++; - - if (self.pendingLoads.length > 0) setImmediate(self._triggerLoad.bind(self)); - } - - return str; - } -} - -function createNew$1(o) { - const initialSize = o.initialSize || 1<<20; - const fd = new MemFile(); - fd.o = o; - fd.o.data = new Uint8Array(initialSize); - fd.allocSize = initialSize; - fd.totalSize = 0; - fd.readOnly = false; - fd.pos = 0; - return fd; -} - -function readExisting$4(o) { - const fd = new MemFile(); - fd.o = o; - fd.allocSize = o.data.byteLength; - fd.totalSize = o.data.byteLength; - fd.readOnly = true; - fd.pos = 0; - return fd; -} - -function readWriteExisting$2(o) { - const fd = new MemFile(); - fd.o = o; - fd.allocSize = o.data.byteLength; - fd.totalSize = o.data.byteLength; - fd.readOnly = false; - fd.pos = 0; - return fd; -} - -const tmpBuff32$1 = new Uint8Array(4); -const tmpBuff32v$1 = new DataView(tmpBuff32$1.buffer); -const tmpBuff64$1 = new Uint8Array(8); -const tmpBuff64v$1 = new DataView(tmpBuff64$1.buffer); - -class MemFile { - - constructor() { - this.pageSize = 1 << 14; // for compatibility - } - - _resizeIfNeeded(newLen) { - if (newLen > this.allocSize) { - const newAllocSize = Math.max( - this.allocSize + (1 << 20), - Math.floor(this.allocSize * 1.1), - newLen - ); - const newData = new Uint8Array(newAllocSize); - newData.set(this.o.data); - this.o.data = newData; - this.allocSize = newAllocSize; - } - } - - async write(buff, pos) { - const self =this; - if (typeof pos == "undefined") pos = self.pos; - if (this.readOnly) throw new Error("Writing a read only file"); - - this._resizeIfNeeded(pos + buff.byteLength); - - this.o.data.set(buff.slice(), pos); - - if (pos + buff.byteLength > this.totalSize) this.totalSize = pos + buff.byteLength; - - this.pos = pos + buff.byteLength; - } - - async readToBuffer(buffDest, offset, len, pos) { - const self = this; - if (typeof pos == "undefined") pos = self.pos; - if (this.readOnly) { - if (pos + len > this.totalSize) throw new Error("Reading out of bounds"); - } - this._resizeIfNeeded(pos + len); - - const buffSrc = new Uint8Array(this.o.data.buffer, this.o.data.byteOffset + pos, len); - - buffDest.set(buffSrc, offset); - - this.pos = pos + len; - } - - async read(len, pos) { - const self = this; - - const buff = new Uint8Array(len); - await self.readToBuffer(buff, 0, len, pos); - - return buff; - } - - close() { - if (this.o.data.byteLength != this.totalSize) { - this.o.data = this.o.data.slice(0, this.totalSize); - } - } - - async discard() { - } - - - async writeULE32(v, pos) { - const self = this; - - tmpBuff32v$1.setUint32(0, v, true); - - await self.write(tmpBuff32$1, pos); - } - - async writeUBE32(v, pos) { - const self = this; - - tmpBuff32v$1.setUint32(0, v, false); - - await self.write(tmpBuff32$1, pos); - } - - - async writeULE64(v, pos) { - const self = this; - - tmpBuff64v$1.setUint32(0, v & 0xFFFFFFFF, true); - tmpBuff64v$1.setUint32(4, Math.floor(v / 0x100000000) , true); - - await self.write(tmpBuff64$1, pos); - } - - - async readULE32(pos) { - const self = this; - const b = await self.read(4, pos); - - const view = new Uint32Array(b.buffer); - - return view[0]; - } - - async readUBE32(pos) { - const self = this; - const b = await self.read(4, pos); - - const view = new DataView(b.buffer); - - return view.getUint32(0, false); - } - - async readULE64(pos) { - const self = this; - const b = await self.read(8, pos); - - const view = new Uint32Array(b.buffer); - - return view[1] * 0x100000000 + view[0]; - } - - async readString(pos) { - const self = this; - - let currentPosition = typeof pos == "undefined" ? self.pos : pos; - - if (currentPosition > this.totalSize) { - if (this.readOnly) { - throw new Error("Reading out of bounds"); - } - this._resizeIfNeeded(pos); - } - const dataArray = new Uint8Array( - self.o.data.buffer, - currentPosition, - this.totalSize - currentPosition - ); - - let indexEndOfString = dataArray.findIndex(element => element === 0); - let endOfStringFound = indexEndOfString !== -1; - - let str = ""; - if (endOfStringFound) { - str = new TextDecoder().decode(dataArray.slice(0, indexEndOfString)); - self.pos = currentPosition + indexEndOfString + 1; - } else { - self.pos = currentPosition; - } - return str; - } -} - -const PAGE_SIZE = 1<<22; - -function createNew(o) { - const initialSize = o.initialSize || 0; - const fd = new BigMemFile(); - fd.o = o; - const nPages = initialSize ? Math.floor((initialSize - 1) / PAGE_SIZE)+1 : 0; - fd.o.data = []; - for (let i=0; i0) { - const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; - const srcView = buff.slice(buff.byteLength - r, buff.byteLength - r + l); - const dstView = new Uint8Array(self.o.data[p].buffer, o, l); - dstView.set(srcView); - r = r-l; - p ++; - o = 0; - } - - this.pos = pos + buff.byteLength; - } - - async readToBuffer(buffDst, offset, len, pos) { - const self = this; - if (typeof pos == "undefined") pos = self.pos; - if (this.readOnly) { - if (pos + len > this.totalSize) throw new Error("Reading out of bounds"); - } - this._resizeIfNeeded(pos + len); - - const firstPage = Math.floor(pos / PAGE_SIZE); - - let p = firstPage; - let o = pos % PAGE_SIZE; - // Remaining bytes to read - let r = len; - while (r>0) { - // bytes to copy from this page - const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; - const srcView = new Uint8Array(self.o.data[p].buffer, o, l); - buffDst.set(srcView, offset+len-r); - r = r-l; - p ++; - o = 0; - } - - this.pos = pos + len; - } - - async read(len, pos) { - const self = this; - const buff = new Uint8Array(len); - - await self.readToBuffer(buff, 0, len, pos); - - return buff; - } - - close() { - } - - async discard() { - } - - - async writeULE32(v, pos) { - const self = this; - - tmpBuff32v.setUint32(0, v, true); - - await self.write(tmpBuff32, pos); - } - - async writeUBE32(v, pos) { - const self = this; - - tmpBuff32v.setUint32(0, v, false); - - await self.write(tmpBuff32, pos); - } - - - async writeULE64(v, pos) { - const self = this; - - tmpBuff64v.setUint32(0, v & 0xFFFFFFFF, true); - tmpBuff64v.setUint32(4, Math.floor(v / 0x100000000) , true); - - await self.write(tmpBuff64, pos); - } - - - async readULE32(pos) { - const self = this; - const b = await self.read(4, pos); - - const view = new Uint32Array(b.buffer); - - return view[0]; - } - - async readUBE32(pos) { - const self = this; - const b = await self.read(4, pos); - - const view = new DataView(b.buffer); - - return view.getUint32(0, false); - } - - async readULE64(pos) { - const self = this; - const b = await self.read(8, pos); - - const view = new Uint32Array(b.buffer); - - return view[1] * 0x100000000 + view[0]; - } - - async readString(pos) { - const self = this; - const fixedSize = 2048; - - let currentPosition = typeof pos == "undefined" ? self.pos : pos; - - if (currentPosition > this.totalSize) { - if (this.readOnly) { - throw new Error("Reading out of bounds"); - } - this._resizeIfNeeded(pos); - } - - let endOfStringFound = false; - let str = ""; - - while (!endOfStringFound) { - let currentPage = Math.floor(currentPosition / PAGE_SIZE); - let offsetOnPage = currentPosition % PAGE_SIZE; - - if (self.o.data[currentPage] === undefined) { - throw new Error("ERROR"); - } - - let readLength = Math.min(fixedSize, self.o.data[currentPage].length - offsetOnPage); - const dataArray = new Uint8Array(self.o.data[currentPage].buffer, offsetOnPage, readLength); - - let indexEndOfString = dataArray.findIndex(element => element === 0); - endOfStringFound = indexEndOfString !== -1; - - if (endOfStringFound) { - str += new TextDecoder().decode(dataArray.slice(0, indexEndOfString)); - self.pos = currentPage * PAGE_SIZE + offsetOnPage + indexEndOfString + 1; - } else { - str += new TextDecoder().decode(dataArray); - self.pos = currentPage * PAGE_SIZE + offsetOnPage + dataArray.length; - } - - currentPosition = self.pos; - } - return str; - } -} - -// Shared read-only file over an abstract positioned range reader. -// -// Backends (httpfile, blobfile) supply a single primitive: -// readRangeInto(dstBuff, dstOffset, pos, len) -> Promise -// and this class layers the FastFile read interface on top: -// - reads >= pageSize bypass the cache and go straight to the backend -// (the file is read-only, so a direct read is always coherent); -// - smaller reads (header scans: magics, section tables, ULE32/64) are -// served from page-aligned cached ranges so that e.g. a binfile section -// scan does not issue one backend request per 4-byte field. -// -// Write operations throw: remote ranges are strictly a read transport. - -const DEFAULT_CACHE_SIZE$1 = 1 << 20; -const DEFAULT_PAGE_SIZE$1 = 1 << 13; - -class RangeFile { - - constructor(readRangeInto, totalSize, cacheSize, pageSize) { - this.readRangeInto = readRangeInto; - this.totalSize = totalSize; - this.pos = 0; - this.pageSize = pageSize || DEFAULT_PAGE_SIZE$1; - this.maxPagesLoaded = Math.floor((cacheSize || DEFAULT_CACHE_SIZE$1) / this.pageSize) + 1; - this.pages = new Map(); // page index -> { buff: Uint8Array|null, promise: Promise } - this.readOnly = true; - } - - _pageLen(p) { - const start = p * this.pageSize; - const end = Math.min(start + this.pageSize, this.totalSize); - return end - start; - } - - // Load (or join the in-flight load of) page p; resolves to its buffer. - // A failed load removes the entry so a later retry re-requests it. - _loadPage(p) { - const self = this; - let page = self.pages.get(p); - if (page) { - // LRU touch: re-insert so Map iteration order tracks recency. - self.pages.delete(p); - self.pages.set(p, page); - return page.promise; - } - const buff = new Uint8Array(self._pageLen(p)); - page = { buff: null, promise: null }; - page.promise = self.readRangeInto(buff, 0, p * self.pageSize, buff.byteLength).then(function () { - page.buff = buff; - return buff; - }, function (err) { - self.pages.delete(p); - throw err; - }); - self.pages.set(p, page); - self._trimCache(); - return page.promise; - } - - _trimCache() { - const self = this; - if (self.pages.size <= self.maxPagesLoaded) return; - // Evict oldest settled pages first; in-flight loads are kept. - for (const entry of self.pages) { - if (self.pages.size <= self.maxPagesLoaded) return; - if (entry[1].buff) self.pages.delete(entry[0]); - } - } - - async readToBuffer(buffDst, offset, len, pos) { - const self = this; - if (len === 0) return; - if (self.pendingClose) throw new Error("Reading a closing file"); - if (typeof pos === "undefined") pos = self.pos; - if (pos + len > self.totalSize) throw new Error("Reading out of bounds"); - self.pos = pos + len; - - // Direct path: one backend request straight into the destination - // (works for both typed arrays and BigBuffer via .set()). - if (len >= self.pageSize) { - await self.readRangeInto(buffDst, offset, pos, len); - return; - } - - const firstPage = Math.floor(pos / self.pageSize); - const lastPage = Math.floor((pos + len - 1) / self.pageSize); - let o = pos % self.pageSize; - let done = 0; - for (let p = firstPage; p <= lastPage; p++) { - const buff = await self._loadPage(p); - const l = Math.min(len - done, self.pageSize - o); - buffDst.set(buff.subarray(o, o + l), offset + done); - done += l; - o = 0; - } - } - - async read(len, pos) { - const buff = new Uint8Array(len); - await this.readToBuffer(buff, 0, len, pos); - return buff; - } - - async readULE32(pos) { - const b = await this.read(4, pos); - const view = new Uint32Array(b.buffer); - return view[0]; - } - - async readUBE32(pos) { - const b = await this.read(4, pos); - const view = new DataView(b.buffer); - return view.getUint32(0, false); - } - - async readULE64(pos) { - const b = await this.read(8, pos); - const view = new Uint32Array(b.buffer); - return view[1] * 0x100000000 + view[0]; - } - - async readString(pos) { - const self = this; - if (self.pendingClose) throw new Error("Reading a closing file"); - let p = typeof pos === "undefined" ? self.pos : pos; - const chunks = []; - while (p < self.totalSize) { - const l = Math.min(self.pageSize, self.totalSize - p); - const chunk = await self.read(l, p); - const z = chunk.indexOf(0); - if (z >= 0) { - chunks.push(chunk.subarray(0, z)); - self.pos = p + z + 1; - return decodeChunks(chunks); - } - chunks.push(chunk); - p += l; - } - // No terminator before EOF: the string ends at EOF. - self.pos = p; - return decodeChunks(chunks); - } - - async write() { - throw new Error("Writing a read only file"); - } - - async writeULE32() { - throw new Error("Writing a read only file"); - } - - async writeUBE32() { - throw new Error("Writing a read only file"); - } - - async writeULE64() { - throw new Error("Writing a read only file"); - } - - async close() { - if (this.pendingClose) throw new Error("Closing the file twice"); - this.pendingClose = true; - this.pages.clear(); - } - - async discard() { - await this.close(); - } -} - -function decodeChunks(chunks) { - let total = 0; - for (let i = 0; i < chunks.length; i++) total += chunks[i].byteLength; - const all = new Uint8Array(total); - let o = 0; - for (let i = 0; i < chunks.length; i++) { - all.set(chunks[i], o); - o += chunks[i].byteLength; - } - return new TextDecoder().decode(all); -} - -// Read-only file over HTTP(S) using Range requests. - -// Callers tune cacheSize/pageSize for the disk backend (snarkjs passes 8 MiB -// pages); over HTTP a page that large turns a 4-byte header read into a -// megabytes-range request -- for files below the page size, the whole file in -// one response, defeating streaming. Cap pages at 64 KiB: big enough to -// coalesce a binfile section-table scan, small enough to stay incidental. -// Reads >= the page size bypass the cache entirely (RangeFile direct path), -// so large section/chunk reads are unaffected by the cap. -const MAX_HTTP_PAGE_SIZE = 1 << 16; - -async function readExisting$2(o) { - const url = o.url; - const probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); - - if (probe.status === 206) { - const contentRange = probe.headers.get("content-range"); - const m = contentRange ? /\/(\d+)\s*$/.exec(contentRange) : null; - if (m) { - const totalSize = parseInt(m[1]); - // Drain the 1-byte probe body so the connection can be reused. - await probe.arrayBuffer(); - const validator = strongValidator(probe); - const readRangeInto = function (dst, dstOffset, pos, len) { - return httpReadRangeInto(url, validator, dst, dstOffset, pos, len); - }; - const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); - return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); - } - // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot - // do bounded positioned reads; refetch whole and buffer. - await probe.arrayBuffer(); - return await readFullyToMem(url); - } - - if (!probe.ok && probe.status !== 416) { - throw new Error("HTTP " + probe.status + " fetching " + url); - } - - if (probe.status === 416) { - // Range not satisfiable -- only legitimate for an empty file. - const contentRange = probe.headers.get("content-range"); - if (contentRange && /\/0\s*$/.test(contentRange)) { - return readExisting$4({ type: "mem", data: new Uint8Array(0) }); - } - return await readFullyToMem(url); - } - - // 200: the server ignored Range and sent the whole file; reuse this body. - const data = new Uint8Array(await probe.arrayBuffer()); - return readExisting$4({ type: "mem", data: data }); -} - -async function readFullyToMem(url) { - const res = await fetch(url); - if (!res.ok) throw new Error("HTTP " + res.status + " fetching " + url); - const data = new Uint8Array(await res.arrayBuffer()); - return readExisting$4({ type: "mem", data: data }); -} - -// Only a strong validator is usable with If-Range (RFC 9110 §13.1.5): a weak -// ETag would make conditional range requests always fail on some servers. -function strongValidator(res) { - const etag = res.headers.get("etag"); - if (etag && etag.indexOf("W/") !== 0) return etag; - const lastModified = res.headers.get("last-modified"); - if (lastModified) return lastModified; - return null; -} - -async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { - const headers = { "Range": "bytes=" + pos + "-" + (pos + len - 1) }; - if (validator) headers["If-Range"] = validator; - const res = await fetch(url, { headers: headers }); - if (res.status === 200) { - // With If-Range this is the server signalling the file changed; - // without it, the server stopped honoring ranges mid-session. - await abandonBody(res); - throw new Error(url + ": file changed (or server stopped honoring Range) while reading"); - } - if (res.status !== 206) { - await abandonBody(res); - throw new Error("HTTP " + res.status + " reading range " + pos + "+" + len + " of " + url); - } - const contentRange = res.headers.get("content-range"); - const m = contentRange ? /bytes\s+(\d+)-(\d+)\//.exec(contentRange) : null; - if (m && parseInt(m[1]) !== pos) { - await abandonBody(res); - throw new Error(url + ": server returned range starting at " + m[1] + ", requested " + pos); - } - - // Stream the body straight into the destination (typed array or - // BigBuffer -- both expose .set(chunk, offset)); avoids materializing - // a second full-size copy of large section reads. - let done = 0; - if (res.body && typeof res.body.getReader === "function") { - const reader = res.body.getReader(); - for (;;) { - const it = await reader.read(); - if (it.done) break; - if (done + it.value.byteLength > len) { - reader.cancel().catch(function () {}); - throw new Error(url + ": range response longer than requested"); - } - dst.set(it.value, dstOffset + done); - done += it.value.byteLength; - } - } else { - const buff = new Uint8Array(await res.arrayBuffer()); - if (buff.byteLength > len) throw new Error(url + ": range response longer than requested"); - dst.set(buff, dstOffset); - done = buff.byteLength; - } - if (done !== len) { - throw new Error(url + ": short range response (" + done + "/" + len + " bytes at " + pos + ")"); - } -} - -async function abandonBody(res) { - try { - if (res.body && typeof res.body.cancel === "function") await res.body.cancel(); - else await res.arrayBuffer(); - } catch (e) { /* body teardown is best-effort */ } -} - -// Read-only file over a Blob/File (e.g. a browser - -// Same rationale as httpfile's cap, relaxed for a local source: callers pass -// disk-tuned page sizes (MiBs) that would make every small header read -// materialize a huge slice; 1 MiB keeps that bounded while costing at most a -// handful of slice reads per file open. -const MAX_BLOB_PAGE_SIZE = 1 << 20; - -function readExisting$1(o) { - const blob = o.blob; - const readRangeInto = async function (dst, dstOffset, pos, len) { - const ab = await blob.slice(pos, pos + len).arrayBuffer(); - if (ab.byteLength !== len) { - throw new Error("short blob read (" + ab.byteLength + "/" + len + " bytes at " + pos + ")"); - } - dst.set(new Uint8Array(ab), dstOffset); - }; - const pageSize = Math.min(o.pageSize || MAX_BLOB_PAGE_SIZE, MAX_BLOB_PAGE_SIZE); - return new RangeFile(readRangeInto, blob.size, o.cacheSize, pageSize); -} - -const DEFAULT_CACHE_SIZE = (1 << 16); -const DEFAULT_PAGE_SIZE = (1 << 13); - -// Robust Node detection that never throws (unlike `process.browser`, which is a -// webpack-ism and is undefined under Vite/esbuild/SES). -const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; - - -async function createOverride(o, b, c) { - if (typeof o === "string") { - o = { - type: "file", - fileName: o, - cacheSize: b || DEFAULT_CACHE_SIZE, - pageSize: c || DEFAULT_PAGE_SIZE - }; - } - if (o.type == "file") { - return await open(o.fileName, constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR, o.cacheSize, o.pageSize); - } else if (o.type == "mem") { - return createNew$1(o); - } else if (o.type == "bigMem") { - return createNew(o); - } else { - throw new Error("Invalid FastFile type: "+o.type); - } -} - -function createNoOverride(o, b, c) { - if (typeof o === "string") { - o = { - type: "file", - fileName: o, - cacheSize: b || DEFAULT_CACHE_SIZE, - pageSize: c || DEFAULT_PAGE_SIZE - }; - } - if (o.type == "file") { - return open(o.fileName, constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR | constants.O_EXCL, o.cacheSize, o.pageSize); - } else if (o.type == "mem") { - return createNew$1(o); - } else if (o.type == "bigMem") { - return createNew(o); - } else { - throw new Error("Invalid FastFile type: "+o.type); - } -} - -async function readExisting(o, b, c) { - if (o instanceof Uint8Array) { - o = { - type: "mem", - data: o - }; - } - if (typeof Blob !== "undefined" && o instanceof Blob) { - o = { - type: "blob", - blob: o, - cacheSize: b, - pageSize: c - }; - } - if (typeof o === "string") { - // URLs (and, in the browser, any string -- historically fetched - // whole) go through the http backend: it streams via Range requests - // when the server supports them and falls back to buffering the - // full body (the previous behavior) when it does not. - if (!isNode || /^https?:\/\//i.test(o)) { - o = { - type: "http", - url: o, - cacheSize: b, - pageSize: c - }; - } else { - o = { - type: "file", - fileName: o, - cacheSize: b || DEFAULT_CACHE_SIZE, - pageSize: c || DEFAULT_PAGE_SIZE - }; - } - } - if (o.type == "file") { - return await open(o.fileName, constants.O_RDONLY, o.cacheSize, o.pageSize); - } else if (o.type == "mem") { - return await readExisting$4(o); - } else if (o.type == "bigMem") { - return await readExisting$3(o); - } else if (o.type == "http") { - return await readExisting$2(o); - } else if (o.type == "blob") { - return readExisting$1(o); - } else { - throw new Error("Invalid FastFile type: "+o.type); - } -} - -function readWriteExisting(o, b, c) { - if (typeof o === "string") { - o = { - type: "file", - fileName: o, - cacheSize: b || DEFAULT_CACHE_SIZE, - pageSize: c || DEFAULT_PAGE_SIZE - }; - } - if (o.type == "file") { - return open(o.fileName, constants.O_CREAT | constants.O_RDWR, o.cacheSize, o.pageSize); - } else if (o.type == "mem") { - return readWriteExisting$2(o); - } else if (o.type == "bigMem") { - return readWriteExisting$1(o); - } else { - throw new Error("Invalid FastFile type: "+o.type); - } -} - -function readWriteExistingOrCreate(o, b, c) { - if (typeof o === "string") { - o = { - type: "file", - fileName: o, - cacheSize: b || DEFAULT_CACHE_SIZE, - pageSize: c || DEFAULT_PAGE_SIZE - }; - } - if (o.type == "file") { - return open(o.fileName, constants.O_CREAT | constants.O_RDWR | constants.O_EXCL, o.cacheSize); - } else if (o.type == "mem") { - return readWriteExisting$2(o); - } else if (o.type == "bigMem") { - return readWriteExisting$1(o); - } else { - throw new Error("Invalid FastFile type: "+o.type); - } -} - -exports.createNoOverride = createNoOverride; -exports.createOverride = createOverride; -exports.readExisting = readExisting; -exports.readWriteExisting = readWriteExisting; -exports.readWriteExistingOrCreate = readWriteExistingOrCreate; diff --git a/build/node/main.cjs b/build/node/main.cjs new file mode 100644 index 0000000..2bb37ce --- /dev/null +++ b/build/node/main.cjs @@ -0,0 +1 @@ +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},k=1<<20,A=8192,j=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||A,this.maxPagesLoaded=Math.floor((n||k)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,M(r);r.push(i),n+=e}return t.pos=n,M(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function M(e){let t=0;for(let n=0;ns.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return new j(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await F(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await F(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function L(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await R(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await R(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await R(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function R(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var z=1<<20;function B(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||z,z);return new j(n,t.size,e.cacheSize,r)}var V=65536,H=8192,U=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function W(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function G(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function K(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!U||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await P(e);if(e.type==`blob`)return B(e);throw Error(`Invalid FastFile type: `+e.type)}function q(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function J(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=G,exports.createOverride=W,exports.readExisting=K,exports.readWriteExisting=q,exports.readWriteExistingOrCreate=J; \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..db65781 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,33 @@ +import js from "@eslint/js"; +import globals from "globals"; + +export default [ + { ignores: ["build/**", "node_modules/**"] }, + js.configs.recommended, + { + languageOptions: { + ecmaVersion: 2022, + sourceType: "module", + globals: { + ...globals.node, + ...globals.browser, + }, + }, + rules: { + "no-unused-vars": ["error", { caughtErrors: "none" }], + "indent": ["error", 4], + "linebreak-style": ["warn", "unix"], + "quotes": ["error", "double"], + "semi": ["error", "always"], + }, + }, + { + // Vitest globals for test files (describe, it, beforeEach, etc.) + files: ["test/**/*.js"], + languageOptions: { + globals: { + ...globals.mocha, + }, + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index ec454e5..33c522a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,431 +9,883 @@ "version": "0.0.20", "license": "GPL-3.0", "devDependencies": { - "chai": "^4.3.6", - "chai-as-promised": "^7.1.1", - "eslint": "^8.17.0", - "ffjavascript": "^0.3.0", - "mocha": "^10.0.0", - "rollup": "^2.20.0" + "@eslint/js": "^10.0.1", + "@vitest/browser": "^4.1.11", + "@vitest/browser-playwright": "^4.1.11", + "eslint": "^10.1.0", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "globals": "^17.4.0", + "playwright": "^1.59.0", + "vite": "^8.0.3", + "vitest": "^4.1.11" + } + }, + "node_modules/@blazediff/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", + "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", - "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": ">=10.10.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.22" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" }, "engines": { - "node": ">= 8" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { - "node": ">= 8" + "node": ">=18.18.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">= 8" + "node": ">=18.18.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "license": "Apache-2.0", "engines": { - "node": ">=0.4.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/@oxc-project/types": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "*" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "cpu": [ + "arm" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "cpu": [ + "s390x" + ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - }, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "cpu": [ + "wasm32" + ], "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "get-func-name": "^2.0.2" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { - "node": "*" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "cpu": [ + "arm64" + ], "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/color-convert": { + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/browser": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.11.tgz", + "integrity": "sha512-bwMovvAeuTFOK5kIFevw4VEf+1gVEICv4SYK4k3knJOxl6b1zEWud8mYKD73e1B0odAn174h1MofURy2TPWf3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@blazediff/core": "1.9.1", + "@vitest/mocker": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.1.0", + "ws": "^8.19.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.11" + } + }, + "node_modules/@vitest/browser-playwright": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.11.tgz", + "integrity": "sha512-riLBxPqwnJ0lWs2DN2WeUfYeKLoAjbP2Xx8cLQdSddzMi20sksIa6K2mPz79DyMZKKVKH2ksOC2yJvtNcZg8cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/browser": "4.1.11", + "@vitest/mocker": "4.1.11", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "4.1.11" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": false + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.11", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=7.0.0" + "node": "20 || >=22" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -468,72 +920,29 @@ } } }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } + "license": "MIT" }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, + "license": "Apache-2.0", "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, - "engines": { - "node": ">=6" - } + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -548,83 +957,91 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", + "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -635,6 +1052,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -643,27 +1061,29 @@ } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -676,6 +1096,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -688,85 +1109,93 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } + "license": "MIT" }, "node_modules/ffjavascript": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.3.0.tgz", - "integrity": "sha512-l7sR5kmU3gRwDy8g0Z2tYBXy5ttmafRPFOqY7S6af5cq51JqJWt5eQ/lSR/rs2wQNbDYaYlQr5O+OSUf/oMLoQ==", + "version": "0.3.1", + "resolved": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "integrity": "sha512-9yAz8ZrGRHrZ4i0y0zVZVXWbv0LLFBn30NEW8wKJsWYQ7ntXzzIQH0Pjq5Ir/8aAoM06pVC2R4BzoQ4Sojyukg==", "dev": true, + "license": "GPL-3.0", "dependencies": { + "web-worker": "1.5.0" + }, + "optionalDependencies": { "wasmbuilder": "0.0.16", - "wasmcurves": "0.2.2", - "web-worker": "1.2.0" + "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c" } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { "locate-path": "^6.0.0", @@ -779,26 +1208,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -808,12 +1229,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -828,157 +1243,39 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -988,15 +1285,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1009,97 +1297,50 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } + "license": "ISC" }, - "node_modules/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -1108,182 +1349,325 @@ "node": ">= 0.8.0" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, + "license": "MPL-2.0", "dependencies": { - "p-locate": "^5.0.0" + "detect-libc": "^2.0.3" }, "engines": { - "node": ">=10" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "type": "opencollective", + "url": "https://opencollective.com/parcel" }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "*" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 14.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "*" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=10" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" } }, "node_modules/ms": { @@ -1293,42 +1677,59 @@ "dev": true, "license": "MIT" }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/once": { + "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "dependencies": { - "wrappy": "1" + "license": "MIT" + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -1373,18 +1774,6 @@ "node": ">=6" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1394,186 +1783,166 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/pathval": { + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "engines": { - "node": "*" - } + "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "github", + "url": "https://github.com/sponsors/ai" } - ] - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, + ], + "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/rolldown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "@oxc-project/types": "=0.137.0", + "@rolldown/pluginutils": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "2.80.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", - "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", - "dev": true, - "license": "MIT", "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=10.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/serialize-javascript": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.6.tgz", - "integrity": "sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/shebang-command": { @@ -1581,6 +1950,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1593,83 +1963,143 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "dependencies": { - "is-number": "^7.0.0" + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -1677,62 +2107,231 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "engines": { - "node": ">=4" + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/vite": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", + "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "~1.1.2", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "dev": true, + "license": "MIT", "dependencies": { - "punycode": "^2.1.0" + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, "node_modules/wasmbuilder": { "version": "0.0.16", "resolved": "https://registry.npmjs.org/wasmbuilder/-/wasmbuilder-0.0.16.tgz", "integrity": "sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==", - "dev": true + "dev": true, + "license": "GPL-3.0", + "optional": true }, "node_modules/wasmcurves": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.2.2.tgz", - "integrity": "sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==", + "resolved": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", + "integrity": "sha512-hs7ESAiIMAfP37+pbgN88qlRkEWNOnZwWiwbcrJUC1IY3LOfU/Ss1NGGSPVldDpyIqRQGRQ4pjnK6Zl6zAni0w==", "dev": true, + "license": "GPL-3.0", + "optional": true, "dependencies": { "wasmbuilder": "0.0.16" } }, "node_modules/web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", - "dev": true + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1743,389 +2342,557 @@ "node": ">= 8" } }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "@blazediff/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", + "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", + "dev": true + }, + "@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, - "license": "Apache-2.0" + "optional": true, + "requires": { + "tslib": "^2.4.0" + } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.4.3" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^3.1.4" } }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" + "requires": { + "@eslint/core": "^1.2.1" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" + "requires": { + "@types/json-schema": "^7.0.15" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "requires": {} + }, + "@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true }, - "@eslint-community/eslint-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", - "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "requires": { - "eslint-visitor-keys": "^3.3.0" + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" } }, - "@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.4", - "strip-json-comments": "^3.1.1" + "@humanfs/types": "^0.15.0" } }, - "@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.1.4" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" } }, + "@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true + }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, + "optional": true, "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@tybys/wasm-util": "^0.10.3" } }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "@oxc-project/types": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "dev": true }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "@rolldown/binding-android-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "dev": true, - "requires": {} + "optional": true }, - "ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "@rolldown/binding-darwin-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "dev": true, + "optional": true + }, + "@rolldown/binding-darwin-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "dev": true, + "optional": true + }, + "@rolldown/binding-freebsd-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-arm64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-x64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "dev": true, + "optional": true + }, + "@rolldown/binding-linux-x64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "dev": true, + "optional": true + }, + "@rolldown/binding-openharmony-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "dev": true, + "optional": true + }, + "@rolldown/binding-wasm32-wasi": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "dev": true, + "optional": true, "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" } }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "dev": true, + "optional": true + }, + "@rolldown/binding-win32-x64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "dev": true, + "optional": true + }, + "@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, + "optional": true, "requires": { - "color-convert": "^2.0.1" + "tslib": "^2.4.0" } }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", "dev": true }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true }, - "brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@vitest/browser": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.11.tgz", + "integrity": "sha512-bwMovvAeuTFOK5kIFevw4VEf+1gVEICv4SYK4k3knJOxl6b1zEWud8mYKD73e1B0odAn174h1MofURy2TPWf3w==", "dev": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@blazediff/core": "1.9.1", + "@vitest/mocker": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.1.0", + "ws": "^8.19.0" } }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "@vitest/browser-playwright": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.11.tgz", + "integrity": "sha512-riLBxPqwnJ0lWs2DN2WeUfYeKLoAjbP2Xx8cLQdSddzMi20sksIa6K2mPz79DyMZKKVKH2ksOC2yJvtNcZg8cg==", "dev": true, "requires": { - "fill-range": "^7.1.1" + "@vitest/browser": "4.1.11", + "@vitest/mocker": "4.1.11", + "tinyrainbow": "^3.1.0" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true + "@vitest/expect": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", + "dev": true, + "requires": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + } }, - "chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "@vitest/mocker": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "@vitest/spy": "4.1.11", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" } }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "@vitest/pretty-format": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "requires": { - "check-error": "^1.0.2" + "tinyrainbow": "^3.1.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "@vitest/runner": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" } }, - "check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "@vitest/snapshot": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "requires": { - "get-func-name": "^2.0.2" + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "@vitest/spy": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", + "dev": true + }, + "@vitest/utils": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "color-convert": { + "assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true + }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "requires": { - "color-name": "~1.1.4" + "balanced-match": "^4.0.2" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, "cross-spawn": { @@ -2148,52 +2915,22 @@ "ms": "^2.1.3" } }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true }, "escape-string-regexp": { @@ -2203,49 +2940,41 @@ "dev": true }, "eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", + "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", "minimatch": "^3.1.4", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "dependencies": { "glob-parent": { @@ -2260,36 +2989,38 @@ } }, "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "requires": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true }, "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "requires": { - "acorn": "^8.9.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^5.0.1" } }, "esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -2310,12 +3041,27 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2334,210 +3080,83 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, "ffjavascript": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.3.0.tgz", - "integrity": "sha512-l7sR5kmU3gRwDy8g0Z2tYBXy5ttmafRPFOqY7S6af5cq51JqJWt5eQ/lSR/rs2wQNbDYaYlQr5O+OSUf/oMLoQ==", + "version": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "integrity": "sha512-9yAz8ZrGRHrZ4i0y0zVZVXWbv0LLFBn30NEW8wKJsWYQ7ntXzzIQH0Pjq5Ir/8aAoM06pVC2R4BzoQ4Sojyukg==", "dev": true, + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", "requires": { "wasmbuilder": "0.0.16", - "wasmcurves": "0.2.2", - "web-worker": "1.2.0" + "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", + "web-worker": "1.5.0" } }, "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "requires": { - "to-regex-range": "^5.0.1" + "flat-cache": "^4.0.0" } }, "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" } }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "globals": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", "dev": true }, "ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2547,52 +3166,17 @@ "is-extglob": "^2.1.1" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - } - } + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -2603,9 +3187,18 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2616,38 +3209,119 @@ "type-check": "~0.4.0" } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "detect-libc": "^2.0.3", + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "dev": true, + "optional": true + }, + "lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "dev": true, + "optional": true + }, + "lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "dev": true, + "optional": true + }, + "lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "dev": true, + "optional": true + }, + "lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "dev": true, + "optional": true + }, + "lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "dev": true, + "optional": true + }, + "lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "dev": true, + "optional": true }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "dev": true, + "optional": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "p-locate": "^5.0.0" } }, - "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "requires": { - "get-func-name": "^2.0.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "minimatch": { @@ -2656,98 +3330,14 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.9" } }, - "mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^8.0.3", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^3.1.4", - "ms": "^2.1.3", - "serialize-javascript": "^7.0.5", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.4", - "once": "^1.3.0" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "minimatch": { - "version": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } + "mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true }, "ms": { "version": "2.1.3", @@ -2755,39 +3345,36 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" } }, "p-limit": { @@ -2814,123 +3401,106 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "fsevents": "2.3.2", + "playwright-core": "1.61.1" } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "dev": true }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "requires": { - "glob": "^7.1.3" + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" } }, - "rollup": { - "version": "2.80.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", - "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "rolldown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "requires": { - "queue-microtask": "^1.2.2" + "@oxc-project/types": "=0.137.0", + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3", + "@rolldown/pluginutils": "^1.0.0" } }, - "serialize-javascript": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.6.tgz", - "integrity": "sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==", - "dev": true - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2946,55 +3516,90 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, "requires": { - "ansi-regex": "^5.0.1" + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true + }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true + }, + "tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "dependencies": { + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "requires": {} + } } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "requires": { - "is-number": "^7.0.0" - } + "optional": true }, "type-check": { "version": "0.4.0", @@ -3005,18 +3610,6 @@ "prelude-ls": "^1.2.1" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3026,25 +3619,78 @@ "punycode": "^2.1.0" } }, + "vite": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", + "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", + "dev": true, + "requires": { + "fsevents": "~2.3.3", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.26", + "rolldown": "~1.1.2", + "tinyglobby": "^0.2.17" + }, + "dependencies": { + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + } + } + }, + "vitest": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "dev": true, + "requires": { + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + } + }, "wasmbuilder": { "version": "0.0.16", "resolved": "https://registry.npmjs.org/wasmbuilder/-/wasmbuilder-0.0.16.tgz", "integrity": "sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==", - "dev": true + "dev": true, + "optional": true }, "wasmcurves": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.2.2.tgz", - "integrity": "sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==", + "version": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", + "integrity": "sha512-hs7ESAiIMAfP37+pbgN88qlRkEWNOnZwWiwbcrJUC1IY3LOfU/Ss1NGGSPVldDpyIqRQGRQ4pjnK6Zl6zAni0w==", "dev": true, + "from": "wasmcurves@git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", + "optional": true, "requires": { "wasmbuilder": "0.0.16" } }, "web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", "dev": true }, "which": { @@ -3056,67 +3702,28 @@ "isexe": "^2.0.0" } }, - "workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "siginfo": "^2.0.0", + "stackback": "0.0.2" } }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } + "requires": {} } } } diff --git a/package.json b/package.json index 0861541..a1a8bf3 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,32 @@ "type": "module", "version": "0.0.20", "description": "fast cached read write of big binary files", - "main": "./build/main.cjs", + "main": "./build/node/main.cjs", "module": "./src/fastfile.js", "exports": { - "browser": "./src/fastfile.js", - "import": "./src/fastfile.js", - "require": "./build/main.cjs" - }, - "browser": { - "fs": false, - "constants": "./src/constants.browser.js" + ".": { + "node": { + "import": "./src/fastfile.js", + "require": "./build/node/main.cjs" + }, + "browser": { + "import": "./build/browser/browser.esm.js", + "default": "./build/browser/browser.iife.js" + }, + "default": "./src/fastfile.js" + } }, "scripts": { - "pretest": "eslint .", - "test": "mocha", - "build": "rollup -c rollup.cjs.config.js" + "lint": "eslint .", + "pretest": "npm run build && npm run lint", + "test": "vitest run", + "test:node": "vitest run --project node-esm --project node-cjs", + "test:browser": "vitest run --project browser-esm", + "test:watch": "vitest", + "test:integration": "vitest run test/test.js --testTimeout=1000000000", + "build:node": "vite build --mode node", + "build:browser": "vite build --mode browser", + "build": "npm run build:node && npm run build:browser" }, "keywords": [ "file", @@ -34,16 +45,21 @@ "url": "https://github.com/iden3/fastfile.git" }, "devDependencies": { - "chai": "^4.3.6", - "chai-as-promised": "^7.1.1", - "eslint": "^8.17.0", - "ffjavascript": "^0.3.0", - "mocha": "^10.0.0", - "rollup": "^2.20.0" + "@eslint/js": "^10.0.1", + "@vitest/browser": "^4.1.11", + "@vitest/browser-playwright": "^4.1.11", + "eslint": "^10.1.0", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "globals": "^17.4.0", + "playwright": "^1.59.0", + "vite": "^8.0.3", + "vitest": "^4.1.11" }, "overrides": { "serialize-javascript": "^7.0.5", "diff": "^8.0.3", - "minimatch": "^3.1.4" + "minimatch": "^3.1.4", + "postcss": "^8.5.26", + "brace-expansion": "^5.0.9" } } diff --git a/rollup.cjs.config.js b/rollup.cjs.config.js deleted file mode 100644 index 70cebf2..0000000 --- a/rollup.cjs.config.js +++ /dev/null @@ -1,16 +0,0 @@ -import fs from "fs"; -import { builtinModules as builtin } from "module"; - -const pkg = JSON.parse(fs.readFileSync("./package.json")); - -export default { - input: "./src/fastfile.js", - output: { - file: "build/main.cjs", - format: "cjs", - }, - external: [ - ...Object.keys(pkg.dependencies || {}), - ...builtin, - ] -}; diff --git a/src/constants.browser.js b/src/constants.browser.js deleted file mode 100644 index 26c19e7..0000000 --- a/src/constants.browser.js +++ /dev/null @@ -1,11 +0,0 @@ -// Browser stub for the Node "constants" module, mapped via the package.json -// "browser" field. fastfile.js does a NAMED import of these open-mode flags, so -// the stub must provide them as named exports (an empty `false` stub has none -// and would fail the named import). The values are never used in the browser: -// file operations go through the Node-only OsFile path, while browsers use -// MemFile. They exist only so the import resolves. -export const O_TRUNC = 0; -export const O_CREAT = 0; -export const O_RDWR = 0; -export const O_EXCL = 0; -export const O_RDONLY = 0; diff --git a/src/fastfile.browser.js b/src/fastfile.browser.js new file mode 100644 index 0000000..b7dba5c --- /dev/null +++ b/src/fastfile.browser.js @@ -0,0 +1,55 @@ +import * as memFile from "./memfile.js"; +import * as bigMemFile from "./bigmemfile.js"; +import * as httpFile from "./httpfile.js"; +import * as blobFile from "./blobfile.js"; + +function noFileSupport() { + throw new Error("File I/O is not supported in the browser"); +} + +function normalizeOpts(o) { + if (o instanceof Uint8Array) return { type: "mem", data: o }; + // A string means a file path/name in the Node API. There are no files in + // the browser, so reject it instead of silently creating an anonymous mem + // file (which would mask code copied from the Node README). + if (typeof o === "string") noFileSupport(); + return o; +} + +function dispatchMem(o, memFn, bigMemFn) { + if (o.type === "file") noFileSupport(); + if (o.type === "mem") return memFn(o); + if (o.type === "bigMem") return bigMemFn(o); + throw new Error("Invalid FastFile type: " + o.type); +} + +export function createOverride(o) { + return dispatchMem(normalizeOpts(o), memFile.createNew, bigMemFile.createNew); +} + +export const createNoOverride = createOverride; + +export async function readExisting(o, b, c) { + if (o instanceof Uint8Array) { + o = { type: "mem", data: o }; + } + if (typeof Blob !== "undefined" && o instanceof Blob) { + o = { type: "blob", blob: o, cacheSize: b, pageSize: c }; + } + if (typeof o === "string") { + // URLs (historically fetched whole) go through the http backend: it + // streams via Range requests when the server supports them and falls + // back to buffering the full body when it does not. + o = { type: "http", url: o, cacheSize: b, pageSize: c }; + } + if (o.type === "http") return await httpFile.readExisting(o); + if (o.type === "blob") return blobFile.readExisting(o); + return dispatchMem(o, memFile.readExisting, bigMemFile.readExisting); +} + +export function readWriteExisting(o) { + if (typeof o === "string") noFileSupport(); + return dispatchMem(o, memFile.readWriteExisting, bigMemFile.readWriteExisting); +} + +export const readWriteExistingOrCreate = readWriteExisting; diff --git a/test/__screenshots__/fastfile.browser.test.js/fastfile-browser---mem-readExisting-from-URL-fetches-data-1.png b/test/__screenshots__/fastfile.browser.test.js/fastfile-browser---mem-readExisting-from-URL-fetches-data-1.png new file mode 100644 index 0000000000000000000000000000000000000000..47767d2f3db3e57c7b5cb7d5e6f5d4e0ed04e8ff GIT binary patch literal 2082 zcmeAS@N?(olHy`uVBq!ia0y~yVDx2RV7kD;1QeOwv~@BA1N(JP7srr_Id3jA@-i53 z95DEFXMgI4ja%e?m6tbcF?X2z{K9j~FO__?^PK08GDpKv_u@O4hD6# qC=0CM|NmbQa_}pV!3L`0nHgejI2P=w?qmguFnGH9xvX { - await expect(fastFile.createOverride({ type: "nope" })).to.be.rejectedWith("Invalid FastFile type"); + await expect(fastFile.createOverride({ type: "nope" })).rejects.toThrow("Invalid FastFile type"); expect(() => fastFile.createNoOverride({ type: "nope" })).to.throw("Invalid FastFile type"); - await expect(fastFile.readExisting({ type: "nope" })).to.be.rejectedWith("Invalid FastFile type"); + await expect(fastFile.readExisting({ type: "nope" })).rejects.toThrow("Invalid FastFile type"); expect(() => fastFile.readWriteExisting({ type: "nope" })).to.throw("Invalid FastFile type"); expect(() => fastFile.readWriteExistingOrCreate({ type: "nope" })).to.throw("Invalid FastFile type"); }); @@ -62,7 +58,7 @@ describe("fastfile API surface", function () { await fd.write(new Uint8Array([9]), 0); await fd.close(); // O_EXCL: a second createNoOverride on the same path must fail - await expect(fastFile.createNoOverride(fileName)).to.be.rejected; + await expect(fastFile.createNoOverride(fileName)).rejects.toThrow(); await fs.promises.unlink(fileName); }); @@ -142,8 +138,8 @@ describe("fastfile API surface", function () { assert.deepStrictEqual([...got.slice(4)], [0, 0, 0, 0]); const ro = await fastFile.readExisting({ type: "mem", data: makeData(4) }); - await expect(ro.read(8, 0)).to.be.rejectedWith("Reading out of bounds"); - await expect(ro.write(new Uint8Array(1), 0)).to.be.rejectedWith("read only"); + await expect(ro.read(8, 0)).rejects.toThrow("Reading out of bounds"); + await expect(ro.write(new Uint8Array(1), 0)).rejects.toThrow("read only"); }); it("readString finds terminated strings and returns empty when unterminated", async () => { @@ -160,7 +156,7 @@ describe("fastfile API surface", function () { // read-only fd: reading a string past the end rejects const ro = await fastFile.readExisting({ type: "mem", data: enc.encode("x") }); - await expect(ro.readString(10)).to.be.rejectedWith("Reading out of bounds"); + await expect(ro.readString(10)).rejects.toThrow("Reading out of bounds"); // writable fd: a position past the end resizes and yields "" assert.strictEqual(await fd.readString(1000), ""); @@ -192,8 +188,8 @@ describe("fastfile API surface", function () { it("read-only files reject out-of-bounds reads and writes", async () => { const fd = await fastFile.readExisting({ type: "bigMem", data: [makeData(16)] }); - await expect(fd.read(32, 0)).to.be.rejected; - await expect(fd.write(new Uint8Array(1), 0)).to.be.rejectedWith("read only"); + await expect(fd.read(32, 0)).rejects.toThrow(); + await expect(fd.write(new Uint8Array(1), 0)).rejects.toThrow("read only"); }); it("readString handles long strings, page boundaries and a missing terminator", async () => { @@ -226,7 +222,7 @@ describe("fastfile API surface", function () { await fd.write(new Uint8Array([1, 2, 3]), 0); assert.strictEqual(await fd.readString(100), ""); // exactly at a page boundary the resized data has no next page - await expect(fd.readString(BIGMEM_PAGE)).to.be.rejectedWith("ERROR"); + await expect(fd.readString(BIGMEM_PAGE)).rejects.toThrow("ERROR"); fd.close(); }); }); @@ -299,15 +295,15 @@ describe("fastfile API surface", function () { const fd = await fastFile.createOverride(fileName); await fd.write(makeData(8), 0); await fd.discard(); - await expect(fs.promises.stat(fileName)).to.be.rejected; + await expect(fs.promises.stat(fileName)).rejects.toThrow(); }); it("writing after close fails fast", async () => { const fd = await fastFile.createOverride(fileName); await fd.write(makeData(8), 0); await fd.close(); - await expect(fd.write(makeData(8), 0)).to.be.rejectedWith("Writing a closing file"); - await expect(fd.readString(0)).to.be.rejectedWith("Reading a closing file"); + await expect(fd.write(makeData(8), 0)).rejects.toThrow("Writing a closing file"); + await expect(fd.readString(0)).rejects.toThrow("Reading a closing file"); }); }); @@ -349,13 +345,13 @@ describe("fastfile API surface", function () { it("rejects out-of-bounds reads and every write helper", async () => { const fd = await fastFile.readExisting({ type: "blob", blob: blobOf(makeData(16)) }); - await expect(fd.read(32, 0)).to.be.rejectedWith("Reading out of bounds"); - await expect(fd.write(new Uint8Array(1), 0)).to.be.rejectedWith("read only"); - await expect(fd.writeULE32(1, 0)).to.be.rejectedWith("read only"); - await expect(fd.writeUBE32(1, 0)).to.be.rejectedWith("read only"); - await expect(fd.writeULE64(1, 0)).to.be.rejectedWith("read only"); + await expect(fd.read(32, 0)).rejects.toThrow("Reading out of bounds"); + await expect(fd.write(new Uint8Array(1), 0)).rejects.toThrow("read only"); + await expect(fd.writeULE32(1, 0)).rejects.toThrow("read only"); + await expect(fd.writeUBE32(1, 0)).rejects.toThrow("read only"); + await expect(fd.writeULE64(1, 0)).rejects.toThrow("read only"); await fd.discard(); // close()s; second close via discard is a no-op - await expect(fd.readString(0)).to.be.rejectedWith("Reading a closing file"); + await expect(fd.readString(0)).rejects.toThrow("Reading a closing file"); }); it("surfaces short reads from a lying blob", async () => { @@ -366,7 +362,7 @@ describe("fastfile API surface", function () { } }; const fd = await fastFile.readExisting({ type: "blob", blob: fake }); - await expect(fd.read(50, 0)).to.be.rejectedWith("short blob read"); + await expect(fd.read(50, 0)).rejects.toThrow("short blob read"); // the failed page load was evicted, not cached assert.strictEqual(fd.pages.size, 0); }); @@ -459,7 +455,7 @@ describe("fastfile API surface", function () { it("throws on a failing probe", async () => { const srv = await startServer(makeData(16), "probe-500"); try { - await expect(fastFile.readExisting(srv.url)).to.be.rejectedWith("HTTP 500"); + await expect(fastFile.readExisting(srv.url)).rejects.toThrow("HTTP 500"); } finally { await srv.close(); } }); @@ -486,7 +482,7 @@ describe("fastfile API surface", function () { const srv = await startServer(makeData(1 << 17), "wrong-start"); try { const fd = await fastFile.readExisting(srv.url); - await expect(fd.read(1 << 16, 0)).to.be.rejectedWith("server returned range starting at"); + await expect(fd.read(1 << 16, 0)).rejects.toThrow("server returned range starting at"); fd.close(); } finally { await srv.close(); } }); @@ -495,7 +491,7 @@ describe("fastfile API surface", function () { const srv = await startServer(makeData(1 << 17), "overlong"); try { const fd = await fastFile.readExisting(srv.url); - await expect(fd.read(1 << 16, 0)).to.be.rejectedWith("longer than requested"); + await expect(fd.read(1 << 16, 0)).rejects.toThrow("longer than requested"); fd.close(); } finally { await srv.close(); } }); @@ -504,7 +500,7 @@ describe("fastfile API surface", function () { const srv = await startServer(makeData(1 << 17), "short"); try { const fd = await fastFile.readExisting(srv.url); - await expect(fd.read(1 << 16, 0)).to.be.rejectedWith("short range response"); + await expect(fd.read(1 << 16, 0)).rejects.toThrow("short range response"); fd.close(); } finally { await srv.close(); } }); @@ -513,9 +509,9 @@ describe("fastfile API surface", function () { const srv = await startServer(makeData(1 << 14), "fail-after-probe"); try { const fd = await fastFile.readExisting(srv.url); - await expect(fd.read(4, 0)).to.be.rejectedWith("HTTP 500"); + await expect(fd.read(4, 0)).rejects.toThrow("HTTP 500"); // the rejected page was evicted so a retry issues a new request - await expect(fd.read(4, 0)).to.be.rejectedWith("HTTP 500"); + await expect(fd.read(4, 0)).rejects.toThrow("HTTP 500"); fd.close(); } finally { await srv.close(); } }); diff --git a/test/bigmemfile.test.js b/test/bigmemfile.test.js index 719bb4b..d94885e 100644 --- a/test/bigmemfile.test.js +++ b/test/bigmemfile.test.js @@ -1,16 +1,8 @@ import * as testUtils from "./testUtils.js"; import * as fastFile from "../src/fastfile.js"; - -import assert from "assert"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import { expect } from "vitest"; describe("fastfile testing suite for bigMemFile", function () { - this.timeout(100000); - let str1 = "0123456789"; let str2 = "Hi_there"; let str3 = "/!!--::**"; @@ -29,13 +21,13 @@ describe("fastfile testing suite for bigMemFile", function () { await testUtils.writeStringToFile(fd, str3); let str = await fd.readString((1 << 22) - 9); - assert.strictEqual(str, str1); + expect(str).toBe(str1); str = await fd.readString(); - assert.strictEqual(str, str2); + expect(str).toBe(str2); str = await fd.readString(); - assert.strictEqual(str, str3); + expect(str).toBe(str3); await fd.close(); }); @@ -47,8 +39,8 @@ describe("fastfile testing suite for bigMemFile", function () { byteLength: 1 }] }; - let fd = await fastFile.readExisting(file); - expect(fd.readString(10)).to.be.rejectedWith("Reading out of bounds"); + const fd = await fastFile.readExisting(file); + await expect(fd.readString(10)).rejects.toThrow("Reading out of bounds"); }); }); diff --git a/test/blobfile.test.js b/test/blobfile.test.js index ceb8254..50ec9fc 100644 --- a/test/blobfile.test.js +++ b/test/blobfile.test.js @@ -1,11 +1,8 @@ import * as fastFile from "../src/fastfile.js"; import assert from "assert"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; +import { expect } from "vitest"; -chai.use(chaiAsPromised); -const expect = chai.expect; function makeData(n) { const data = new Uint8Array(n); @@ -20,7 +17,6 @@ function makeData(n) { } describe("fastfile testing suite for blobfile", function () { - this.timeout(100000); it("should read positioned ranges from a Blob", async () => { const data = makeData(1 << 18); @@ -43,8 +39,8 @@ describe("fastfile testing suite for blobfile", function () { it("should reject reads out of bounds and any write", async () => { const fd = await fastFile.readExisting(new Blob([makeData(100)])); - await expect(fd.read(8, 96)).to.be.rejectedWith(/out of bounds/); - await expect(fd.write(new Uint8Array(4), 0)).to.be.rejectedWith(/read only/); + await expect(fd.read(8, 96)).rejects.toThrow(/out of bounds/); + await expect(fd.write(new Uint8Array(4), 0)).rejects.toThrow(/read only/); await fd.close(); }); }); diff --git a/test/fastfile.browser.test.js b/test/fastfile.browser.test.js new file mode 100644 index 0000000..663b3a2 --- /dev/null +++ b/test/fastfile.browser.test.js @@ -0,0 +1,107 @@ +import * as fastFile from "../src/fastfile.browser.js"; +import * as testUtils from "./testUtils.js"; +import { vi, expect } from "vitest"; + +describe("fastfile browser — mem", function () { + const str1 = "0123456789"; + const str2 = "Hi_there"; + const str3 = "/!!--::**"; + + it("should read valid strings from mem file", async () => { + const fd = await fastFile.createOverride({ type: "mem" }); + + await testUtils.writeFakeStringToFile(fd, 10); + await testUtils.writeStringToFile(fd, str1); + await testUtils.writeStringToFile(fd, str2); + await testUtils.writeStringToFile(fd, str3); + + let str = await fd.readString(10); + expect(str).toBe(str1); + + str = await fd.readString(); + expect(str).toBe(str2); + + str = await fd.readString(); + expect(str).toBe(str3); + + await fd.close(); + }); + + it("createNoOverride is an alias for createOverride in browser", async () => { + const fd = await fastFile.createNoOverride({ type: "mem" }); + expect(fd).toBeTruthy(); + await fd.close(); + }); + + it("readExisting from Uint8Array", async () => { + const data = new Uint8Array([10, 20, 30]); + const fd = await fastFile.readExisting(data); + const result = await fd.read(3, 0); + expect([...result]).toEqual([10, 20, 30]); + await fd.close(); + }); + + it("readExisting from URL fetches data", async () => { + const mockData = new Uint8Array([1, 2, 3]); + // A server that ignores Range and sends the whole body (the http + // backend probes with Range: bytes=0-0 and falls back to buffering). + vi.stubGlobal("fetch", () => + Promise.resolve({ + ok: true, + status: 200, + arrayBuffer: () => Promise.resolve(mockData.buffer.slice(0)), + }) + ); + try { + const fd = await fastFile.readExisting("https://example.com/test.bin"); + const result = await fd.read(3, 0); + expect([...result]).toEqual([1, 2, 3]); + await fd.close(); + } finally { + vi.unstubAllGlobals(); + } + }); + + it("throws when using file type", () => { + expect(() => fastFile.createOverride({ type: "file", fileName: "test" })) + .toThrow("not supported in the browser"); + }); + + it("readWriteExistingOrCreate is an alias for readWriteExisting", async () => { + const o = { type: "mem", data: new Uint8Array([5, 6, 7]) }; + const fd = await fastFile.readWriteExistingOrCreate(o); + expect(fd).toBeTruthy(); + await fd.close(); + }); +}); + +describe("fastfile browser — bigMem", function () { + const str1 = "0123456789"; + const str2 = "Hi_there"; + const str3 = "/!!--::**"; + + it("should read valid strings from bigMem file", async () => { + const fd = await fastFile.createOverride({ type: "bigMem" }); + + await testUtils.writeFakeStringToFile(fd, 10); + await testUtils.writeStringToFile(fd, str1); + await testUtils.writeStringToFile(fd, str2); + await testUtils.writeStringToFile(fd, str3); + + let str = await fd.readString(10); + expect(str).toBe(str1); + + str = await fd.readString(); + expect(str).toBe(str2); + + str = await fd.readString(); + expect(str).toBe(str3); + + await fd.close(); + }); + + it("throws for file type on bigMem project", () => { + expect(() => fastFile.readWriteExisting({ type: "file" })) + .toThrow("not supported in the browser"); + }); +}); diff --git a/test/httpfile.test.js b/test/httpfile.test.js index df9ffaf..5df4280 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -2,11 +2,8 @@ import * as fastFile from "../src/fastfile.js"; import http from "http"; import assert from "assert"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; +import { expect } from "vitest"; -chai.use(chaiAsPromised); -const expect = chai.expect; // Deterministic pseudo-random content so failures are reproducible. function makeData(n) { @@ -79,7 +76,6 @@ function startServer(state, opts) { } describe("fastfile testing suite for httpfile", function () { - this.timeout(100000); it("should read ranges without ever downloading the full file", async () => { const data = makeData(1 << 20); // 1 MiB @@ -176,7 +172,7 @@ describe("fastfile testing suite for httpfile", function () { // direct (uncached) read must reject, not silently mix versions await expect(fd.readToBuffer(new Uint8Array(1 << 16), 0, 1 << 16, 1 << 17)) - .to.be.rejectedWith(/file changed/); + .rejects.toThrow(/file changed/); await fd.close(); } finally { await srv.close(); @@ -188,9 +184,9 @@ describe("fastfile testing suite for httpfile", function () { const srv = await startServer({ data: data, etag: "\"v1\"" }); try { const fd = await fastFile.readExisting(srv.url); - await expect(fd.read(16, 992)).to.be.rejectedWith(/out of bounds/); - await expect(fd.write(new Uint8Array(4), 0)).to.be.rejectedWith(/read only/); - await expect(fd.writeULE32(1, 0)).to.be.rejectedWith(/read only/); + await expect(fd.read(16, 992)).rejects.toThrow(/out of bounds/); + await expect(fd.write(new Uint8Array(4), 0)).rejects.toThrow(/read only/); + await expect(fd.writeULE32(1, 0)).rejects.toThrow(/read only/); await fd.close(); } finally { await srv.close(); @@ -281,7 +277,7 @@ describe("fastfile testing suite for httpfile", function () { const url = "http://127.0.0.1:" + server.address().port + "/data.bin"; try { const fd = await fastFile.readExisting(url); - await expect(fd.read(64, 0)).to.be.rejectedWith(/file changed/); + await expect(fd.read(64, 0)).rejects.toThrow(/file changed/); await fd.close(); } finally { await new Promise((r) => server.close(r)); diff --git a/test/memfile.test.js b/test/memfile.test.js index 89c780a..67985db 100644 --- a/test/memfile.test.js +++ b/test/memfile.test.js @@ -1,16 +1,8 @@ import * as testUtils from "./testUtils.js"; import * as fastFile from "../src/fastfile.js"; - -import assert from "assert"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import { expect } from "vitest"; describe("fastfile testing suite for memfile", function () { - this.timeout(100000); - let str1 = "0123456789"; let str2 = "Hi_there"; let str3 = "/!!--::**"; @@ -29,13 +21,13 @@ describe("fastfile testing suite for memfile", function () { await testUtils.writeStringToFile(fd, str3); let str = await fd.readString(10); - assert.strictEqual(str, str1); + expect(str).toBe(str1); str = await fd.readString(); - assert.strictEqual(str, str2); + expect(str).toBe(str2); str = await fd.readString(); - assert.strictEqual(str, str3); + expect(str).toBe(str3); await fd.close(); }); @@ -47,8 +39,8 @@ describe("fastfile testing suite for memfile", function () { byteLength: 1 } }; - let fd = await fastFile.readExisting(file); - expect(fd.readString(10)).to.be.rejectedWith("Reading out of bounds"); + const fd = await fastFile.readExisting(file); + await expect(fd.readString(10)).rejects.toThrow("Reading out of bounds"); }); }); diff --git a/test/osfile.test.js b/test/osfile.test.js index bb9e201..603152d 100644 --- a/test/osfile.test.js +++ b/test/osfile.test.js @@ -1,21 +1,11 @@ import fs from "fs"; import * as testUtils from "./testUtils.js"; import * as fastFile from "../src/fastfile.js"; - import { BigBuffer } from "ffjavascript"; - -import assert from "assert"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import { expect, assert } from "vitest"; describe("fastfile testing suite for osfile", function () { let fileName = "test_osfile.bin"; - - this.timeout(100000); - let str1 = "0123456789"; let str2 = "Hi_there"; let str3 = "/!!--::**"; @@ -30,25 +20,23 @@ describe("fastfile testing suite for osfile", function () { await testUtils.writeStringToFile(fd, str3); let str = await fd.readString(fd.pageSize - 11); - assert.strictEqual(str, str1); + expect(str).toBe(str1); str = await fd.readString(); - assert.strictEqual(str, str2); + expect(str).toBe(str2); str = await fd.readString(); - assert.strictEqual(str, str3); + expect(str).toBe(str3); await fd.close(); - assert(fs.existsSync(fileName)); - await fs.promises.unlink(fileName); }); it("should throw error when try to read a closed file", async () => { - let fd = await fastFile.createOverride(fileName); + const fd = await fastFile.createOverride(fileName); await fd.close(); - expect(fd.readString(0)).to.be.rejectedWith("Reading a closing file"); + await expect(fd.readString(0)).rejects.toThrow("Reading a closing file"); await fs.promises.unlink(fileName); }); @@ -67,12 +55,12 @@ describe("fastfile testing suite for osfile", function () { await fd.close(); fd = await fastFile.readExisting(fileName); - assert(!ArrayBuffer.isView(new BigBuffer(8)), "BigBuffer must not be an ArrayBufferView"); + expect(ArrayBuffer.isView(new BigBuffer(8))).toBe(false); const bb = new BigBuffer(N); await fd.readToBuffer(bb, 0, N, 0); await fd.close(); - assert(Buffer.from(bb.slice(0, N)).equals(Buffer.from(ref)), "BigBuffer read does not match file"); + expect(Buffer.from(bb.slice(0, N)).equals(Buffer.from(ref))).toBe(true); await fs.promises.unlink(fileName); }); @@ -88,8 +76,8 @@ describe("fastfile testing suite for osfile", function () { await fd.close(); const onDisk = fs.readFileSync(fileName); - assert.strictEqual(onDisk.length, N); - assert(onDisk.equals(Buffer.from(ref)), "BigBuffer write does not match disk"); + expect(onDisk.length).toBe(N); + expect(onDisk.equals(Buffer.from(ref))).toBe(true); await fs.promises.unlink(fileName); }); @@ -169,7 +157,7 @@ describe("fastfile testing suite for osfile", function () { await fd.close(); // I/O after close keeps failing fast - await expect(fd.read(4, 0)).to.be.rejectedWith("Reading a closing file"); + await expect(fd.read(4, 0)).rejects.toThrow("Reading a closing file"); await fs.promises.unlink(fileName); }); @@ -181,10 +169,10 @@ describe("fastfile testing suite for osfile", function () { fd.fd.write = () => Promise.reject(new Error("simulated ENOSPC")); await fd.write(new Uint8Array(64).fill(1), 0); // dirty page; flush will fail - await expect(fd.close()).to.be.rejectedWith("simulated ENOSPC"); + await expect(fd.close()).rejects.toThrow("simulated ENOSPC"); // the repeated close reports the same failure instead of throwing // "Closing the file twice" - await expect(fd.close()).to.be.rejectedWith("simulated ENOSPC"); + await expect(fd.close()).rejects.toThrow("simulated ENOSPC"); fd.fd.write = realWrite; await fs.promises.unlink(fileName); @@ -199,14 +187,10 @@ describe("fastfile testing suite for osfile", function () { await fd.write(new Uint8Array(64).fill(1), 0); // dirty page; background flush fails await new Promise((resolve) => setTimeout(resolve, 50)); - await expect(fd.write(new Uint8Array(64).fill(2), 4096)).to.be.rejectedWith("simulated ENOSPC"); - await expect(fd.close()).to.be.rejectedWith("simulated ENOSPC"); + await expect(fd.write(new Uint8Array(64).fill(2), 4096)).rejects.toThrow("simulated ENOSPC"); + await expect(fd.close()).rejects.toThrow("simulated ENOSPC"); fd.fd.write = realWrite; // let the underlying fd close cleanly await fs.promises.unlink(fileName); }); }); - - - - diff --git a/test/test.js b/test/test.js index 38da971..7195aaa 100644 --- a/test/test.js +++ b/test/test.js @@ -1,4 +1,4 @@ -import assert from "assert"; +import { expect } from "vitest"; import fs from "fs"; import * as fastFile from "../src/fastfile.js"; @@ -20,14 +20,13 @@ async function readBigInt(f, pos) { const n8 = 32; const buff = await f.read(n8, pos); - assert(buff.byteLength == n8); + expect(buff.byteLength).toBe(n8); return Scalar.fromRprLE(buff, 0); } describe("fastfile test", function () { let fileName = "test.bin"; - this.timeout(1000000000); const values = {}; it("should write a big file sequentially", async () => { @@ -43,40 +42,15 @@ describe("fastfile test", function () { }); it("should fail if trying to override", async () => { - let throwed = false; - try { - await fastFile.createNoOverride(fileName); - } catch (err) { - throwed = true; - } - assert(throwed == true); + await expect(fastFile.createNoOverride(fileName)).rejects.toThrow(); }); it("trying to write a readonly File", async () => { - let throwed = false; - try { + await expect(async () => { const f = await fastFile.readExisting(fileName); await writeBigInt(f, Scalar.add(q,3), 0); await f.close(); - } catch (err) { - throwed = true; - } - assert(throwed == true); - }); - - it("should read the file", async () => { - const f = await fastFile.readExisting(fileName); - for (let i=0; i<1000000; i++) { - const n = await readBigInt(f, i*32); - if (Scalar.sub(n, q) != i) { - console.log(f); - console.log(Scalar.sub(n, q)); - console.log(i); - } - assert(Scalar.sub(n, q) == i); - if ((i%100000) == 0) console.log("Reading: " + i); - } - await f.close(); + }).rejects.toThrow(); }); it("should read the file", async () => { @@ -88,7 +62,7 @@ describe("fastfile test", function () { console.log(Scalar.sub(n, q)); console.log(i); } - assert(Scalar.sub(n, q) == i); + expect(Scalar.sub(n, q) == i).toBe(true); if ((i%100000) == 0) console.log("Reading: " + i); } await f.close(); @@ -106,7 +80,7 @@ describe("fastfile test", function () { } else { expectedOldVal = Scalar.add(q,j); } - assert(Scalar.eq(expectedOldVal, oldVal)); + expect(Scalar.eq(expectedOldVal, oldVal)).toBe(true); const newVal = F.random(); values[j] = newVal; // console.log("Start Writing", j); @@ -137,7 +111,7 @@ describe("fastfile test", function () { const vals = await Promise.all(ops); for (let i=0; i resolve(root, ...parts); + +export default defineConfig(({ mode }) => { + const build = mode === "browser" + ? { + lib: { + entry: "./src/fastfile.browser.js", + name: "fastfile", + formats: ["es", "iife"], + fileName: (format) => format === "es" ? "browser.esm.js" : "browser.iife.js", + }, + outDir: "build/browser", + emptyOutDir: true, + } + : { + lib: { + entry: "./src/fastfile.js", + formats: ["cjs"], + fileName: () => "main.cjs", + }, + outDir: "build/node", + emptyOutDir: true, + rollupOptions: { + external: builtinModules, + }, + }; + + return { + build, + test: { + projects: [ + { + // Node — ESM source + test: { + name: "node-esm", + include: ["test/**/*.test.js"], + exclude: ["test/fastfile.browser.test.js"], + environment: "node", + globals: true, + testTimeout: 100_000, + }, + }, + { + // Node — CJS build + test: { + name: "node-cjs", + include: [ + "test/memfile.test.js", + "test/bigmemfile.test.js", + ], + environment: "node", + globals: true, + testTimeout: 100_000, + }, + resolve: { + alias: [ + { + find: abs("src/fastfile.js"), + replacement: abs("build/node/main.cjs"), + }, + ], + }, + }, + { + // Browser — ESM source inside real Chromium via Playwright + test: { + name: "browser-esm", + include: ["test/fastfile.browser.test.js"], + globals: true, + testTimeout: 100_000, + hookTimeout: 100_000, + browser: { + provider: playwright(), + enabled: true, + headless: true, + instances: [{ browser: "chromium" }], + }, + }, + }, + ], + }, + }; +}); From 748772be5a736f1fe25ffa703db0c1f41df280c4 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Wed, 26 Aug 2026 18:32:18 +0100 Subject: [PATCH 18/41] chore: re-pin ffjavascript (sequential vitest files) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33c522a..00943b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", - "integrity": "sha512-9yAz8ZrGRHrZ4i0y0zVZVXWbv0LLFBn30NEW8wKJsWYQ7ntXzzIQH0Pjq5Ir/8aAoM06pVC2R4BzoQ4Sojyukg==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", + "integrity": "sha512-9jx1R/lKRldaIqF4C+I8yog1lLgqdw8cqcAsCcQPwriFH6eoZInHgohDUD+OnTmUpcCzOPwdQm0TiypnfdBMeQ==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", - "integrity": "sha512-9yAz8ZrGRHrZ4i0y0zVZVXWbv0LLFBn30NEW8wKJsWYQ7ntXzzIQH0Pjq5Ir/8aAoM06pVC2R4BzoQ4Sojyukg==", + "version": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", + "integrity": "sha512-9jx1R/lKRldaIqF4C+I8yog1lLgqdw8cqcAsCcQPwriFH6eoZInHgohDUD+OnTmUpcCzOPwdQm0TiypnfdBMeQ==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", diff --git a/package.json b/package.json index a1a8bf3..f1c7390 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4ac1cba04d29eb7130aa2d09312e5779a28a62d4", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From e74f95dff5c12fcc65604cfead7508d51c1e29e5 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 00:39:26 +0100 Subject: [PATCH 19/41] chore: re-pin ffjavascript (CI-hang fix: lockdown harness explicit exit) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00943b4..1ae58ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", - "integrity": "sha512-9jx1R/lKRldaIqF4C+I8yog1lLgqdw8cqcAsCcQPwriFH6eoZInHgohDUD+OnTmUpcCzOPwdQm0TiypnfdBMeQ==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", + "integrity": "sha512-wte9SIcbI0Y+lo4ZqElgF2+2wQAH7QGhKCmdUb08CO5WsKUPVXXBRABlLpOk2yCRXw148P7ka9QZL/Pw+gMlCw==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", - "integrity": "sha512-9jx1R/lKRldaIqF4C+I8yog1lLgqdw8cqcAsCcQPwriFH6eoZInHgohDUD+OnTmUpcCzOPwdQm0TiypnfdBMeQ==", + "version": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", + "integrity": "sha512-wte9SIcbI0Y+lo4ZqElgF2+2wQAH7QGhKCmdUb08CO5WsKUPVXXBRABlLpOk2yCRXw148P7ka9QZL/Pw+gMlCw==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", diff --git a/package.json b/package.json index f1c7390..e60532f 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#2c20f300b86bd45abb11e7439603b013c8f3e62d", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From 38ffb38efdf3397b538ef46c670422dce723a819 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 12:18:21 +0100 Subject: [PATCH 20/41] chore: re-pin ffjavascript (wasmcurves re-pin cascade) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 26 +++++++++++++------------- package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ae58ca..6b9514e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", - "integrity": "sha512-wte9SIcbI0Y+lo4ZqElgF2+2wQAH7QGhKCmdUb08CO5WsKUPVXXBRABlLpOk2yCRXw148P7ka9QZL/Pw+gMlCw==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", + "integrity": "sha512-2fq+VW5ZBEv2KzDOvVY4GPAXs64RimqGTMRTLgllTepG1tFVMaFPmKqPMh6nfWuk20bwZJ7y/K0l6angFRSwTg==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -1176,7 +1176,7 @@ }, "optionalDependencies": { "wasmbuilder": "0.0.16", - "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c" + "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536" } }, "node_modules/file-entry-cache": { @@ -2310,8 +2310,8 @@ }, "node_modules/wasmcurves": { "version": "0.2.2", - "resolved": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", - "integrity": "sha512-hs7ESAiIMAfP37+pbgN88qlRkEWNOnZwWiwbcrJUC1IY3LOfU/Ss1NGGSPVldDpyIqRQGRQ4pjnK6Zl6zAni0w==", + "resolved": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", + "integrity": "sha512-bhCOIMIwZ10pBc2R4v9ruAkmDCgeWsAORe1/ibnYRNh1qQNHhfAW+ondNc0+ZiJJJ6Sq2KQ7/hBbwYr35EbeMQ==", "dev": true, "license": "GPL-3.0", "optional": true, @@ -3081,13 +3081,13 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", - "integrity": "sha512-wte9SIcbI0Y+lo4ZqElgF2+2wQAH7QGhKCmdUb08CO5WsKUPVXXBRABlLpOk2yCRXw148P7ka9QZL/Pw+gMlCw==", + "version": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", + "integrity": "sha512-2fq+VW5ZBEv2KzDOvVY4GPAXs64RimqGTMRTLgllTepG1tFVMaFPmKqPMh6nfWuk20bwZJ7y/K0l6angFRSwTg==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", "requires": { "wasmbuilder": "0.0.16", - "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", + "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", "web-worker": "1.5.0" } }, @@ -3678,10 +3678,10 @@ "optional": true }, "wasmcurves": { - "version": "git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", - "integrity": "sha512-hs7ESAiIMAfP37+pbgN88qlRkEWNOnZwWiwbcrJUC1IY3LOfU/Ss1NGGSPVldDpyIqRQGRQ4pjnK6Zl6zAni0w==", + "version": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", + "integrity": "sha512-bhCOIMIwZ10pBc2R4v9ruAkmDCgeWsAORe1/ibnYRNh1qQNHhfAW+ondNc0+ZiJJJ6Sq2KQ7/hBbwYr35EbeMQ==", "dev": true, - "from": "wasmcurves@git+https://github.com/iden3/wasmcurves.git#b83021fb6ad70661ba6333c4137a1cf644b8a57c", + "from": "wasmcurves@git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", "optional": true, "requires": { "wasmbuilder": "0.0.16" diff --git a/package.json b/package.json index e60532f..c0245bf 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#a6abbfe62f9e884449a08fcb2e17e1798cb2d9cc", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From 35cdf64900e0b28f3b9798c2814aa464ad7f653f Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 12:39:15 +0100 Subject: [PATCH 21/41] chore: drop committed vitest browser artifacts and ignore them .vitest-attachments/ and test/__screenshots__/ are debris from a failing browser-test run that git add -A swept into the migration commit; with no "files" field they would ship into every consumer's node_modules on a git install. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- .gitignore | 4 ++++ .../0baf8651c53057a5979ec541fda0458ee631238a.png | Bin 2082 -> 0 bytes ...-mem-readExisting-from-URL-fetches-data-1.png | Bin 2082 -> 0 bytes 3 files changed, 4 insertions(+) delete mode 100644 .vitest-attachments/0baf8651c53057a5979ec541fda0458ee631238a.png delete mode 100644 test/__screenshots__/fastfile.browser.test.js/fastfile-browser---mem-readExisting-from-URL-fetches-data-1.png diff --git a/.gitignore b/.gitignore index 9e7d574..03111aa 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,7 @@ tmp # Workspace files are user-specific *.sublime-workspace + +# vitest browser-mode artifacts (failure screenshots, attachments) +.vitest-attachments/ +test/__screenshots__/ diff --git a/.vitest-attachments/0baf8651c53057a5979ec541fda0458ee631238a.png b/.vitest-attachments/0baf8651c53057a5979ec541fda0458ee631238a.png deleted file mode 100644 index 47767d2f3db3e57c7b5cb7d5e6f5d4e0ed04e8ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2082 zcmeAS@N?(olHy`uVBq!ia0y~yVDx2RV7kD;1QeOwv~@BA1N(JP7srr_Id3jA@-i53 z95DEFXMgI4ja%e?m6tbcF?X2z{K9j~FO__?^PK08GDpKv_u@O4hD6# qC=0CM|NmbQa_}pV!3L`0nHgejI2P=w?qmguFnGH9xvXv_u@O4hD6# qC=0CM|NmbQa_}pV!3L`0nHgejI2P=w?qmguFnGH9xvX Date: Thu, 27 Aug 2026 12:39:15 +0100 Subject: [PATCH 22/41] ci: test on lts/-1 as well Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fd5ddc..6f3d14e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [lts/*] + node-version: [lts/*, lts/-1] steps: - name: Checkout project From 1f8d3d1939a699b9b46db9823bd0b1405b390418 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 12:41:28 +0100 Subject: [PATCH 23/41] chore: re-pin ffjavascript (wasmcurves lts/-1 cascade) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 26 +++++++++++++------------- package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b9514e..a98d4bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", - "integrity": "sha512-2fq+VW5ZBEv2KzDOvVY4GPAXs64RimqGTMRTLgllTepG1tFVMaFPmKqPMh6nfWuk20bwZJ7y/K0l6angFRSwTg==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", + "integrity": "sha512-P0/ckQOG105ejjdHZIIKbgRV42wP4PKKQBo1rxkCmK/ZWu+DhAuTvPfuFFZm4VxyQdlYNyxFjuduZKDKzyGYng==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -1176,7 +1176,7 @@ }, "optionalDependencies": { "wasmbuilder": "0.0.16", - "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536" + "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98" } }, "node_modules/file-entry-cache": { @@ -2310,8 +2310,8 @@ }, "node_modules/wasmcurves": { "version": "0.2.2", - "resolved": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", - "integrity": "sha512-bhCOIMIwZ10pBc2R4v9ruAkmDCgeWsAORe1/ibnYRNh1qQNHhfAW+ondNc0+ZiJJJ6Sq2KQ7/hBbwYr35EbeMQ==", + "resolved": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", + "integrity": "sha512-HYvEBTL7NaGyLvhCT3as3/SIFWloyB+4iUy4wt3OSwTgK7jQXNRiXcHXsUwSCtLDPOnU9qX/gI+EKb5/UFyw4g==", "dev": true, "license": "GPL-3.0", "optional": true, @@ -3081,13 +3081,13 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", - "integrity": "sha512-2fq+VW5ZBEv2KzDOvVY4GPAXs64RimqGTMRTLgllTepG1tFVMaFPmKqPMh6nfWuk20bwZJ7y/K0l6angFRSwTg==", + "version": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", + "integrity": "sha512-P0/ckQOG105ejjdHZIIKbgRV42wP4PKKQBo1rxkCmK/ZWu+DhAuTvPfuFFZm4VxyQdlYNyxFjuduZKDKzyGYng==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", "requires": { "wasmbuilder": "0.0.16", - "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", + "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", "web-worker": "1.5.0" } }, @@ -3678,10 +3678,10 @@ "optional": true }, "wasmcurves": { - "version": "git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", - "integrity": "sha512-bhCOIMIwZ10pBc2R4v9ruAkmDCgeWsAORe1/ibnYRNh1qQNHhfAW+ondNc0+ZiJJJ6Sq2KQ7/hBbwYr35EbeMQ==", + "version": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", + "integrity": "sha512-HYvEBTL7NaGyLvhCT3as3/SIFWloyB+4iUy4wt3OSwTgK7jQXNRiXcHXsUwSCtLDPOnU9qX/gI+EKb5/UFyw4g==", "dev": true, - "from": "wasmcurves@git+https://github.com/iden3/wasmcurves.git#c380d6cfab309edd34bd940d7f7c3d25f1cc7536", + "from": "wasmcurves@git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", "optional": true, "requires": { "wasmbuilder": "0.0.16" diff --git a/package.json b/package.json index c0245bf..0df1384 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#5475db8191bc7d18c6201b030445a8c6a9aa797f", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From ad59e79d25665c55514975c5e6626311e8088b12 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 12:45:03 +0100 Subject: [PATCH 24/41] test: cover the Blob streaming path in the real-browser suite The Chromium suite verified URL fetching but not Blob reads; the blob backend (page-cached small reads + direct large slices) is the primary browser use case, so exercise both read shapes against a 64 KiB Blob. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- test/fastfile.browser.test.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/fastfile.browser.test.js b/test/fastfile.browser.test.js index 663b3a2..e8d9446 100644 --- a/test/fastfile.browser.test.js +++ b/test/fastfile.browser.test.js @@ -41,6 +41,19 @@ describe("fastfile browser — mem", function () { await fd.close(); }); + it("readExisting from a Blob streams through the blob backend", async () => { + const bytes = new Uint8Array(64 * 1024); + for (let i = 0; i < bytes.length; i++) bytes[i] = i & 0xff; + const fd = await fastFile.readExisting(new Blob([bytes])); + expect(fd.totalSize).toBe(bytes.length); + // small read (page-cached) and large read (direct slice) both match + const head = await fd.read(16, 0); + expect([...head]).toEqual([...bytes.subarray(0, 16)]); + const tail = await fd.read(32 * 1024, 32 * 1024); + expect([...tail]).toEqual([...bytes.subarray(32 * 1024)]); + await fd.close(); + }); + it("readExisting from URL fetches data", async () => { const mockData = new Uint8Array([1, 2, 3]); // A server that ignores Range and sends the whole body (the http From e82f4522bd9aaae8d4c4f2ea5607f399dda97ee1 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 13:09:22 +0100 Subject: [PATCH 25/41] docs: describe capabilities and per-backend usage; update copyright year README now covers the page cache, the five backends (file/mem/bigMem/ http/blob) with examples for each, the API surface, browser resolution via the exports map, and idempotent close. Copyright updated to 2018-2026. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- README.md | 152 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 4efaf3d..ee5916b 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,151 @@ # fastfile -fastfile is a package to read/write binary files with a transparent cache. +fastfile is a package to read/write binary files through one small async API +backed by a transparent page cache. The same `FastFile` interface works over +several backends, so code written against it (for example the snarkjs +zkey/ptau readers) runs unchanged whether the data lives on disk, in memory, +behind an HTTP URL, or inside a browser `Blob`. + +## Capabilities + +- **Page-cached reads and writes** — small reads (headers, section tables, + `readULE32`-style scans) are served from cached pages; writes are cached and + flushed asynchronously, so sequential access to big files stays fast. +- **Multiple backends behind one API**: + + | Backend | Type tag | Where | What it is for | + |---|---|---|---| + | OS file | `file` | Node | Regular files, with direct-I/O fast paths for large aligned reads/writes | + | Memory | `mem` | Node + browser | A `Uint8Array` you already have, or a scratch file that never touches disk | + | Big memory | `bigMem` | Node + browser | Like `mem`, but paged so it can hold buffers past the V8 single-allocation limits | + | HTTP(S) | `http` | Node + browser | Read-only streaming over `Range` requests; falls back to buffering the whole body when the server does not support ranges | + | Blob | `blob` | Browser (and Node ≥ 18) | Read-only zero-copy reads from a `Blob`/`File` (e.g. ``), so multi-GB files stream chunk-by-chunk | + +- **Bounded memory on huge remote files** — with the `http` and `blob` + backends, large reads stream straight into the caller's buffer and only the + small-read page cache is retained; the whole file is never resident. +- **Node/browser split at package level** — the `exports` map gives Node the + filesystem implementation and gives bundlers a browser build with the + file backend removed (`mem`, `bigMem`, `http` and `blob` remain). + +## Install -This library maintains a read and write cache that allows to write asynchronously -improving the speed of the reads and writes of big files. +``` +npm install fastfile +``` + +## API + +All entry points return a promise of a `FastFile` handle: + +- `createOverride(o, cacheSize?, pageSize?)` — create a file, truncating any + existing one. +- `createNoOverride(o, cacheSize?, pageSize?)` — create a file, failing if it + already exists (Node only distinction; alias of `createOverride` in the + browser). +- `readExisting(o, cacheSize?, pageSize?)` — open read-only. Accepts the most + input shapes (see the examples). +- `readWriteExisting(o, cacheSize?, pageSize?)` — open an existing file + read/write. +- `readWriteExistingOrCreate(o, cacheSize?, pageSize?)` — like the above but + creates the file when missing. + +`o` is a file name/URL string, a `Uint8Array`, a `Blob`, or an options object +with a `type` tag from the table above. `cacheSize` (default 64 KiB) and +`pageSize` (default 8 KiB) tune the page cache. -## Usage +The returned handle offers, among others: ``` -npm install fastfile +read(len, pos) readToBuffer(buff, offset, len, pos) +write(buff, pos) readString(pos) +readULE32/readULE64/readUBE32(pos) +writeULE32/writeULE64/writeUBE32(v, pos) +totalSize close() discard() ``` -```javascript +Reads return `Uint8Array`s. `close()` flushes pending writes and is +idempotent (repeat calls return the same promise instead of throwing). -const fastFile = require("fastfile"); +## Examples -async function writeFile() { - const f = await fastFile.createOverride("pattern.bin"); +### OS files (Node) - const buff = Buffer.from("0001020304050607", "hex"); - for (let i=0; i<1000; i++) { - await f.write(buff, i*8); - } +```javascript +import * as fastFile from "fastfile"; - await f.close(); +const f = await fastFile.createOverride("pattern.bin"); +const buff = Buffer.from("0001020304050607", "hex"); +for (let i = 0; i < 1000; i++) { + await f.write(buff, i * 8); } +await f.close(); -async function readFile() { - const f = await fastFile.readExisting("pattern.bin"); +const r = await fastFile.readExisting("pattern.bin"); +const bytes = await r.read(16, 8); // 16 bytes from offset 8 +await r.close(); +``` - const buff = await f.read(16, 8); +From CommonJS use `const fastFile = require("fastfile");` — the `require` +condition resolves to the CJS build. - await f.close(); +### Memory - return buff; +```javascript +// Wrap bytes you already have (zero-copy): +const f1 = await fastFile.readExisting(new Uint8Array([1, 2, 3, 4])); -} +// Scratch file that never touches disk: +const f2 = await fastFile.createOverride({ type: "mem" }); +await f2.writeULE32(42, 0); + +// Beyond the single-allocation limit (paged): +const f3 = await fastFile.createOverride({ type: "bigMem" }); +``` + +### HTTP(S) with Range streaming -writeFile().then( () => { - readFile().then( (buff) => { - console.log(buff.toString("hex")); - }); +```javascript +// Works in Node and in the browser. Probes the server with +// `Range: bytes=0-0`: when ranges are supported, every read becomes a range +// request and large reads stream directly into your buffer -- the file is +// never fully resident. When the server ignores Range, the body is buffered +// once (the historical behavior). +const f = await fastFile.readExisting("https://example.com/circuit_final.zkey"); +console.log(f.totalSize); +const header = await f.read(32, 0); +await f.close(); + +// Explicit form with cache tuning: +const g = await fastFile.readExisting({ + type: "http", + url: "https://example.com/powersOfTau28_hez_final_10.ptau", + cacheSize: 1 << 20, + pageSize: 1 << 16, }); +``` + +### Blob / File (browser) +```javascript +// e.g. a multi-GB zkey selected by the user; blob.slice() is zero-copy, so +// bytes reach memory only as they are read. +document.querySelector("input[type=file]").addEventListener("change", async (e) => { + const f = await fastFile.readExisting(e.target.files[0]); + const magic = await f.read(4, 0); + await f.close(); +}); ``` -## License +### In the browser + +Bundlers pick the browser build automatically via the package `exports`. +String inputs to `readExisting` are treated as URLs (there is no filesystem); +writable files must be `mem`/`bigMem`. The file backend throws +`"File I/O is not supported in the browser"`. -fastfile is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details. +## License +fastfile is part of the iden3 project copyright 2018-2026 0KIMS association +and published with GPL-3 license. Please check the COPYING file for more +details. From 87d38de72cc711819c1480924069af68ca247834 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 13:09:26 +0100 Subject: [PATCH 26/41] chore: re-pin ffjavascript (worker unref-on-terminate fix) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index a98d4bf..0dd2e26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", - "integrity": "sha512-P0/ckQOG105ejjdHZIIKbgRV42wP4PKKQBo1rxkCmK/ZWu+DhAuTvPfuFFZm4VxyQdlYNyxFjuduZKDKzyGYng==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", + "integrity": "sha512-P0zFCXFy/EZdYCtB19pTmngyTske2pgpiY6FaAVr68tH7UgWXJrpqyto1USd4fGtaWBT/MJ+Ah48D1mk9/407A==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", - "integrity": "sha512-P0/ckQOG105ejjdHZIIKbgRV42wP4PKKQBo1rxkCmK/ZWu+DhAuTvPfuFFZm4VxyQdlYNyxFjuduZKDKzyGYng==", + "version": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", + "integrity": "sha512-P0zFCXFy/EZdYCtB19pTmngyTske2pgpiY6FaAVr68tH7UgWXJrpqyto1USd4fGtaWBT/MJ+Ah48D1mk9/407A==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", diff --git a/package.json b/package.json index 0df1384..7acd7ba 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#76fc09aad04ef472a70405474ea537daa6800b16", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From fd4c1b527c721cbed1abe9590bcb0a02c6baaeb3 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 15:25:18 +0100 Subject: [PATCH 27/41] feat: opt-in IndexedDB-backed persistent block cache for the http backend Prototype of the warm-start capability that chunked-zkey schemes (zkEmail) get from localforage, without their costs: the standard single-file zkey, no snarkjs fork, and bounded memory. readExisting({type: "http", url, persistentCache: true}) wraps the range reader with a block cache (default 2 MiB blocks) persisted in IndexedDB: - blocks fully inside a request are fetched straight into the caller's buffer (no extra copy); only the <=2 boundary blocks use a one-block temp, so the bounded-memory property of the streaming design is preserved; - entries are keyed by URL + strong validator + size, and stale blocks are dropped at open -- without a strong validator nothing is cached, so a replaced file can never serve mixed or stale bytes; - total storage is bounded (maxBytes, default 512 MiB) with per-file LRU eviction at open, never evicting the file being opened; - everywhere IndexedDB is missing or broken (Node, private windows, denied storage, quota errors mid-session) the cache degrades to a no-op and reads keep streaming. Prototype limitations noted in the module header: no in-flight dedupe for concurrent cold reads of one block, the degraded full-body path is not persisted, and eviction runs only at open. 4 new in-Chromium tests (cold correctness across block boundaries, warm start with zero network requests, validator invalidation, LRU eviction) plus a Node no-op test; 78 tests total pass. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- README.md | 20 +++ build/browser/browser.esm.js | 188 +++++++++++++++++++++++----- build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/httpfile.js | 15 ++- src/idbcache.js | 226 ++++++++++++++++++++++++++++++++++ test/httpfile.test.js | 15 +++ test/idbcache.browser.test.js | 185 ++++++++++++++++++++++++++++ vite.config.js | 4 +- 9 files changed, 619 insertions(+), 38 deletions(-) create mode 100644 src/idbcache.js create mode 100644 test/idbcache.browser.test.js diff --git a/README.md b/README.md index ee5916b..8091d7f 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,26 @@ const g = await fastFile.readExisting({ }); ``` +#### Persistent warm start (browsers, experimental) + +```javascript +// Opt-in: fetched blocks persist in IndexedDB, so a later session against +// the same URL serves its reads locally instead of re-downloading. +const f = await fastFile.readExisting({ + type: "http", + url: "https://example.com/circuit_final.zkey", + persistentCache: true, // or tune it: + // persistentCache: { blockSize: 1 << 21, maxBytes: 1 << 29, dbName: "fastfile-http-cache" }, +}); +``` + +Entries are keyed by URL plus the server's strong validator (ETag / +Last-Modified) and are dropped when the remote file changes; without a +strong validator nothing is cached. Total storage is bounded (`maxBytes`, +default 512 MiB) with least-recently-opened files evicted first. Where +IndexedDB is unavailable (Node, private windows, blocked storage) the option +is a silent no-op and reads stream as usual. + ### Blob / File (browser) ```javascript diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index 3827898..2a7efa4 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -271,18 +271,135 @@ function b(e) { return new TextDecoder().decode(n); } //#endregion +//#region src/idbcache.js +var x = 1 << 21, S = 1 << 29, C = "fastfile-http-cache", w = /* @__PURE__ */ new Map(); +function T(e) { + if (w.has(e)) return w.get(e); + let t = new Promise((t, n) => { + let r = indexedDB.open(e, 1); + r.onupgradeneeded = () => { + let e = r.result; + e.createObjectStore("files"), e.createObjectStore("blocks"); + }, r.onsuccess = () => t(r.result), r.onerror = () => n(r.error), r.onblocked = () => n(/* @__PURE__ */ Error("IndexedDB open blocked")); + }); + return w.set(e, t), t.catch(() => w.delete(e)), t; +} +function E(e) { + return new Promise((t, n) => { + e.onsuccess = () => t(e.result), e.onerror = () => n(e.error); + }); +} +function D(e) { + return new Promise((t, n) => { + e.oncomplete = () => t(), e.onerror = () => n(e.error), e.onabort = () => n(e.error || /* @__PURE__ */ Error("IndexedDB transaction aborted")); + }); +} +function O(e, t, n) { + return IDBKeyRange.bound([e, t], [e, n]); +} +async function k(e, t) { + let n = e.transaction(["files", "blocks"], "readwrite"); + n.objectStore("files").delete(t), n.objectStore("blocks").delete(O(t, 0, Infinity)), await D(n); +} +async function A(e, t, n, r, i, a) { + let o = e.transaction(["files", "blocks"], "readwrite"), s = o.objectStore("files"), c = await E(s.get(t)), l = 0; + c && c.validator === n && c.totalSize === r && c.blockSize === i ? l = c.bytes : c && o.objectStore("blocks").delete(O(t, 0, Infinity)), s.put({ + validator: n, + totalSize: r, + blockSize: i, + bytes: l, + lastUsed: Date.now() + }, t), await D(o); + let u = e.transaction("files", "readonly"), d = u.objectStore("files"), [f, p] = await Promise.all([E(d.getAllKeys()), E(d.getAll())]); + await D(u); + let m = p.reduce((e, t) => e + t.bytes, 0); + if (m <= a) return; + let h = f.map((e, t) => ({ + key: e, + meta: p[t] + })).filter((e) => e.key !== t).sort((e, t) => e.meta.lastUsed - t.meta.lastUsed); + for (let t of h) { + if (m <= a) break; + await k(e, t.key), m -= t.meta.bytes; + } +} +async function j(e, t) { + let { fileKey: n, validator: r, totalSize: i } = t, a = typeof t.options == "object" && t.options || {}, o = a.blockSize || x, s = a.maxBytes || S, c = a.dbName || C; + if (typeof indexedDB > "u" || !r) return e; + let l; + try { + l = await T(c), await A(l, n, r, i, o, s); + } catch { + return e; + } + let u = !1; + async function d(e, t) { + let r = l.transaction("blocks", "readonly"), i = r.objectStore("blocks"), a = O(n, e, t), [o, s] = await Promise.all([E(i.getAllKeys(a)), E(i.getAll(a))]); + await D(r); + let c = /* @__PURE__ */ new Map(); + for (let e = 0; e < o.length; e++) c.set(o[e][1], s[e]); + return c; + } + async function f(e) { + if (!(u || e.length === 0)) try { + let t = l.transaction(["files", "blocks"], "readwrite"), r = t.objectStore("blocks"), i = t.objectStore("files"); + for (let t of e) r.put(t.data, [n, t.index]); + let a = await E(i.get(n)); + a && (a.bytes += e.reduce((e, t) => e + t.data.byteLength, 0), a.lastUsed = Date.now(), i.put(a, n)), await D(t); + } catch { + u = !0; + } + } + let p = (e) => Math.min(o, i - e * o); + return async function(t, n, r, i) { + if (i === 0) return; + let a = Math.floor(r / o), s = Math.floor((r + i - 1) / o), c = await d(a, s), l = [], u = a; + for (; u <= s;) { + let a = u * o, d = a + p(u), f = c.get(u); + if (f) { + let e = Math.max(r, a), o = Math.min(r + i, d); + t.set(f.subarray(e - a, o - a), n + (e - r)), u++; + continue; + } + if (a >= r && d <= r + i) { + let d = u; + for (; d + 1 <= s && !c.get(d + 1) && (d + 1) * o + p(d + 1) <= r + i;) d++; + let f = a, m = d * o + p(d); + await e(t, n + (f - r), f, m - f); + for (let e = u; e <= d; e++) { + let i = e * o; + l.push({ + index: e, + data: t.slice(n + (i - r), n + (i - r) + p(e)) + }); + } + u = d + 1; + } else { + let o = new Uint8Array(p(u)); + await e(o, 0, a, o.length); + let s = Math.max(r, a), c = Math.min(r + i, d); + t.set(o.subarray(s - a, c - a), n + (s - r)), l.push({ + index: u, + data: o + }), u++; + } + } + await f(l); + }; +} +//#endregion //#region src/httpfile.js -var x = 65536; -async function S(e) { +var M = 65536; +async function N(e) { let n = e.url, r = await fetch(n, { headers: { Range: "bytes=0-0" } }); if (r.status === 206) { let t = r.headers.get("content-range"), i = t ? /\/(\d+)\s*$/.exec(t) : null; if (i) { let t = parseInt(i[1]); await r.arrayBuffer(); - let a = w(r), o = null, s = async function(e, t, r, i) { + let a = F(r), o = null, s = async function(e, t, r, i) { if (!o) try { - return await T(n, a, e, t, r, i); + return await I(n, a, e, t, r, i); } catch (e) { if (!e || !e.degradeToFull) throw e; o = e.fullBodyPromise; @@ -290,10 +407,15 @@ async function S(e) { let s = await o; if (r + i > s.byteLength) throw Error(n + ": read past the end of the buffered body"); e.set(s.subarray(r, r + i), t); - }, c = Math.min(e.pageSize || x, x); - return new y(s, t, e.cacheSize, c); + }, c = Math.min(e.pageSize || M, M); + return e.persistentCache && (s = await j(s, { + fileKey: n, + validator: a, + totalSize: t, + options: e.persistentCache + })), new y(s, t, e.cacheSize, c); } - return await r.arrayBuffer(), await C(n); + return await r.arrayBuffer(), await P(n); } if (!r.ok && r.status !== 416) throw Error("HTTP " + r.status + " fetching " + n); if (r.status === 416) { @@ -301,14 +423,14 @@ async function S(e) { return e && /\/0\s*$/.test(e) ? t({ type: "mem", data: /* @__PURE__ */ new Uint8Array() - }) : await C(n); + }) : await P(n); } return t({ type: "mem", data: new Uint8Array(await r.arrayBuffer()) }); } -async function C(e) { +async function P(e) { let n = await fetch(e); if (!n.ok) throw Error("HTTP " + n.status + " fetching " + e); return t({ @@ -316,25 +438,25 @@ async function C(e) { data: new Uint8Array(await n.arrayBuffer()) }); } -function w(e) { +function F(e) { let t = e.headers.get("etag"); return t && t.indexOf("W/") !== 0 ? t : e.headers.get("last-modified") || null; } -async function T(e, t, n, r, i, a) { +async function I(e, t, n, r, i, a) { let o = { Range: "bytes=" + i + "-" + (i + a - 1) }; t && (o["If-Range"] = t); let s = await fetch(e, { headers: o }); if (s.status === 200) { - let n = w(s); + let n = F(s); if (!t || n && n === t) { let t = /* @__PURE__ */ Error(e + ": origin ignored Range; degrading to full buffering"); throw t.degradeToFull = !0, t.fullBodyPromise = s.arrayBuffer().then((e) => new Uint8Array(e)), t; } - throw await E(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); + throw await L(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); } - if (s.status !== 206) throw await E(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); + if (s.status !== 206) throw await L(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); let c = s.headers.get("content-range"), l = c ? /bytes\s+(\d+)-(\d+)\//.exec(c) : null; - if (l && parseInt(l[1]) !== i) throw await E(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); + if (l && parseInt(l[1]) !== i) throw await L(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); let u = 0; if (s.body && typeof s.body.getReader == "function") { let t = s.body.getReader(); @@ -352,43 +474,43 @@ async function T(e, t, n, r, i, a) { } if (u !== a) throw Error(e + ": short range response (" + u + "/" + a + " bytes at " + i + ")"); } -async function E(e) { +async function L(e) { try { e.body && typeof e.body.cancel == "function" ? await e.body.cancel() : await e.arrayBuffer(); } catch {} } //#endregion //#region src/blobfile.js -var D = 1 << 20; -function O(e) { +var R = 1 << 20; +function z(e) { let t = e.blob, n = async function(e, n, r, i) { let a = await t.slice(r, r + i).arrayBuffer(); if (a.byteLength !== i) throw Error("short blob read (" + a.byteLength + "/" + i + " bytes at " + r + ")"); e.set(new Uint8Array(a), n); - }, r = Math.min(e.pageSize || D, D); + }, r = Math.min(e.pageSize || R, R); return new y(n, t.size, e.cacheSize, r); } //#endregion //#region src/fastfile.browser.js -function k() { +function B() { throw Error("File I/O is not supported in the browser"); } -function A(e) { +function V(e) { return e instanceof Uint8Array ? { type: "mem", data: e - } : (typeof e == "string" && k(), e); + } : (typeof e == "string" && B(), e); } -function j(e, t, n) { - if (e.type === "file" && k(), e.type === "mem") return t(e); +function H(e, t, n) { + if (e.type === "file" && B(), e.type === "mem") return t(e); if (e.type === "bigMem") return n(e); throw Error("Invalid FastFile type: " + e.type); } -function M(t) { - return j(A(t), e, l); +function U(t) { + return H(V(t), e, l); } -var N = M; -async function P(e, n, r) { +var W = U; +async function G(e, n, r) { return e instanceof Uint8Array && (e = { type: "mem", data: e @@ -402,11 +524,11 @@ async function P(e, n, r) { url: e, cacheSize: n, pageSize: r - }), e.type === "http" ? await S(e) : e.type === "blob" ? O(e) : j(e, t, u); + }), e.type === "http" ? await N(e) : e.type === "blob" ? z(e) : H(e, t, u); } -function F(e) { - return typeof e == "string" && k(), j(e, n, d); +function K(e) { + return typeof e == "string" && B(), H(e, n, d); } -var I = F; +var q = K; //#endregion -export { N as createNoOverride, M as createOverride, P as readExisting, F as readWriteExisting, I as readWriteExistingOrCreate }; +export { W as createNoOverride, U as createOverride, G as readExisting, K as readWriteExisting, q as readWriteExistingOrCreate }; diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index 482fdcf..2f66f34 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;ns.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||S,S);return new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await w(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await w(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function w(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function T(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function E(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=T(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await D(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await D(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await D(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function D(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var O=1<<20;function k(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||O,O);return new b(n,t.size,e.cacheSize,r)}function A(){throw Error(`File I/O is not supported in the browser`)}function j(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&A(),e)}function M(e,t,n){if(e.type===`file`&&A(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function N(e){return M(j(e),t,u)}var P=N;async function F(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await C(e):e.type===`blob`?k(e):M(e,n,d)}function I(e){return typeof e==`string`&&A(),M(e,r,f)}var L=I;return e.createNoOverride=P,e.createOverride=N,e.readExisting=F,e.readWriteExisting=I,e.readWriteExistingOrCreate=L,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o);return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=a;for(;u<=s;){let a=u*o,d=a+p(u),f=c.get(u);if(f){let e=Math.max(r,a),o=Math.min(r+i,d);t.set(f.subarray(e-a,o-a),n+(e-r)),u++;continue}if(a>=r&&d<=r+i){let d=u;for(;d+1<=s&&!c.get(d+1)&&(d+1)*o+p(d+1)<=r+i;)d++;let f=a,m=d*o+p(d);await e(t,n+(f-r),f,m-f);for(let e=u;e<=d;e++){let i=e*o;l.push({index:e,data:t.slice(n+(i-r),n+(i-r)+p(e))})}u=d+1}else{let o=new Uint8Array(p(u));await e(o,0,a,o.length);let s=Math.max(r,a),c=Math.min(r+i,d);t.set(o.subarray(s-a,c-a),n+(s-r)),l.push({index:u,data:o}),u++}}await f(l)}}var N=65536;async function P(e){let t=e.url,r=await fetch(t,{headers:{Range:`bytes=0-0`}});if(r.status===206){let n=r.headers.get(`content-range`),i=n?/\/(\d+)\s*$/.exec(n):null;if(i){let n=parseInt(i[1]);await r.arrayBuffer();let a=I(r),o=null,s=async function(e,n,r,i){if(!o)try{return await L(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return e.persistentCache&&(s=await M(s,{fileKey:t,validator:a,totalSize:n,options:e.persistentCache})),new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await F(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await F(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function L(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await R(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await R(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await R(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function R(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var z=1<<20;function B(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||z,z);return new b(n,t.size,e.cacheSize,r)}function V(){throw Error(`File I/O is not supported in the browser`)}function H(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&V(),e)}function U(e,t,n){if(e.type===`file`&&V(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function W(e){return U(H(e),t,u)}var G=W;async function K(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await P(e):e.type===`blob`?B(e):U(e,n,d)}function q(e){return typeof e==`string`&&V(),U(e,r,f)}var J=q;return e.createNoOverride=G,e.createOverride=W,e.readExisting=K,e.readWriteExisting=q,e.readWriteExistingOrCreate=J,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index 2bb37ce..9cc78a3 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},k=1<<20,A=8192,j=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||A,this.maxPagesLoaded=Math.floor((n||k)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,M(r);r.push(i),n+=e}return t.pos=n,M(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function M(e){let t=0;for(let n=0;ns.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return new j(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await F(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await F(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function L(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await R(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await R(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await R(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function R(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var z=1<<20;function B(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||z,z);return new j(n,t.size,e.cacheSize,r)}var V=65536,H=8192,U=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function W(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function G(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function K(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!U||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await P(e);if(e.type==`blob`)return B(e);throw Error(`Invalid FastFile type: `+e.type)}function q(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function J(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||V,pageSize:n||H}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=G,exports.createOverride=W,exports.readExisting=K,exports.readWriteExisting=q,exports.readWriteExistingOrCreate=J; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},k=1<<20,A=8192,j=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||A,this.maxPagesLoaded=Math.floor((n||k)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,M(r);r.push(i),n+=e}return t.pos=n,M(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function M(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return I.set(e,t),t.catch(()=>I.delete(e)),t}function R(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function z(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function B(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function V(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(B(t,0,1/0)),await z(n)}async function H(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await R(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(B(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await z(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([R(d.getAllKeys()),R(d.getAll())]);await z(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await V(e,t.key),m-=t.meta.bytes}}async function U(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||N,s=a.maxBytes||P,c=a.dbName||F;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await L(c),await H(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=B(n,e,t),[o,s]=await Promise.all([R(i.getAllKeys(a)),R(i.getAll(a))]);await z(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await z(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o);return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=a;for(;u<=s;){let a=u*o,d=a+p(u),f=c.get(u);if(f){let e=Math.max(r,a),o=Math.min(r+i,d);t.set(f.subarray(e-a,o-a),n+(e-r)),u++;continue}if(a>=r&&d<=r+i){let d=u;for(;d+1<=s&&!c.get(d+1)&&(d+1)*o+p(d+1)<=r+i;)d++;let f=a,m=d*o+p(d);await e(t,n+(f-r),f,m-f);for(let e=u;e<=d;e++){let i=e*o;l.push({index:e,data:t.slice(n+(i-r),n+(i-r)+p(e))})}u=d+1}else{let o=new Uint8Array(p(u));await e(o,0,a,o.length);let s=Math.max(r,a),c=Math.min(r+i,d);t.set(o.subarray(s-a,c-a),n+(s-r)),l.push({index:u,data:o}),u++}}await f(l)}}var W=65536;async function G(e){let t=e.url,n=await fetch(t,{headers:{Range:`bytes=0-0`}});if(n.status===206){let r=n.headers.get(`content-range`),i=r?/\/(\d+)\s*$/.exec(r):null;if(i){let r=parseInt(i[1]);await n.arrayBuffer();let a=q(n),o=null,s=async function(e,n,r,i){if(!o)try{return await J(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||W,W);return e.persistentCache&&(s=await U(s,{fileKey:t,validator:a,totalSize:r,options:e.persistentCache})),new j(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await K(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await K(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function K(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function q(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function J(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=q(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new j(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ee=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function te(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ne(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function re(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ee||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await G(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function ie(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function ae(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ne,exports.createOverride=te,exports.readExisting=re,exports.readWriteExisting=ie,exports.readWriteExistingOrCreate=ae; \ No newline at end of file diff --git a/src/httpfile.js b/src/httpfile.js index 56e63ac..a154947 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -19,6 +19,7 @@ import { RangeFile } from "./rangefile.js"; import * as memFile from "./memfile.js"; +import { wrapWithPersistentCache } from "./idbcache.js"; // Callers tune cacheSize/pageSize for the disk backend (snarkjs passes 8 MiB // pages); over HTTP a page that large turns a 4-byte header read into a @@ -48,7 +49,7 @@ export async function readExisting(o) { // validator still matches, the file is unchanged -- buffer that // full body once and serve every subsequent read from it. let fullBody = null; - const readRangeInto = async function (dst, dstOffset, pos, len) { + let readRangeInto = async function (dst, dstOffset, pos, len) { if (!fullBody) { try { return await httpReadRangeInto(url, validator, dst, dstOffset, pos, len); @@ -64,6 +65,18 @@ export async function readExisting(o) { dst.set(data.subarray(pos, pos + len), dstOffset); }; const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); + if (o.persistentCache) { + // Opt-in warm start: persist fetched blocks in IndexedDB so + // a later session against the same URL reads locally. Keyed + // on the strong validator -- without one the wrapper + // declines and this stays a plain streaming reader. + readRangeInto = await wrapWithPersistentCache(readRangeInto, { + fileKey: url, + validator: validator, + totalSize: totalSize, + options: o.persistentCache, + }); + } return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); } // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot diff --git a/src/idbcache.js b/src/idbcache.js new file mode 100644 index 0000000..60f8164 --- /dev/null +++ b/src/idbcache.js @@ -0,0 +1,226 @@ +// IndexedDB-backed persistent block cache for positioned range readers. +// +// Wraps a `readRangeInto(dst, dstOffset, pos, len)` function (the http +// backend's reader) so that fetched bytes persist across page loads: a +// second proving session against the same URL serves its reads from +// IndexedDB instead of the network ("warm start"), while keeping every +// property of the streaming design -- the standard single-file format, and +// bounded memory (reads land directly in the caller's buffer; the cache +// only ever allocates up to two extra blocks for the request boundaries). +// +// Identity and consistency: entries are keyed by URL and stamped with the +// file's strong validator (ETag / Last-Modified) and total size. A changed +// validator or size drops the stale blocks at open. Without a strong +// validator the wrapper refuses to cache and returns the reader unchanged -- +// a URL-only identity could silently serve bytes of a replaced file. +// +// Storage layout (one shared database): +// files: fileKey -> { validator, totalSize, blockSize, bytes, lastUsed } +// blocks: [fileKey, blockIndex] -> Uint8Array (blockSize, tail may be short) +// +// Eviction is per-file LRU, applied at open: when the sum of cached bytes +// exceeds maxBytes, least-recently-opened files are dropped (never the one +// being opened). A zkey is useful as a whole or not at all, so file +// granularity beats block granularity here. +// +// Prototype limitations (documented, deliberate): +// - concurrent readers of the same cold block both fetch it (last write +// wins; bytes are identical by the validator guard); +// - the degraded full-body path and the no-Range fallback are not +// persisted; only the streaming path populates the cache; +// - eviction only runs at open, not while a session writes new blocks. + +const DEFAULT_BLOCK_SIZE = 1 << 21; // 2 MiB: amortizes IDB per-op cost +const DEFAULT_MAX_BYTES = 1 << 29; // 512 MiB across all cached files +const DEFAULT_DB_NAME = "fastfile-http-cache"; + +const dbConnections = new Map(); // dbName -> Promise + +function openDb(dbName) { + if (dbConnections.has(dbName)) return dbConnections.get(dbName); + const p = new Promise((resolve, reject) => { + const req = indexedDB.open(dbName, 1); + req.onupgradeneeded = () => { + const db = req.result; + db.createObjectStore("files"); + db.createObjectStore("blocks"); + }; + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + req.onblocked = () => reject(new Error("IndexedDB open blocked")); + }); + dbConnections.set(dbName, p); + p.catch(() => dbConnections.delete(dbName)); + return p; +} + +// Small promise adapters over the IDB request API. +function idbReq(req) { + return new Promise((resolve, reject) => { + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + }); +} +function txDone(tx) { + return new Promise((resolve, reject) => { + tx.oncomplete = () => resolve(); + tx.onerror = () => reject(tx.error); + tx.onabort = () => reject(tx.error || new Error("IndexedDB transaction aborted")); + }); +} + +function blockRange(fileKey, from, to) { + return IDBKeyRange.bound([fileKey, from], [fileKey, to]); +} + +async function deleteFileEntries(db, fileKey) { + const tx = db.transaction(["files", "blocks"], "readwrite"); + tx.objectStore("files").delete(fileKey); + tx.objectStore("blocks").delete(blockRange(fileKey, 0, Infinity)); + await txDone(tx); +} + +// Register the file, invalidate stale blocks, apply LRU eviction. +async function prepareFile(db, fileKey, validator, totalSize, blockSize, maxBytes) { + const tx = db.transaction(["files", "blocks"], "readwrite"); + const files = tx.objectStore("files"); + const existing = await idbReq(files.get(fileKey)); + let bytes = 0; + if (existing && + existing.validator === validator && + existing.totalSize === totalSize && + existing.blockSize === blockSize) { + bytes = existing.bytes; + } else if (existing) { + // stale: same URL, different content or geometry + tx.objectStore("blocks").delete(blockRange(fileKey, 0, Infinity)); + } + files.put({ validator, totalSize, blockSize, bytes, lastUsed: Date.now() }, fileKey); + await txDone(tx); + + // LRU eviction across files (never the one just opened). + const rtx = db.transaction("files", "readonly"); + const store = rtx.objectStore("files"); + const [keys, metas] = await Promise.all([idbReq(store.getAllKeys()), idbReq(store.getAll())]); + await txDone(rtx); + let total = metas.reduce((s, mm) => s + mm.bytes, 0); + if (total <= maxBytes) return; + const victims = keys.map((k, i) => ({ key: k, meta: metas[i] })) + .filter((f) => f.key !== fileKey) + .sort((a, b) => a.meta.lastUsed - b.meta.lastUsed); + for (const v of victims) { + if (total <= maxBytes) break; + await deleteFileEntries(db, v.key); + total -= v.meta.bytes; + } +} + +// Wrap `readRangeInto` with the persistent cache. Returns the original +// reader unchanged when caching is impossible (no IndexedDB in this +// environment, storage blocked, or no strong validator to key on). +export async function wrapWithPersistentCache(readRangeInto, o) { + const { fileKey, validator, totalSize } = o; + const opts = (typeof o.options === "object" && o.options) || {}; + const blockSize = opts.blockSize || DEFAULT_BLOCK_SIZE; + const maxBytes = opts.maxBytes || DEFAULT_MAX_BYTES; + const dbName = opts.dbName || DEFAULT_DB_NAME; + + if (typeof indexedDB === "undefined" || !validator) return readRangeInto; + let db; + try { + db = await openDb(dbName); + await prepareFile(db, fileKey, validator, totalSize, blockSize, maxBytes); + } catch (e) { + // Private windows, blocked site data, quota pressure at open: the + // cache is a convenience, never a requirement. + return readRangeInto; + } + + let broken = false; // a failed write disables persistence, not reads + + async function loadCached(firstBlock, lastBlock) { + const tx = db.transaction("blocks", "readonly"); + const store = tx.objectStore("blocks"); + const range = blockRange(fileKey, firstBlock, lastBlock); + const [keys, values] = await Promise.all([idbReq(store.getAllKeys(range)), idbReq(store.getAll(range))]); + await txDone(tx); + const found = new Map(); + for (let i = 0; i < keys.length; i++) found.set(keys[i][1], values[i]); + return found; + } + + async function persistBlocks(entries) { // [{index, data}] with data copied already + if (broken || entries.length === 0) return; + try { + const tx = db.transaction(["files", "blocks"], "readwrite"); + const blocks = tx.objectStore("blocks"); + const files = tx.objectStore("files"); + for (const e of entries) blocks.put(e.data, [fileKey, e.index]); + const meta = await idbReq(files.get(fileKey)); + /* c8 ignore next -- meta vanishes only if another tab evicts mid-session */ + if (meta) { + meta.bytes += entries.reduce((s, e) => s + e.data.byteLength, 0); + meta.lastUsed = Date.now(); + files.put(meta, fileKey); + } + await txDone(tx); + } catch (e) { + broken = true; // quota exceeded etc.: keep reading, stop writing + } + } + + const blockLen = (index) => + Math.min(blockSize, totalSize - index * blockSize); + + return async function cachedReadRangeInto(dst, dstOffset, pos, len) { + if (len === 0) return; + const firstBlock = Math.floor(pos / blockSize); + const lastBlock = Math.floor((pos + len - 1) / blockSize); + const found = await loadCached(firstBlock, lastBlock); + + const toPersist = []; + // Walk the block range, serving hits and grouping misses into runs + // of blocks that lie fully inside the request (fetched directly + // into dst, no extra copy) -- boundary blocks that stick out past + // the request are fetched whole into a one-block temp so the cache + // always stores complete blocks. + let i = firstBlock; + while (i <= lastBlock) { + const bStart = i * blockSize; + const bEnd = bStart + blockLen(i); + const hit = found.get(i); + if (hit) { + const from = Math.max(pos, bStart); + const to = Math.min(pos + len, bEnd); + dst.set(hit.subarray(from - bStart, to - bStart), dstOffset + (from - pos)); + i++; + continue; + } + if (bStart >= pos && bEnd <= pos + len) { + // interior run: extend over consecutive interior misses + let j = i; + while (j + 1 <= lastBlock && !found.get(j + 1) && + (j + 1) * blockSize + blockLen(j + 1) <= pos + len) j++; + const runStart = bStart; + const runEnd = j * blockSize + blockLen(j); + await readRangeInto(dst, dstOffset + (runStart - pos), runStart, runEnd - runStart); + for (let b = i; b <= j; b++) { + const s = b * blockSize; + toPersist.push({ index: b, data: dst.slice(dstOffset + (s - pos), dstOffset + (s - pos) + blockLen(b)) }); + } + i = j + 1; + } else { + // boundary block partially outside the request: fetch it + // whole so the stored block is complete + const block = new Uint8Array(blockLen(i)); + await readRangeInto(block, 0, bStart, block.length); + const from = Math.max(pos, bStart); + const to = Math.min(pos + len, bEnd); + dst.set(block.subarray(from - bStart, to - bStart), dstOffset + (from - pos)); + toPersist.push({ index: i, data: block }); + i++; + } + } + await persistBlocks(toPersist); + }; +} diff --git a/test/httpfile.test.js b/test/httpfile.test.js index 5df4280..d5bc8b8 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -77,6 +77,21 @@ function startServer(state, opts) { describe("fastfile testing suite for httpfile", function () { + it("persistentCache is a no-op where IndexedDB does not exist (Node)", async () => { + const data = makeData(1 << 18); + const srv = await startServer({ data: data, etag: "\"v1\"" }); + try { + const fd = await fastFile.readExisting({ + type: "http", url: srv.url, persistentCache: true, + }); + const got = await fd.read(64, 12345); + assert.deepStrictEqual(Buffer.from(got), Buffer.from(data.slice(12345, 12345 + 64))); + await fd.close(); + } finally { + await srv.close(); + } + }); + it("should read ranges without ever downloading the full file", async () => { const data = makeData(1 << 20); // 1 MiB const srv = await startServer({ data: data, etag: "\"v1\"" }); diff --git a/test/idbcache.browser.test.js b/test/idbcache.browser.test.js new file mode 100644 index 0000000..4eae829 --- /dev/null +++ b/test/idbcache.browser.test.js @@ -0,0 +1,185 @@ +import * as fastFile from "../src/fastfile.browser.js"; +import { expect, vi } from "vitest"; + +// In-Chromium tests for the IndexedDB-backed persistent block cache +// (persistentCache option of the http backend). fetch is stubbed with a +// deterministic Range-honoring server that counts requests, so warm starts +// are provable as "zero network requests". + +const FILE_SIZE = 5 * 1024 * 1024 + 123; // deliberately not block-aligned +const BLOCK = 1 << 20; // 1 MiB test blocks + +function makeContent(seed) { + const data = new Uint8Array(FILE_SIZE); + for (let i = 0; i < data.length; i++) data[i] = (i * 31 + seed) & 0xff; + return data; +} + +// A minimal Range-capable origin: 206 with Content-Range, strong ETag. +function serveRanges(content, etag, counter) { + return (url, opts) => { + counter.count++; + const range = opts && opts.headers && opts.headers["Range"]; + const m = /bytes=(\d+)-(\d+)/.exec(range || ""); + const from = m ? parseInt(m[1]) : 0; + const to = m ? Math.min(parseInt(m[2]), content.length - 1) : content.length - 1; + counter.bytes += to - from + 1; + const body = content.slice(from, to + 1); + return Promise.resolve({ + ok: true, + status: 206, + headers: new Headers({ + "content-range": `bytes ${from}-${to}/${content.length}`, + "etag": etag, + }), + arrayBuffer: () => Promise.resolve(body.buffer.slice(0)), + body: null, // exercise the non-streaming fallback (jsdom-style) + }); + }; +} + +async function openCached(url, cacheOpts) { + return fastFile.readExisting({ + type: "http", + url, + persistentCache: cacheOpts || { blockSize: BLOCK, dbName: "fastfile-test-cache" }, + }); +} + +function clearDb(name) { + return new Promise((resolve) => { + const req = indexedDB.deleteDatabase(name); + req.onsuccess = req.onerror = req.onblocked = () => resolve(); + }); +} + +describe("IndexedDB persistent block cache", function () { + const URL1 = "https://cache.example/circuit.zkey"; + + beforeEach(async () => { + // fresh DB per test -- but note the module keeps its connection + // per dbName, so tests use distinct dbNames where isolation matters + await clearDb("fastfile-test-cache"); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("cold reads are correct across block boundaries and warm reads hit no network", async () => { + const content = makeContent(7); + const counter = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(content, '"v1"', counter)); + + const dbName = "fastfile-test-warm"; + await clearDb(dbName); + const fd = await openCached(URL1, { blockSize: BLOCK, dbName }); + + // unaligned read spanning interior + boundary blocks + const big = await fd.read(3 * BLOCK, BLOCK + 17); + expect([...big.slice(0, 8)]).toEqual([...content.subarray(BLOCK + 17, BLOCK + 25)]); + expect([...big.slice(-8)]).toEqual([...content.subarray(4 * BLOCK + 9, 4 * BLOCK + 17)]); + // small header-style read + const head = await fd.read(16, 4); + expect([...head]).toEqual([...content.subarray(4, 20)]); + // tail read hitting the short last block + const tail = await fd.read(100, FILE_SIZE - 100); + expect([...tail]).toEqual([...content.subarray(FILE_SIZE - 100)]); + await fd.close(); + const coldFetches = counter.count; + expect(coldFetches).toBeGreaterThan(0); + + // warm start: new handle, same URL -- all reads served from IDB + const fd2 = await openCached(URL1, { blockSize: BLOCK, dbName }); + const probeFetches = counter.count - coldFetches; // open() probe only + const big2 = await fd2.read(3 * BLOCK, BLOCK + 17); + expect([...big2.slice(0, 32)]).toEqual([...big.slice(0, 32)]); + const tail2 = await fd2.read(100, FILE_SIZE - 100); + expect([...tail2]).toEqual([...tail]); + await fd2.close(); + expect(counter.count - coldFetches - probeFetches).toBe(0); + expect(probeFetches).toBe(1); + }); + + it("a changed validator invalidates the cached blocks", async () => { + const dbName = "fastfile-test-inval"; + await clearDb(dbName); + + const v1 = makeContent(1); + const c1 = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(v1, '"v1"', c1)); + const fd1 = await openCached(URL1, { blockSize: BLOCK, dbName }); + await fd1.read(64, 0); + await fd1.close(); + + // same URL, new content + new ETag + const v2 = makeContent(2); + const c2 = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(v2, '"v2"', c2)); + const fd2 = await openCached(URL1, { blockSize: BLOCK, dbName }); + const got = await fd2.read(64, 0); + await fd2.close(); + expect([...got]).toEqual([...v2.subarray(0, 64)]); + expect(c2.count).toBeGreaterThan(1); // probe + at least one real refetch + }); + + it("LRU eviction drops the least recently opened file, never the current one", async () => { + const dbName = "fastfile-test-evict"; + await clearDb(dbName); + const opts = { blockSize: BLOCK, dbName, maxBytes: 4 * BLOCK }; + + const contentA = makeContent(10); + const cA = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(contentA, '"a1"', cA)); + const fa = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", persistentCache: opts }); + await fa.read(3 * BLOCK, 0); // caches 3 MiB for A + await fa.close(); + + const contentB = makeContent(20); + const cB = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(contentB, '"b1"', cB)); + const fb = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", persistentCache: opts }); + await fb.read(3 * BLOCK, 0); // A(3) + B(3) > maxBytes at next open + await fb.close(); + + // reopening B evicts A (older lastUsed) and keeps B warm + const cB2 = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(contentB, '"b1"', cB2)); + const fb2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", persistentCache: opts }); + await fb2.read(3 * BLOCK, 0); + await fb2.close(); + expect(cB2.count).toBe(1); // probe only: B stayed cached + + // A must refetch its data + const cA2 = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(contentA, '"a1"', cA2)); + const fa2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", persistentCache: opts }); + const back = await fa2.read(64, 0); + await fa2.close(); + expect([...back]).toEqual([...contentA.subarray(0, 64)]); + expect(cA2.count).toBeGreaterThan(1); + }); + + it("without a strong validator the reader works but nothing persists", async () => { + const dbName = "fastfile-test-noval"; + await clearDb(dbName); + const content = makeContent(3); + const counter = { count: 0, bytes: 0 }; + const server = serveRanges(content, "", counter); + vi.stubGlobal("fetch", (url, opts) => server(url, opts).then((r) => { + r.headers = new Headers({ "content-range": r.headers.get("content-range") }); + return r; + })); + + const fd = await openCached("https://cache.example/nova.zkey", { blockSize: BLOCK, dbName }); + const got = await fd.read(64, 0); + expect([...got]).toEqual([...content.subarray(0, 64)]); + await fd.close(); + const cold = counter.count; + + const fd2 = await openCached("https://cache.example/nova.zkey", { blockSize: BLOCK, dbName }); + await fd2.read(64, 0); + await fd2.close(); + expect(counter.count).toBeGreaterThan(cold + 1); // re-fetched: no cache without validator + }); +}); diff --git a/vite.config.js b/vite.config.js index 11fdaf6..a9075a4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -41,7 +41,7 @@ export default defineConfig(({ mode }) => { test: { name: "node-esm", include: ["test/**/*.test.js"], - exclude: ["test/fastfile.browser.test.js"], + exclude: ["test/fastfile.browser.test.js", "test/idbcache.browser.test.js"], environment: "node", globals: true, testTimeout: 100_000, @@ -72,7 +72,7 @@ export default defineConfig(({ mode }) => { // Browser — ESM source inside real Chromium via Playwright test: { name: "browser-esm", - include: ["test/fastfile.browser.test.js"], + include: ["test/fastfile.browser.test.js", "test/idbcache.browser.test.js"], globals: true, testTimeout: 100_000, hookTimeout: 100_000, From 4ee05be5c8c3969c40a8c6d6a6dc68954b3c8d91 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 15:29:18 +0100 Subject: [PATCH 28/41] style: doublequotes in the idbcache test (lint) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- test/idbcache.browser.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/idbcache.browser.test.js b/test/idbcache.browser.test.js index 4eae829..d0dbcf5 100644 --- a/test/idbcache.browser.test.js +++ b/test/idbcache.browser.test.js @@ -69,7 +69,7 @@ describe("IndexedDB persistent block cache", function () { it("cold reads are correct across block boundaries and warm reads hit no network", async () => { const content = makeContent(7); const counter = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(content, '"v1"', counter)); + vi.stubGlobal("fetch", serveRanges(content, "\"v1\"", counter)); const dbName = "fastfile-test-warm"; await clearDb(dbName); @@ -107,7 +107,7 @@ describe("IndexedDB persistent block cache", function () { const v1 = makeContent(1); const c1 = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(v1, '"v1"', c1)); + vi.stubGlobal("fetch", serveRanges(v1, "\"v1\"", c1)); const fd1 = await openCached(URL1, { blockSize: BLOCK, dbName }); await fd1.read(64, 0); await fd1.close(); @@ -115,7 +115,7 @@ describe("IndexedDB persistent block cache", function () { // same URL, new content + new ETag const v2 = makeContent(2); const c2 = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(v2, '"v2"', c2)); + vi.stubGlobal("fetch", serveRanges(v2, "\"v2\"", c2)); const fd2 = await openCached(URL1, { blockSize: BLOCK, dbName }); const got = await fd2.read(64, 0); await fd2.close(); @@ -130,21 +130,21 @@ describe("IndexedDB persistent block cache", function () { const contentA = makeContent(10); const cA = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(contentA, '"a1"', cA)); + vi.stubGlobal("fetch", serveRanges(contentA, "\"a1\"", cA)); const fa = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", persistentCache: opts }); await fa.read(3 * BLOCK, 0); // caches 3 MiB for A await fa.close(); const contentB = makeContent(20); const cB = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(contentB, '"b1"', cB)); + vi.stubGlobal("fetch", serveRanges(contentB, "\"b1\"", cB)); const fb = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", persistentCache: opts }); await fb.read(3 * BLOCK, 0); // A(3) + B(3) > maxBytes at next open await fb.close(); // reopening B evicts A (older lastUsed) and keeps B warm const cB2 = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(contentB, '"b1"', cB2)); + vi.stubGlobal("fetch", serveRanges(contentB, "\"b1\"", cB2)); const fb2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", persistentCache: opts }); await fb2.read(3 * BLOCK, 0); await fb2.close(); @@ -152,7 +152,7 @@ describe("IndexedDB persistent block cache", function () { // A must refetch its data const cA2 = { count: 0, bytes: 0 }; - vi.stubGlobal("fetch", serveRanges(contentA, '"a1"', cA2)); + vi.stubGlobal("fetch", serveRanges(contentA, "\"a1\"", cA2)); const fa2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", persistentCache: opts }); const back = await fa2.read(64, 0); await fa2.close(); From 044abe97eb8d73e17ea397b48eb27b057a8b9c32 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 17:00:08 +0100 Subject: [PATCH 29/41] fix: in-flight block dedupe in the persistent cache The first realistic benchmark caught the documented prototype limitation being far from theoretical: provers issue concurrent, overlapping reads (MSM read-ahead, coefficient streaming), and every concurrent miss of the same block refetched it whole -- a cold authV3 proof moved 92 MiB for a 28 MiB zkey (46 block fetches for 15 blocks) and took 8.8s vs 3.1s plain streaming. A missing block now registers an in-flight promise before its fetch starts (both the interior-run and boundary paths); every other reader awaits that promise and copies from the resolved block. Entries are removed once persisted (waiters then fall through to IndexedDB), and a failed fetch rejects and clears its entries so a retry can refetch. Interior runs still land in the caller's buffer in one range request each. New in-Chromium regression test: 8 concurrent cold 128 KiB reads inside one block cause at most 2 block fetches (was 8). 79 tests pass; lint clean. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- build/browser/browser.esm.js | 71 +++++++++++++++++++++++----------- build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/idbcache.js | 72 +++++++++++++++++++++++++++-------- test/idbcache.browser.test.js | 22 +++++++++++ 5 files changed, 130 insertions(+), 39 deletions(-) diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index 2a7efa4..98a57f0 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -350,41 +350,68 @@ async function j(e, t) { u = !0; } } - let p = (e) => Math.min(o, i - e * o); + let p = (e) => Math.min(o, i - e * o), m = /* @__PURE__ */ new Map(); + function h(e) { + let t, n, r = new Promise((e, r) => { + t = e, n = r; + }); + return r.catch(() => {}), m.set(e, r), { + resolve: t, + reject: n, + promise: r + }; + } return async function(t, n, r, i) { if (i === 0) return; - let a = Math.floor(r / o), s = Math.floor((r + i - 1) / o), c = await d(a, s), l = [], u = a; - for (; u <= s;) { - let a = u * o, d = a + p(u), f = c.get(u); + let a = Math.floor(r / o), s = Math.floor((r + i - 1) / o), c = await d(a, s), l = [], u = (e, a) => { + let s = a * o, c = Math.max(r, s), l = Math.min(r + i, s + p(a)); + t.set(e.subarray(c - s, l - s), n + (c - r)); + }, g = a; + for (; g <= s;) { + let a = g * o, d = a + p(g), f = c.get(g); if (f) { - let e = Math.max(r, a), o = Math.min(r + i, d); - t.set(f.subarray(e - a, o - a), n + (e - r)), u++; + u(f, g), g++; + continue; + } + let _ = m.get(g); + if (_) { + u(await _, g), g++; continue; } if (a >= r && d <= r + i) { - let d = u; - for (; d + 1 <= s && !c.get(d + 1) && (d + 1) * o + p(d + 1) <= r + i;) d++; - let f = a, m = d * o + p(d); - await e(t, n + (f - r), f, m - f); - for (let e = u; e <= d; e++) { - let i = e * o; - l.push({ + let u = g; + for (; u + 1 <= s && !c.get(u + 1) && !m.get(u + 1) && (u + 1) * o + p(u + 1) <= r + i;) u++; + let d = a, f = u * o + p(u), _ = []; + for (let e = g; e <= u; e++) _.push(h(e)); + try { + await e(t, n + (d - r), d, f - d); + } catch (e) { + for (let t = g; t <= u; t++) _[t - g].reject(e), m.delete(t); + throw e; + } + for (let e = g; e <= u; e++) { + let i = e * o, a = t.slice(n + (i - r), n + (i - r) + p(e)); + _[e - g].resolve(a), l.push({ index: e, - data: t.slice(n + (i - r), n + (i - r) + p(e)) + data: a }); } - u = d + 1; + g = u + 1; } else { - let o = new Uint8Array(p(u)); - await e(o, 0, a, o.length); - let s = Math.max(r, a), c = Math.min(r + i, d); - t.set(o.subarray(s - a, c - a), n + (s - r)), l.push({ - index: u, - data: o - }), u++; + let t = h(g), n = new Uint8Array(p(g)); + try { + await e(n, 0, a, n.length); + } catch (e) { + throw t.reject(e), m.delete(g), e; + } + t.resolve(n), u(n, g), l.push({ + index: g, + data: n + }), g++; } } await f(l); + for (let e of l) m.delete(e.index); }; } //#endregion diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index 2f66f34..dc7523a 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o);return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=a;for(;u<=s;){let a=u*o,d=a+p(u),f=c.get(u);if(f){let e=Math.max(r,a),o=Math.min(r+i,d);t.set(f.subarray(e-a,o-a),n+(e-r)),u++;continue}if(a>=r&&d<=r+i){let d=u;for(;d+1<=s&&!c.get(d+1)&&(d+1)*o+p(d+1)<=r+i;)d++;let f=a,m=d*o+p(d);await e(t,n+(f-r),f,m-f);for(let e=u;e<=d;e++){let i=e*o;l.push({index:e,data:t.slice(n+(i-r),n+(i-r)+p(e))})}u=d+1}else{let o=new Uint8Array(p(u));await e(o,0,a,o.length);let s=Math.max(r,a),c=Math.min(r+i,d);t.set(o.subarray(s-a,c-a),n+(s-r)),l.push({index:u,data:o}),u++}}await f(l)}}var N=65536;async function P(e){let t=e.url,r=await fetch(t,{headers:{Range:`bytes=0-0`}});if(r.status===206){let n=r.headers.get(`content-range`),i=n?/\/(\d+)\s*$/.exec(n):null;if(i){let n=parseInt(i[1]);await r.arrayBuffer();let a=I(r),o=null,s=async function(e,n,r,i){if(!o)try{return await L(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return e.persistentCache&&(s=await M(s,{fileKey:t,validator:a,totalSize:n,options:e.persistentCache})),new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await F(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await F(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function L(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await R(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await R(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await R(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function R(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var z=1<<20;function B(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||z,z);return new b(n,t.size,e.cacheSize,r)}function V(){throw Error(`File I/O is not supported in the browser`)}function H(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&V(),e)}function U(e,t,n){if(e.type===`file`&&V(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function W(e){return U(H(e),t,u)}var G=W;async function K(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await P(e):e.type===`blob`?B(e):U(e,n,d)}function q(e){return typeof e==`string`&&V(),U(e,r,f)}var J=q;return e.createNoOverride=G,e.createOverride=W,e.readExisting=K,e.readWriteExisting=q,e.readWriteExistingOrCreate=J,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var N=65536;async function P(e){let t=e.url,r=await fetch(t,{headers:{Range:`bytes=0-0`}});if(r.status===206){let n=r.headers.get(`content-range`),i=n?/\/(\d+)\s*$/.exec(n):null;if(i){let n=parseInt(i[1]);await r.arrayBuffer();let a=I(r),o=null,s=async function(e,n,r,i){if(!o)try{return await L(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return e.persistentCache&&(s=await M(s,{fileKey:t,validator:a,totalSize:n,options:e.persistentCache})),new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await F(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await F(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function L(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await R(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await R(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await R(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function R(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var z=1<<20;function B(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||z,z);return new b(n,t.size,e.cacheSize,r)}function V(){throw Error(`File I/O is not supported in the browser`)}function H(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&V(),e)}function U(e,t,n){if(e.type===`file`&&V(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function W(e){return U(H(e),t,u)}var G=W;async function K(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await P(e):e.type===`blob`?B(e):U(e,n,d)}function q(e){return typeof e==`string`&&V(),U(e,r,f)}var J=q;return e.createNoOverride=G,e.createOverride=W,e.readExisting=K,e.readWriteExisting=q,e.readWriteExistingOrCreate=J,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index 9cc78a3..5db338b 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},k=1<<20,A=8192,j=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||A,this.maxPagesLoaded=Math.floor((n||k)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,M(r);r.push(i),n+=e}return t.pos=n,M(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function M(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return I.set(e,t),t.catch(()=>I.delete(e)),t}function R(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function z(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function B(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function V(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(B(t,0,1/0)),await z(n)}async function H(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await R(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(B(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await z(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([R(d.getAllKeys()),R(d.getAll())]);await z(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await V(e,t.key),m-=t.meta.bytes}}async function U(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||N,s=a.maxBytes||P,c=a.dbName||F;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await L(c),await H(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=B(n,e,t),[o,s]=await Promise.all([R(i.getAllKeys(a)),R(i.getAll(a))]);await z(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await z(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o);return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=a;for(;u<=s;){let a=u*o,d=a+p(u),f=c.get(u);if(f){let e=Math.max(r,a),o=Math.min(r+i,d);t.set(f.subarray(e-a,o-a),n+(e-r)),u++;continue}if(a>=r&&d<=r+i){let d=u;for(;d+1<=s&&!c.get(d+1)&&(d+1)*o+p(d+1)<=r+i;)d++;let f=a,m=d*o+p(d);await e(t,n+(f-r),f,m-f);for(let e=u;e<=d;e++){let i=e*o;l.push({index:e,data:t.slice(n+(i-r),n+(i-r)+p(e))})}u=d+1}else{let o=new Uint8Array(p(u));await e(o,0,a,o.length);let s=Math.max(r,a),c=Math.min(r+i,d);t.set(o.subarray(s-a,c-a),n+(s-r)),l.push({index:u,data:o}),u++}}await f(l)}}var W=65536;async function G(e){let t=e.url,n=await fetch(t,{headers:{Range:`bytes=0-0`}});if(n.status===206){let r=n.headers.get(`content-range`),i=r?/\/(\d+)\s*$/.exec(r):null;if(i){let r=parseInt(i[1]);await n.arrayBuffer();let a=q(n),o=null,s=async function(e,n,r,i){if(!o)try{return await J(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||W,W);return e.persistentCache&&(s=await U(s,{fileKey:t,validator:a,totalSize:r,options:e.persistentCache})),new j(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await K(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await K(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function K(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function q(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function J(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=q(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new j(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ee=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function te(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ne(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function re(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ee||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await G(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function ie(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function ae(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ne,exports.createOverride=te,exports.readExisting=re,exports.readWriteExisting=ie,exports.readWriteExistingOrCreate=ae; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},k=1<<20,A=8192,j=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||A,this.maxPagesLoaded=Math.floor((n||k)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,M(r);r.push(i),n+=e}return t.pos=n,M(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function M(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return I.set(e,t),t.catch(()=>I.delete(e)),t}function R(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function z(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function B(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function V(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(B(t,0,1/0)),await z(n)}async function H(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await R(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(B(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await z(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([R(d.getAllKeys()),R(d.getAll())]);await z(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await V(e,t.key),m-=t.meta.bytes}}async function U(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||N,s=a.maxBytes||P,c=a.dbName||F;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await L(c),await H(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=B(n,e,t),[o,s]=await Promise.all([R(i.getAllKeys(a)),R(i.getAll(a))]);await z(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await z(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var W=65536;async function G(e){let t=e.url,n=await fetch(t,{headers:{Range:`bytes=0-0`}});if(n.status===206){let r=n.headers.get(`content-range`),i=r?/\/(\d+)\s*$/.exec(r):null;if(i){let r=parseInt(i[1]);await n.arrayBuffer();let a=q(n),o=null,s=async function(e,n,r,i){if(!o)try{return await J(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||W,W);return e.persistentCache&&(s=await U(s,{fileKey:t,validator:a,totalSize:r,options:e.persistentCache})),new j(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await K(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await K(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function K(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function q(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function J(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=q(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new j(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ee=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function te(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ne(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function re(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ee||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await G(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function ie(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function ae(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ne,exports.createOverride=te,exports.readExisting=re,exports.readWriteExisting=ie,exports.readWriteExistingOrCreate=ae; \ No newline at end of file diff --git a/src/idbcache.js b/src/idbcache.js index 60f8164..2cea5a7 100644 --- a/src/idbcache.js +++ b/src/idbcache.js @@ -172,6 +172,20 @@ export async function wrapWithPersistentCache(readRangeInto, o) { const blockLen = (index) => Math.min(blockSize, totalSize - index * blockSize); + // In-flight dedupe: provers issue concurrent, overlapping reads (MSM + // read-ahead, coefficient streaming), and without this every concurrent + // miss of the same block refetched it -- measured 3.3x the zkey size on + // the wire for a cold authV3 proof. A missing block gets one fetch; every + // other reader awaits its promise. + const inFlight = new Map(); // blockIndex -> Promise + function deferBlock(index) { + let resolve, reject; + const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); + promise.catch(() => {}); // waiters handle it; avoid unhandled-rejection noise + inFlight.set(index, promise); + return { resolve, reject, promise }; + } + return async function cachedReadRangeInto(dst, dstOffset, pos, len) { if (len === 0) return; const firstBlock = Math.floor(pos / blockSize); @@ -179,48 +193,76 @@ export async function wrapWithPersistentCache(readRangeInto, o) { const found = await loadCached(firstBlock, lastBlock); const toPersist = []; - // Walk the block range, serving hits and grouping misses into runs - // of blocks that lie fully inside the request (fetched directly - // into dst, no extra copy) -- boundary blocks that stick out past - // the request are fetched whole into a one-block temp so the cache - // always stores complete blocks. + const copyInto = (block, i) => { + const bStart = i * blockSize; + const from = Math.max(pos, bStart); + const to = Math.min(pos + len, bStart + blockLen(i)); + dst.set(block.subarray(from - bStart, to - bStart), dstOffset + (from - pos)); + }; + // Walk the block range, serving hits (cached or in-flight) and + // grouping misses into runs of blocks that lie fully inside the + // request (fetched directly into dst, no extra copy) -- boundary + // blocks that stick out past the request are fetched whole into a + // one-block temp so the cache always stores complete blocks. let i = firstBlock; while (i <= lastBlock) { const bStart = i * blockSize; const bEnd = bStart + blockLen(i); const hit = found.get(i); if (hit) { - const from = Math.max(pos, bStart); - const to = Math.min(pos + len, bEnd); - dst.set(hit.subarray(from - bStart, to - bStart), dstOffset + (from - pos)); + copyInto(hit, i); + i++; + continue; + } + const pending = inFlight.get(i); + if (pending) { + copyInto(await pending, i); i++; continue; } if (bStart >= pos && bEnd <= pos + len) { // interior run: extend over consecutive interior misses let j = i; - while (j + 1 <= lastBlock && !found.get(j + 1) && + while (j + 1 <= lastBlock && !found.get(j + 1) && !inFlight.get(j + 1) && (j + 1) * blockSize + blockLen(j + 1) <= pos + len) j++; const runStart = bStart; const runEnd = j * blockSize + blockLen(j); - await readRangeInto(dst, dstOffset + (runStart - pos), runStart, runEnd - runStart); + const defers = []; + for (let b = i; b <= j; b++) defers.push(deferBlock(b)); + try { + await readRangeInto(dst, dstOffset + (runStart - pos), runStart, runEnd - runStart); + } catch (err) { + for (let b = i; b <= j; b++) { defers[b - i].reject(err); inFlight.delete(b); } + throw err; + } for (let b = i; b <= j; b++) { const s = b * blockSize; - toPersist.push({ index: b, data: dst.slice(dstOffset + (s - pos), dstOffset + (s - pos) + blockLen(b)) }); + const data = dst.slice(dstOffset + (s - pos), dstOffset + (s - pos) + blockLen(b)); + defers[b - i].resolve(data); + toPersist.push({ index: b, data }); } i = j + 1; } else { // boundary block partially outside the request: fetch it // whole so the stored block is complete + const defer = deferBlock(i); const block = new Uint8Array(blockLen(i)); - await readRangeInto(block, 0, bStart, block.length); - const from = Math.max(pos, bStart); - const to = Math.min(pos + len, bEnd); - dst.set(block.subarray(from - bStart, to - bStart), dstOffset + (from - pos)); + try { + await readRangeInto(block, 0, bStart, block.length); + } catch (err) { + defer.reject(err); + inFlight.delete(i); + throw err; + } + defer.resolve(block); + copyInto(block, i); toPersist.push({ index: i, data: block }); i++; } } await persistBlocks(toPersist); + // keep resolved blocks discoverable until they are persisted, then + // let waiters fall through to IndexedDB + for (const e of toPersist) inFlight.delete(e.index); }; } diff --git a/test/idbcache.browser.test.js b/test/idbcache.browser.test.js index d0dbcf5..e121a6a 100644 --- a/test/idbcache.browser.test.js +++ b/test/idbcache.browser.test.js @@ -160,6 +160,28 @@ describe("IndexedDB persistent block cache", function () { expect(cA2.count).toBeGreaterThan(1); }); + it("concurrent cold reads of one block fetch it once (in-flight dedupe)", async () => { + const dbName = "fastfile-test-dedupe"; + await clearDb(dbName); + const content = makeContent(9); + const counter = { count: 0, bytes: 0 }; + vi.stubGlobal("fetch", serveRanges(content, "\"d1\"", counter)); + + const fd = await openCached("https://cache.example/dedupe.zkey", { blockSize: BLOCK, dbName }); + const afterOpen = counter.count; // the probe + // 8 concurrent direct reads inside block 0 (each is a boundary-case + // miss); without dedupe each fetched the whole block independently + const reads = []; + for (let k = 0; k < 8; k++) reads.push(fd.read(128 * 1024, k * 64 * 1024)); + const got = await Promise.all(reads); + for (let k = 0; k < 8; k++) { + expect([...got[k].slice(0, 8)]).toEqual([...content.subarray(k * 64 * 1024, k * 64 * 1024 + 8)]); + } + await fd.close(); + // block 0 once, plus at most one more block touched by the last read + expect(counter.count - afterOpen).toBeLessThanOrEqual(2); + }); + it("without a strong validator the reader works but nothing persists", async () => { const dbName = "fastfile-test-noval"; await clearDb(dbName); From 8a68e0695425e902cb3ab86955a52c352163fc93 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 18:01:15 +0100 Subject: [PATCH 30/41] fix: cap concurrent range fetches below the browser connection limit Browsers cap HTTP/1.1 connections per host (Chrome: 6). A prover's MSM read-ahead can put more range requests in flight; on a bandwidth-limited link the extras queue behind stalled not-yet-consumed responses and the connection pool deadlocks. Observed streaming a 1 GiB zkey in Chromium over a shaped 100 Mbps link: the proof froze with exactly six open responses -- the consumer awaited request 7, Chrome queued it until a connection freed, and connections only free when the first six bodies are consumed. Unshaped links never hit it (the server finishes sending, so connections free regardless of consumption), which is why localhost testing missed it. httpReadRangeInto now takes one of 4 semaphore slots and holds it until the response body is fully consumed. Slotted reads complete independently of one another, so the semaphore itself cannot deadlock. Regression test: 12 concurrent direct reads through a delayed fetch stub never exceed 4 in flight. 80 tests pass; lint clean; bundles rebuilt. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- build/browser/browser.esm.js | 61 ++++++++++++++++++++++------------- build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/httpfile.js | 29 +++++++++++++++++ test/httpfile.test.js | 35 ++++++++++++++++++++ 5 files changed, 104 insertions(+), 25 deletions(-) diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index 98a57f0..08a5dc8 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -426,7 +426,7 @@ async function N(e) { await r.arrayBuffer(); let a = F(r), o = null, s = async function(e, t, r, i) { if (!o) try { - return await I(n, a, e, t, r, i); + return await B(n, a, e, t, r, i); } catch (e) { if (!e || !e.degradeToFull) throw e; o = e.fullBodyPromise; @@ -469,7 +469,22 @@ function F(e) { let t = e.headers.get("etag"); return t && t.indexOf("W/") !== 0 ? t : e.headers.get("last-modified") || null; } -async function I(e, t, n, r, i, a) { +var I = 4, L = 0, R = []; +async function z(e) { + for (; L >= I;) await new Promise((e) => R.push(e)); + L++; + try { + return await e(); + } finally { + L--; + let e = R.shift(); + e && e(); + } +} +async function B(e, t, n, r, i, a) { + return z(() => V(e, t, n, r, i, a)); +} +async function V(e, t, n, r, i, a) { let o = { Range: "bytes=" + i + "-" + (i + a - 1) }; t && (o["If-Range"] = t); let s = await fetch(e, { headers: o }); @@ -479,11 +494,11 @@ async function I(e, t, n, r, i, a) { let t = /* @__PURE__ */ Error(e + ": origin ignored Range; degrading to full buffering"); throw t.degradeToFull = !0, t.fullBodyPromise = s.arrayBuffer().then((e) => new Uint8Array(e)), t; } - throw await L(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); + throw await H(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); } - if (s.status !== 206) throw await L(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); + if (s.status !== 206) throw await H(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); let c = s.headers.get("content-range"), l = c ? /bytes\s+(\d+)-(\d+)\//.exec(c) : null; - if (l && parseInt(l[1]) !== i) throw await L(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); + if (l && parseInt(l[1]) !== i) throw await H(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); let u = 0; if (s.body && typeof s.body.getReader == "function") { let t = s.body.getReader(); @@ -501,43 +516,43 @@ async function I(e, t, n, r, i, a) { } if (u !== a) throw Error(e + ": short range response (" + u + "/" + a + " bytes at " + i + ")"); } -async function L(e) { +async function H(e) { try { e.body && typeof e.body.cancel == "function" ? await e.body.cancel() : await e.arrayBuffer(); } catch {} } //#endregion //#region src/blobfile.js -var R = 1 << 20; -function z(e) { +var U = 1 << 20; +function W(e) { let t = e.blob, n = async function(e, n, r, i) { let a = await t.slice(r, r + i).arrayBuffer(); if (a.byteLength !== i) throw Error("short blob read (" + a.byteLength + "/" + i + " bytes at " + r + ")"); e.set(new Uint8Array(a), n); - }, r = Math.min(e.pageSize || R, R); + }, r = Math.min(e.pageSize || U, U); return new y(n, t.size, e.cacheSize, r); } //#endregion //#region src/fastfile.browser.js -function B() { +function G() { throw Error("File I/O is not supported in the browser"); } -function V(e) { +function K(e) { return e instanceof Uint8Array ? { type: "mem", data: e - } : (typeof e == "string" && B(), e); + } : (typeof e == "string" && G(), e); } -function H(e, t, n) { - if (e.type === "file" && B(), e.type === "mem") return t(e); +function q(e, t, n) { + if (e.type === "file" && G(), e.type === "mem") return t(e); if (e.type === "bigMem") return n(e); throw Error("Invalid FastFile type: " + e.type); } -function U(t) { - return H(V(t), e, l); +function J(t) { + return q(K(t), e, l); } -var W = U; -async function G(e, n, r) { +var Y = J; +async function X(e, n, r) { return e instanceof Uint8Array && (e = { type: "mem", data: e @@ -551,11 +566,11 @@ async function G(e, n, r) { url: e, cacheSize: n, pageSize: r - }), e.type === "http" ? await N(e) : e.type === "blob" ? z(e) : H(e, t, u); + }), e.type === "http" ? await N(e) : e.type === "blob" ? W(e) : q(e, t, u); } -function K(e) { - return typeof e == "string" && B(), H(e, n, d); +function Z(e) { + return typeof e == "string" && G(), q(e, n, d); } -var q = K; +var Q = Z; //#endregion -export { W as createNoOverride, U as createOverride, G as readExisting, K as readWriteExisting, q as readWriteExistingOrCreate }; +export { Y as createNoOverride, J as createOverride, X as readExisting, Z as readWriteExisting, Q as readWriteExistingOrCreate }; diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index dc7523a..0cadd0b 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var N=65536;async function P(e){let t=e.url,r=await fetch(t,{headers:{Range:`bytes=0-0`}});if(r.status===206){let n=r.headers.get(`content-range`),i=n?/\/(\d+)\s*$/.exec(n):null;if(i){let n=parseInt(i[1]);await r.arrayBuffer();let a=I(r),o=null,s=async function(e,n,r,i){if(!o)try{return await L(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return e.persistentCache&&(s=await M(s,{fileKey:t,validator:a,totalSize:n,options:e.persistentCache})),new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await F(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await F(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function L(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await R(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await R(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await R(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function R(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var z=1<<20;function B(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||z,z);return new b(n,t.size,e.cacheSize,r)}function V(){throw Error(`File I/O is not supported in the browser`)}function H(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&V(),e)}function U(e,t,n){if(e.type===`file`&&V(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function W(e){return U(H(e),t,u)}var G=W;async function K(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await P(e):e.type===`blob`?B(e):U(e,n,d)}function q(e){return typeof e==`string`&&V(),U(e,r,f)}var J=q;return e.createNoOverride=G,e.createOverride=W,e.readExisting=K,e.readWriteExisting=q,e.readWriteExistingOrCreate=J,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var N=65536;async function P(e){let t=e.url,r=await fetch(t,{headers:{Range:`bytes=0-0`}});if(r.status===206){let n=r.headers.get(`content-range`),i=n?/\/(\d+)\s*$/.exec(n):null;if(i){let n=parseInt(i[1]);await r.arrayBuffer();let a=I(r),o=null,s=async function(e,n,r,i){if(!o)try{return await V(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return e.persistentCache&&(s=await M(s,{fileKey:t,validator:a,totalSize:n,options:e.persistentCache})),new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await F(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await F(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var L=4,R=0,z=[];async function B(e){for(;R>=L;)await new Promise(e=>z.push(e));R++;try{return await e()}finally{R--;let e=z.shift();e&&e()}}async function V(e,t,n,r,i,a){return B(()=>H(e,t,n,r,i,a))}async function H(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await U(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await U(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await U(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function U(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var W=1<<20;function G(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||W,W);return new b(n,t.size,e.cacheSize,r)}function K(){throw Error(`File I/O is not supported in the browser`)}function q(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&K(),e)}function J(e,t,n){if(e.type===`file`&&K(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Y(e){return J(q(e),t,u)}var X=Y;async function Z(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await P(e):e.type===`blob`?G(e):J(e,n,d)}function Q(e){return typeof e==`string`&&K(),J(e,r,f)}var $=Q;return e.createNoOverride=X,e.createOverride=Y,e.readExisting=Z,e.readWriteExisting=Q,e.readWriteExistingOrCreate=$,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index 5db338b..12c240b 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},k=1<<20,A=8192,j=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||A,this.maxPagesLoaded=Math.floor((n||k)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,M(r);r.push(i),n+=e}return t.pos=n,M(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function M(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return I.set(e,t),t.catch(()=>I.delete(e)),t}function R(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function z(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function B(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function V(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(B(t,0,1/0)),await z(n)}async function H(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await R(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(B(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await z(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([R(d.getAllKeys()),R(d.getAll())]);await z(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await V(e,t.key),m-=t.meta.bytes}}async function U(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||N,s=a.maxBytes||P,c=a.dbName||F;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await L(c),await H(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=B(n,e,t),[o,s]=await Promise.all([R(i.getAllKeys(a)),R(i.getAll(a))]);await z(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await z(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var W=65536;async function G(e){let t=e.url,n=await fetch(t,{headers:{Range:`bytes=0-0`}});if(n.status===206){let r=n.headers.get(`content-range`),i=r?/\/(\d+)\s*$/.exec(r):null;if(i){let r=parseInt(i[1]);await n.arrayBuffer();let a=q(n),o=null,s=async function(e,n,r,i){if(!o)try{return await J(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||W,W);return e.persistentCache&&(s=await U(s,{fileKey:t,validator:a,totalSize:r,options:e.persistentCache})),new j(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await K(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await K(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function K(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function q(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}async function J(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=q(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new j(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ee=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function te(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ne(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function re(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ee||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await G(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function ie(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function ae(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ne,exports.createOverride=te,exports.readExisting=re,exports.readWriteExisting=ie,exports.readWriteExistingOrCreate=ae; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},O=1<<20,k=8192,A=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||k,this.maxPagesLoaded=Math.floor((n||O)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,j(r);r.push(i),n+=e}return t.pos=n,j(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function j(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return P.set(e,t),t.catch(()=>P.delete(e)),t}function I(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function L(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function R(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ne(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(R(t,0,1/0)),await L(n)}async function re(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await I(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(R(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await L(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([I(d.getAllKeys()),I(d.getAll())]);await L(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ne(e,t.key),m-=t.meta.bytes}}async function z(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||te,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await F(c),await re(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=R(n,e,t),[o,s]=await Promise.all([I(i.getAllKeys(a)),I(i.getAll(a))]);await L(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await L(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var B=65536;async function V(e){let t=e.url,n=await fetch(t,{headers:{Range:`bytes=0-0`}});if(n.status===206){let r=n.headers.get(`content-range`),i=r?/\/(\d+)\s*$/.exec(r):null;if(i){let r=parseInt(i[1]);await n.arrayBuffer();let a=U(n),o=null,s=async function(e,n,r,i){if(!o)try{return await J(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||B,B);return e.persistentCache&&(s=await z(s,{fileKey:t,validator:a,totalSize:r,options:e.persistentCache})),new A(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await H(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await H(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function H(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function U(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var W=4,G=0,K=[];async function q(e){for(;G>=W;)await new Promise(e=>K.push(e));G++;try{return await e()}finally{G--;let e=K.shift();e&&e()}}async function J(e,t,n,r,i,a){return q(()=>Y(e,t,n,r,i,a))}async function Y(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=U(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await X(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await X(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await X(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function X(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var Z=1<<20;function ie(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||Z,Z);return new A(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ae=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function oe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function se(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function ce(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ae||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await V(e);if(e.type==`blob`)return ie(e);throw Error(`Invalid FastFile type: `+e.type)}function le(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function ue(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=se,exports.createOverride=oe,exports.readExisting=ce,exports.readWriteExisting=le,exports.readWriteExistingOrCreate=ue; \ No newline at end of file diff --git a/src/httpfile.js b/src/httpfile.js index a154947..7ac411f 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -120,7 +120,36 @@ function strongValidator(res) { return null; } +// Browsers cap HTTP/1.1 connections per host (Chrome: 6). A prover's +// read-ahead can put more range requests in flight than that; on a +// bandwidth-limited link the extra requests then queue behind stalled +// not-yet-consumed responses and the connection pool deadlocks -- observed +// as a browser proof of a 1 GiB zkey freezing with exactly six open +// responses. Cap concurrent range fetches below the browser's limit, and +// hold each slot until the response body is fully consumed (that is when +// the connection actually frees). +const MAX_CONCURRENT_RANGE_FETCHES = 4; +let activeRangeFetches = 0; +const rangeFetchQueue = []; +async function withRangeFetchSlot(fn) { + while (activeRangeFetches >= MAX_CONCURRENT_RANGE_FETCHES) { + await new Promise((res) => rangeFetchQueue.push(res)); + } + activeRangeFetches++; + try { + return await fn(); + } finally { + activeRangeFetches--; + const next = rangeFetchQueue.shift(); + if (next) next(); + } +} + async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { + return withRangeFetchSlot(() => httpReadRangeIntoUnlimited(url, validator, dst, dstOffset, pos, len)); +} + +async function httpReadRangeIntoUnlimited(url, validator, dst, dstOffset, pos, len) { const headers = { "Range": "bytes=" + pos + "-" + (pos + len - 1) }; if (validator) headers["If-Range"] = validator; const res = await fetch(url, { headers: headers }); diff --git a/test/httpfile.test.js b/test/httpfile.test.js index d5bc8b8..b229e79 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -77,6 +77,41 @@ function startServer(state, opts) { describe("fastfile testing suite for httpfile", function () { + it("caps concurrent range fetches below the browser connection limit", async () => { + const data = makeData(1 << 20); + let inFlight = 0, maxInFlight = 0; + const fakeFetch = async (url, opts) => { + const m = /bytes=(\d+)-(\d+)/.exec((opts && opts.headers && opts.headers["Range"]) || ""); + const from = m ? parseInt(m[1]) : 0; + const to = m ? Math.min(parseInt(m[2]), data.length - 1) : data.length - 1; + inFlight++; maxInFlight = Math.max(maxInFlight, inFlight); + await new Promise((r) => setTimeout(r, 20)); + inFlight--; + return { + ok: true, status: 206, + headers: new Map([ + ["content-range", `bytes ${from}-${to}/${data.length}`], + ["etag", "\"cap\""], + ]), + arrayBuffer: async () => data.slice(from, to + 1).buffer, + body: null, + }; + }; + fakeFetch.headersGet = true; + const realFetch = globalThis.fetch; + globalThis.fetch = (u, o) => fakeFetch(u, o).then((r) => ({ ...r, headers: { get: (k) => r.headers.get(k.toLowerCase()) } })); + try { + const fd = await fastFile.readExisting({ type: "http", url: "http://cap.example/f.bin" }); + const reads = []; + for (let k = 0; k < 12; k++) reads.push(fd.read(70000, k * 80000)); // > pageSize: direct path + await Promise.all(reads); + await fd.close(); + assert.ok(maxInFlight <= 4, "expected <= 4 concurrent range fetches, saw " + maxInFlight); + } finally { + globalThis.fetch = realFetch; + } + }); + it("persistentCache is a no-op where IndexedDB does not exist (Node)", async () => { const data = makeData(1 << 18); const srv = await startServer({ data: data, etag: "\"v1\"" }); From bf54b79ce4d6b954181b9ed6e7ebc17acef3d86b Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 18:24:58 +0100 Subject: [PATCH 31/41] feat: persistent cache covers full-download servers too Previously only the Range/206 streaming path populated and used the IndexedDB cache; a Range-less origin re-shipped the whole file every session (the probe itself is a full download there, so the cache never got a chance). - readExisting probes conditionally when it holds cached meta for the URL (If-None-Match for ETags, If-Modified-Since for Last-Modified): an unchanged file answers a bodyless 304 and the reader is built over the cache -- zero body bytes even from a server that ignores Range. Missing blocks fall back to range requests, or the degrade-to-full path when the origin turns out to ignore Range. - The 200 full-body path persists the downloaded body (persistFullBody: batched block writes, exact meta.bytes) so the next session hits the 304 path. - The streaming builder is extracted (buildStreamingFile) and shared by the fresh-206 and warm-304 paths; a changed validator falls through to fresh content and re-persists as before. New in-Chromium test: cold 200 persists the body; warm open makes exactly one bodyless 304 probe and reads locally across block boundaries and the short tail; a changed ETag serves and re-persists the new content, warm on the following open. 81 tests pass; lint clean; bundles rebuilt. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- README.md | 5 +- build/browser/browser.esm.js | 183 ++++++++++++++++++++++------------ build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/httpfile.js | 120 +++++++++++++++------- src/idbcache.js | 68 ++++++++++++- test/idbcache.browser.test.js | 71 +++++++++++++ 7 files changed, 344 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 8091d7f..92f6c78 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,10 @@ const f = await fastFile.readExisting({ Entries are keyed by URL plus the server's strong validator (ETag / Last-Modified) and are dropped when the remote file changes; without a -strong validator nothing is cached. Total storage is bounded (`maxBytes`, +strong validator nothing is cached. Works for Range-less servers too: the +first session persists the fully-downloaded body, and later sessions probe +conditionally (If-None-Match / If-Modified-Since) -- an unchanged file +answers a bodyless 304 and every read is served locally. Total storage is bounded (`maxBytes`, default 512 MiB) with least-recently-opened files evicted first. Where IndexedDB is unavailable (Node, private windows, blocked storage) the option is a silent no-op and reads stream as usual. diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index 08a5dc8..a664a72 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -414,50 +414,105 @@ async function j(e, t) { for (let e of l) m.delete(e.index); }; } +async function M(e) { + let t = (typeof e.options == "object" && e.options || {}).dbName || C; + if (typeof indexedDB > "u") return null; + try { + let n = (await T(t)).transaction("files", "readonly"), r = await E(n.objectStore("files").get(e.fileKey)); + return await D(n), r ? { + validator: r.validator, + totalSize: r.totalSize, + blockSize: r.blockSize, + bytes: r.bytes + } : null; + } catch { + return null; + } +} +async function N(e) { + let { fileKey: t, validator: n, totalSize: r, data: i } = e, a = typeof e.options == "object" && e.options || {}, o = a.blockSize || x, s = a.maxBytes || S, c = a.dbName || C; + if (typeof indexedDB > "u" || !n) return !1; + try { + let e = await T(c); + await A(e, t, n, r, o, s); + let a = Math.ceil(r / o); + for (let n = 0; n < a; n += 64) { + let s = e.transaction("blocks", "readwrite"), c = s.objectStore("blocks"); + for (let e = n; e < Math.min(n + 64, a); e++) { + let n = e * o; + c.put(i.slice(n, Math.min(n + o, r)), [t, e]); + } + await D(s); + } + let l = e.transaction("files", "readwrite"), u = l.objectStore("files"), d = await E(u.get(t)); + return d && (d.bytes = r, d.lastUsed = Date.now(), u.put(d, t)), await D(l), !0; + } catch { + return !1; + } +} //#endregion //#region src/httpfile.js -var M = 65536; -async function N(e) { - let n = e.url, r = await fetch(n, { headers: { Range: "bytes=0-0" } }); - if (r.status === 206) { - let t = r.headers.get("content-range"), i = t ? /\/(\d+)\s*$/.exec(t) : null; - if (i) { - let t = parseInt(i[1]); - await r.arrayBuffer(); - let a = F(r), o = null, s = async function(e, t, r, i) { - if (!o) try { - return await B(n, a, e, t, r, i); - } catch (e) { - if (!e || !e.degradeToFull) throw e; - o = e.fullBodyPromise; - } - let s = await o; - if (r + i > s.byteLength) throw Error(n + ": read past the end of the buffered body"); - e.set(s.subarray(r, r + i), t); - }, c = Math.min(e.pageSize || M, M); - return e.persistentCache && (s = await j(s, { - fileKey: n, - validator: a, - totalSize: t, - options: e.persistentCache - })), new y(s, t, e.cacheSize, c); +var P = 65536; +async function F(e) { + let n = e.url, r = e.persistentCache ? await M({ + fileKey: n, + options: e.persistentCache + }) : null, i = { Range: "bytes=0-0" }; + r && r.validator && (r.validator[0] === "\"" || r.validator.indexOf("W/") === 0 ? i["If-None-Match"] = r.validator : i["If-Modified-Since"] = r.validator); + let a = await fetch(n, { headers: i }); + if (a.status === 304) return await G(a), await I(n, r.validator, r.totalSize, e); + if (a.status === 206) { + let t = a.headers.get("content-range"), r = t ? /\/(\d+)\s*$/.exec(t) : null; + if (r) { + let t = parseInt(r[1]); + return await a.arrayBuffer(), await I(n, R(a), t, e); } - return await r.arrayBuffer(), await P(n); + return await a.arrayBuffer(), await L(n); } - if (!r.ok && r.status !== 416) throw Error("HTTP " + r.status + " fetching " + n); - if (r.status === 416) { - let e = r.headers.get("content-range"); + if (!a.ok && a.status !== 416) throw Error("HTTP " + a.status + " fetching " + n); + if (a.status === 416) { + let e = a.headers.get("content-range"); return e && /\/0\s*$/.test(e) ? t({ type: "mem", data: /* @__PURE__ */ new Uint8Array() - }) : await P(n); + }) : await L(n); + } + let o = new Uint8Array(await a.arrayBuffer()); + if (e.persistentCache) { + let t = R(a); + t && await N({ + fileKey: n, + validator: t, + totalSize: o.length, + options: e.persistentCache, + data: o + }); } return t({ type: "mem", - data: new Uint8Array(await r.arrayBuffer()) + data: o }); } -async function P(e) { +async function I(e, t, n, r) { + let i = null, a = async function(n, r, a, o) { + if (!i) try { + return await U(e, t, n, r, a, o); + } catch (e) { + if (!e || !e.degradeToFull) throw e; + i = e.fullBodyPromise; + } + let s = await i; + if (a + o > s.byteLength) throw Error(e + ": read past the end of the buffered body"); + n.set(s.subarray(a, a + o), r); + }, o = Math.min(r.pageSize || P, P); + return r.persistentCache && (a = await j(a, { + fileKey: e, + validator: t, + totalSize: n, + options: r.persistentCache + })), new y(a, n, r.cacheSize, o); +} +async function L(e) { let n = await fetch(e); if (!n.ok) throw Error("HTTP " + n.status + " fetching " + e); return t({ @@ -465,40 +520,40 @@ async function P(e) { data: new Uint8Array(await n.arrayBuffer()) }); } -function F(e) { +function R(e) { let t = e.headers.get("etag"); return t && t.indexOf("W/") !== 0 ? t : e.headers.get("last-modified") || null; } -var I = 4, L = 0, R = []; -async function z(e) { - for (; L >= I;) await new Promise((e) => R.push(e)); - L++; +var z = 4, B = 0, V = []; +async function H(e) { + for (; B >= z;) await new Promise((e) => V.push(e)); + B++; try { return await e(); } finally { - L--; - let e = R.shift(); + B--; + let e = V.shift(); e && e(); } } -async function B(e, t, n, r, i, a) { - return z(() => V(e, t, n, r, i, a)); +async function U(e, t, n, r, i, a) { + return H(() => W(e, t, n, r, i, a)); } -async function V(e, t, n, r, i, a) { +async function W(e, t, n, r, i, a) { let o = { Range: "bytes=" + i + "-" + (i + a - 1) }; t && (o["If-Range"] = t); let s = await fetch(e, { headers: o }); if (s.status === 200) { - let n = F(s); + let n = R(s); if (!t || n && n === t) { let t = /* @__PURE__ */ Error(e + ": origin ignored Range; degrading to full buffering"); throw t.degradeToFull = !0, t.fullBodyPromise = s.arrayBuffer().then((e) => new Uint8Array(e)), t; } - throw await H(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); + throw await G(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); } - if (s.status !== 206) throw await H(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); + if (s.status !== 206) throw await G(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); let c = s.headers.get("content-range"), l = c ? /bytes\s+(\d+)-(\d+)\//.exec(c) : null; - if (l && parseInt(l[1]) !== i) throw await H(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); + if (l && parseInt(l[1]) !== i) throw await G(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); let u = 0; if (s.body && typeof s.body.getReader == "function") { let t = s.body.getReader(); @@ -516,43 +571,43 @@ async function V(e, t, n, r, i, a) { } if (u !== a) throw Error(e + ": short range response (" + u + "/" + a + " bytes at " + i + ")"); } -async function H(e) { +async function G(e) { try { e.body && typeof e.body.cancel == "function" ? await e.body.cancel() : await e.arrayBuffer(); } catch {} } //#endregion //#region src/blobfile.js -var U = 1 << 20; -function W(e) { +var K = 1 << 20; +function q(e) { let t = e.blob, n = async function(e, n, r, i) { let a = await t.slice(r, r + i).arrayBuffer(); if (a.byteLength !== i) throw Error("short blob read (" + a.byteLength + "/" + i + " bytes at " + r + ")"); e.set(new Uint8Array(a), n); - }, r = Math.min(e.pageSize || U, U); + }, r = Math.min(e.pageSize || K, K); return new y(n, t.size, e.cacheSize, r); } //#endregion //#region src/fastfile.browser.js -function G() { +function J() { throw Error("File I/O is not supported in the browser"); } -function K(e) { +function Y(e) { return e instanceof Uint8Array ? { type: "mem", data: e - } : (typeof e == "string" && G(), e); + } : (typeof e == "string" && J(), e); } -function q(e, t, n) { - if (e.type === "file" && G(), e.type === "mem") return t(e); +function X(e, t, n) { + if (e.type === "file" && J(), e.type === "mem") return t(e); if (e.type === "bigMem") return n(e); throw Error("Invalid FastFile type: " + e.type); } -function J(t) { - return q(K(t), e, l); +function Z(t) { + return X(Y(t), e, l); } -var Y = J; -async function X(e, n, r) { +var Q = Z; +async function ee(e, n, r) { return e instanceof Uint8Array && (e = { type: "mem", data: e @@ -566,11 +621,11 @@ async function X(e, n, r) { url: e, cacheSize: n, pageSize: r - }), e.type === "http" ? await N(e) : e.type === "blob" ? W(e) : q(e, t, u); + }), e.type === "http" ? await F(e) : e.type === "blob" ? q(e) : X(e, t, u); } -function Z(e) { - return typeof e == "string" && G(), q(e, n, d); +function $(e) { + return typeof e == "string" && J(), X(e, n, d); } -var Q = Z; +var te = $; //#endregion -export { Y as createNoOverride, J as createOverride, X as readExisting, Z as readWriteExisting, Q as readWriteExistingOrCreate }; +export { Q as createNoOverride, Z as createOverride, ee as readExisting, $ as readWriteExisting, te as readWriteExistingOrCreate }; diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index 0cadd0b..2eb1f6f 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var N=65536;async function P(e){let t=e.url,r=await fetch(t,{headers:{Range:`bytes=0-0`}});if(r.status===206){let n=r.headers.get(`content-range`),i=n?/\/(\d+)\s*$/.exec(n):null;if(i){let n=parseInt(i[1]);await r.arrayBuffer();let a=I(r),o=null,s=async function(e,n,r,i){if(!o)try{return await V(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||N,N);return e.persistentCache&&(s=await M(s,{fileKey:t,validator:a,totalSize:n,options:e.persistentCache})),new b(s,n,e.cacheSize,c)}return await r.arrayBuffer(),await F(t)}if(!r.ok&&r.status!==416)throw Error(`HTTP `+r.status+` fetching `+t);if(r.status===416){let e=r.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?n({type:`mem`,data:new Uint8Array}):await F(t)}return n({type:`mem`,data:new Uint8Array(await r.arrayBuffer())})}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function I(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var L=4,R=0,z=[];async function B(e){for(;R>=L;)await new Promise(e=>z.push(e));R++;try{return await e()}finally{R--;let e=z.shift();e&&e()}}async function V(e,t,n,r,i,a){return B(()=>H(e,t,n,r,i,a))}async function H(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=I(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await U(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await U(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await U(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function U(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var W=1<<20;function G(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||W,W);return new b(n,t.size,e.cacheSize,r)}function K(){throw Error(`File I/O is not supported in the browser`)}function q(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&K(),e)}function J(e,t,n){if(e.type===`file`&&K(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Y(e){return J(q(e),t,u)}var X=Y;async function Z(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await P(e):e.type===`blob`?G(e):J(e,n,d)}function Q(e){return typeof e==`string`&&K(),J(e,r,f)}var $=Q;return e.createNoOverride=X,e.createOverride=Y,e.readExisting=Z,e.readWriteExisting=Q,e.readWriteExistingOrCreate=$,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function N(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function P(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await j(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||F,F);return r.persistentCache&&(a=await M(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new b(a,n,r.cacheSize,o)}async function R(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function z(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var B=4,V=0,H=[];async function U(e){for(;V>=B;)await new Promise(e=>H.push(e));V++;try{return await e()}finally{V--;let e=H.shift();e&&e()}}async function W(e,t,n,r,i,a){return U(()=>G(e,t,n,r,i,a))}async function G(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=z(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await K(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await K(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await K(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ee=Q;async function te(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await I(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ne=$;return e.createNoOverride=ee,e.createOverride=Q,e.readExisting=te,e.readWriteExisting=$,e.readWriteExistingOrCreate=ne,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index 12c240b..d373c50 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},O=1<<20,k=8192,A=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||k,this.maxPagesLoaded=Math.floor((n||O)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,j(r);r.push(i),n+=e}return t.pos=n,j(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function j(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return P.set(e,t),t.catch(()=>P.delete(e)),t}function I(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function L(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function R(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ne(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(R(t,0,1/0)),await L(n)}async function re(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await I(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(R(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await L(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([I(d.getAllKeys()),I(d.getAll())]);await L(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ne(e,t.key),m-=t.meta.bytes}}async function z(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||te,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await F(c),await re(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=R(n,e,t),[o,s]=await Promise.all([I(i.getAllKeys(a)),I(i.getAll(a))]);await L(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await L(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}var B=65536;async function V(e){let t=e.url,n=await fetch(t,{headers:{Range:`bytes=0-0`}});if(n.status===206){let r=n.headers.get(`content-range`),i=r?/\/(\d+)\s*$/.exec(r):null;if(i){let r=parseInt(i[1]);await n.arrayBuffer();let a=U(n),o=null,s=async function(e,n,r,i){if(!o)try{return await J(t,a,e,n,r,i)}catch(e){if(!e||!e.degradeToFull)throw e;o=e.fullBodyPromise}let s=await o;if(r+i>s.byteLength)throw Error(t+`: read past the end of the buffered body`);e.set(s.subarray(r,r+i),n)},c=Math.min(e.pageSize||B,B);return e.persistentCache&&(s=await z(s,{fileKey:t,validator:a,totalSize:r,options:e.persistentCache})),new A(s,r,e.cacheSize,c)}return await n.arrayBuffer(),await H(t)}if(!n.ok&&n.status!==416)throw Error(`HTTP `+n.status+` fetching `+t);if(n.status===416){let e=n.headers.get(`content-range`);return e&&/\/0\s*$/.test(e)?p({type:`mem`,data:new Uint8Array}):await H(t)}return p({type:`mem`,data:new Uint8Array(await n.arrayBuffer())})}async function H(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function U(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var W=4,G=0,K=[];async function q(e){for(;G>=W;)await new Promise(e=>K.push(e));G++;try{return await e()}finally{G--;let e=K.shift();e&&e()}}async function J(e,t,n,r,i,a){return q(()=>Y(e,t,n,r,i,a))}async function Y(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=U(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await X(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await X(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await X(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function X(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var Z=1<<20;function ie(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||Z,Z);return new A(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ae=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function oe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function se(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function ce(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ae||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await V(e);if(e.type==`blob`)return ie(e);throw Error(`Invalid FastFile type: `+e.type)}function le(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function ue(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=se,exports.createOverride=oe,exports.readExisting=ce,exports.readWriteExisting=le,exports.readWriteExistingOrCreate=ue; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},te=1<<20,ne=8192,O=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||ne,this.maxPagesLoaded=Math.floor((n||te)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,k(r);r.push(i),n+=e}return t.pos=n,k(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function k(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return N.set(e,t),t.catch(()=>N.delete(e)),t}function F(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function I(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function L(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function re(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(L(t,0,1/0)),await I(n)}async function R(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await F(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(L(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await I(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([F(d.getAllKeys()),F(d.getAll())]);await I(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await re(e,t.key),m-=t.meta.bytes}}async function z(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await P(c),await R(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=L(n,e,t),[o,s]=await Promise.all([F(i.getAllKeys(a)),F(i.getAll(a))]);await I(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await I(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function B(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||M;if(typeof indexedDB>`u`)return null;try{let n=(await P(t)).transaction(`files`,`readonly`),r=await F(n.objectStore(`files`).get(e.fileKey));return await I(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function V(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!n)return!1;try{let e=await P(c);await R(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||H,H);return r.persistentCache&&(a=await z(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new O(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var K=4,q=0,J=[];async function ae(e){for(;q>=K;)await new Promise(e=>J.push(e));q++;try{return await e()}finally{q--;let e=J.shift();e&&e()}}async function oe(e,t,n,r,i,a){return ae(()=>se(e,t,n,r,i,a))}async function se(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=G(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new O(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ce=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function le(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ue(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function de(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ce||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await ie(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function pe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ue,exports.createOverride=le,exports.readExisting=de,exports.readWriteExisting=fe,exports.readWriteExistingOrCreate=pe; \ No newline at end of file diff --git a/src/httpfile.js b/src/httpfile.js index 7ac411f..487a568 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -19,7 +19,7 @@ import { RangeFile } from "./rangefile.js"; import * as memFile from "./memfile.js"; -import { wrapWithPersistentCache } from "./idbcache.js"; +import { wrapWithPersistentCache, peekPersistentMeta, persistFullBody } from "./idbcache.js"; // Callers tune cacheSize/pageSize for the disk backend (snarkjs passes 8 MiB // pages); over HTTP a page that large turns a 4-byte header read into a @@ -32,7 +32,30 @@ const MAX_HTTP_PAGE_SIZE = 1 << 16; export async function readExisting(o) { const url = o.url; - const probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); + // With the persistent cache on, probe conditionally: if we hold cached + // blocks for this URL, an unchanged file answers 304 with no body -- + // which is what makes warm starts work even against servers that ignore + // Range and would otherwise ship the entire file in the probe response. + const cacheMeta = o.persistentCache + ? await peekPersistentMeta({ fileKey: url, options: o.persistentCache }) + : null; + const probeHeaders = { "Range": "bytes=0-0" }; + if (cacheMeta && cacheMeta.validator) { + if (cacheMeta.validator[0] === "\"" || cacheMeta.validator.indexOf("W/") === 0) { + probeHeaders["If-None-Match"] = cacheMeta.validator; + } else { + probeHeaders["If-Modified-Since"] = cacheMeta.validator; + } + } + const probe = await fetch(url, { headers: probeHeaders }); + + if (probe.status === 304) { + // Unchanged since it was cached. Serve through the cache; missing + // blocks go over range requests, or through the degrade-to-full path + // when the origin turns out to ignore Range. + await abandonBody(probe); + return await buildStreamingFile(url, cacheMeta.validator, cacheMeta.totalSize, o); + } if (probe.status === 206) { const contentRange = probe.headers.get("content-range"); @@ -42,42 +65,7 @@ export async function readExisting(o) { // Drain the 1-byte probe body so the connection can be reused. await probe.arrayBuffer(); const validator = strongValidator(probe); - // Degrade-to-buffering escape hatch: a 206 probe answer can come - // from an intermediary (browsers satisfy ranges out of their own - // HTTP cache of a full 200), while the origin itself ignores - // Range. When a later range request gets a 200 whose strong - // validator still matches, the file is unchanged -- buffer that - // full body once and serve every subsequent read from it. - let fullBody = null; - let readRangeInto = async function (dst, dstOffset, pos, len) { - if (!fullBody) { - try { - return await httpReadRangeInto(url, validator, dst, dstOffset, pos, len); - } catch (err) { - if (!err || !err.degradeToFull) throw err; - fullBody = err.fullBodyPromise; - } - } - const data = await fullBody; - if (pos + len > data.byteLength) { - throw new Error(url + ": read past the end of the buffered body"); - } - dst.set(data.subarray(pos, pos + len), dstOffset); - }; - const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); - if (o.persistentCache) { - // Opt-in warm start: persist fetched blocks in IndexedDB so - // a later session against the same URL reads locally. Keyed - // on the strong validator -- without one the wrapper - // declines and this stays a plain streaming reader. - readRangeInto = await wrapWithPersistentCache(readRangeInto, { - fileKey: url, - validator: validator, - totalSize: totalSize, - options: o.persistentCache, - }); - } - return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); + return await buildStreamingFile(url, validator, totalSize, o); } // 206 but total size unknown (Content-Range: bytes 0-0/*): we cannot // do bounded positioned reads; refetch whole and buffer. @@ -100,9 +88,65 @@ export async function readExisting(o) { // 200: the server ignored Range and sent the whole file; reuse this body. const data = new Uint8Array(await probe.arrayBuffer()); + if (o.persistentCache) { + // Persist the body so the next session's conditional probe gets a + // bodyless 304 and reads locally. Best-effort, validator-keyed. + const validator = strongValidator(probe); + if (validator) { + await persistFullBody({ + fileKey: url, + validator: validator, + totalSize: data.length, + options: o.persistentCache, + data: data, + }); + } + } return memFile.readExisting({ type: "mem", data: data }); } +// Positioned reader over range requests with the degrade-to-buffering escape +// hatch, optionally wrapped with the persistent block cache, presented as a +// RangeFile. Shared by the fresh (206 probe) and warm (304 probe) paths. +async function buildStreamingFile(url, validator, totalSize, o) { + // Degrade-to-buffering escape hatch: a 206 probe answer can come from an + // intermediary (browsers satisfy ranges out of their own HTTP cache of a + // full 200), while the origin itself ignores Range. When a later range + // request gets a 200 whose strong validator still matches, the file is + // unchanged -- buffer that full body once and serve every subsequent + // read from it. + let fullBody = null; + let readRangeInto = async function (dst, dstOffset, pos, len) { + if (!fullBody) { + try { + return await httpReadRangeInto(url, validator, dst, dstOffset, pos, len); + } catch (err) { + if (!err || !err.degradeToFull) throw err; + fullBody = err.fullBodyPromise; + } + } + const data = await fullBody; + if (pos + len > data.byteLength) { + throw new Error(url + ": read past the end of the buffered body"); + } + dst.set(data.subarray(pos, pos + len), dstOffset); + }; + const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); + if (o.persistentCache) { + // Opt-in warm start: persist fetched blocks in IndexedDB so a later + // session against the same URL reads locally. Keyed on the strong + // validator -- without one the wrapper declines and this stays a + // plain streaming reader. + readRangeInto = await wrapWithPersistentCache(readRangeInto, { + fileKey: url, + validator: validator, + totalSize: totalSize, + options: o.persistentCache, + }); + } + return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); +} + async function readFullyToMem(url) { const res = await fetch(url); if (!res.ok) throw new Error("HTTP " + res.status + " fetching " + url); diff --git a/src/idbcache.js b/src/idbcache.js index 2cea5a7..caf6551 100644 --- a/src/idbcache.js +++ b/src/idbcache.js @@ -26,8 +26,9 @@ // Prototype limitations (documented, deliberate): // - concurrent readers of the same cold block both fetch it (last write // wins; bytes are identical by the validator guard); -// - the degraded full-body path and the no-Range fallback are not -// persisted; only the streaming path populates the cache; +// - the 206-unknown-total and 416 fallbacks are not persisted; the +// streaming path and the Range-less 200 path both populate the cache +// (the latter via persistFullBody + conditional 304 probes); // - eviction only runs at open, not while a session writes new blocks. const DEFAULT_BLOCK_SIZE = 1 << 21; // 2 MiB: amortizes IDB per-op cost @@ -266,3 +267,66 @@ export async function wrapWithPersistentCache(readRangeInto, o) { for (const e of toPersist) inFlight.delete(e.index); }; } + +// Peek at what the cache knows about a URL before any network request. +// Returns { validator, totalSize, blockSize, bytes } or null. Used to send a +// conditional probe (If-None-Match / If-Modified-Since): an unchanged file +// answers 304 with no body, which is what makes warm starts work even +// against servers that ignore Range and would otherwise ship the whole file +// in the probe response. +export async function peekPersistentMeta(o) { + const opts = (typeof o.options === "object" && o.options) || {}; + const dbName = opts.dbName || DEFAULT_DB_NAME; + if (typeof indexedDB === "undefined") return null; + try { + const db = await openDb(dbName); + const tx = db.transaction("files", "readonly"); + const meta = await idbReq(tx.objectStore("files").get(o.fileKey)); + await txDone(tx); + return meta ? { validator: meta.validator, totalSize: meta.totalSize, blockSize: meta.blockSize, bytes: meta.bytes } : null; + } catch (e) { + return null; + } +} + +// Persist a fully-downloaded body (the Range-less server path) so the next +// session can warm-start from it. Blocks are written in bounded batches; +// meta.bytes is set to the exact total afterwards (overwrites make +// incremental accounting drift). Best-effort: returns false when the +// environment has no usable IndexedDB or a write fails. +export async function persistFullBody(o) { + const { fileKey, validator, totalSize, data } = o; + const opts = (typeof o.options === "object" && o.options) || {}; + const blockSize = opts.blockSize || DEFAULT_BLOCK_SIZE; + const maxBytes = opts.maxBytes || DEFAULT_MAX_BYTES; + const dbName = opts.dbName || DEFAULT_DB_NAME; + if (typeof indexedDB === "undefined" || !validator) return false; + try { + const db = await openDb(dbName); + await prepareFile(db, fileKey, validator, totalSize, blockSize, maxBytes); + const nBlocks = Math.ceil(totalSize / blockSize); + const BATCH = 64; + for (let b0 = 0; b0 < nBlocks; b0 += BATCH) { + const tx = db.transaction("blocks", "readwrite"); + const blocks = tx.objectStore("blocks"); + for (let b = b0; b < Math.min(b0 + BATCH, nBlocks); b++) { + const s = b * blockSize; + blocks.put(data.slice(s, Math.min(s + blockSize, totalSize)), [fileKey, b]); + } + await txDone(tx); + } + const tx = db.transaction("files", "readwrite"); + const files = tx.objectStore("files"); + const meta = await idbReq(files.get(fileKey)); + /* c8 ignore else -- meta vanishes only if another tab evicts mid-write */ + if (meta) { + meta.bytes = totalSize; + meta.lastUsed = Date.now(); + files.put(meta, fileKey); + } + await txDone(tx); + return true; + } catch (e) { + return false; + } +} diff --git a/test/idbcache.browser.test.js b/test/idbcache.browser.test.js index e121a6a..b581c47 100644 --- a/test/idbcache.browser.test.js +++ b/test/idbcache.browser.test.js @@ -182,6 +182,77 @@ describe("IndexedDB persistent block cache", function () { expect(counter.count - afterOpen).toBeLessThanOrEqual(2); }); + it("full-download servers: body persists cold, 304 probe serves warm from cache", async () => { + const dbName = "fastfile-test-fulldl"; + await clearDb(dbName); + const content = makeContent(11); + const etag = "\"full-v1\""; + const counter = { count: 0, bytes: 0 }; + // Range-less origin honoring conditional requests: If-None-Match + // match -> bodyless 304; otherwise 200 with the whole body. + const fullServer = (url, opts) => { + counter.count++; + const inm = opts && opts.headers && opts.headers["If-None-Match"]; + if (inm === etag) { + return Promise.resolve({ ok: false, status: 304, headers: new Headers({ etag }), + arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)), body: null }); + } + counter.bytes += content.length; + return Promise.resolve({ ok: true, status: 200, headers: new Headers({ etag }), + arrayBuffer: () => Promise.resolve(content.buffer.slice(0)), body: null }); + }; + vi.stubGlobal("fetch", fullServer); + + const url = "https://cache.example/fulldl.zkey"; + // cold: 200 with the whole body; body gets persisted + const fd = await openCached(url, { blockSize: BLOCK, dbName }); + expect(fd.totalSize).toBe(content.length); + const a = await fd.read(64, 12345); + expect([...a]).toEqual([...content.subarray(12345, 12345 + 64)]); + await fd.close(); + expect(counter.count).toBe(1); + expect(counter.bytes).toBe(content.length); + + // warm: conditional probe -> 304, no body; reads served from IDB + const fd2 = await openCached(url, { blockSize: BLOCK, dbName }); + expect(fd2.totalSize).toBe(content.length); + const b = await fd2.read(3 * BLOCK, BLOCK + 17); + expect([...b.slice(0, 8)]).toEqual([...content.subarray(BLOCK + 17, BLOCK + 25)]); + const tail = await fd2.read(100, FILE_SIZE - 100); + expect([...tail]).toEqual([...content.subarray(FILE_SIZE - 100)]); + await fd2.close(); + expect(counter.count).toBe(2); // just the 304 probe + expect(counter.bytes).toBe(content.length); // no second body + + // changed file: validator mismatch -> fresh 200, new bytes served + const v2 = makeContent(12); + const c2 = { count: 0, bytes: 0 }; + const etag2 = "\"full-v2\""; + vi.stubGlobal("fetch", (u, opts) => { + c2.count++; + const inm = opts && opts.headers && opts.headers["If-None-Match"]; + if (inm === etag2) { + return Promise.resolve({ ok: false, status: 304, headers: new Headers({ etag: etag2 }), + arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)), body: null }); + } + c2.bytes += v2.length; + return Promise.resolve({ ok: true, status: 200, headers: new Headers({ etag: etag2 }), + arrayBuffer: () => Promise.resolve(v2.buffer.slice(0)), body: null }); + }); + const fd3 = await openCached(url, { blockSize: BLOCK, dbName }); + const g = await fd3.read(64, 0); + expect([...g]).toEqual([...v2.subarray(0, 64)]); + await fd3.close(); + expect(c2.bytes).toBe(v2.length); // one fresh body, re-persisted + + // and the new version is warm on the next open + const fd4 = await openCached(url, { blockSize: BLOCK, dbName }); + const h = await fd4.read(64, 0); + expect([...h]).toEqual([...v2.subarray(0, 64)]); + await fd4.close(); + expect(c2.bytes).toBe(v2.length); // still only the one body + }); + it("without a strong validator the reader works but nothing persists", async () => { const dbName = "fastfile-test-noval"; await clearDb(dbName); From b588ea36907215630bed8f3c169b939494041d70 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 18:28:42 +0100 Subject: [PATCH 32/41] fix: retry the probe unconditionally when conditional headers fail CORS If-None-Match/If-Modified-Since are not CORS-safelisted; a cross-origin server whose preflight does not allow them fails the fetch outright. The cache must never break an open that works without it: on a thrown conditional probe, retry with the plain Range probe (costing only the warm-start for that origin). Server CORS requirements documented in the module header. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- build/browser/browser.esm.js | 8 +++++++- build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/httpfile.js | 20 ++++++++++++++++---- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index a664a72..0e61c4f 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -459,7 +459,13 @@ async function F(e) { options: e.persistentCache }) : null, i = { Range: "bytes=0-0" }; r && r.validator && (r.validator[0] === "\"" || r.validator.indexOf("W/") === 0 ? i["If-None-Match"] = r.validator : i["If-Modified-Since"] = r.validator); - let a = await fetch(n, { headers: i }); + let a; + try { + a = await fetch(n, { headers: i }); + } catch (e) { + if (!("If-None-Match" in i) && !("If-Modified-Since" in i)) throw e; + a = await fetch(n, { headers: { Range: "bytes=0-0" } }); + } if (a.status === 304) return await G(a), await I(n, r.validator, r.totalSize, e); if (a.status === 206) { let t = a.headers.get("content-range"), r = t ? /\/(\d+)\s*$/.exec(t) : null; diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index 2eb1f6f..3140611 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function N(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function P(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await j(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||F,F);return r.persistentCache&&(a=await M(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new b(a,n,r.cacheSize,o)}async function R(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function z(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var B=4,V=0,H=[];async function U(e){for(;V>=B;)await new Promise(e=>H.push(e));V++;try{return await e()}finally{V--;let e=H.shift();e&&e()}}async function W(e,t,n,r,i,a){return U(()=>G(e,t,n,r,i,a))}async function G(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=z(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await K(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await K(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await K(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ee=Q;async function te(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await I(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ne=$;return e.createNoOverride=ee,e.createOverride=Q,e.readExisting=te,e.readWriteExisting=$,e.readWriteExistingOrCreate=ne,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function N(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function P(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await j(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||F,F);return r.persistentCache&&(a=await M(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new b(a,n,r.cacheSize,o)}async function R(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function z(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var B=4,V=0,H=[];async function U(e){for(;V>=B;)await new Promise(e=>H.push(e));V++;try{return await e()}finally{V--;let e=H.shift();e&&e()}}async function W(e,t,n,r,i,a){return U(()=>G(e,t,n,r,i,a))}async function G(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=z(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await K(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await K(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await K(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ee=Q;async function te(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await I(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ne=$;return e.createNoOverride=ee,e.createOverride=Q,e.readExisting=te,e.readWriteExisting=$,e.readWriteExistingOrCreate=ne,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index d373c50..ca2f223 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},te=1<<20,ne=8192,O=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||ne,this.maxPagesLoaded=Math.floor((n||te)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,k(r);r.push(i),n+=e}return t.pos=n,k(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function k(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return N.set(e,t),t.catch(()=>N.delete(e)),t}function F(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function I(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function L(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function re(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(L(t,0,1/0)),await I(n)}async function R(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await F(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(L(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await I(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([F(d.getAllKeys()),F(d.getAll())]);await I(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await re(e,t.key),m-=t.meta.bytes}}async function z(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await P(c),await R(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=L(n,e,t),[o,s]=await Promise.all([F(i.getAllKeys(a)),F(i.getAll(a))]);await I(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await I(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function B(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||M;if(typeof indexedDB>`u`)return null;try{let n=(await P(t)).transaction(`files`,`readonly`),r=await F(n.objectStore(`files`).get(e.fileKey));return await I(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function V(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!n)return!1;try{let e=await P(c);await R(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||H,H);return r.persistentCache&&(a=await z(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new O(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var K=4,q=0,J=[];async function ae(e){for(;q>=K;)await new Promise(e=>J.push(e));q++;try{return await e()}finally{q--;let e=J.shift();e&&e()}}async function oe(e,t,n,r,i,a){return ae(()=>se(e,t,n,r,i,a))}async function se(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=G(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new O(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ce=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function le(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ue(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function de(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ce||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await ie(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function pe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ue,exports.createOverride=le,exports.readExisting=de,exports.readWriteExisting=fe,exports.readWriteExistingOrCreate=pe; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},te=1<<20,ne=8192,O=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||ne,this.maxPagesLoaded=Math.floor((n||te)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,k(r);r.push(i),n+=e}return t.pos=n,k(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function k(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return N.set(e,t),t.catch(()=>N.delete(e)),t}function F(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function I(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function L(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function re(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(L(t,0,1/0)),await I(n)}async function R(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await F(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(L(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await I(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([F(d.getAllKeys()),F(d.getAll())]);await I(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await re(e,t.key),m-=t.meta.bytes}}async function z(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await P(c),await R(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=L(n,e,t),[o,s]=await Promise.all([F(i.getAllKeys(a)),F(i.getAll(a))]);await I(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await I(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function B(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||M;if(typeof indexedDB>`u`)return null;try{let n=(await P(t)).transaction(`files`,`readonly`),r=await F(n.objectStore(`files`).get(e.fileKey));return await I(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function V(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!n)return!1;try{let e=await P(c);await R(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||H,H);return r.persistentCache&&(a=await z(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new O(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var K=4,q=0,J=[];async function ae(e){for(;q>=K;)await new Promise(e=>J.push(e));q++;try{return await e()}finally{q--;let e=J.shift();e&&e()}}async function oe(e,t,n,r,i,a){return ae(()=>se(e,t,n,r,i,a))}async function se(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=G(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new O(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ce=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function le(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ue(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function de(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ce||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await ie(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function pe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ue,exports.createOverride=le,exports.readExisting=de,exports.readWriteExisting=fe,exports.readWriteExistingOrCreate=pe; \ No newline at end of file diff --git a/src/httpfile.js b/src/httpfile.js index 487a568..e3673b8 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -13,9 +13,11 @@ // file changes mid-read the server answers 200 instead of 206 and the read // throws, rather than silently mixing chunks of two file versions. // -// Server requirements (CORS deployments): allow the `Range` request header -// and expose `Content-Range` + `ETag`; serve zkeys without Content-Encoding -// (ranges address encoded bytes). +// Server requirements (CORS deployments): allow the `Range`, `If-Range`, +// `If-None-Match` and `If-Modified-Since` request headers and expose +// `Content-Range` + `ETag`; serve zkeys without Content-Encoding (ranges +// address encoded bytes). A missing allow-list entry for the conditional +// headers only costs the 304 warm-start (the probe retries unconditionally). import { RangeFile } from "./rangefile.js"; import * as memFile from "./memfile.js"; @@ -47,7 +49,17 @@ export async function readExisting(o) { probeHeaders["If-Modified-Since"] = cacheMeta.validator; } } - const probe = await fetch(url, { headers: probeHeaders }); + let probe; + try { + probe = await fetch(url, { headers: probeHeaders }); + } catch (err) { + // Conditional headers are not CORS-safelisted; a cross-origin server + // whose preflight does not allow If-None-Match/If-Modified-Since + // fails the fetch outright. Never let the cache break an open that + // would work without it: retry unconditionally. + if (!("If-None-Match" in probeHeaders) && !("If-Modified-Since" in probeHeaders)) throw err; + probe = await fetch(url, { headers: { "Range": "bytes=0-0" } }); + } if (probe.status === 304) { // Unchanged since it was cached. Serve through the cache; missing From 3c0b9080a1139d523cacd12b46b86ebaa2e48d92 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 27 Aug 2026 21:18:46 +0100 Subject: [PATCH 33/41] feat: retry with backoff + stall timeout for range requests (evaluation) Fault injection showed the http backend dies on the first hiccup: one mid-body connection drop or transient 503 fails the whole proof, and a stalled response hangs it forever (fetch has no timeout of its own). Range GETs are idempotent and If-Range already guards consistency, so transient failures are safely retryable: - httpReadRangeInto retries up to 3 times with exponential backoff (300ms doubling). Retries happen inside the connection-cap slot, so a failing origin is not hammered by other readers meanwhile. - A stall watchdog (AbortController, bumped on headers and on every body chunk, default 10s) turns a wedged connection into a bounded, retryable failure. - Errors that retrying cannot fix are classified permanent at their throw site and rethrown immediately: 4xx (except 429) and a changed validator. The degrade-to-full control-flow signal passes through untouched. - Tunables exported as httpRetryConfig {retries, backoffMs, stallTimeoutMs} rather than plumbed through per-call options. Out of scope, deliberately: retrying the open() probe, resuming a partial range from its break point, and un-sticking a failed degrade-mode full-body download. 4 new tests against a fault-injecting server: mid-body drop recovery, transient-503 recovery vs fail-fast 404, bounded stall recovery, retry exhaustion. 82 tests pass; lint clean; bundles rebuilt. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- build/browser/browser.esm.js | 181 ++++++++++++++++++++-------------- build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/httpfile.js | 102 +++++++++++++++---- test/httpfile.test.js | 106 ++++++++++++++++++++ 5 files changed, 298 insertions(+), 95 deletions(-) diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index 0e61c4f..6fe1c76 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -297,11 +297,11 @@ function D(e) { function O(e, t, n) { return IDBKeyRange.bound([e, t], [e, n]); } -async function k(e, t) { +async function ee(e, t) { let n = e.transaction(["files", "blocks"], "readwrite"); n.objectStore("files").delete(t), n.objectStore("blocks").delete(O(t, 0, Infinity)), await D(n); } -async function A(e, t, n, r, i, a) { +async function k(e, t, n, r, i, a) { let o = e.transaction(["files", "blocks"], "readwrite"), s = o.objectStore("files"), c = await E(s.get(t)), l = 0; c && c.validator === n && c.totalSize === r && c.blockSize === i ? l = c.bytes : c && o.objectStore("blocks").delete(O(t, 0, Infinity)), s.put({ validator: n, @@ -320,15 +320,15 @@ async function A(e, t, n, r, i, a) { })).filter((e) => e.key !== t).sort((e, t) => e.meta.lastUsed - t.meta.lastUsed); for (let t of h) { if (m <= a) break; - await k(e, t.key), m -= t.meta.bytes; + await ee(e, t.key), m -= t.meta.bytes; } } -async function j(e, t) { +async function A(e, t) { let { fileKey: n, validator: r, totalSize: i } = t, a = typeof t.options == "object" && t.options || {}, o = a.blockSize || x, s = a.maxBytes || S, c = a.dbName || C; if (typeof indexedDB > "u" || !r) return e; let l; try { - l = await T(c), await A(l, n, r, i, o, s); + l = await T(c), await k(l, n, r, i, o, s); } catch { return e; } @@ -414,7 +414,7 @@ async function j(e, t) { for (let e of l) m.delete(e.index); }; } -async function M(e) { +async function j(e) { let t = (typeof e.options == "object" && e.options || {}).dbName || C; if (typeof indexedDB > "u") return null; try { @@ -429,12 +429,12 @@ async function M(e) { return null; } } -async function N(e) { +async function M(e) { let { fileKey: t, validator: n, totalSize: r, data: i } = e, a = typeof e.options == "object" && e.options || {}, o = a.blockSize || x, s = a.maxBytes || S, c = a.dbName || C; if (typeof indexedDB > "u" || !n) return !1; try { let e = await T(c); - await A(e, t, n, r, o, s); + await k(e, t, n, r, o, s); let a = Math.ceil(r / o); for (let n = 0; n < a; n += 64) { let s = e.transaction("blocks", "readwrite"), c = s.objectStore("blocks"); @@ -452,9 +452,9 @@ async function N(e) { } //#endregion //#region src/httpfile.js -var P = 65536; -async function F(e) { - let n = e.url, r = e.persistentCache ? await M({ +var N = 65536; +async function P(e) { + let n = e.url, r = e.persistentCache ? await j({ fileKey: n, options: e.persistentCache }) : null, i = { Range: "bytes=0-0" }; @@ -466,14 +466,14 @@ async function F(e) { if (!("If-None-Match" in i) && !("If-Modified-Since" in i)) throw e; a = await fetch(n, { headers: { Range: "bytes=0-0" } }); } - if (a.status === 304) return await G(a), await I(n, r.validator, r.totalSize, e); + if (a.status === 304) return await K(a), await F(n, r.validator, r.totalSize, e); if (a.status === 206) { let t = a.headers.get("content-range"), r = t ? /\/(\d+)\s*$/.exec(t) : null; if (r) { let t = parseInt(r[1]); - return await a.arrayBuffer(), await I(n, R(a), t, e); + return await a.arrayBuffer(), await F(n, L(a), t, e); } - return await a.arrayBuffer(), await L(n); + return await a.arrayBuffer(), await I(n); } if (!a.ok && a.status !== 416) throw Error("HTTP " + a.status + " fetching " + n); if (a.status === 416) { @@ -481,12 +481,12 @@ async function F(e) { return e && /\/0\s*$/.test(e) ? t({ type: "mem", data: /* @__PURE__ */ new Uint8Array() - }) : await L(n); + }) : await I(n); } let o = new Uint8Array(await a.arrayBuffer()); if (e.persistentCache) { - let t = R(a); - t && await N({ + let t = L(a); + t && await M({ fileKey: n, validator: t, totalSize: o.length, @@ -499,10 +499,10 @@ async function F(e) { data: o }); } -async function I(e, t, n, r) { +async function F(e, t, n, r) { let i = null, a = async function(n, r, a, o) { if (!i) try { - return await U(e, t, n, r, a, o); + return await W(e, t, n, r, a, o); } catch (e) { if (!e || !e.degradeToFull) throw e; i = e.fullBodyPromise; @@ -510,15 +510,15 @@ async function I(e, t, n, r) { let s = await i; if (a + o > s.byteLength) throw Error(e + ": read past the end of the buffered body"); n.set(s.subarray(a, a + o), r); - }, o = Math.min(r.pageSize || P, P); - return r.persistentCache && (a = await j(a, { + }, o = Math.min(r.pageSize || N, N); + return r.persistentCache && (a = await A(a, { fileKey: e, validator: t, totalSize: n, options: r.persistentCache })), new y(a, n, r.cacheSize, o); } -async function L(e) { +async function I(e) { let n = await fetch(e); if (!n.ok) throw Error("HTTP " + n.status + " fetching " + e); return t({ @@ -526,94 +526,131 @@ async function L(e) { data: new Uint8Array(await n.arrayBuffer()) }); } -function R(e) { +function L(e) { let t = e.headers.get("etag"); return t && t.indexOf("W/") !== 0 ? t : e.headers.get("last-modified") || null; } -var z = 4, B = 0, V = []; -async function H(e) { - for (; B >= z;) await new Promise((e) => V.push(e)); - B++; +var R = 4, z = 0, B = []; +async function V(e) { + for (; z >= R;) await new Promise((e) => B.push(e)); + z++; try { return await e(); } finally { - B--; - let e = V.shift(); + z--; + let e = B.shift(); e && e(); } } -async function U(e, t, n, r, i, a) { - return H(() => W(e, t, n, r, i, a)); -} +var H = { + retries: 3, + backoffMs: 300, + stallTimeoutMs: 1e4 +}, U = (e) => new Promise((t) => setTimeout(t, e)); async function W(e, t, n, r, i, a) { + return V(async () => { + for (let o = 0;; o++) try { + return await G(e, t, n, r, i, a); + } catch (e) { + if (e && (e.degradeToFull || e.permanent) || o >= H.retries) throw e; + await U(H.backoffMs * (1 << o)); + } + }); +} +async function G(e, t, n, r, i, a) { let o = { Range: "bytes=" + i + "-" + (i + a - 1) }; t && (o["If-Range"] = t); - let s = await fetch(e, { headers: o }); - if (s.status === 200) { - let n = R(s); + let s = typeof AbortController < "u" ? new AbortController() : null, c = null, l = () => { + s && (c && clearTimeout(c), c = setTimeout(() => s.abort(/* @__PURE__ */ Error(e + ": no data for " + H.stallTimeoutMs + "ms")), H.stallTimeoutMs)); + }, u = () => { + c && clearTimeout(c), c = null; + }; + l(); + let d; + try { + d = await fetch(e, s ? { + headers: o, + signal: s.signal + } : { headers: o }); + } catch (e) { + throw u(), e; + } + if (l(), d.status === 200) { + let n = L(d); if (!t || n && n === t) { + u(); let t = /* @__PURE__ */ Error(e + ": origin ignored Range; degrading to full buffering"); - throw t.degradeToFull = !0, t.fullBodyPromise = s.arrayBuffer().then((e) => new Uint8Array(e)), t; + throw t.degradeToFull = !0, t.fullBodyPromise = d.arrayBuffer().then((e) => new Uint8Array(e)), t; } - throw await G(s), Error(e + ": file changed (or server stopped honoring Range) while reading"); - } - if (s.status !== 206) throw await G(s), Error("HTTP " + s.status + " reading range " + i + "+" + a + " of " + e); - let c = s.headers.get("content-range"), l = c ? /bytes\s+(\d+)-(\d+)\//.exec(c) : null; - if (l && parseInt(l[1]) !== i) throw await G(s), Error(e + ": server returned range starting at " + l[1] + ", requested " + i); - let u = 0; - if (s.body && typeof s.body.getReader == "function") { - let t = s.body.getReader(); - for (;;) { - let i = await t.read(); - if (i.done) break; - if (u + i.value.byteLength > a) throw t.cancel().catch(function() {}), Error(e + ": range response longer than requested"); - n.set(i.value, r + u), u += i.value.byteLength; + u(), await K(d); + let r = /* @__PURE__ */ Error(e + ": file changed (or server stopped honoring Range) while reading"); + throw r.permanent = !0, r; + } + if (d.status !== 206) { + u(), await K(d); + let t = /* @__PURE__ */ Error("HTTP " + d.status + " reading range " + i + "+" + a + " of " + e); + throw d.status < 500 && d.status !== 429 && (t.permanent = !0), t; + } + let f = d.headers.get("content-range"), p = f ? /bytes\s+(\d+)-(\d+)\//.exec(f) : null; + if (p && parseInt(p[1]) !== i) throw await K(d), Error(e + ": server returned range starting at " + p[1] + ", requested " + i); + let m = 0; + try { + if (d.body && typeof d.body.getReader == "function") { + let t = d.body.getReader(); + for (;;) { + let i = await t.read(); + if (l(), i.done) break; + if (m + i.value.byteLength > a) throw t.cancel().catch(function() {}), Error(e + ": range response longer than requested"); + n.set(i.value, r + m), m += i.value.byteLength; + } + } else { + /* c8 ignore start */ + let t = new Uint8Array(await d.arrayBuffer()); + if (t.byteLength > a) throw Error(e + ": range response longer than requested"); + n.set(t, r), m = t.byteLength; } - } else { - /* c8 ignore start */ - let t = new Uint8Array(await s.arrayBuffer()); - if (t.byteLength > a) throw Error(e + ": range response longer than requested"); - n.set(t, r), u = t.byteLength; + } finally { + u(); } - if (u !== a) throw Error(e + ": short range response (" + u + "/" + a + " bytes at " + i + ")"); + if (m !== a) throw Error(e + ": short range response (" + m + "/" + a + " bytes at " + i + ")"); } -async function G(e) { +async function K(e) { try { e.body && typeof e.body.cancel == "function" ? await e.body.cancel() : await e.arrayBuffer(); } catch {} } //#endregion //#region src/blobfile.js -var K = 1 << 20; -function q(e) { +var q = 1 << 20; +function J(e) { let t = e.blob, n = async function(e, n, r, i) { let a = await t.slice(r, r + i).arrayBuffer(); if (a.byteLength !== i) throw Error("short blob read (" + a.byteLength + "/" + i + " bytes at " + r + ")"); e.set(new Uint8Array(a), n); - }, r = Math.min(e.pageSize || K, K); + }, r = Math.min(e.pageSize || q, q); return new y(n, t.size, e.cacheSize, r); } //#endregion //#region src/fastfile.browser.js -function J() { +function Y() { throw Error("File I/O is not supported in the browser"); } -function Y(e) { +function X(e) { return e instanceof Uint8Array ? { type: "mem", data: e - } : (typeof e == "string" && J(), e); + } : (typeof e == "string" && Y(), e); } -function X(e, t, n) { - if (e.type === "file" && J(), e.type === "mem") return t(e); +function Z(e, t, n) { + if (e.type === "file" && Y(), e.type === "mem") return t(e); if (e.type === "bigMem") return n(e); throw Error("Invalid FastFile type: " + e.type); } -function Z(t) { - return X(Y(t), e, l); +function Q(t) { + return Z(X(t), e, l); } -var Q = Z; -async function ee(e, n, r) { +var te = Q; +async function ne(e, n, r) { return e instanceof Uint8Array && (e = { type: "mem", data: e @@ -627,11 +664,11 @@ async function ee(e, n, r) { url: e, cacheSize: n, pageSize: r - }), e.type === "http" ? await F(e) : e.type === "blob" ? q(e) : X(e, t, u); + }), e.type === "http" ? await P(e) : e.type === "blob" ? J(e) : Z(e, t, u); } function $(e) { - return typeof e == "string" && J(), X(e, n, d); + return typeof e == "string" && Y(), Z(e, n, d); } -var te = $; +var re = $; //#endregion -export { Q as createNoOverride, Z as createOverride, ee as readExisting, $ as readWriteExisting, te as readWriteExistingOrCreate }; +export { te as createNoOverride, Q as createOverride, ne as readExisting, $ as readWriteExisting, re as readWriteExistingOrCreate }; diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index 3140611..7587b2f 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function A(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function j(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await A(e,t.key),m-=t.meta.bytes}}async function M(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await j(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function N(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function P(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await j(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||F,F);return r.persistentCache&&(a=await M(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new b(a,n,r.cacheSize,o)}async function R(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function z(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var B=4,V=0,H=[];async function U(e){for(;V>=B;)await new Promise(e=>H.push(e));V++;try{return await e()}finally{V--;let e=H.shift();e&&e()}}async function W(e,t,n,r,i,a){return U(()=>G(e,t,n,r,i,a))}async function G(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=z(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await K(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await K(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await K(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ee=Q;async function te(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await I(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ne=$;return e.createNoOverride=ee,e.createOverride=Q,e.readExisting=te,e.readWriteExisting=$,e.readWriteExistingOrCreate=ne,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ee(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function A(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ee(e,t.key),m-=t.meta.bytes}}async function j(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await A(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function M(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function N(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await A(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||P,P);return r.persistentCache&&(a=await j(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new b(a,n,r.cacheSize,o)}async function L(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function R(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var z=4,B=0,V=[];async function te(e){for(;B>=z;)await new Promise(e=>V.push(e));B++;try{return await e()}finally{B--;let e=V.shift();e&&e()}}var H={retries:3,backoffMs:300,stallTimeoutMs:1e4},U=e=>new Promise(t=>setTimeout(t,e));async function W(e,t,n,r,i,a){return te(async()=>{for(let o=0;;o++)try{return await G(e,t,n,r,i,a)}catch(e){if(e&&(e.degradeToFull||e.permanent)||o>=H.retries)throw e;await U(H.backoffMs*(1<{s&&(c&&clearTimeout(c),c=setTimeout(()=>s.abort(Error(e+`: no data for `+H.stallTimeoutMs+`ms`)),H.stallTimeoutMs))},u=()=>{c&&clearTimeout(c),c=null};l();let d;try{d=await fetch(e,s?{headers:o,signal:s.signal}:{headers:o})}catch(e){throw u(),e}if(l(),d.status===200){let n=R(d);if(!t||n&&n===t){u();let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=d.arrayBuffer().then(e=>new Uint8Array(e)),t}u(),await K(d);let r=Error(e+`: file changed (or server stopped honoring Range) while reading`);throw r.permanent=!0,r}if(d.status!==206){u(),await K(d);let t=Error(`HTTP `+d.status+` reading range `+i+`+`+a+` of `+e);throw d.status<500&&d.status!==429&&(t.permanent=!0),t}let f=d.headers.get(`content-range`),p=f?/bytes\s+(\d+)-(\d+)\//.exec(f):null;if(p&&parseInt(p[1])!==i)throw await K(d),Error(e+`: server returned range starting at `+p[1]+`, requested `+i);let m=0;try{if(d.body&&typeof d.body.getReader==`function`){let t=d.body.getReader();for(;;){let i=await t.read();if(l(),i.done)break;if(m+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+m),m+=i.value.byteLength}}else{let t=new Uint8Array(await d.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),m=t.byteLength}}finally{u()}if(m!==a)throw Error(e+`: short range response (`+m+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ne=Q;async function re(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await F(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ie=$;return e.createNoOverride=ne,e.createOverride=Q,e.readExisting=re,e.readWriteExisting=$,e.readWriteExistingOrCreate=ie,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index ca2f223..7da7c4d 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},te=1<<20,ne=8192,O=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||ne,this.maxPagesLoaded=Math.floor((n||te)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,k(r);r.push(i),n+=e}return t.pos=n,k(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function k(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return N.set(e,t),t.catch(()=>N.delete(e)),t}function F(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function I(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function L(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function re(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(L(t,0,1/0)),await I(n)}async function R(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await F(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(L(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await I(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([F(d.getAllKeys()),F(d.getAll())]);await I(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await re(e,t.key),m-=t.meta.bytes}}async function z(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await P(c),await R(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=L(n,e,t),[o,s]=await Promise.all([F(i.getAllKeys(a)),F(i.getAll(a))]);await I(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await I(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function B(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||M;if(typeof indexedDB>`u`)return null;try{let n=(await P(t)).transaction(`files`,`readonly`),r=await F(n.objectStore(`files`).get(e.fileKey));return await I(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function V(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||A,s=a.maxBytes||j,c=a.dbName||M;if(typeof indexedDB>`u`||!n)return!1;try{let e=await P(c);await R(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||H,H);return r.persistentCache&&(a=await z(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new O(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var K=4,q=0,J=[];async function ae(e){for(;q>=K;)await new Promise(e=>J.push(e));q++;try{return await e()}finally{q--;let e=J.shift();e&&e()}}async function oe(e,t,n,r,i,a){return ae(()=>se(e,t,n,r,i,a))}async function se(e,t,n,r,i,a){let o={Range:`bytes=`+i+`-`+(i+a-1)};t&&(o[`If-Range`]=t);let s=await fetch(e,{headers:o});if(s.status===200){let n=G(s);if(!t||n&&n===t){let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=s.arrayBuffer().then(e=>new Uint8Array(e)),t}throw await Y(s),Error(e+`: file changed (or server stopped honoring Range) while reading`)}if(s.status!==206)throw await Y(s),Error(`HTTP `+s.status+` reading range `+i+`+`+a+` of `+e);let c=s.headers.get(`content-range`),l=c?/bytes\s+(\d+)-(\d+)\//.exec(c):null;if(l&&parseInt(l[1])!==i)throw await Y(s),Error(e+`: server returned range starting at `+l[1]+`, requested `+i);let u=0;if(s.body&&typeof s.body.getReader==`function`){let t=s.body.getReader();for(;;){let i=await t.read();if(i.done)break;if(u+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+u),u+=i.value.byteLength}}else{let t=new Uint8Array(await s.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),u=t.byteLength}if(u!==a)throw Error(e+`: short range response (`+u+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function Z(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new O(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ce=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function le(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function ue(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function de(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ce||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await ie(e);if(e.type==`blob`)return Z(e);throw Error(`Invalid FastFile type: `+e.type)}function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function pe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=ue,exports.createOverride=le,exports.readExisting=de,exports.readWriteExisting=fe,exports.readWriteExistingOrCreate=pe; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},ee=1<<20,te=8192,k=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||te,this.maxPagesLoaded=Math.floor((n||ee)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,A(r);r.push(i),n+=e}return t.pos=n,A(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function A(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return P.set(e,t),t.catch(()=>P.delete(e)),t}function I(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function L(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function R(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ne(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(R(t,0,1/0)),await L(n)}async function z(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await I(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(R(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await L(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([I(d.getAllKeys()),I(d.getAll())]);await L(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ne(e,t.key),m-=t.meta.bytes}}async function re(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||j,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await F(c),await z(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=R(n,e,t),[o,s]=await Promise.all([I(i.getAllKeys(a)),I(i.getAll(a))]);await L(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await L(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function B(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||N;if(typeof indexedDB>`u`)return null;try{let n=(await F(t)).transaction(`files`,`readonly`),r=await I(n.objectStore(`files`).get(e.fileKey));return await L(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function V(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||j,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!n)return!1;try{let e=await F(c);await z(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||H,H);return r.persistentCache&&(a=await re(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new k(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var K=4,q=0,J=[];async function ae(e){for(;q>=K;)await new Promise(e=>J.push(e));q++;try{return await e()}finally{q--;let e=J.shift();e&&e()}}var Y={retries:3,backoffMs:300,stallTimeoutMs:1e4},oe=e=>new Promise(t=>setTimeout(t,e));async function se(e,t,n,r,i,a){return ae(async()=>{for(let o=0;;o++)try{return await ce(e,t,n,r,i,a)}catch(e){if(e&&(e.degradeToFull||e.permanent)||o>=Y.retries)throw e;await oe(Y.backoffMs*(1<{s&&(c&&clearTimeout(c),c=setTimeout(()=>s.abort(Error(e+`: no data for `+Y.stallTimeoutMs+`ms`)),Y.stallTimeoutMs))},u=()=>{c&&clearTimeout(c),c=null};l();let d;try{d=await fetch(e,s?{headers:o,signal:s.signal}:{headers:o})}catch(e){throw u(),e}if(l(),d.status===200){let n=G(d);if(!t||n&&n===t){u();let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=d.arrayBuffer().then(e=>new Uint8Array(e)),t}u(),await X(d);let r=Error(e+`: file changed (or server stopped honoring Range) while reading`);throw r.permanent=!0,r}if(d.status!==206){u(),await X(d);let t=Error(`HTTP `+d.status+` reading range `+i+`+`+a+` of `+e);throw d.status<500&&d.status!==429&&(t.permanent=!0),t}let f=d.headers.get(`content-range`),p=f?/bytes\s+(\d+)-(\d+)\//.exec(f):null;if(p&&parseInt(p[1])!==i)throw await X(d),Error(e+`: server returned range starting at `+p[1]+`, requested `+i);let m=0;try{if(d.body&&typeof d.body.getReader==`function`){let t=d.body.getReader();for(;;){let i=await t.read();if(l(),i.done)break;if(m+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+m),m+=i.value.byteLength}}else{let t=new Uint8Array(await d.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),m=t.byteLength}}finally{u()}if(m!==a)throw Error(e+`: short range response (`+m+`/`+a+` bytes at `+i+`)`)}async function X(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var Z=1<<20;function le(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||Z,Z);return new k(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ue=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function de(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function pe(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ue||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await ie(e);if(e.type==`blob`)return le(e);throw Error(`Invalid FastFile type: `+e.type)}function me(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function he(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=fe,exports.createOverride=de,exports.readExisting=pe,exports.readWriteExisting=me,exports.readWriteExistingOrCreate=he; \ No newline at end of file diff --git a/src/httpfile.js b/src/httpfile.js index e3673b8..0266208 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -201,14 +201,61 @@ async function withRangeFetchSlot(fn) { } } +// Unstable-connection handling. A range GET is idempotent and If-Range +// guards consistency, so transient failures (network drops, 5xx/429, and +// stalls -- a response that stops delivering bytes) are retried with +// exponential backoff. Errors that retrying cannot fix (4xx, a changed +// validator) are marked err.permanent at their throw site and rethrown +// immediately; the degrade-to-full signal passes through untouched. +// Exported so tests (and unusual deployments) can tune it. +export const httpRetryConfig = { + retries: 3, // extra attempts after the first + backoffMs: 300, // doubled per retry + stallTimeoutMs: 10_000, // abort when no bytes (or headers) arrive for this long +}; + +const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); + async function httpReadRangeInto(url, validator, dst, dstOffset, pos, len) { - return withRangeFetchSlot(() => httpReadRangeIntoUnlimited(url, validator, dst, dstOffset, pos, len)); + return withRangeFetchSlot(async () => { + for (let attempt = 0; ; attempt++) { + try { + return await httpReadRangeIntoOnce(url, validator, dst, dstOffset, pos, len); + } catch (err) { + if (err && (err.degradeToFull || err.permanent)) throw err; + if (attempt >= httpRetryConfig.retries) throw err; + await sleep(httpRetryConfig.backoffMs * (1 << attempt)); + } + } + }); } -async function httpReadRangeIntoUnlimited(url, validator, dst, dstOffset, pos, len) { +async function httpReadRangeIntoOnce(url, validator, dst, dstOffset, pos, len) { const headers = { "Range": "bytes=" + pos + "-" + (pos + len - 1) }; if (validator) headers["If-Range"] = validator; - const res = await fetch(url, { headers: headers }); + // Stall watchdog: abort when neither headers nor body bytes arrive for + // stallTimeoutMs. Without it a wedged connection hangs the read forever + // (fetch has no timeout of its own); with it, the retry loop above turns + // a stall into a bounded delay. + const ctrl = typeof AbortController !== "undefined" ? new AbortController() : null; + let stallTimer = null; + const bumpStall = () => { + if (!ctrl) return; + if (stallTimer) clearTimeout(stallTimer); + stallTimer = setTimeout( + () => ctrl.abort(new Error(url + ": no data for " + httpRetryConfig.stallTimeoutMs + "ms")), + httpRetryConfig.stallTimeoutMs); + }; + const clearStall = () => { if (stallTimer) clearTimeout(stallTimer); stallTimer = null; }; + bumpStall(); + let res; + try { + res = await fetch(url, ctrl ? { headers: headers, signal: ctrl.signal } : { headers: headers }); + } catch (err) { + clearStall(); + throw err; + } + bumpStall(); if (res.status === 200) { // A 200 mid-session means the origin does not honor Range (the 206 // probe answer may have come from a browser cache). If the strong @@ -217,17 +264,25 @@ async function httpReadRangeIntoUnlimited(url, validator, dst, dstOffset, pos, l // buffered mode. Only a changed validator is a hard error. const nowValidator = strongValidator(res); if (!validator || (nowValidator && nowValidator === validator)) { + clearStall(); const degrade = new Error(url + ": origin ignored Range; degrading to full buffering"); degrade.degradeToFull = true; degrade.fullBodyPromise = res.arrayBuffer().then((b) => new Uint8Array(b)); throw degrade; } + clearStall(); await abandonBody(res); - throw new Error(url + ": file changed (or server stopped honoring Range) while reading"); + const changed = new Error(url + ": file changed (or server stopped honoring Range) while reading"); + changed.permanent = true; // retrying cannot bring the old bytes back + throw changed; } if (res.status !== 206) { + clearStall(); await abandonBody(res); - throw new Error("HTTP " + res.status + " reading range " + pos + "+" + len + " of " + url); + const httpErr = new Error("HTTP " + res.status + " reading range " + pos + "+" + len + " of " + url); + // 5xx and 429 are transient by definition; other 4xx will not change + if (res.status < 500 && res.status !== 429) httpErr.permanent = true; + throw httpErr; } const contentRange = res.headers.get("content-range"); const m = contentRange ? /bytes\s+(\d+)-(\d+)\//.exec(contentRange) : null; @@ -240,27 +295,32 @@ async function httpReadRangeIntoUnlimited(url, validator, dst, dstOffset, pos, l // BigBuffer -- both expose .set(chunk, offset)); avoids materializing // a second full-size copy of large section reads. let done = 0; - if (res.body && typeof res.body.getReader === "function") { - const reader = res.body.getReader(); - for (;;) { - const it = await reader.read(); - if (it.done) break; - if (done + it.value.byteLength > len) { - reader.cancel().catch(function () {}); - throw new Error(url + ": range response longer than requested"); + try { + if (res.body && typeof res.body.getReader === "function") { + const reader = res.body.getReader(); + for (;;) { + const it = await reader.read(); + bumpStall(); + if (it.done) break; + if (done + it.value.byteLength > len) { + reader.cancel().catch(function () {}); + throw new Error(url + ": range response longer than requested"); + } + dst.set(it.value, dstOffset + done); + done += it.value.byteLength; } - dst.set(it.value, dstOffset + done); - done += it.value.byteLength; - } - } else { + } else { // coverage: fetch implementations without a streaming body (older // browser polyfills); Node's undici always streams /* c8 ignore start */ - const buff = new Uint8Array(await res.arrayBuffer()); - if (buff.byteLength > len) throw new Error(url + ": range response longer than requested"); - dst.set(buff, dstOffset); - done = buff.byteLength; + const buff = new Uint8Array(await res.arrayBuffer()); + if (buff.byteLength > len) throw new Error(url + ": range response longer than requested"); + dst.set(buff, dstOffset); + done = buff.byteLength; /* c8 ignore stop */ + } + } finally { + clearStall(); } if (done !== len) { throw new Error(url + ": short range response (" + done + "/" + len + " bytes at " + pos + ")"); diff --git a/test/httpfile.test.js b/test/httpfile.test.js index b229e79..b907b91 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -77,6 +77,112 @@ function startServer(state, opts) { describe("fastfile testing suite for httpfile", function () { + describe("unstable connections (retry + stall timeout)", () => { + let saved; + beforeEach(async () => { + const { httpRetryConfig } = await import("../src/httpfile.js"); + saved = { ...httpRetryConfig }; + httpRetryConfig.backoffMs = 10; + httpRetryConfig.stallTimeoutMs = 250; + }); + afterEach(async () => { + const { httpRetryConfig } = await import("../src/httpfile.js"); + Object.assign(httpRetryConfig, saved); + }); + + // Minimal faulty range server: `plan` decides per zkey request. + function faultyServer(data, plan) { + let n = 0; + const server = http.createServer((req, res) => { + const m = /^bytes=(\d+)-(\d+)$/.exec(req.headers.range || ""); + const start = m ? parseInt(m[1]) : 0; + const end = m ? Math.min(parseInt(m[2]), data.length - 1) : data.length - 1; + const isProbe = start === 0 && end === 0; + const action = isProbe ? "ok" : plan(++n); + if (action === "503") { res.writeHead(503); res.end(); return; } + res.writeHead(action === "404" ? 404 : 206, { + "Content-Range": `bytes ${start}-${end}/${data.length}`, + "Content-Length": end - start + 1, + "ETag": "\"u1\"", "Accept-Ranges": "bytes", + }); + if (action === "404") { res.end(); return; } + const body = Buffer.from(data.slice(start, end + 1)); + if (action === "drop") { + res.write(body.subarray(0, Math.floor(body.length / 2))); + setTimeout(() => res.destroy(), 10); + return; + } + if (action === "stall") { + res.write(body.subarray(0, 16)); // then silence, socket open + return; + } + res.end(body); + }); + return new Promise((r) => server.listen(0, "127.0.0.1", () => r({ + url: `http://127.0.0.1:${server.address().port}/f.bin`, + attempts: () => n, + close: () => new Promise((x) => { server.closeAllConnections(); server.close(x); }), + }))); + } + + it("recovers from a mid-body connection drop", async () => { + const data = makeData(1 << 18); + const srv = await faultyServer(data, (n) => (n === 1 ? "drop" : "ok")); + try { + const fd = await fastFile.readExisting(srv.url); + const got = await fd.read(200000, 1000); + assert.deepStrictEqual(Buffer.from(got), Buffer.from(data.slice(1000, 201000))); + await fd.close(); + assert.strictEqual(srv.attempts(), 2); + } finally { await srv.close(); } + }); + + it("retries transient 503s but fails fast on 404", async () => { + const data = makeData(1 << 17); + const srv = await faultyServer(data, (n) => (n <= 2 ? "503" : "ok")); + try { + const fd = await fastFile.readExisting(srv.url); + const got = await fd.read(65536, 0); + assert.deepStrictEqual(Buffer.from(got), Buffer.from(data.slice(0, 65536))); + await fd.close(); + assert.strictEqual(srv.attempts(), 3); + } finally { await srv.close(); } + + const srv2 = await faultyServer(data, () => "404"); + try { + const fd = await fastFile.readExisting(srv2.url); + await expect(fd.read(65536, 0)).rejects.toThrow("HTTP 404"); + assert.strictEqual(srv2.attempts(), 1); // permanent: no retry + await fd.close(); + } finally { await srv2.close(); } + }); + + it("aborts a stalled response and recovers on retry", async () => { + const data = makeData(1 << 18); + const srv = await faultyServer(data, (n) => (n === 1 ? "stall" : "ok")); + try { + const t0 = Date.now(); + const fd = await fastFile.readExisting(srv.url); + const got = await fd.read(150000, 5000); + assert.deepStrictEqual(Buffer.from(got), Buffer.from(data.slice(5000, 155000))); + await fd.close(); + assert.strictEqual(srv.attempts(), 2); + assert.ok(Date.now() - t0 < 5000, "stall was not bounded"); + } finally { await srv.close(); } + }); + + it("gives up after exhausting retries", async () => { + const data = makeData(1 << 17); + const srv = await faultyServer(data, () => "503"); + try { + const fd = await fastFile.readExisting(srv.url); + await expect(fd.read(65536, 0)).rejects.toThrow("HTTP 503"); + assert.strictEqual(srv.attempts(), 4); // 1 try + 3 retries + await fd.close(); + } finally { await srv.close(); } + }); + }); + it("caps concurrent range fetches below the browser connection limit", async () => { const data = makeData(1 << 20); let inFlight = 0, maxInFlight = 0; From 52b173292f66450f9726016bba6ffae965043f4e Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Fri, 28 Aug 2026 13:00:00 +0100 Subject: [PATCH 34/41] chore: re-pin ffjavascript (fsqrt c=0 hang fix) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0dd2e26..50c60b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", - "integrity": "sha512-P0zFCXFy/EZdYCtB19pTmngyTske2pgpiY6FaAVr68tH7UgWXJrpqyto1USd4fGtaWBT/MJ+Ah48D1mk9/407A==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", + "integrity": "sha512-l7c9lxNePCkxxY4aUu8guyxaFDKCuXrgzjTb0b9P9lPSL1xHzAKJkxPaEBL4mksXe6Kb/W5wHwekp57tO9z1OQ==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", - "integrity": "sha512-P0zFCXFy/EZdYCtB19pTmngyTske2pgpiY6FaAVr68tH7UgWXJrpqyto1USd4fGtaWBT/MJ+Ah48D1mk9/407A==", + "version": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", + "integrity": "sha512-l7c9lxNePCkxxY4aUu8guyxaFDKCuXrgzjTb0b9P9lPSL1xHzAKJkxPaEBL4mksXe6Kb/W5wHwekp57tO9z1OQ==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", diff --git a/package.json b/package.json index 7acd7ba..2ef9261 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#f16db3c1719428b9c1867f28f008f20a7b84f572", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From 653bce61ee166f52161abcd00c96652b5d98f58c Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Fri, 28 Aug 2026 14:46:17 +0100 Subject: [PATCH 35/41] chore: re-pin ffjavascript (idle-termination dispatch-race fix) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50c60b6..cb215d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", - "integrity": "sha512-l7c9lxNePCkxxY4aUu8guyxaFDKCuXrgzjTb0b9P9lPSL1xHzAKJkxPaEBL4mksXe6Kb/W5wHwekp57tO9z1OQ==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", + "integrity": "sha512-6jptHcDnx70C+w4jo8MJo9WgDbBJEi1zf5wWMEdsAMQeSalJN8MWoccgQDHxQdGX9Z8NhTqexHQMWqMccWrBzw==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", - "integrity": "sha512-l7c9lxNePCkxxY4aUu8guyxaFDKCuXrgzjTb0b9P9lPSL1xHzAKJkxPaEBL4mksXe6Kb/W5wHwekp57tO9z1OQ==", + "version": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", + "integrity": "sha512-6jptHcDnx70C+w4jo8MJo9WgDbBJEi1zf5wWMEdsAMQeSalJN8MWoccgQDHxQdGX9Z8NhTqexHQMWqMccWrBzw==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", diff --git a/package.json b/package.json index 2ef9261..6cd45dc 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#4f77fd9ff94b3dee5b786fb16f87261c42433b85", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From 3014b8cfba63eaba4f83a231ceee278d526c5307 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Fri, 28 Aug 2026 15:34:28 +0100 Subject: [PATCH 36/41] chore: re-pin ffjavascript (terminate-settles-tasks + build-dedupe fixes) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb215d9..9d6f371 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", - "integrity": "sha512-6jptHcDnx70C+w4jo8MJo9WgDbBJEi1zf5wWMEdsAMQeSalJN8MWoccgQDHxQdGX9Z8NhTqexHQMWqMccWrBzw==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", + "integrity": "sha512-6WGzPZlG1ulAGe7IgU4ZlBgoyWHEaE+yZKR/VQe44j1Fh0V+wJq0xxCSWeyCSLTT2+BR4JhpzcUmbSYdq/hMGQ==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", - "integrity": "sha512-6jptHcDnx70C+w4jo8MJo9WgDbBJEi1zf5wWMEdsAMQeSalJN8MWoccgQDHxQdGX9Z8NhTqexHQMWqMccWrBzw==", + "version": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", + "integrity": "sha512-6WGzPZlG1ulAGe7IgU4ZlBgoyWHEaE+yZKR/VQe44j1Fh0V+wJq0xxCSWeyCSLTT2+BR4JhpzcUmbSYdq/hMGQ==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", diff --git a/package.json b/package.json index 6cd45dc..3861cbe 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#8430220603e7f1d57ee0a18cac2f14bf507a5326", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From 011644e9811c43c421d77991d85d174135d362d4 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sat, 29 Aug 2026 03:22:00 +0100 Subject: [PATCH 37/41] chore: re-pin ffjavascript (worker input-buffer transfer-back) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d6f371..b63021f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#cc88563e1686646616ad0fcfafa6cb09c879dd25", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", @@ -1167,8 +1167,8 @@ }, "node_modules/ffjavascript": { "version": "0.3.1", - "resolved": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", - "integrity": "sha512-6WGzPZlG1ulAGe7IgU4ZlBgoyWHEaE+yZKR/VQe44j1Fh0V+wJq0xxCSWeyCSLTT2+BR4JhpzcUmbSYdq/hMGQ==", + "resolved": "git+https://github.com/iden3/ffjavascript.git#cc88563e1686646616ad0fcfafa6cb09c879dd25", + "integrity": "sha512-CLLd+R83k5OOIj14sPLlxlLX8YwuOFUn51Rt3XwQIegHbQyga5fMZ8DO/uCx/jVZzm1NEqSaevmQO7VNW8KVtA==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -3081,10 +3081,10 @@ "dev": true }, "ffjavascript": { - "version": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", - "integrity": "sha512-6WGzPZlG1ulAGe7IgU4ZlBgoyWHEaE+yZKR/VQe44j1Fh0V+wJq0xxCSWeyCSLTT2+BR4JhpzcUmbSYdq/hMGQ==", + "version": "git+https://github.com/iden3/ffjavascript.git#cc88563e1686646616ad0fcfafa6cb09c879dd25", + "integrity": "sha512-CLLd+R83k5OOIj14sPLlxlLX8YwuOFUn51Rt3XwQIegHbQyga5fMZ8DO/uCx/jVZzm1NEqSaevmQO7VNW8KVtA==", "dev": true, - "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", + "from": "ffjavascript@git+https://github.com/iden3/ffjavascript.git#cc88563e1686646616ad0fcfafa6cb09c879dd25", "requires": { "wasmbuilder": "0.0.16", "wasmcurves": "git+https://github.com/iden3/wasmcurves.git#13d4acc125b794a4e28a5dc1ea99f015d863ec98", diff --git a/package.json b/package.json index 3861cbe..6e8a046 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "eslint": "^10.1.0", - "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#9f3109dad2f585b61ad054ed9fe5fc40681bad2f", + "ffjavascript": "git+https://github.com/iden3/ffjavascript.git#cc88563e1686646616ad0fcfafa6cb09c879dd25", "globals": "^17.4.0", "playwright": "^1.59.0", "vite": "^8.0.3", From 1514b2a81810cdeb4e1b47d433dddb970697b0dd Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sat, 29 Aug 2026 20:46:07 +0100 Subject: [PATCH 38/41] feat: apply cacheSize/pageSize fallbacks to descriptor-object sources The b/c arguments of the entry points only applied while coercing string (and Blob) sources into descriptor objects; a caller passing {type: "file"|"http"} without explicit sizes silently got the small built-in defaults even when it supplied tuned hints (snarkjs passes 32 MiB cache / 8 MiB pages for its binfiles). The hints now fill exactly the fields the descriptor leaves undefined, in every entry point; explicit fields keep winning and the caller's object is never mutated. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- README.md | 4 ++- src/fastfile.js | 27 +++++++++++++++ test/size_fallbacks.test.js | 65 +++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 test/size_fallbacks.test.js diff --git a/README.md b/README.md index 92f6c78..d46396e 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,9 @@ All entry points return a promise of a `FastFile` handle: `o` is a file name/URL string, a `Uint8Array`, a `Blob`, or an options object with a `type` tag from the table above. `cacheSize` (default 64 KiB) and -`pageSize` (default 8 KiB) tune the page cache. +`pageSize` (default 8 KiB) tune the page cache. They apply to every source +form: for an options object they fill only the fields the object leaves +undefined, so explicit fields on the object always win. The returned handle offers, among others: diff --git a/src/fastfile.js b/src/fastfile.js index 715ce7f..afb639a 100644 --- a/src/fastfile.js +++ b/src/fastfile.js @@ -13,6 +13,28 @@ const DEFAULT_PAGE_SIZE = (1 << 13); // webpack-ism and is undefined under Vite/esbuild/SES). const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; +// The b/c arguments are the caller's cacheSize/pageSize hints. Historically +// they were only applied while coercing string (and Blob) sources into +// descriptor objects, so a caller passing {type: "file"|"http"} without +// explicit sizes silently fell back to the small built-in defaults even when +// it supplied tuned hints for the string case. Fill exactly the fields the +// descriptor leaves undefined; never mutate the caller's object. +function withSizeFallbacks(o, b, c) { + if (typeof o !== "object" || o === null) return o; + // Only the page-cache backends consume these sizes. mem/bigMem + // descriptors must pass through UNTOUCHED and by identity: the mem + // backend stores the written data on the caller's object (o.data), so + // handing a copy to createNew would leave the caller's descriptor empty. + if (o.type !== "file" && o.type !== "http" && o.type !== "blob") return o; + const needCache = o.cacheSize === undefined && b !== undefined; + const needPage = o.pageSize === undefined && c !== undefined; + if (!needCache && !needPage) return o; + const res = Object.assign({}, o); + if (needCache) res.cacheSize = b; + if (needPage) res.pageSize = c; + return res; +} + export async function createOverride(o, b, c) { if (typeof o === "string") { @@ -23,6 +45,7 @@ export async function createOverride(o, b, c) { pageSize: c || DEFAULT_PAGE_SIZE }; } + o = withSizeFallbacks(o, b, c); if (o.type == "file") { return await open(o.fileName, O_TRUNC | O_CREAT | O_RDWR, o.cacheSize, o.pageSize); } else if (o.type == "mem") { @@ -43,6 +66,7 @@ export function createNoOverride(o, b, c) { pageSize: c || DEFAULT_PAGE_SIZE }; } + o = withSizeFallbacks(o, b, c); if (o.type == "file") { return open(o.fileName, O_TRUNC | O_CREAT | O_RDWR | O_EXCL, o.cacheSize, o.pageSize); } else if (o.type == "mem") { @@ -90,6 +114,7 @@ export async function readExisting(o, b, c) { }; } } + o = withSizeFallbacks(o, b, c); if (o.type == "file") { return await open(o.fileName, O_RDONLY, o.cacheSize, o.pageSize); } else if (o.type == "mem") { @@ -114,6 +139,7 @@ export function readWriteExisting(o, b, c) { pageSize: c || DEFAULT_PAGE_SIZE }; } + o = withSizeFallbacks(o, b, c); if (o.type == "file") { return open(o.fileName, O_CREAT | O_RDWR, o.cacheSize, o.pageSize); } else if (o.type == "mem") { @@ -134,6 +160,7 @@ export function readWriteExistingOrCreate(o, b, c) { pageSize: c || DEFAULT_PAGE_SIZE }; } + o = withSizeFallbacks(o, b, c); if (o.type == "file") { return open(o.fileName, O_CREAT | O_RDWR | O_EXCL, o.cacheSize); } else if (o.type == "mem") { diff --git a/test/size_fallbacks.test.js b/test/size_fallbacks.test.js new file mode 100644 index 0000000..a758377 --- /dev/null +++ b/test/size_fallbacks.test.js @@ -0,0 +1,65 @@ +import fs from "fs"; +import * as fastFile from "../src/fastfile.js"; +import { afterAll, expect } from "vitest"; + +// The b/c (cacheSize/pageSize) arguments of the entry points historically +// applied only to string/Blob sources; a descriptor object without explicit +// sizes silently got the small built-in defaults. They now act as fallbacks +// for object sources too, filling exactly the fields the caller left +// undefined -- so e.g. snarkjs passing {type: "file"} gets the same tuned +// cache as when it passes a plain path string. +describe("cacheSize/pageSize fallbacks for descriptor objects", function () { + const fileName = "test_size_fallbacks.bin"; + + afterAll(() => { + if (fs.existsSync(fileName)) fs.unlinkSync(fileName); + }); + + it("object source picks up the fallback sizes it does not set", async () => { + const fdString = await fastFile.createOverride(fileName, 1 << 22, 1 << 16); + await fdString.write(new Uint8Array(64), 0); + await fdString.close(); + + const fdObject = await fastFile.readExisting({ type: "file", fileName }, 1 << 22, 1 << 16); + const fdPlain = await fastFile.readExisting(fileName, 1 << 22, 1 << 16); + // identical tuning to the string source + expect(fdObject.pageSize).toBe(fdPlain.pageSize); + expect(fdObject.maxPagesLoaded).toBe(fdPlain.maxPagesLoaded); + await fdObject.close(); + await fdPlain.close(); + }); + + it("explicit object fields win over the fallbacks", async () => { + const fd = await fastFile.readExisting( + { type: "file", fileName, cacheSize: 1 << 20, pageSize: 1 << 13 }, 1 << 25, 1 << 16); + const fdRef = await fastFile.readExisting( + { type: "file", fileName, cacheSize: 1 << 20, pageSize: 1 << 13 }); + expect(fd.pageSize).toBe(fdRef.pageSize); + expect(fd.maxPagesLoaded).toBe(fdRef.maxPagesLoaded); + await fd.close(); + await fdRef.close(); + }); + + it("mem descriptors keep their identity (data written through createNew stays on the caller's object)", async () => { + // regression: applying the fallbacks via a copy to a {type: "mem"} + // descriptor detached the memfile store from the caller's object, + // so a later readExisting(sameObject) found no data + const memSrc = { type: "mem" }; + const fdW = await fastFile.createOverride(memSrc, 1 << 22, 1 << 16); + await fdW.write(new Uint8Array([1, 2, 3, 4]), 0); + await fdW.close(); + expect(memSrc.data).toBeInstanceOf(Uint8Array); + const fdR = await fastFile.readExisting(memSrc, 1 << 22, 1 << 16); + const back = await fdR.read(4, 0); + expect(Array.from(back)).toEqual([1, 2, 3, 4]); + await fdR.close(); + }); + + it("does not mutate the caller's descriptor object", async () => { + const src = { type: "file", fileName }; + const fd = await fastFile.readExisting(src, 1 << 22, 1 << 16); + expect(src.cacheSize).toBe(undefined); + expect(src.pageSize).toBe(undefined); + await fd.close(); + }); +}); From 83ccfe522f3320b4a5b2bdf706ecfdb93dcd7105 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sat, 29 Aug 2026 22:41:15 +0100 Subject: [PATCH 39/41] feat!: rename the http descriptor field persistentCache -> cache The option keeps its accepted shapes (true, or {blockSize, maxBytes, dbName}) and its behavior; the new name leaves room for a future shape that injects a custom cache implementation instead of the built-in IndexedDB store. Unreleased API, so no compatibility shim. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- README.md | 4 ++-- build/browser/browser.esm.js | 12 ++++++------ build/browser/browser.iife.js | 2 +- build/node/main.cjs | 2 +- src/httpfile.js | 12 ++++++------ test/httpfile.test.js | 4 ++-- test/idbcache.browser.test.js | 12 ++++++------ 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index d46396e..e1b2e3d 100644 --- a/README.md +++ b/README.md @@ -135,8 +135,8 @@ const g = await fastFile.readExisting({ const f = await fastFile.readExisting({ type: "http", url: "https://example.com/circuit_final.zkey", - persistentCache: true, // or tune it: - // persistentCache: { blockSize: 1 << 21, maxBytes: 1 << 29, dbName: "fastfile-http-cache" }, + cache: true, // or tune it: + // cache: { blockSize: 1 << 21, maxBytes: 1 << 29, dbName: "fastfile-http-cache" }, }); ``` diff --git a/build/browser/browser.esm.js b/build/browser/browser.esm.js index 6fe1c76..93fcf17 100644 --- a/build/browser/browser.esm.js +++ b/build/browser/browser.esm.js @@ -454,9 +454,9 @@ async function M(e) { //#region src/httpfile.js var N = 65536; async function P(e) { - let n = e.url, r = e.persistentCache ? await j({ + let n = e.url, r = e.cache ? await j({ fileKey: n, - options: e.persistentCache + options: e.cache }) : null, i = { Range: "bytes=0-0" }; r && r.validator && (r.validator[0] === "\"" || r.validator.indexOf("W/") === 0 ? i["If-None-Match"] = r.validator : i["If-Modified-Since"] = r.validator); let a; @@ -484,13 +484,13 @@ async function P(e) { }) : await I(n); } let o = new Uint8Array(await a.arrayBuffer()); - if (e.persistentCache) { + if (e.cache) { let t = L(a); t && await M({ fileKey: n, validator: t, totalSize: o.length, - options: e.persistentCache, + options: e.cache, data: o }); } @@ -511,11 +511,11 @@ async function F(e, t, n, r) { if (a + o > s.byteLength) throw Error(e + ": read past the end of the buffered body"); n.set(s.subarray(a, a + o), r); }, o = Math.min(r.pageSize || N, N); - return r.persistentCache && (a = await A(a, { + return r.cache && (a = await A(a, { fileKey: e, validator: t, totalSize: n, - options: r.persistentCache + options: r.cache })), new y(a, n, r.cacheSize, o); } async function I(e) { diff --git a/build/browser/browser.iife.js b/build/browser/browser.iife.js index 7587b2f..cbe3261 100644 --- a/build/browser/browser.iife.js +++ b/build/browser/browser.iife.js @@ -1 +1 @@ -var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ee(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function A(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ee(e,t.key),m-=t.meta.bytes}}async function j(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await A(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function M(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function N(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await A(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||P,P);return r.persistentCache&&(a=await j(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new b(a,n,r.cacheSize,o)}async function L(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function R(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var z=4,B=0,V=[];async function te(e){for(;B>=z;)await new Promise(e=>V.push(e));B++;try{return await e()}finally{B--;let e=V.shift();e&&e()}}var H={retries:3,backoffMs:300,stallTimeoutMs:1e4},U=e=>new Promise(t=>setTimeout(t,e));async function W(e,t,n,r,i,a){return te(async()=>{for(let o=0;;o++)try{return await G(e,t,n,r,i,a)}catch(e){if(e&&(e.degradeToFull||e.permanent)||o>=H.retries)throw e;await U(H.backoffMs*(1<{s&&(c&&clearTimeout(c),c=setTimeout(()=>s.abort(Error(e+`: no data for `+H.stallTimeoutMs+`ms`)),H.stallTimeoutMs))},u=()=>{c&&clearTimeout(c),c=null};l();let d;try{d=await fetch(e,s?{headers:o,signal:s.signal}:{headers:o})}catch(e){throw u(),e}if(l(),d.status===200){let n=R(d);if(!t||n&&n===t){u();let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=d.arrayBuffer().then(e=>new Uint8Array(e)),t}u(),await K(d);let r=Error(e+`: file changed (or server stopped honoring Range) while reading`);throw r.permanent=!0,r}if(d.status!==206){u(),await K(d);let t=Error(`HTTP `+d.status+` reading range `+i+`+`+a+` of `+e);throw d.status<500&&d.status!==429&&(t.permanent=!0),t}let f=d.headers.get(`content-range`),p=f?/bytes\s+(\d+)-(\d+)\//.exec(f):null;if(p&&parseInt(p[1])!==i)throw await K(d),Error(e+`: server returned range starting at `+p[1]+`, requested `+i);let m=0;try{if(d.body&&typeof d.body.getReader==`function`){let t=d.body.getReader();for(;;){let i=await t.read();if(l(),i.done)break;if(m+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+m),m+=i.value.byteLength}}else{let t=new Uint8Array(await d.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),m=t.byteLength}}finally{u()}if(m!==a)throw Error(e+`: short range response (`+m+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ne=Q;async function re(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await F(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ie=$;return e.createNoOverride=ne,e.createOverride=Q,e.readExisting=re,e.readWriteExisting=$,e.readWriteExistingOrCreate=ie,e})({}); \ No newline at end of file +var fastfile=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){let t=e.initialSize||1<<20,n=new c;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function n(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function r(e){let t=new c;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var i=new Uint8Array(4),a=new DataView(i.buffer),o=new Uint8Array(8),s=new DataView(o.buffer),c=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;a.setUint32(0,e,!0),await n.write(i,t)}async writeUBE32(e,t){let n=this;a.setUint32(0,e,!1),await n.write(i,t)}async writeULE64(e,t){let n=this;s.setUint32(0,e&4294967295,!0),s.setUint32(4,Math.floor(e/4294967296),!0),await n.write(o,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},l=1<<22;function u(e){let t=e.initialSize||0,n=new _;n.o=e;let r=t?Math.floor((t-1)/l)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>l?l-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/l),o=r%l,s=n;for(;s>0;){let r=o+s>l?l-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;m.setUint32(0,e,!0),await n.write(p,t)}async writeUBE32(e,t){let n=this;m.setUint32(0,e,!1),await n.write(p,t)}async writeULE64(e,t){let n=this;g.setUint32(0,e&4294967295,!0),g.setUint32(4,Math.floor(e/4294967296),!0),await n.write(h,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/l),a=n%l;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*l+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*l+a+s.length),n=t.pos}return i}},v=1<<20,y=8192,b=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||y,this.maxPagesLoaded=Math.floor((n||v)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,x(r);r.push(i),n+=e}return t.pos=n,x(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function x(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return T.set(e,t),t.catch(()=>T.delete(e)),t}function D(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function k(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ee(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(k(t,0,1/0)),await O(n)}async function A(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await D(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(k(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await O(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([D(d.getAllKeys()),D(d.getAll())]);await O(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ee(e,t.key),m-=t.meta.bytes}}async function j(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await E(c),await A(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=k(n,e,t),[o,s]=await Promise.all([D(i.getAllKeys(a)),D(i.getAll(a))]);await O(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await O(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function M(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||w;if(typeof indexedDB>`u`)return null;try{let n=(await E(t)).transaction(`files`,`readonly`),r=await D(n.objectStore(`files`).get(e.fileKey));return await O(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function N(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||S,s=a.maxBytes||C,c=a.dbName||w;if(typeof indexedDB>`u`||!n)return!1;try{let e=await E(c);await A(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||P,P);return r.cache&&(a=await j(a,{fileKey:e,validator:t,totalSize:n,options:r.cache})),new b(a,n,r.cacheSize,o)}async function L(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return n({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function R(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var z=4,B=0,V=[];async function te(e){for(;B>=z;)await new Promise(e=>V.push(e));B++;try{return await e()}finally{B--;let e=V.shift();e&&e()}}var H={retries:3,backoffMs:300,stallTimeoutMs:1e4},U=e=>new Promise(t=>setTimeout(t,e));async function W(e,t,n,r,i,a){return te(async()=>{for(let o=0;;o++)try{return await G(e,t,n,r,i,a)}catch(e){if(e&&(e.degradeToFull||e.permanent)||o>=H.retries)throw e;await U(H.backoffMs*(1<{s&&(c&&clearTimeout(c),c=setTimeout(()=>s.abort(Error(e+`: no data for `+H.stallTimeoutMs+`ms`)),H.stallTimeoutMs))},u=()=>{c&&clearTimeout(c),c=null};l();let d;try{d=await fetch(e,s?{headers:o,signal:s.signal}:{headers:o})}catch(e){throw u(),e}if(l(),d.status===200){let n=R(d);if(!t||n&&n===t){u();let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=d.arrayBuffer().then(e=>new Uint8Array(e)),t}u(),await K(d);let r=Error(e+`: file changed (or server stopped honoring Range) while reading`);throw r.permanent=!0,r}if(d.status!==206){u(),await K(d);let t=Error(`HTTP `+d.status+` reading range `+i+`+`+a+` of `+e);throw d.status<500&&d.status!==429&&(t.permanent=!0),t}let f=d.headers.get(`content-range`),p=f?/bytes\s+(\d+)-(\d+)\//.exec(f):null;if(p&&parseInt(p[1])!==i)throw await K(d),Error(e+`: server returned range starting at `+p[1]+`, requested `+i);let m=0;try{if(d.body&&typeof d.body.getReader==`function`){let t=d.body.getReader();for(;;){let i=await t.read();if(l(),i.done)break;if(m+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+m),m+=i.value.byteLength}}else{let t=new Uint8Array(await d.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),m=t.byteLength}}finally{u()}if(m!==a)throw Error(e+`: short range response (`+m+`/`+a+` bytes at `+i+`)`)}async function K(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var q=1<<20;function J(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||q,q);return new b(n,t.size,e.cacheSize,r)}function Y(){throw Error(`File I/O is not supported in the browser`)}function X(e){return e instanceof Uint8Array?{type:`mem`,data:e}:(typeof e==`string`&&Y(),e)}function Z(e,t,n){if(e.type===`file`&&Y(),e.type===`mem`)return t(e);if(e.type===`bigMem`)return n(e);throw Error(`Invalid FastFile type: `+e.type)}function Q(e){return Z(X(e),t,u)}var ne=Q;async function re(e,t,r){return e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:r}),typeof e==`string`&&(e={type:`http`,url:e,cacheSize:t,pageSize:r}),e.type===`http`?await F(e):e.type===`blob`?J(e):Z(e,n,d)}function $(e){return typeof e==`string`&&Y(),Z(e,r,f)}var ie=$;return e.createNoOverride=ne,e.createOverride=Q,e.readExisting=re,e.readWriteExisting=$,e.readWriteExistingOrCreate=ie,e})({}); \ No newline at end of file diff --git a/build/node/main.cjs b/build/node/main.cjs index 7da7c4d..22b2962 100644 --- a/build/node/main.cjs +++ b/build/node/main.cjs @@ -1 +1 @@ -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new O;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;T.setUint32(0,e,!0),await n.write(w,t)}async writeUBE32(e,t){let n=this;T.setUint32(0,e,!1),await n.write(w,t)}async writeULE64(e,t){let n=this;D.setUint32(0,e&4294967295,!0),D.setUint32(4,Math.floor(e/4294967296),!0),await n.write(E,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},ee=1<<20,te=8192,k=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||te,this.maxPagesLoaded=Math.floor((n||ee)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,A(r);r.push(i),n+=e}return t.pos=n,A(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function A(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return P.set(e,t),t.catch(()=>P.delete(e)),t}function I(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function L(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function R(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function ne(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(R(t,0,1/0)),await L(n)}async function z(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await I(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(R(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await L(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([I(d.getAllKeys()),I(d.getAll())]);await L(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await ne(e,t.key),m-=t.meta.bytes}}async function re(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||j,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await F(c),await z(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=R(n,e,t),[o,s]=await Promise.all([I(i.getAllKeys(a)),I(i.getAll(a))]);await L(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await L(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function B(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||N;if(typeof indexedDB>`u`)return null;try{let n=(await F(t)).transaction(`files`,`readonly`),r=await I(n.objectStore(`files`).get(e.fileKey));return await L(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function V(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||j,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!n)return!1;try{let e=await F(c);await z(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||H,H);return r.persistentCache&&(a=await re(a,{fileKey:e,validator:t,totalSize:n,options:r.persistentCache})),new k(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var K=4,q=0,J=[];async function ae(e){for(;q>=K;)await new Promise(e=>J.push(e));q++;try{return await e()}finally{q--;let e=J.shift();e&&e()}}var Y={retries:3,backoffMs:300,stallTimeoutMs:1e4},oe=e=>new Promise(t=>setTimeout(t,e));async function se(e,t,n,r,i,a){return ae(async()=>{for(let o=0;;o++)try{return await ce(e,t,n,r,i,a)}catch(e){if(e&&(e.degradeToFull||e.permanent)||o>=Y.retries)throw e;await oe(Y.backoffMs*(1<{s&&(c&&clearTimeout(c),c=setTimeout(()=>s.abort(Error(e+`: no data for `+Y.stallTimeoutMs+`ms`)),Y.stallTimeoutMs))},u=()=>{c&&clearTimeout(c),c=null};l();let d;try{d=await fetch(e,s?{headers:o,signal:s.signal}:{headers:o})}catch(e){throw u(),e}if(l(),d.status===200){let n=G(d);if(!t||n&&n===t){u();let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=d.arrayBuffer().then(e=>new Uint8Array(e)),t}u(),await X(d);let r=Error(e+`: file changed (or server stopped honoring Range) while reading`);throw r.permanent=!0,r}if(d.status!==206){u(),await X(d);let t=Error(`HTTP `+d.status+` reading range `+i+`+`+a+` of `+e);throw d.status<500&&d.status!==429&&(t.permanent=!0),t}let f=d.headers.get(`content-range`),p=f?/bytes\s+(\d+)-(\d+)\//.exec(f):null;if(p&&parseInt(p[1])!==i)throw await X(d),Error(e+`: server returned range starting at `+p[1]+`, requested `+i);let m=0;try{if(d.body&&typeof d.body.getReader==`function`){let t=d.body.getReader();for(;;){let i=await t.read();if(l(),i.done)break;if(m+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+m),m+=i.value.byteLength}}else{let t=new Uint8Array(await d.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),m=t.byteLength}}finally{u()}if(m!==a)throw Error(e+`: short range response (`+m+`/`+a+` bytes at `+i+`)`)}async function X(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var Z=1<<20;function le(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||Z,Z);return new k(n,t.size,e.cacheSize,r)}var Q=65536,$=8192,ue=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;async function de(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function pe(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!ue||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await S(e);if(e.type==`http`)return await ie(e);if(e.type==`blob`)return le(e);throw Error(`Invalid FastFile type: `+e.type)}function me(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}function he(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Q,pageSize:n||$}),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return C(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=fe,exports.createOverride=de,exports.readExisting=pe,exports.readWriteExisting=me,exports.readWriteExistingOrCreate=he; \ No newline at end of file +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fs");c=s(c,1);let l=require("constants");async function u(e,t,n,r){if(n||=4096*64,typeof t!=`number`&&[`w+`,`wx+`,`r`,`ax+`,`a+`].indexOf(t)<0)throw Error(`Invalid open option`);let i=await c.default.promises.open(e,t);return new d(i,await i.stat(),n,r,e)}var d=class{constructor(e,t,n,r,i){for(this.fileName=i,this.fd=e,this.pos=0,this.pageSize=r||256;this.pageSize{t.pendingLoads.push({page:e,resolve:n,reject:r})});return t.__statusPage(`After Load request: `,e),n}__statusPage(e,t){let n=[],r=this;if(!r.logHistory)return;n.push(`==`+e+` `+t);let i=``;for(let e=0;e `+t.history[e][n][r])}_triggerLoad(){let e=this;if(e.reading||e.pendingLoads.length==0)return;let t=Object.keys(e.pages),n=[];for(let r=0;r0&&(e.pages[e.pendingLoads[0].page]!==void 0||r>0||n.length>0);){let t=e.pendingLoads.shift();if(e.pages[t.page]!==void 0){e.pages[t.page].pendingOps++;let r=n.indexOf(t.page);r>=0&&n.splice(r,1),e.pages[t.page].loading?e.pages[t.page].loading.push(t):t.resolve(),e.__statusPage(`After Load (cached): `,t.page)}else{if(r)r--;else{let t=n.shift();e.__statusPage(`Before Unload: `,t),e.avBuffs.unshift(e.pages[t]),delete e.pages[t],e.__statusPage(`After Unload: `,t)}t.page>=e.totalPages?(e.pages[t.page]=a(),t.resolve(),e.__statusPage(`After Load (new): `,t.page)):(e.reading=!0,e.pages[t.page]=a(),e.pages[t.page].loading=[t],i.push(e.fd.read(e.pages[t.page].buff,0,e.pageSize,t.page*e.pageSize).then(n=>{e.pages[t.page].size=n.bytesRead;let r=e.pages[t.page].loading;delete e.pages[t.page].loading;for(let e=0;e{let r=e.pages[t.page],i=r&&r.loading?r.loading:[t];delete e.pages[t.page];for(let e=0;e{e.reading=!1,e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e)),e._tryClose()});function a(){if(e.avBuffs.length>0){let t=e.avBuffs.shift();return t.dirty=!1,t.pendingOps=1,t.size=0,t}else return{dirty:!1,buff:new Uint8Array(e.pageSize),pendingOps:1,size:0}}}_triggerWrite(){let e=this;if(e.writing)return;let t=Object.keys(e.pages),n=[];for(let r=0;r{i.writing=!1},t=>{console.log(`ERROR Writing: `+t),i.writing=!1,e.error=t,e._tryClose()})))}e.writing&&Promise.all(n).then(()=>{e.writing=!1,setImmediate(e._triggerWrite.bind(e)),e._tryClose(),e.pendingLoads.length>0&&setImmediate(e._triggerLoad.bind(e))})}_getDirtyPage(){for(let e in this.pages)if(this.pages[e].dirty)return e;return-1}_rangeHasCachedPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r)return!0}return!1}async write(e,t){if(e.byteLength==0)return;let n=this;if(n.error)throw n.error;if(t===void 0&&(t=n.pos),n.pos=t+e.byteLength,n.totalSize=n.directWriteThreshold&&ArrayBuffer.isView(e)&&!n._rangeHasCachedPages(t,e.byteLength)){let r=0;for(;rn.totalPages&&(n.totalPages=i+1);return}let r=Math.floor(t/n.pageSize),i=Math.floor((t+e.byteLength-1)/n.pageSize),a=[];for(let e=r;e<=i;e++)a.push(n._loadPage(e));n._triggerLoad();let o=r,s=t%n.pageSize,c=e.byteLength;for(;c>0;){await a[o-r];let t=s+c>n.pageSize?n.pageSize-s:c,i=e.slice(e.byteLength-c,e.byteLength-c+t);new Uint8Array(n.pages[o].buff.buffer,s,t).set(i),n.pages[o].dirty=!0,n.pages[o].pendingOps--,n.pages[o].size=Math.max(s+t,n.pages[o].size),o>=n.totalPages&&(n.totalPages=o+1),c-=t,o++,s=0,n.writing||setImmediate(n._triggerWrite.bind(n))}}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}_rangeHasDirtyPages(e,t){let n=Math.floor(e/this.pageSize),r=Math.floor((e+t-1)/this.pageSize);for(let e of Object.keys(this.pages)){let t=+e;if(t>=n&&t<=r){let e=this.pages[t];if(e.dirty||e.writing)return!0}}return!1}async readToBuffer(e,t,n,r){if(n==0)return;let i=this;if(i.error)throw i.error;if(r===void 0&&(r=i.pos),i.pos=r+n,i.pendingClose)throw Error(`Reading a closing file`);if(n>=i.directReadThreshold&&ArrayBuffer.isView(e)&&!i._rangeHasDirtyPages(r,n)){let a=r+n>i.totalSize?i.totalSize-r:n;a<0&&(a=0);let o=0;for(;oi.pageSize*i.maxPagesLoaded*.8){let e=Math.floor(n*1.1);this.maxPagesLoaded=Math.floor(e/i.pageSize)+1}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=[];for(let e=a;e<=o;e++)s.push(i._loadPage(e));i._triggerLoad();let c=a,l=r%i.pageSize,u=r+n>i.totalSize?n-(r+n-i.totalSize):n,d=0;for(;u>0;){await s[c-a],i.__statusPage(`After Await (read): `,c);let n=l+u>i.pageSize?i.pageSize-l:u,r=new Uint8Array(i.pages[c].buff.buffer,i.pages[c].buff.byteOffset+l,n);e.set(r,t+d),i.pages[c].pendingOps--,i.__statusPage(`After Op done: `,c),u-=n,d+=n,c++,l=0,i.pendingLoads.length>0&&setImmediate(i._triggerLoad.bind(i))}this.pos=r+n}_tryClose(){let e=this;if(e.pendingClose){if(e.error){e.pendingCloseReject(e.error);return}e._getDirtyPage()>=0||e.writing||e.reading||e.pendingLoads.length>0||e.pendingClose()}}close(){let e=this;return e.closePromise||=new Promise((t,n)=>{e.pendingClose=t,e.pendingCloseReject=n,e._tryClose()}).then(()=>{e.fd.close()},t=>{throw e.fd.close(),t}),e.closePromise}async discard(){await this.close(),await c.default.promises.unlink(this.fileName)}async writeULE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!0),await n.write(r,t)}async writeUBE32(e,t){let n=this,r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,e,!1),await n.write(r,t)}async writeULE64(e,t){let n=this,r=new Uint8Array(8),i=new DataView(r.buffer);i.setUint32(0,e&4294967295,!0),i.setUint32(4,Math.floor(e/4294967296),!0),await n.write(r,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=Math.floor(n/t.pageSize),i=!1,a=``;for(;!i;){let e=t._loadPage(r);t._triggerLoad(),await e,t.__statusPage(`After Await (read): `,r);let o=n%t.pageSize,s=new Uint8Array(t.pages[r].buff.buffer,t.pages[r].buff.byteOffset+o,t.pageSize-o),c=s.findIndex(e=>e===0);i=c!==-1,i?(a+=new TextDecoder().decode(s.slice(0,c)),t.pos=r*this.pageSize+o+c+1):(a+=new TextDecoder().decode(s),t.pos=r*this.pageSize+o+s.length),t.pages[r].pendingOps--,t.__statusPage(`After Op done: `,r),n=t.pos,r++,t.pendingLoads.length>0&&setImmediate(t._triggerLoad.bind(t))}return a}};function f(e){let t=e.initialSize||1<<20,n=new y;return n.o=e,n.o.data=new Uint8Array(t),n.allocSize=t,n.totalSize=0,n.readOnly=!1,n.pos=0,n}function p(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!0,t.pos=0,t}function m(e){let t=new y;return t.o=e,t.allocSize=e.data.byteLength,t.totalSize=e.data.byteLength,t.readOnly=!1,t.pos=0,t}var h=new Uint8Array(4),g=new DataView(h.buffer),_=new Uint8Array(8),v=new DataView(_.buffer),y=class{constructor(){this.pageSize=16384}_resizeIfNeeded(e){if(e>this.allocSize){let t=Math.max(this.allocSize+(1<<20),Math.floor(this.allocSize*1.1),e),n=new Uint8Array(t);n.set(this.o.data),this.o.data=n,this.allocSize=t}}async write(e,t){let n=this;if(t===void 0&&(t=n.pos),this.readOnly)throw Error(`Writing a read only file`);this._resizeIfNeeded(t+e.byteLength),this.o.data.set(e.slice(),t),t+e.byteLength>this.totalSize&&(this.totalSize=t+e.byteLength),this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=new Uint8Array(this.o.data.buffer,this.o.data.byteOffset+r,n);e.set(a,t),this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){this.o.data.byteLength!=this.totalSize&&(this.o.data=this.o.data.slice(0,this.totalSize))}async discard(){}async writeULE32(e,t){let n=this;g.setUint32(0,e,!0),await n.write(h,t)}async writeUBE32(e,t){let n=this;g.setUint32(0,e,!1),await n.write(h,t)}async writeULE64(e,t){let n=this;v.setUint32(0,e&4294967295,!0),v.setUint32(4,Math.floor(e/4294967296),!0),await n.write(_,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>=this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);return``}let r=new Uint8Array(t.o.data.buffer,n,this.totalSize-n),i=r.findIndex(e=>e===0),a=i!==-1,o=``;return a?(o=new TextDecoder().decode(r.slice(0,i)),t.pos=n+i+1):t.pos=n,o}},b=1<<22;function x(e){let t=e.initialSize||0,n=new D;n.o=e;let r=t?Math.floor((t-1)/b)+1:0;n.o.data=[];for(let e=0;e0;){let t=i+a>b?b-i:a,o=e.slice(e.byteLength-a,e.byteLength-a+t);new Uint8Array(n.o.data[r].buffer,i,t).set(o),a-=t,r++,i=0}this.pos=t+e.byteLength}async readToBuffer(e,t,n,r){let i=this;if(r===void 0&&(r=i.pos),this.readOnly&&r+n>this.totalSize)throw Error(`Reading out of bounds`);this._resizeIfNeeded(r+n);let a=Math.floor(r/b),o=r%b,s=n;for(;s>0;){let r=o+s>b?b-o:s,c=new Uint8Array(i.o.data[a].buffer,o,r);e.set(c,t+n-s),s-=r,a++,o=0}this.pos=r+n}async read(e,t){let n=this,r=new Uint8Array(e);return await n.readToBuffer(r,0,e,t),r}close(){}async discard(){}async writeULE32(e,t){let n=this;w.setUint32(0,e,!0),await n.write(C,t)}async writeUBE32(e,t){let n=this;w.setUint32(0,e,!1),await n.write(C,t)}async writeULE64(e,t){let n=this;E.setUint32(0,e&4294967295,!0),E.setUint32(4,Math.floor(e/4294967296),!0),await n.write(T,t)}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this,n=e===void 0?t.pos:e;if(n>this.totalSize){if(this.readOnly)throw Error(`Reading out of bounds`);this._resizeIfNeeded(e)}let r=!1,i=``;for(;!r;){let e=Math.floor(n/b),a=n%b;if(t.o.data[e]===void 0)throw Error(`ERROR`);let o=Math.min(2048,t.o.data[e].length-a);if(o<=0)return t.pos=n,i;let s=new Uint8Array(t.o.data[e].buffer,a,o),c=s.findIndex(e=>e===0);r=c!==-1,r?(i+=new TextDecoder().decode(s.slice(0,c)),t.pos=e*b+a+c+1):(i+=new TextDecoder().decode(s),t.pos=e*b+a+s.length),n=t.pos}return i}},te=1<<20,O=8192,k=class{constructor(e,t,n,r){this.readRangeInto=e,this.totalSize=t,this.pos=0,this.pageSize=r||O,this.maxPagesLoaded=Math.floor((n||te)/this.pageSize)+1,this.pages=new Map,this.readOnly=!0}_pageLen(e){let t=e*this.pageSize;return Math.min(t+this.pageSize,this.totalSize)-t}_loadPage(e){let t=this,n=t.pages.get(e);if(n)return t.pages.delete(e),t.pages.set(e,n),n.promise;let r=new Uint8Array(t._pageLen(e));return n={buff:null,promise:null},n.promise=t.readRangeInto(r,0,e*t.pageSize,r.byteLength).then(function(){return n.buff=r,r},function(n){throw t.pages.delete(e),n}),t.pages.set(e,n),t._trimCache(),n.promise}_trimCache(){let e=this;if(!(e.pages.size<=e.maxPagesLoaded))for(let t of e.pages){if(e.pages.size<=e.maxPagesLoaded)return;t[1].buff&&e.pages.delete(t[0])}}async readToBuffer(e,t,n,r){let i=this;if(n===0)return;if(i.pendingClose)throw Error(`Reading a closing file`);if(r===void 0&&(r=i.pos),r+n>i.totalSize)throw Error(`Reading out of bounds`);if(i.pos=r+n,n>=i.pageSize){await i.readRangeInto(e,t,r,n);return}let a=Math.floor(r/i.pageSize),o=Math.floor((r+n-1)/i.pageSize),s=r%i.pageSize,c=0;for(let r=a;r<=o;r++){let a=await i._loadPage(r),o=Math.min(n-c,i.pageSize-s);e.set(a.subarray(s,s+o),t+c),c+=o,s=0}}async read(e,t){let n=new Uint8Array(e);return await this.readToBuffer(n,0,e,t),n}async readULE32(e){let t=await this.read(4,e);return new Uint32Array(t.buffer)[0]}async readUBE32(e){let t=await this.read(4,e);return new DataView(t.buffer).getUint32(0,!1)}async readULE64(e){let t=await this.read(8,e),n=new Uint32Array(t.buffer);return n[1]*4294967296+n[0]}async readString(e){let t=this;if(t.pendingClose)throw Error(`Reading a closing file`);let n=e===void 0?t.pos:e,r=[];for(;n=0)return r.push(i.subarray(0,a)),t.pos=n+a+1,A(r);r.push(i),n+=e}return t.pos=n,A(r)}async write(){throw Error(`Writing a read only file`)}async writeULE32(){throw Error(`Writing a read only file`)}async writeUBE32(){throw Error(`Writing a read only file`)}async writeULE64(){throw Error(`Writing a read only file`)}async close(){this.pendingClose||(this.pendingClose=!0,this.pages.clear())}async discard(){await this.close()}};function A(e){let t=0;for(let n=0;n{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.createObjectStore(`files`),e.createObjectStore(`blocks`)},r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error),r.onblocked=()=>n(Error(`IndexedDB open blocked`))});return P.set(e,t),t.catch(()=>P.delete(e)),t}function I(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function L(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onerror=()=>n(e.error),e.onabort=()=>n(e.error||Error(`IndexedDB transaction aborted`))})}function R(e,t,n){return IDBKeyRange.bound([e,t],[e,n])}async function z(e,t){let n=e.transaction([`files`,`blocks`],`readwrite`);n.objectStore(`files`).delete(t),n.objectStore(`blocks`).delete(R(t,0,1/0)),await L(n)}async function B(e,t,n,r,i,a){let o=e.transaction([`files`,`blocks`],`readwrite`),s=o.objectStore(`files`),c=await I(s.get(t)),l=0;c&&c.validator===n&&c.totalSize===r&&c.blockSize===i?l=c.bytes:c&&o.objectStore(`blocks`).delete(R(t,0,1/0)),s.put({validator:n,totalSize:r,blockSize:i,bytes:l,lastUsed:Date.now()},t),await L(o);let u=e.transaction(`files`,`readonly`),d=u.objectStore(`files`),[f,p]=await Promise.all([I(d.getAllKeys()),I(d.getAll())]);await L(u);let m=p.reduce((e,t)=>e+t.bytes,0);if(m<=a)return;let h=f.map((e,t)=>({key:e,meta:p[t]})).filter(e=>e.key!==t).sort((e,t)=>e.meta.lastUsed-t.meta.lastUsed);for(let t of h){if(m<=a)break;await z(e,t.key),m-=t.meta.bytes}}async function ne(e,t){let{fileKey:n,validator:r,totalSize:i}=t,a=typeof t.options==`object`&&t.options||{},o=a.blockSize||j,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!r)return e;let l;try{l=await F(c),await B(l,n,r,i,o,s)}catch{return e}let u=!1;async function d(e,t){let r=l.transaction(`blocks`,`readonly`),i=r.objectStore(`blocks`),a=R(n,e,t),[o,s]=await Promise.all([I(i.getAllKeys(a)),I(i.getAll(a))]);await L(r);let c=new Map;for(let e=0;ee+t.data.byteLength,0),a.lastUsed=Date.now(),i.put(a,n)),await L(t)}catch{u=!0}}let p=e=>Math.min(o,i-e*o),m=new Map;function h(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.catch(()=>{}),m.set(e,r),{resolve:t,reject:n,promise:r}}return async function(t,n,r,i){if(i===0)return;let a=Math.floor(r/o),s=Math.floor((r+i-1)/o),c=await d(a,s),l=[],u=(e,a)=>{let s=a*o,c=Math.max(r,s),l=Math.min(r+i,s+p(a));t.set(e.subarray(c-s,l-s),n+(c-r))},g=a;for(;g<=s;){let a=g*o,d=a+p(g),f=c.get(g);if(f){u(f,g),g++;continue}let _=m.get(g);if(_){u(await _,g),g++;continue}if(a>=r&&d<=r+i){let u=g;for(;u+1<=s&&!c.get(u+1)&&!m.get(u+1)&&(u+1)*o+p(u+1)<=r+i;)u++;let d=a,f=u*o+p(u),_=[];for(let e=g;e<=u;e++)_.push(h(e));try{await e(t,n+(d-r),d,f-d)}catch(e){for(let t=g;t<=u;t++)_[t-g].reject(e),m.delete(t);throw e}for(let e=g;e<=u;e++){let i=e*o,a=t.slice(n+(i-r),n+(i-r)+p(e));_[e-g].resolve(a),l.push({index:e,data:a})}g=u+1}else{let t=h(g),n=new Uint8Array(p(g));try{await e(n,0,a,n.length)}catch(e){throw t.reject(e),m.delete(g),e}t.resolve(n),u(n,g),l.push({index:g,data:n}),g++}}await f(l);for(let e of l)m.delete(e.index)}}async function re(e){let t=(typeof e.options==`object`&&e.options||{}).dbName||N;if(typeof indexedDB>`u`)return null;try{let n=(await F(t)).transaction(`files`,`readonly`),r=await I(n.objectStore(`files`).get(e.fileKey));return await L(n),r?{validator:r.validator,totalSize:r.totalSize,blockSize:r.blockSize,bytes:r.bytes}:null}catch{return null}}async function ie(e){let{fileKey:t,validator:n,totalSize:r,data:i}=e,a=typeof e.options==`object`&&e.options||{},o=a.blockSize||j,s=a.maxBytes||M,c=a.dbName||N;if(typeof indexedDB>`u`||!n)return!1;try{let e=await F(c);await B(e,t,n,r,o,s);let a=Math.ceil(r/o);for(let n=0;ns.byteLength)throw Error(e+`: read past the end of the buffered body`);n.set(s.subarray(a,a+o),r)},o=Math.min(r.pageSize||V,V);return r.cache&&(a=await ne(a,{fileKey:e,validator:t,totalSize:n,options:r.cache})),new k(a,n,r.cacheSize,o)}async function W(e){let t=await fetch(e);if(!t.ok)throw Error(`HTTP `+t.status+` fetching `+e);return p({type:`mem`,data:new Uint8Array(await t.arrayBuffer())})}function G(e){let t=e.headers.get(`etag`);return t&&t.indexOf(`W/`)!==0?t:e.headers.get(`last-modified`)||null}var ae=4,K=0,q=[];async function oe(e){for(;K>=ae;)await new Promise(e=>q.push(e));K++;try{return await e()}finally{K--;let e=q.shift();e&&e()}}var J={retries:3,backoffMs:300,stallTimeoutMs:1e4},se=e=>new Promise(t=>setTimeout(t,e));async function ce(e,t,n,r,i,a){return oe(async()=>{for(let o=0;;o++)try{return await le(e,t,n,r,i,a)}catch(e){if(e&&(e.degradeToFull||e.permanent)||o>=J.retries)throw e;await se(J.backoffMs*(1<{s&&(c&&clearTimeout(c),c=setTimeout(()=>s.abort(Error(e+`: no data for `+J.stallTimeoutMs+`ms`)),J.stallTimeoutMs))},u=()=>{c&&clearTimeout(c),c=null};l();let d;try{d=await fetch(e,s?{headers:o,signal:s.signal}:{headers:o})}catch(e){throw u(),e}if(l(),d.status===200){let n=G(d);if(!t||n&&n===t){u();let t=Error(e+`: origin ignored Range; degrading to full buffering`);throw t.degradeToFull=!0,t.fullBodyPromise=d.arrayBuffer().then(e=>new Uint8Array(e)),t}u(),await Y(d);let r=Error(e+`: file changed (or server stopped honoring Range) while reading`);throw r.permanent=!0,r}if(d.status!==206){u(),await Y(d);let t=Error(`HTTP `+d.status+` reading range `+i+`+`+a+` of `+e);throw d.status<500&&d.status!==429&&(t.permanent=!0),t}let f=d.headers.get(`content-range`),p=f?/bytes\s+(\d+)-(\d+)\//.exec(f):null;if(p&&parseInt(p[1])!==i)throw await Y(d),Error(e+`: server returned range starting at `+p[1]+`, requested `+i);let m=0;try{if(d.body&&typeof d.body.getReader==`function`){let t=d.body.getReader();for(;;){let i=await t.read();if(l(),i.done)break;if(m+i.value.byteLength>a)throw t.cancel().catch(function(){}),Error(e+`: range response longer than requested`);n.set(i.value,r+m),m+=i.value.byteLength}}else{let t=new Uint8Array(await d.arrayBuffer());if(t.byteLength>a)throw Error(e+`: range response longer than requested`);n.set(t,r),m=t.byteLength}}finally{u()}if(m!==a)throw Error(e+`: short range response (`+m+`/`+a+` bytes at `+i+`)`)}async function Y(e){try{e.body&&typeof e.body.cancel==`function`?await e.body.cancel():await e.arrayBuffer()}catch{}}var X=1<<20;function ue(e){let t=e.blob,n=async function(e,n,r,i){let a=await t.slice(r,r+i).arrayBuffer();if(a.byteLength!==i)throw Error(`short blob read (`+a.byteLength+`/`+i+` bytes at `+r+`)`);e.set(new Uint8Array(a),n)},r=Math.min(e.pageSize||X,X);return new k(n,t.size,e.cacheSize,r)}var Z=65536,Q=8192,de=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;function $(e,t,n){if(typeof e!=`object`||!e||e.type!==`file`&&e.type!==`http`&&e.type!==`blob`)return e;let r=e.cacheSize===void 0&&t!==void 0,i=e.pageSize===void 0&&n!==void 0;if(!r&&!i)return e;let a=Object.assign({},e);return r&&(a.cacheSize=t),i&&(a.pageSize=n),a}async function fe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Z,pageSize:n||Q}),e=$(e,t,n),e.type==`file`)return await u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}function pe(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Z,pageSize:n||Q}),e=$(e,t,n),e.type==`file`)return u(e.fileName,l.O_TRUNC|l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize,e.pageSize);if(e.type==`mem`)return f(e);if(e.type==`bigMem`)return x(e);throw Error(`Invalid FastFile type: `+e.type)}async function me(e,t,n){if(e instanceof Uint8Array&&(e={type:`mem`,data:e}),typeof Blob<`u`&&e instanceof Blob&&(e={type:`blob`,blob:e,cacheSize:t,pageSize:n}),typeof e==`string`&&(e=!de||/^https?:\/\//i.test(e)?{type:`http`,url:e,cacheSize:t,pageSize:n}:{type:`file`,fileName:e,cacheSize:t||Z,pageSize:n||Q}),e=$(e,t,n),e.type==`file`)return await u(e.fileName,l.O_RDONLY,e.cacheSize,e.pageSize);if(e.type==`mem`)return await p(e);if(e.type==`bigMem`)return await ee(e);if(e.type==`http`)return await H(e);if(e.type==`blob`)return ue(e);throw Error(`Invalid FastFile type: `+e.type)}function he(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Z,pageSize:n||Q}),e=$(e,t,n),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR,e.cacheSize,e.pageSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}function ge(e,t,n){if(typeof e==`string`&&(e={type:`file`,fileName:e,cacheSize:t||Z,pageSize:n||Q}),e=$(e,t,n),e.type==`file`)return u(e.fileName,l.O_CREAT|l.O_RDWR|l.O_EXCL,e.cacheSize);if(e.type==`mem`)return m(e);if(e.type==`bigMem`)return S(e);throw Error(`Invalid FastFile type: `+e.type)}exports.createNoOverride=pe,exports.createOverride=fe,exports.readExisting=me,exports.readWriteExisting=he,exports.readWriteExistingOrCreate=ge; \ No newline at end of file diff --git a/src/httpfile.js b/src/httpfile.js index 0266208..1d003ce 100644 --- a/src/httpfile.js +++ b/src/httpfile.js @@ -38,8 +38,8 @@ export async function readExisting(o) { // blocks for this URL, an unchanged file answers 304 with no body -- // which is what makes warm starts work even against servers that ignore // Range and would otherwise ship the entire file in the probe response. - const cacheMeta = o.persistentCache - ? await peekPersistentMeta({ fileKey: url, options: o.persistentCache }) + const cacheMeta = o.cache + ? await peekPersistentMeta({ fileKey: url, options: o.cache }) : null; const probeHeaders = { "Range": "bytes=0-0" }; if (cacheMeta && cacheMeta.validator) { @@ -100,7 +100,7 @@ export async function readExisting(o) { // 200: the server ignored Range and sent the whole file; reuse this body. const data = new Uint8Array(await probe.arrayBuffer()); - if (o.persistentCache) { + if (o.cache) { // Persist the body so the next session's conditional probe gets a // bodyless 304 and reads locally. Best-effort, validator-keyed. const validator = strongValidator(probe); @@ -109,7 +109,7 @@ export async function readExisting(o) { fileKey: url, validator: validator, totalSize: data.length, - options: o.persistentCache, + options: o.cache, data: data, }); } @@ -144,7 +144,7 @@ async function buildStreamingFile(url, validator, totalSize, o) { dst.set(data.subarray(pos, pos + len), dstOffset); }; const pageSize = Math.min(o.pageSize || MAX_HTTP_PAGE_SIZE, MAX_HTTP_PAGE_SIZE); - if (o.persistentCache) { + if (o.cache) { // Opt-in warm start: persist fetched blocks in IndexedDB so a later // session against the same URL reads locally. Keyed on the strong // validator -- without one the wrapper declines and this stays a @@ -153,7 +153,7 @@ async function buildStreamingFile(url, validator, totalSize, o) { fileKey: url, validator: validator, totalSize: totalSize, - options: o.persistentCache, + options: o.cache, }); } return new RangeFile(readRangeInto, totalSize, o.cacheSize, pageSize); diff --git a/test/httpfile.test.js b/test/httpfile.test.js index b907b91..6ad2569 100644 --- a/test/httpfile.test.js +++ b/test/httpfile.test.js @@ -218,12 +218,12 @@ describe("fastfile testing suite for httpfile", function () { } }); - it("persistentCache is a no-op where IndexedDB does not exist (Node)", async () => { + it("http cache option is a no-op where IndexedDB does not exist (Node)", async () => { const data = makeData(1 << 18); const srv = await startServer({ data: data, etag: "\"v1\"" }); try { const fd = await fastFile.readExisting({ - type: "http", url: srv.url, persistentCache: true, + type: "http", url: srv.url, cache: true, }); const got = await fd.read(64, 12345); assert.deepStrictEqual(Buffer.from(got), Buffer.from(data.slice(12345, 12345 + 64))); diff --git a/test/idbcache.browser.test.js b/test/idbcache.browser.test.js index b581c47..a502a6f 100644 --- a/test/idbcache.browser.test.js +++ b/test/idbcache.browser.test.js @@ -2,7 +2,7 @@ import * as fastFile from "../src/fastfile.browser.js"; import { expect, vi } from "vitest"; // In-Chromium tests for the IndexedDB-backed persistent block cache -// (persistentCache option of the http backend). fetch is stubbed with a +// (cache option of the http backend). fetch is stubbed with a // deterministic Range-honoring server that counts requests, so warm starts // are provable as "zero network requests". @@ -42,7 +42,7 @@ async function openCached(url, cacheOpts) { return fastFile.readExisting({ type: "http", url, - persistentCache: cacheOpts || { blockSize: BLOCK, dbName: "fastfile-test-cache" }, + cache: cacheOpts || { blockSize: BLOCK, dbName: "fastfile-test-cache" }, }); } @@ -131,21 +131,21 @@ describe("IndexedDB persistent block cache", function () { const contentA = makeContent(10); const cA = { count: 0, bytes: 0 }; vi.stubGlobal("fetch", serveRanges(contentA, "\"a1\"", cA)); - const fa = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", persistentCache: opts }); + const fa = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", cache: opts }); await fa.read(3 * BLOCK, 0); // caches 3 MiB for A await fa.close(); const contentB = makeContent(20); const cB = { count: 0, bytes: 0 }; vi.stubGlobal("fetch", serveRanges(contentB, "\"b1\"", cB)); - const fb = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", persistentCache: opts }); + const fb = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", cache: opts }); await fb.read(3 * BLOCK, 0); // A(3) + B(3) > maxBytes at next open await fb.close(); // reopening B evicts A (older lastUsed) and keeps B warm const cB2 = { count: 0, bytes: 0 }; vi.stubGlobal("fetch", serveRanges(contentB, "\"b1\"", cB2)); - const fb2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", persistentCache: opts }); + const fb2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/b.zkey", cache: opts }); await fb2.read(3 * BLOCK, 0); await fb2.close(); expect(cB2.count).toBe(1); // probe only: B stayed cached @@ -153,7 +153,7 @@ describe("IndexedDB persistent block cache", function () { // A must refetch its data const cA2 = { count: 0, bytes: 0 }; vi.stubGlobal("fetch", serveRanges(contentA, "\"a1\"", cA2)); - const fa2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", persistentCache: opts }); + const fa2 = await fastFile.readExisting({ type: "http", url: "https://cache.example/a.zkey", cache: opts }); const back = await fa2.read(64, 0); await fa2.close(); expect([...back]).toEqual([...contentA.subarray(0, 64)]); From 081fc5abce9d387a1269432fcd05c9ac4f070cf0 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sun, 30 Aug 2026 15:02:32 +0100 Subject: [PATCH 40/41] chore: pin LF line endings via .gitattributes Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- .gitattributes | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6df4591 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Normalize all text to LF in the repository and in every checkout, +# regardless of the local core.autocrlf setting. +* text=auto eol=lf + +# Binary test fixtures and artifacts: never subject to EOL conversion. +*.zkey -text +*.ptau -text +*.wtns -text +*.r1cs -text +*.wasm -text +*.png -text From 6bf1b2838d7be78d036d3aec80eee95e3f98a30b Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Wed, 2 Sep 2026 01:18:49 +0100 Subject: [PATCH 41/41] chore: supply-chain hardening (cascade from snarkjs#634) CI: actions SHA-pinned, contents:read permissions, GITHUB_TOKEN never persisted, npm ci --ignore-scripts with a 10-minute cap (npm applies no timeout to git-dependency clones), npm audit signatures step. .npmrc: ignore-scripts (tree verified script-free) + save-exact; the pretest hook is folded into the test script since ignore-scripts also disables pre/post hooks. Dependabot: 7-day cooldown for npm and the pinned actions. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01833VaUEJmrFZ7bVprrWwpp --- .github/dependabot.yml | 16 ++++++++++++++-- .github/workflows/ci.yml | 18 +++++++++++++++--- .npmrc | 11 +++++++++++ package.json | 3 +-- 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 .npmrc diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5e21113..b366017 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,8 +2,20 @@ version: 2 updates: # Enable version updates for npm - package-ecosystem: "npm" - # Look for `package.json` and `lock` files in the `root` directory directory: "/" - # Check the npm registry for updates every day (weekdays) schedule: interval: "daily" + # Supply-chain cooldown: never propose a release younger than 7 days. + # Registry-takeover malware is typically published and yanked within + # hours; the delay makes that window unreachable. + cooldown: + default-days: 7 + + # Keep the SHA-pinned GitHub Actions current (Dependabot updates the + # pinned commit and the version comment together) + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 7 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f3d14e..e2f17b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,9 @@ name: Continuous Integration + +# Least privilege: no job needs write access to the repository. +permissions: + contents: read + on: push: branches: @@ -19,17 +24,24 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@v6 + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + # no job pushes: never persist the GITHUB_TOKEN into .git/config + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: ${{ matrix.node-version }} check-latest: true cache: "npm" - name: Install dependencies - run: npm ci + timeout-minutes: 10 + run: npm ci --ignore-scripts + + - name: Verify registry signatures and provenance attestations + run: npm audit signatures - name: Install Playwright dependencies run: npx playwright install diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..bce7e62 --- /dev/null +++ b/.npmrc @@ -0,0 +1,11 @@ +# Supply-chain hardening. +# +# ignore-scripts: no dependency may run code at install time. The whole tree +# is currently verified script-free, so this costs nothing today and turns a +# future dep adding a postinstall into an explicit, reviewed decision. Note: +# this also disables package.json pre/post hooks, so the test script chains +# its build and lint steps explicitly. +# +# save-exact: npm install --save records exact versions, not ^ranges. +ignore-scripts=true +save-exact=true diff --git a/package.json b/package.json index 6e8a046..a1d6ef0 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,7 @@ }, "scripts": { "lint": "eslint .", - "pretest": "npm run build && npm run lint", - "test": "vitest run", + "test": "npm run build && npm run lint && vitest run", "test:node": "vitest run --project node-esm --project node-cjs", "test:browser": "vitest run --project browser-esm", "test:watch": "vitest",