rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
74 lines (73 loc) • 2.94 kB
JavaScript
import debug from "debug";
const log = debug("rws-vite-plugin:hmr-stability");
let stabilityPromise = null;
let stabilityResolver = null;
let debounceTimer = null;
const DEBOUNCE_MS = 500;
function startWaitingForStability() {
if (!stabilityPromise) {
log("Starting to wait for server stability...");
stabilityPromise = new Promise((resolve) => {
stabilityResolver = resolve;
});
// Start the timer. If it fires, we're stable.
debounceTimer = setTimeout(finishWaiting, DEBOUNCE_MS);
}
}
function activityDetected() {
if (stabilityPromise) {
// If we're waiting for stability, reset the timer.
log("Activity detected, resetting stability timer.");
if (debounceTimer)
clearTimeout(debounceTimer);
debounceTimer = setTimeout(finishWaiting, DEBOUNCE_MS);
}
}
function finishWaiting() {
if (stabilityResolver) {
log("Server appears stable. Resolving promise.");
stabilityResolver();
}
stabilityPromise = null;
stabilityResolver = null;
debounceTimer = null;
}
export function hmrStabilityPlugin() {
return {
name: "rws-vite-plugin:hmr-stability",
// Monitor server activity
transform() {
activityDetected();
return null;
},
configureServer(server) {
// context(justinvdm, 19 Nov 2025): This hook adds an error handling
// middleware for stale dependency errors. It runs in a returned function,
// which intentionally places it late in the middleware stack. Unlike
// other plugins that must run before the Cloudflare plugin to prevent
// startup deadlocks, its timing is not critical, so `enforce: 'pre'`
// is not needed.
return () => {
server.middlewares.use(async function rwsdkStaleBundleErrorHandler(err, req, res, next) {
if (err &&
typeof err.message === "string" &&
err.message.includes("new version of the pre-bundle")) {
log("Caught stale pre-bundle error. Waiting for server to stabilize...");
startWaitingForStability();
await stabilityPromise;
log("Server stabilized. Sending full-reload and redirecting.");
// Signal the client to do a full page reload.
server.environments.client.hot.send({
type: "full-reload",
});
// No need to wait further here, the stability promise handled it.
res.writeHead(307, { Location: req.url });
res.end();
return;
}
next(err);
});
};
},
};
}