danielroe / danielroe/cross-origin-storage

Verify the approach for landing plugin chunks on the Public Hash List

Open
#43 0 comments 0 reactions 1 assignee Claimed by @danielroe View on GitHub
Dominant language
TypeScript
Stars
33
Forks
2
Avg merge
1d 24m
Merged PRs (30d)
13

Description

Hi @danielroe, I've added a source to the [Public Hash List](https://github.com/WICG/cross-origin-storage/tree/main/public-hash-list/implementation) (PHL) that covers the content-addressed chunks `nuxt-cos` and `vite-plugin-cross-origin-storage` emit, so they become eligible under COS availability gating.

Commit: https://github.com/WICG/cross-origin-storage/commit/1962c577a9aab29e62cff250a6d8e1f2f2f3e813

This is aimed at your "Automatic detection of allow-listed dependencies" roadmap item, from the other end: the chunks can't be gated on the list until something puts them there.

### The approach

Every other PHL source scrapes bytes from a public URL. These have none, since they exist only inside site builds, so this source **reproduces** them instead: it enumerates published plugin releases from the npm registry, installs the real plugin, builds a minimal Vite app per (plugin version × `vue` version), and harvests the 64-hex chunks, re-hashing each from its bytes rather than trusting the filename.

Currently 75 hashes: `vue`, `@vue/shared`, `@vue/reactivity`, `@vue/runtime-core` and `@vue/runtime-dom` across the 15 most recent `vue` releases, under plugin 2.0.3.

The matrix is built with plain Vite rather than Nuxt, on the assumption that the module doesn't influence chunk *content*. I checked that rather than assuming it: a real `nuxt-cos@2.0.3` build emits 5 chunks, all already covered.

### What I'd most like you to sanity-check

**`RECIPE = 'cos1'` doesn't capture everything that decides the emitted bytes.** The rolldown/oxc minifier version does too. `vue@3.5.39` produces three *different* chunks under rolldown 1.1.0, 1.1.4 and 1.2.0, differing only in minifier whitespace:

```js
// rolldown 1.1.0
(e.appContext.config.idPrefix||`v`)+`-`+e.ids[0]+ e.ids[1]++
// rolldown 1.1.4
(e.appContext.config.idPrefix||`v`)+`-`+e.ids[0]+e.ids[1]++
```

(Those two came from the same plugin code, which is what isolates rolldown as the cause rather than a plugin change.)

This isn't a correctness problem, since the hash is of the content and differing bytes can't collide. But it does mean `cos1` currently spans several mutually byte-incompatible outputs, and that every plugin release changing the rolldown pin silently invalidates the whole chunk set and re-fragments sharing between sites on different plugin versions.

Enumerating the hashes is only tractable because your published releases pin rolldown **exactly** (`"rolldown": "1.2.0"`, no range), which makes `(plugin version, package version) → chunk hash` a total, reproducible function. The source deliberately skips any release pinning a range, since its output would depend on when a site happened to install.

So, concretely:

1. Is reproducing-by-building the right approach, or would you rather expose the hashes directly? The plugin already knows them at build time.
2. Is the exact rolldown pin intentional and something you'd be willing to keep? It's the single assumption everything here rests on.
3. Would it be worth folding the rolldown version into `RECIPE`, so the specifier is self-describing about which build produced it?
4. Is `[/^(?:vue$|@vue\/)/]` stable as the default managed set? Widening it would change what sites actually emit.

A heads-up on releases that change the recipe would be genuinely useful, because each one invalidates every hash currently on the list. The PHL pipeline picks up new releases automatically and reports them, but it can only react after the fact.

Two smaller notes from building this. `nuxt-cos@2.0.1` emits no chunks at all under a current Nuxt, since the plugin peers `vite@^5 || ^6 || ^7` while Nuxt 4 ships Vite 8, so it never applies. And the COS manifest only started recording each chunk's npm package name in 2.0.3, which is why the source starts there; earlier releases would need the package scraped out of the license banner.

Thanks a lot for building this, and for making the chunks reproducible in the first place. That property is the entire reason they can go on a shared allowlist at all.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.