Changelog
All notable changes to this project are documented here. The format follows Keep a Changelog, and the project uses Semantic Versioning. Until 1.0, minor versions may contain breaking changes, and they will be called out.
0.2.0 - 2026-10-20
Section titled “0.2.0 - 2026-10-20”0.2.0 was never published to npm before these changes landed, so everything below ships together in the first 0.2.0 on npm.
-
Large files and AI models (the Stemina case study):
storage: 'opfs'collection option: files go to the Origin Private File System (/offlibrary/<namespace>/<collection>/), one file per URL plus a.meta.jsonsidecar (URL, ETag, Last-Modified, size, type, integrity). Written once and resumed in place at the byte offset, so there is no second copy while downloading. OPFS collections use the fetch transport; the service worker serves OPFS files too.- Dedicated-worker support: the library runs inside a
Workerwith nowindow/document/service-worker assumptions. In a dedicated worker OPFS writes go throughcreateSyncAccessHandle()(1× disk writes, every write’s byte count checked); on a page they fall back tocreateWritable({ keepExistingData }).capabilities.opfsandcapabilities.syncAccessHandlereport what is available. onStorageRefused: 'error' | 'memory': detectsQuotaExceededError, short writes (Chromium’s private-mode OPFS returns a short count instead of throwing) and size mismatches after a commit. With'memory'the download continues in memory from the same byte without re-fetching; the item completes withstorage: 'memory'andstorageRefused, and a newstorage-refusedevent fires.- Streaming SHA-256 integrity:
sha256-SRI values are hashed chunk by chunk (constant memory, about 130 MB/s), and the hash state is checkpointed with the byte offset, so a resumed download only hashes the new bytes.sha384/sha512still use WebCrypto on the finished file. collection.bytes(id)andcollection.arrayBuffer(id), the shapes model runtimes take;match()/blob()work for every storage kind (OPFS returns the disk-backedFile).- Richer
progressevents:{ item, loaded, total, rate, eta, resumed }, plusitem.resumedanditem.storage.
-
Multi-tab / multi-worker downloads: a second context asking for an item that another one is downloading follows its progress (relayed over
BroadcastChannel) and takes over, resuming from the stored bytes, when the owner goes away (tab closed, worker terminated). Cancelling in one tab stops the download in the tab that owns it. -
Resume correctness:
416with a matching total finishes the item instead of failing; weak ETags are never sent inIf-Range; resume state is keyed on the logical URL, so redirects to fresh signed URLs (Hugging Face’s one-hour links) don’t break it; a stored partial download for a different URL is discarded. -
scripts/publish.ps1: one-command npm release for Windows PowerShell 5.1 / PowerShell 7 (preflight, verify, pack preview, publish with OTP, registry check, tag), every registry call with an explicit--registry. SeeRELEASING.md. -
offlibrary/workbox, a first-class Workbox integration (new subpath export, no runtime dependency on Workbox, works with Workbox 6 and 7):OfflibraryPlugin: a Workbox plugin.cachedResponseWillBeUsedserves stored files (with206forRange) inside CacheFirst, CacheOnly and NetworkFirst’s cache step;handlerDidErrormakes them the offline fallback for NetworkOnly/NetworkFirst. Compatible withRangeRequestsPlugin.offlibraryMatch+offlibraryHandler({ fallback }): a ready-made route for the common case. Misses go to any Workbox strategy or handler.initOfflibrary(options): Background Fetch handling without afetchlistener, so Workbox owns routing.dist/offlibrary-workbox.iife.jsforworkbox-sw/importScriptsusers (self.offlibraryWorkbox).- Size budget: 4.5 KB gzip for the whole entry; the route alone is about 1.7 KB.
-
isOfflibraryRequest(request)inofflibrary/sw: the synchronous routing decisionofflibrarySW()uses. -
Documentation site: https://salnikov.no/offlibrary/ (Astro + Starlight), with guides, concepts and a generated API reference. The demo moved to https://salnikov.no/offlibrary/demo/.
Changed
Section titled “Changed”- Resumable checkpoints are append-only. Each 4 MiB checkpoint is one new IndexedDB segment record; the part record keeps only metadata. Before, every checkpoint rewrote the whole growing list of Blobs, so a 256 MiB download wrote 8.6 GB to disk (33×) and took 8.8 s; now it writes 513 MB (the segments plus the final
cache.put) in 1.7 s, and 256 MB in 0.67 s withstorage: 'opfs'in a worker. The IndexedDB schema moves to version 2; partial downloads from 0.1.x are migrated on first open. - Size budgets: the page entry is about 17.5 KB gzip (was about 10.3 KB), the service-worker entries about 5 KB.
- The service-worker module’s routing index and
matchOfflibrary()moved into a shared internal module used by bothofflibrary/swandofflibrary/workbox. Behaviour is unchanged; the index now starts loading and listening for updates on first use, too, not only inofflibrarySW().
0.1.0 - 2026-10-18
Section titled “0.1.0 - 2026-10-18”The first release: a full rewrite of the 2022 prototype.
createOfflibrary(config): validated, typed, configuration-first collections:strategyandbackgroundFetchThreshold;maxItems/maxByteswith LRU eviction (orevict: 'none');retries/retryDelay;resumable,requestInit,autoSize;- Background Fetch
uititles and icons, andclickUrl.
- Background Fetch transport:
- deterministic IDs, so in-flight downloads are re-attached after reloads;
- live progress;
download-total-exceededis retried once without a declared size;- falls back to fetch automatically when Background Fetch refuses to start.
- Fetch transport for every other browser:
- streaming progress,
pause()/resume()/cancel(); - retries with exponential backoff;
- resumable across reloads via IndexedDB segments +
Range/If-Range; - Web Locks prevent duplicate downloads across tabs.
- streaming progress,
- Collections API:
download,downloadAll,done;get/peek,list/snapshot(referentially stable),has;retry,delete,clear,touch;match/blob,usage;sync(items, { prune }),subscribe.
- Typed events:
progress,complete,error,abort,delete,evict,clickandchange, viaon(),subscribe()or plainEventTarget. Pages and the service worker share state through IndexedDB +BroadcastChannel. - Storage helpers:
storage.estimate(),persist(),persisted(). There is a quota pre-check before downloads, andpersist: 'on-download'. - Multi-URL items, integrity (SRI
sha256/384/512), versions, pinned items. offlibrary/sw:offlibrarySW()stores finished Background Fetches, updates the notification, handles notification clicks (same-origin only), and serves stored files withRangesupport (206/416) for offline media seeking;- Workbox-friendly helpers:
matchOfflibrary,handleBackgroundFetchEvent,createRangeResponse,parseRange,verifyIntegrity.
- Builds:
- per-module ESM +
.d.ts; dist/offlibrary.min.js(ESM bundle);dist/offlibrary-sw.min.js;dist/offlibrary-sw.iife.jsforimportScripts().
- per-module ESM +
- Utilities:
formatBytes,isBackgroundFetchSupported,OfflibraryErrorwith stablecodes. - Demo: Offlibrary Radio, an installable podcast PWA with public-domain LibriVox episodes, deployed to GitHub Pages.
- Tests and CI:
- Vitest unit tests;
- Playwright e2e on real Chromium Background Fetch and the fetch fallback;
- CI on Node 20/22 with a size budget.
Removed
Section titled “Removed”- The 2022 Create React App example and the Express test server, including its committed
node_modules. They are still in git history (3e14a45).