npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-deploy-continuity

v0.2.0

Published

Keep stale Vite sessions working across static deployments.

Readme

vite-deploy-continuity

Keep already-open Vite applications working while a new static build is deployed.

Content-hashed chunks are excellent cache keys, but they create version skew: an old browser tab may request a lazy chunk that the newest deployment has already deleted. A reliable response needs three cooperating layers:

  1. retain a bounded number of prior build assets;
  2. reload once when Vite reports a stale dynamic import;
  3. serve versioned and precompressed assets with coherent headers.

This package provides those layers without requiring a hosting vendor or web framework.

Is this for you?

The three layers are most valuable when several of these hold:

  • browser tabs stay open long enough to outlive a deployment;
  • the app uses lazy routes or code splitting, so a live tab can request a chunk it has not loaded yet;
  • deployments happen in place, replacing or pruning an existing asset directory rather than publishing a brand-new immutable origin;
  • you control the filesystem or CDN storage, so old-asset retention is something you can actually manage.

Under those conditions the layers reinforce each other:

  • ordinary stale clients keep working by loading the chunks they still reference from the retained prior generations;
  • clients older than the retention window recover gracefully: when a chunk is finally gone, they reload at most once within the configured recovery window instead of showing a broken screen;
  • static delivery stays coherent, serving fresh precompressed siblings with matching cache and Vary headers so the recovery reload lands on assets that are actually cacheable.

Any one of these alone is a partial workaround; together they cover the common, the edge, and the delivery cases of the same version-skew problem.

It may add little value if:

  • your host serves each deployment from an immutable, per-deployment origin, or already retains every referenced generation for you;
  • pages are short-lived or not code-split, so a tab rarely outlives a deploy or needs a chunk that changed;
  • your pipeline never deletes prior content-hashed assets, so the specific failure this targets — a live tab requesting an old chunk that is already gone — cannot happen (other forms of deployment skew may still exist).

This reduces version-skew failures; it does not make a deployment atomic. See the deployment guide for an adoption checklist and help choosing retention settings.

Install

npm install vite-deploy-continuity

Node.js 22 or newer is required. The package is ESM-only, includes TypeScript declarations, and has no dependencies. Express is needed only when using the Express example below.

Asset retention CLI

Build with manifest: true, deploy new assets without clearing the directory, preview the retention decision, and then prune:

vite-retain-assets --dist dist --dry-run

vite-retain-assets \
  --dist dist \
  --history-limit 5 \
  --grace-hours 24

# Opt in to pruning the broader Vite asset set:
vite-retain-assets --dist dist --asset-preset vite --dry-run

By default, deletion is deliberately limited to JavaScript and CSS filenames with an eight-character-or-longer generated suffix (the code preset). The opt-in --asset-preset vite also prunes hash-suffixed images, fonts, JSON, WebAssembly, source maps, media, and their .br/.gz siblings; programmatic callers can instead supply a custom assetPattern (mutually exclusive with a preset). The default manifest path is dist/.vite/manifest.json, matching current Vite output.

A mutating run takes a default cross-process lock in the history directory, so a second run for the same directory fails fast instead of racing; dry runs stay write-free and take no lock. The result fields and an optional onEvent callback expose retained generations, removable bytes and ages, and — for browser recovery — reload and suppression decisions; see the API reference.

Custom Vite layouts can set --manifest, --assets-dir, --assets-base, and --history-dir. Relative file-system paths are resolved under --dist; absolute paths must still remain within it.

The manifest, history, and asset paths must stay within distDirectory. Resolved symbolic links are checked before any write or deletion. Run vite-retain-assets --help for the complete CLI reference. An empty manifest or a manifest that references a missing current asset is rejected so that a broken build cannot make every old asset look unreferenced.

Browser recovery

Install once near application bootstrap:

import { installViteRecovery } from "vite-deploy-continuity/recovery";

installViteRecovery();

The controller listens to Vite's vite:preloadError event and common browser chunk-load failures. It adds a one-time cache-busting query, which is stripped from the URL once the page reloads, and stores a session-scoped attempt record to prevent reload loops within the configured time window. If session storage is unavailable, the query marker preserves the same one-attempt guard.

HTML should still be served with Cache-Control: no-cache.

Static serving helpers

The static module provides:

  • root-bounded request-path resolution with symbolic-link containment checks in the middleware;
  • selection of .br or .gz siblings only when they are at least as fresh as the original, honoring the client's encoding quality preference while leaving range requests to the downstream server;
  • immutable caching for versioned URLs — by default any non-empty version parameter, or a versionPattern/isVersioned validator for stricter classification;
  • a Connect/Express-compatible precompressed middleware that does not depend on Express itself.
import express from "express";
import {
  createPrecompressedMiddleware
} from "vite-deploy-continuity/static";

const app = express();
app.use(createPrecompressedMiddleware({
  rootDirectory: "/srv/app/dist",
  cacheControl: {
    // Mark ?v= immutable only when it is a real content hash, rather than
    // trusting any non-empty value (the compatibility default).
    versionPattern: /^[a-f0-9]{8,}$/
  }
}));
app.use(express.static("/srv/app/dist"));

Prefer versionPattern (or an isVersioned(url) predicate) so only genuinely versioned requests receive the year-long immutable policy.

The middleware recognizes common Vite assets (.js, .mjs, .css, .json, .map, .svg, .txt, .xml, and WebAssembly) by default. It sets the original media type before rewriting the request to a compressed sibling. HTML is opt-in so its required revalidation policy is not accidentally replaced; pass extensions and contentType to customize the set.

Deployment order

  1. Upload the new hashed assets.
  2. Publish HTML and manifest last.
  3. Run bounded retention after publication.
  4. Remove old assets only after the configured generation and time windows.

The package does not perform remote deployment, mutate server configuration, or assume a particular CI provider.

Retention cannot make a multi-step deployment atomic. Validate history and grace windows against real cache lifetimes, and exercise static middleware ordering in staging. Defaults and API details may change during the 0.x series.

See the deployment guide for a staging checklist, custom-layout example, and rollback procedure. The API reference documents every exported function and option.

Development

npm ci
npm run verify

The real-browser end-to-end suite is separate from npm run verify:

npx playwright install chromium
npm run test:e2e

See CONTRIBUTING.md for safety invariants and test expectations. Report vulnerabilities as described in SECURITY.md.

Changelog

Release history is documented in CHANGELOG.md.

License

MIT