Skip to content

feat: h5p-verify --preview, a package in the real player in your browser - #70

Merged
alekswebnet merged 4 commits into
mainfrom
feat/verify-preview
Oct 23, 2026
Merged

alekswebnet merged 4 commits into
mainfrom
feat/verify-preview

Conversation

@alekswebnet

Copy link
Copy Markdown
Contributor

Closes #69.

The check says whether a package starts. Whether it is right, a person judges by playing it, the same way for every content type. h5p-verify <package.h5p> --preview checks nothing. It opens the package in the real player in the person's own browser, served from their computer:

$ npx @missing-elements/h5p-verify@latest course.h5p --preview
Previewing course.h5p at http://127.0.0.1:4645/
The package stays on this computer. The preview ends when its tab is closed, or with Ctrl+C.

Changes

The preview (lib/preview.mjs, lib/preview.html)

  • Same player as the check. It serves the package and a page of its own from the verifier's server, with the same player, runtime and library sources. --untrusted, --libraries and --no-refresh-libraries mean what they mean for the check.

  • Served with Range, as a static host serves it, rather than handed over as a picked file, which a page cannot get without a click:

    • 206 with Content-Range, 200 whole, 416 past the end;
    • an ETag from the file's size and time, so a package written again at the same path is a new package to the element;
    • the file streamed from disk, never read whole.

    The element reads it range-http: the libraries in the warm's few requests, media by the ranges the player asks for.

  • The page shows the file name, and a status line with the element's error if the package does not open. It sets the element as the check does.

  • It ends by itself. The page holds an event stream open. The preview ends ten seconds after the last stream closes, which a reload reconnects within, or ten minutes after starting if no tab ever opened. Once its server has gone, the page says so and stops reconnecting, so a stale tab cannot keep a later preview alive.

  • Port 4645 when free. Every preview then shares one origin's Service Worker and caches, instead of leaving a new origin's behind for each run. If another preview or another program holds the port, it takes any free one.

  • The browser opener is open, cmd /c start or xdg-open. The address is printed either way.

The command (bin/h5p-verify.mjs)

  • --preview, and --no-open to print the address instead of opening a browser.
  • The check's own options (--out, --browser, --ready, --settle, --json, -q) are refused with --preview, not ignored, and so is --no-open without it.
  • Exit code 0 when the preview ends, by its tab or by Ctrl+C or SIGTERM; 2 when it could not start.

Shared with the check (lib/verify.mjs)

  • serve() takes the caller's routes and a port to try first, closes open connections when it stops, and answers a path that does not decode with a 400 rather than crashing.
  • packageFile() now refuses a folder given as the package by name, for the check too.
  • serve, packageFile, librarySources, playerDist and launch are exported as @internal. The published declarations are unchanged: verifyPackage, summarize, VerifyError and the report's types.

Docs: a Preview section and two option rows in the package README, a paragraph in docs/verify.md, the preview's exit codes in docs/compatibility.md, and AGENTS.md's layout and verifier sections.

Testing

tests/preview.test.ts, 6 tests:

  • byteRange against 14 headers;
  • the server's answers for the package;
  • a browser playing the package from the preview;
  • the preview ending when no tab opens;
  • the command line with --no-open and Ctrl+C;
  • the refused options.

The browser test adds a 32 MB stored video, which nothing plays, beside the demo's clip. It then checks four things:

  • the element reads the package range-http;
  • a 1,000-byte range of that video comes back 206 through the player;
  • every request to the package is ranged, and together they read less than a quarter of the video;
  • the preview ends once the tab closes.

Results:

  • The verifier's suite: 22 of 22 (16 before, plus the 6 new), in 48 s.
  • pnpm --filter @missing-elements/h5p-verify typecheck passes, with checkJs over the new module.

Measured in Chrome, on the 36.1 MB package from the test:

  • 9 ranged requests and 1.58 MB reached ready;
  • after the 1,000-byte range of the 32 MB video: 12 requests and 2.57 MB, most of it the demo's own 1 MB clip, which its player loads at the start;
  • no request without Range.

In each engine: the demo's video package was previewed in installed Chrome and in Playwright's Firefox and WebKit.

Engine Ready Read as Clip after 2 s of play Page errors Ended with its tab
Chrome 0.43 s range-http 1.93 s 0 yes
Firefox 0.88 s range-http 1.99 s 0 yes
WebKit 1.19 s range-http 2.01 s 0 yes

Port fallback: two previews at once took 4645 and a free port, and both played.

Not verified

  • Opening the default browser. The tests and checks above use --no-open, so open, xdg-open and cmd /c start were not run.
  • Windows at all: the opener, and Ctrl+C.
  • Safari itself, beyond Playwright's WebKit.
  • A deflated video. The player extracts it on demand, by range, as on any host with Range, but its time to first frame through the preview was not measured.

Release

A minor of h5p-verify: --preview and --no-open are added, and docs/compatibility.md gains the preview's exit codes.

After that release is on npm, skills/h5p-verify (and skills/h5p-author, once #68 is merged) should tell the agent to open the preview after the check. That change is left out of this PR so that no skill names an option npm does not have yet.

🤖 Generated with Claude Code

The check says whether a package starts. Whether it is right, a person
judges by playing it, the same way for every content type.

- --preview checks nothing. It serves the package and a page of its own
  from the verifier's server, with the same player, runtime and library
  sources as the check, and opens the default browser on it. --no-open
  prints the address instead.
- The package is served with Range, as a static host serves it, so the
  element reads it range-http: the libraries in a few requests, media by
  the ranges asked for. With a 32 MB stored video added beside the demo's
  clip, 1.58 MB of 36.1 MB reached ready, and one range of that video
  cost a kilobyte.
- It ends ten seconds after its last tab closes, or ten minutes after
  starting if no tab opens. It takes port 4645 when that is free, so
  every preview shares one origin's worker and caches.
- The check shares the server: it now takes the caller's routes and a
  port, and a folder given as the package is refused by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/verify/lib/preview.html Fixed
…e natively

- The preview page resolves its src and sets it only when it is http or
  https, as CodeQL's js/xss-through-dom suggested.
- The server test compared the 1.5 MB archive with toEqual, which walks
  it a byte at a time: 2.6 s locally and past the 5 s timeout on CI.
  Buffer.equals takes it to 36 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alekswebnet and others added 2 commits October 23, 2026 09:31
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The file's beforeAll launched Chrome under Vitest's ten-second hook
timeout. On CI, beside verify.test.ts's browsers, it took longer, and
all six tests were skipped. The browser now starts in the browser test,
under its own timeout (120 s, as verify.test.ts gives its browser
tests), so the other five do not wait on it. The options test, three
node start-ups in a row, gets the 30 s the other command test has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alekswebnet
alekswebnet merged commit b28a22b into main Oct 23, 2026
7 checks passed
alekswebnet added a commit that referenced this pull request Oct 23, 2026
…eside the author skill

Two conflicts, both kept whole: AGENTS.md's layout of packages/verify/tests names preview.test.ts
and author.test.mjs, and docs/verify.md has the preview paragraph followed by the author skill's.
The verifier's tests pass on the merged tree, 28 of 28, author.test.mjs and preview.test.ts among
them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alekswebnet
alekswebnet deleted the feat/verify-preview branch October 27, 2026 20:04
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.

A preview in the real player for any package: h5p-verify --preview

2 participants