Skip to content

Fix the first load after upgrading to 1.0 - #50

Merged
alekswebnet merged 2 commits into
mainfrom
fix/upgrade-first-load
Oct 19, 2026
Merged

alekswebnet merged 2 commits into
mainfrom
fix/upgrade-first-load

Conversation

@alekswebnet

@alekswebnet alekswebnet commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Closes #49.

The first load after upgrading from 0.10 to 1.0 could fail. Three causes were reproduced with the published files (player 0.10.0 + runtime 0.2.5, replaced by 1.0.0 + 1.0.0 under the same URLs) and a browser profile that had played packages before. This fixes each.

Changes

1. The runtime's files and the pack are revalidated

  • Before: the demo (vercel.json) and the sites h5p-embed writes served /frame-assets/ (and the embed site /libraries/) with max-age=3600, stale-while-revalidate=86400. The file names are the same in every version, so a returning browser kept runtime 0.2.5's boot script under the new worker. Every play failed with "Cannot read properties of undefined (reading 'H5P')" for up to an hour, plus one more load within a day. This is live on the demo now.
  • Now: Cache-Control: no-cache on those paths, so the browser asks again each time and gets a 304 when nothing changed. resizer.js keeps its hour.
  • Docs: the embed README's header table, the README's copy-the-folders setup and docs/single-worker.md#upgrading say to serve these files revalidated, or under a versioned path.

2. The element waits for a worker of another major (h5p-offline-player.ts, constants.ts)

  • Within a major an older worker serves the element, so going ahead after 3 s stays right there.
  • Across a major it can't. With h5p-sw.js answered after 4 s, the load went ahead with the 0.10 worker, the new one took over halfway through, and the load stayed in indexing until the test gave up at 60 s.
  • Now, when the running worker reports another major, the element waits up to WORKER_MAJOR_UPDATE_TIMEOUT_MS, 30 s.
  • The new worker's activation, which deletes the old major's caches, took about a second with either 4 or 124 caches.

3. The worker names the boot route in every frame document (frame-document.ts)

  • A page from before an upgrade, still open, registers its loads naming main.bundle.js. That includes a tab, and the installed app's old shell until it reloads.
  • For such a load the worker writes the old kind of document. On a host that serves the runtime at one path, the boot script there is 1.0's by then. Without the route it stopped with "The player's Service Worker is older than this runtime…", the message reported.
  • With the route, it boots and leaves the loader's 404 alone. A 0.2 boot script ignores the route.

4. One worker setup at a time (h5p-offline-player.ts, found reviewing the fix)

  • The element keeps its routes before it asks the worker's version. A load that began during the wait — a visitor choosing another package while the first still loads, now up to 30 s — took them as kept and went ahead with the worker of the major before: the failure of 2, by another door.
  • Now ensureWorker holds one setup promise, and every load meanwhile waits on it.

Testing

New tests:

  • worker-update.test.ts, three tests:
    • a worker of the previous major whose script is answered after 4 s is waited for, and the frame runs the new worker;
    • a second src set one second into that wait is held to it, and its frame runs the new worker too (change 4; without it the frame ran the 0.99 worker at 2.3 s);
    • within a major, a worker answered after 8 s is not waited for, and the load goes ahead with the older one, with the warning; the update then lands and takes the frame over.
    • The first test fails with the wait cut back to 3 s (it went ahead at 3.09 s).
    • The dev server's /versioned/__version takes &delay= for this.
  • runtime.test.ts: a load naming 0.2's main.bundle.js, which the host no longer has, with the current boot script at its URL, plays. Without the route in the document it fails with exactly the reported message.
  • frame-document.test.ts: the old kind of document carries the route.
  • embed/tests/build.test.ts: no-cache on /frame-assets/ and /libraries/, in vercel.json and the Caddyfile.

The upgrade, end to end: the published 0.10.0 files, then this branch's build, under the same URLs, in a profile that had played four packages. The scripts are in the session's scratchpad.

Case 1.0.0 as published This branch
Demo's cache headers every play fails, "reading 'H5P'" fixed by the headers, not the build
h5p-sw.js answered after 4 s stuck in indexing plays, after 5.6 s
Page from before the upgrade, still open "older than this runtime" plays
Runtime 0.2.5 pinned, as on a CDN plays plays, with the route ignored

Suites: pnpm typecheck passes. Player: 531 of 531 across all its projects, in Chromium. Embed: 26 of 26. Not run: Firefox and WebKit, which CI covers.

Release

  • Player 1.0.1, for the worker and the element. The fix to the frame document reaches a host only with the new worker.
  • Embed 1.0.1, for the headers it writes. A player domain is fixed by writing the site again with --force.
  • The demo gets its headers from this merge's deploy.
  • Patches: nothing covered by docs/compatibility.md changes.

🤖 Generated with Claude Code

alekswebnet and others added 2 commits October 19, 2026 16:48
Reproduced with the published 0.10.0 + runtime 0.2.5 files replaced by
1.0.0 under the same URLs, in a profile that had played packages:

- The demo and every embed site kept /frame-assets/ (and the embed's
  /libraries/) for an hour plus a day stale, under names that stay the
  same between versions: runtime 0.2.5's boot script ran under the new
  worker and every play failed. Now no-cache, a 304 when unchanged.
- The element waited 3 s for the updated worker and then went ahead with
  the old one, which a worker of another major cannot survive: the load
  stayed in `indexing`. Across a major it now waits up to 30 s.
- A page from before the upgrade, still open, registers loads naming
  main.bundle.js; the worker wrote that document without the boot route,
  and the 1.0 boot script at the same URL stopped with "older than this
  runtime". Every document names the route now; 0.2 ignores it.

Closes #49.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The element keeps its routes before it asks the worker's version, so a
load that began during the wait — up to thirty seconds across a major —
took them as kept and went ahead with the worker of the major before,
the failure of #49 by another door. `ensureWorker` now holds one setup at
a time, and every load meanwhile waits on it.

Also: the last worker-update test now sees the update it gave up on land;
the embed's header comment no longer says a part is kept in the HTTP
cache, which the element never does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alekswebnet
alekswebnet merged commit 76ea9ce into main Oct 19, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The first load after upgrading to 1.0 can fail: stale runtime files, a short worker wait, and pages from before the upgrade

1 participant