Summary
@toapi/worker@1.1.0 documents, as one of its three headline benefits:
Offline resilience — if the network is unavailable, an expired-but-present cache entry is served rather than failing.
That fallback cannot run. In packages/2-toapi-worker/src/handle-toapi-request.ts the expired-entry branch returns the promise instead of awaiting it, so the catch is dead code:
} else {
// cached response is expired
try {
// try to serve from network
return serveFromNetwork(req); // <- not awaited
} catch (error) {
// probably network not available, serve old response
errorLog(error);
return cachedResponse; // <- unreachable
}
}
serveFromNetwork starts with await fetch(req), which rejects when there is no network. Because the promise is returned rather than awaited, that rejection escapes handleToapiRequest, reaches event.respondWith(), and the browser reports a network error — instead of serving the cached copy that is sitting right there.
return await serveFromNetwork(req) fixes it.
Why this is the common path, not an edge case
listenForInvalidations calls expireAll() every time the stream opens, which stamps expiresAt = Date.now() on every entry, including ones cached without a TTL (their expiresAt was null). That is documented and intended — "when the service worker connects to the invalidation stream, it automatically marks every cached entry as expired so the next access revalidates it".
The consequence is that after any service-worker restart, every cached entry takes the broken branch. So the practical behaviour is:
- entries re-fetched since the worker last started → served from cache offline (they take the
expiresAt === null path, which returns cachedResponse directly);
- everything else → hard network error offline.
Since a service worker is killed after ~30s idle and restarted on the next event, "everything else" is most of the cache most of the time.
Reproduction
- Set up a worker with
setupToapiWorker() and load a page that reads a tagged /api route.
- Confirm the response is in the
tapi-cache Cache Storage bucket.
- Let the service worker be terminated (DevTools → Application → Service Workers → Stop, or just idle), so the next start re-opens the invalidation stream and runs
expireAll().
- Go offline and reload.
Expected: the cached response is served (per the docs above).
Actual: the request fails with a network error; event.respondWith() received a rejected promise.
Version
@toapi/worker@1.1.0 — verified both in the published dist/handle-toapi-request.js and in the TypeScript source at main.
Summary
@toapi/worker@1.1.0documents, as one of its three headline benefits:That fallback cannot run. In
packages/2-toapi-worker/src/handle-toapi-request.tsthe expired-entry branch returns the promise instead of awaiting it, so thecatchis dead code:serveFromNetworkstarts withawait fetch(req), which rejects when there is no network. Because the promise is returned rather than awaited, that rejection escapeshandleToapiRequest, reachesevent.respondWith(), and the browser reports a network error — instead of serving the cached copy that is sitting right there.return await serveFromNetwork(req)fixes it.Why this is the common path, not an edge case
listenForInvalidationscallsexpireAll()every time the stream opens, which stampsexpiresAt = Date.now()on every entry, including ones cached without a TTL (theirexpiresAtwasnull). That is documented and intended — "when the service worker connects to the invalidation stream, it automatically marks every cached entry as expired so the next access revalidates it".The consequence is that after any service-worker restart, every cached entry takes the broken branch. So the practical behaviour is:
expiresAt === nullpath, which returnscachedResponsedirectly);Since a service worker is killed after ~30s idle and restarted on the next event, "everything else" is most of the cache most of the time.
Reproduction
setupToapiWorker()and load a page that reads a tagged/apiroute.tapi-cacheCache Storage bucket.expireAll().Expected: the cached response is served (per the docs above).
Actual: the request fails with a network error;
event.respondWith()received a rejected promise.Version
@toapi/worker@1.1.0— verified both in the publisheddist/handle-toapi-request.jsand in the TypeScript source atmain.