Skip to content

packfile

@johnhenry/packfile compresses a directory tree into a single archive — gzip(application/webbundle), via the real wbn package — and serves that archive as HTTP responses through the (Request) => Response handler pattern shared across this family.

Previously developed as lemem, never published under that name. Now @johnhenry/packfile, starting at 0.0.0.

Terminal window
npm install @johnhenry/packfile

Latest on npm: @johnhenry/[email protected].

import { fromDirectory, toArchive, createRouter } from '@johnhenry/packfile';
const files = await fromDirectory('./static');
const archive = await toArchive(files);
// later, from the archive alone:
import { fromArchive } from '@johnhenry/packfile';
const restored = await fromArchive(archive);
const handler = createRouter(restored, { alias: { "/": "index.html" } });
const response = await handler(new Request("http://localhost/"));

Or from the CLI:

Terminal window
npx packfile compress ./static ./compiled.wbn
npx packfile serve ./compiled.wbn 8080

packfile isn’t a standalone compiler/server so much as the designed consumer of one sibling package’s archive output, and a drop-in handler for another’s router:

  • fileable — fileable’s <Dir encode="wbn"> renders a JSX subtree straight to a gzip(application/webbundle) archive, via the same wbn package packfile itself depends on — not via a dependency on packfile. The resulting .wbn is directly readable by this package’s own fromArchive() and servable via createRouter(), no unpacking to disk needed. File names containing #, ? or % don’t survive that trip yet (fileable#20), and an empty <Dir> isn’t kept.
  • sitefile — Sitefile’s EXPORT / AS wbn BASE <url> writes a plain (not gzipped) Web Bundle. fromWebBundle(buffer, { baseURL }) from ./web-bundle reads it back into exactly the exported files, and createRouter() serves it. Fixed on main, in the next release: fromArchive(buffer, { baseURL }) (or, without baseURL, the origin of the bundle’s primary URL) reads bundles built for a real site (packfile#18), so a Sitefile EXPORT … AS wbn COMPRESSION gzip reads with plain fromArchive(), and Sitefile’s HEADER values and exact-path REDIRECTs are in the bundle’s responses for createWebBundleRouter(). For a non-ASCII path, pass createRouter() the path as a string rather than a Request (packfile#20). The full table of what works between the three packages, and the open gaps, is on Sitefile’s Family page.
  • @johnhenry/servable — createRouter() returns a (Request | path, ctx?) => Response handler (aliased as .fetch), the exact shape servable’s Route’s handler prop accepts. Mounting a packfile-served directory inside a servable app is just passing a function.
  • andbox — packfile’s Blob Preview mode is a real, direct npm dependency on @johnhenry/andbox: it delegates JS module-specifier resolution to andbox’s createVirtualModuleRegistry() rather than reimplementing it. This is the same technical link that puts packfile on andbox’s accent hue on this site (see this site’s README.md hue registry if you’re curious why).
  • API — fromDirectory, toArchive, fromArchive, createRouter, hashing, caching middleware, browser usage
  • CLI — compress, decompress, serve
  • Blob preview — host packaged content in a browser tab/iframe with no server at all
  • Web Bundle / IWA primitives — the lower-level toWebBundle/fromWebBundle/createWebBundleRouter API, for real Isolated Web App deployment

Internal data formats — the FileEntry/FilesMap table, the archive byte format, hashing, and how the two gzip implementations (Node/browser) relate — are documented precisely in FORMATS.md in the repo.

MIT

github.com/johnhenry/packfile