fix: bound Service Worker marker reads and attribute native effects

SW-RR-01. The activation marker was read with response.text() whenever no
Content-Length was present, so a large or non-terminating body could consume
the whole activation step. It now reads through a bounded reader that stops one
byte past the ceiling, cancels its reader, applies a read deadline and decodes
UTF-8 fatally.

SW-RR-02. A matching nonce is not identity. An activation or reset result whose
event.source is null can no longer stand in for the expected worker; only a
strict identity match is admitted.

SW-RR-03. The build generator and the shared manifest decoder now read one
exported extension table, so .mjs and .png stop being emitted-then-refused.
.json is deliberately outside it: every JSON file in a build output is a control
document the generator already excludes, not a cacheable asset.

SW-RR-04. Both caches.open and cache.match are closed as a miss. Letting a
match rejection propagate rejected respondWith itself, so the entry never
reached its network fallback.

WP-RR-01. focus and openWindow now carry the certainty phase showNotification
already had — NOT_APPLIED, MAYBE_APPLIED, CONFIRMED — and an effect that lands
after the handler deadline is observed exactly once. The evidence never
authorizes a retry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-08-14 14:10:41 +09:00
co-authored by Claude Opus 5
parent bd90e0c983
commit efc577de63
6 changed files with 272 additions and 25 deletions
+7 -10
View File
@@ -1,6 +1,9 @@
import { createHash } from "node:crypto";
import { canonicalStaticManifestBytes } from "../src/contracts/service-worker-static-manifest.ts";
import {
CACHEABLE_ASSET_CONTENT_TYPES,
canonicalStaticManifestBytes,
} from "../src/contracts/service-worker-static-manifest.ts";
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
import path from "node:path";
@@ -20,15 +23,9 @@ import {
const OUTPUT = ".generated/frontend-runtime/service-worker-assets.ts";
const CACHEABLE_EXTENSIONS: Readonly<Record<string, string>> = Object.freeze({
".js": "text/javascript",
".mjs": "text/javascript",
".css": "text/css",
".woff2": "font/woff2",
".svg": "image/svg+xml",
".png": "image/png",
".webp": "image/webp",
});
// SW-RR-03. The generator and the shared decoder read the same table, so a
// manifest this script produces can never be one the runtime contract refuses.
const CACHEABLE_EXTENSIONS = CACHEABLE_ASSET_CONTENT_TYPES;
const EXCLUDED_FILES: ReadonlySet<string> = new Set([
"index.html",