{"_id":"@alphablue/site-runtime","name":"@alphablue/site-runtime","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@alphablue/site-runtime","version":"0.1.0","description":"Edge injection of AlphaBlue live site state for Cloudflare Pages and Workers.","license":"MIT","type":"module","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"//":"publishConfig rewrites the entry points at publish time (pnpm applies these). In the workspace the package resolves to TypeScript source, so nothing has to be built before another workspace package can typecheck against it; what ships is the compiled dist.","publishConfig":{"access":"public"},"devDependencies":{"@cloudflare/vitest-pool-workers":"^0.18.6","@cloudflare/workers-types":"^5.20260729.1","typescript":"^5.6.0","vitest":"^4.1.10","wrangler":"^4.112.0"},"scripts":{"build":"tsc -p tsconfig.build.json","test":"vitest run","typecheck":"tsc --noEmit"},"main":"./dist/index.js","types":"./dist/index.d.ts","_id":"@alphablue/site-runtime@0.1.0","_integrity":"sha512-8gVWGzCNJ+riJIiOFWQDC228qc9fa9qU/gef1ch0KDPVRtAYb1HwkDCGBLbqFyjPaqQrdROAgW6ugFwB9OCIAA==","_resolved":"/var/folders/x9/t0h3hmd52hq13mvw9drwtz500000gn/T/tmp.0vIxYbgGNJ/alphablue-site-runtime-0.1.0.tgz","_from":"file:/var/folders/x9/t0h3hmd52hq13mvw9drwtz500000gn/T/tmp.0vIxYbgGNJ/alphablue-site-runtime-0.1.0.tgz","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-8gVWGzCNJ+riJIiOFWQDC228qc9fa9qU/gef1ch0KDPVRtAYb1HwkDCGBLbqFyjPaqQrdROAgW6ugFwB9OCIAA==","shasum":"6237b24a8ace0fad9317a25f0773746c399e9717","tarball":"https://registry.npmjs.org/@alphablue/site-runtime/-/site-runtime-0.1.0.tgz","fileCount":6,"unpackedSize":13297,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCbo+nxj3twzwSPjnndHkvNfqN68gUK4JSiaLBtpiQ5OQIhAKA79pYmEPndXjQ7A6N7IkBqmCbS4j9f/mV7DYvyIV05"}]},"_npmUser":{"name":"lukesproxy","email":"luke@soft.pw"},"directories":{},"maintainers":[{"name":"lukesproxy","email":"luke@soft.pw"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/site-runtime_0.1.0_1785597750042_0.05594212126186893"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-01T15:22:29.894Z","0.1.0":"2026-08-01T15:22:30.173Z","modified":"2026-08-01T15:22:30.375Z"},"maintainers":[{"name":"lukesproxy","email":"luke@soft.pw"}],"description":"Edge injection of AlphaBlue live site state for Cloudflare Pages and Workers.","license":"MIT","readme":"# @alphablue/site-runtime\n\nEdge injection of live site state for statically built sites on Cloudflare.\n\nA static site has no code on the request path, so state that must take effect\nwithout a rebuild — a mourning grayscale, an announcement bar — has nowhere to be\napplied. This library provides the one place it can be: between the static asset\nand the browser, rewriting the built HTML as it streams.\n\nMost sites will not use this directly. It is generated for you by\n[`@alphablue/astro-site`](https://www.npmjs.com/package/@alphablue/astro-site).\n\n## Why not client-side JavaScript\n\nGrayscale applied by a script means the browser paints the site in full colour\nfirst and greys it once the script arrives. For most features that is a blemish.\nThe occasion for this one is royal mourning or a national disaster, where a flash\nof colour is a public discourtesy — and where a script that fails to load fails\nsilently, with nobody finding out.\n\n`HTMLRewriter` puts the attribute on the very first tag of the document, so the\nbrowser knows before it has parsed anything else.\n\n## Usage\n\n### Cloudflare Pages\n\n```ts\n// functions/_middleware.ts\nimport { applyLiveState } from \"@alphablue/site-runtime\";\n\nexport const onRequest: PagesFunction<Env> = async (ctx) =>\n  applyLiveState(await ctx.next(), ctx.env);\n```\n\n### Workers Static Assets\n\nUse `fetchAsset` rather than `env.ASSETS.fetch` — see below.\n\n```ts\n// worker/index.ts\nimport { applyLiveState, fetchAsset } from \"@alphablue/site-runtime\";\n\nexport default {\n  async fetch(request, env) {\n    return applyLiveState(await fetchAsset(request, env), env);\n  },\n} satisfies ExportedHandler<Env>;\n```\n\nBoth hosts need a `LIVE_STATE` KV binding and a `SITE_ID` var, and the site's CSS\nneeds one rule:\n\n```css\nhtml[data-mourning=\"1\"] { filter: grayscale(1); }\n```\n\nExported as `MOURNING_CSS` if you would rather inject it.\n\n## `fetchAsset`, and why it exists\n\nWorkers Static Assets sends an `ETag` for every file. State is applied *after* the\nasset is fetched, so a returning visitor's `If-None-Match` gets a 304 with no\nbody — nothing to inject into — and their browser keeps showing the cached colour\nversion. `fetchAsset` strips conditional validators from navigations only, so\nassets keep their caching.\n\nCloudflare Pages sends no validators for HTML, so this is unnecessary there.\n\n## Failure behaviour\n\nIf the KV read fails, the original response is returned untouched. This code runs\nin front of every page of every site using it; a content-service problem must not\nbe able to take those sites offline.\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-93f264b4dd8d199e00a9f81de76e7f77"}