Skip to content

[pull] main from jsr-io:main - #129

Merged
pull[bot] merged 1 commit into
code:mainfrom
jsr-io:main
Aug 27, 2026
Merged

pull[bot] merged 1 commit into
code:mainfrom
jsr-io:main

Conversation

@pull

@pull pull Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

Fixes #1455.

### The bug

The lb keys R2-backed responses under a namespace of their own, because
`/@scope/...` is served as *either* a module file (bucket) or an HTML
page (frontend) depending on request headers, and the two must not
cross-serve. That namespace was a synthetic host:
`https://bucket-cache.jsr.internal/<host><path>`.

Cloudflare's purge-by-URL API only accepts URLs inside the zone, so
every publish-time purge of `https://jsr.io/@scope/pkg/meta.json` missed
the key the manifest was actually cached under. The result is exactly
what was reported: R2 had the regenerated `meta.json`, a cache-busting
query string returned the correct `latest`, but the edge kept serving
the previous copy until its TTL lapsed. `_meta.json` and the version
page were live the whole time, while Deno's resolver — which reads
`meta.json` — could not see the new version, and further publishes did
not dislodge it. The npm version manifest on `npm.jsr.io` had the same
gap.

(#1456 capped how *long* that lasts by dropping SWR and shortening
`s-maxage`; this makes the purge itself land, so a publish is visible
immediately rather than after the TTL.)

### The fix

Namespace bucket entries under a reserved path on the **public** origin
— `https://jsr.io/__bucket-cache/@scope/pkg/meta.json` — instead of a
synthetic host. The host is still part of the key, so the module and npm
buckets stay distinct, and the purge now has a URL inside the zone that
it can target.

`route` rejects inbound requests for the reserved prefix before any
backend, cache read, or cache write, so the namespace cannot be reached
— or poisoned — from outside.

The API mirrors the prefix (`s3_paths::BUCKET_CACHE_PREFIX`, kept in
sync with `lb/proxy.ts`) and purges both the public and the namespaced
form of each mutable manifest. The public URL still matters there: a
browser navigation to the same URL is served, and cached, by the
frontend.

Changing the key also orphans the current entries, so deploying this
clears any manifest that is stuck right now.

### Tests

- `s3_paths`: both purge helpers return the public URL *and* the
namespaced key.
- `lb/proxy_test.ts`: bucket cache keys live on the public origin under
the prefix, and the module vs. npm buckets stay keyed apart.
- `lb/main_test.ts`: `route` 404s the reserved namespace on all three
hosts without touching a backend, while a merely similar-looking path
routes normally.

`cargo clippy --all-targets` clean; `deno test` in `lb/` passes (43
tests).
@pull pull Bot locked and limited conversation to collaborators Aug 27, 2026
@pull pull Bot added the ⤵️ pull label Aug 27, 2026
@pull
pull Bot merged commit 19ad24d into code:main Aug 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant