Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,10 @@ element, and the element acts. That relay is why `frame-document.ts` has a `mess
never promised. `pack` is `@missing-elements/h5p-libraries` on jsDelivr at `LIBRARY_PACK_VERSION`
(its parts under `LIBRARY_PACK_BASE` since 2026-10-18, next invariant but two), which
`sync-h5p-assets` stamps from `packages/libraries/package.json`;
`hub` is read as `pack` with one console note (`parseLibrarySources`), and `pack hub` is not
tried twice. Pinned rather than `@0`, so a player release always plays against the same
`hub` was read as `pack`, with a console note, from 0.6 to 0.10, and went before 1.0
(2026-10-18, #44): an alias kept would have had to stay through 1.x, and a page still writing it
now fails on the address `hub`, named in the error, rather than playing something it did not
name. Pinned rather than `@0`, so a player release always plays against the same
libraries, and `check-library-pack.mjs` in the player's `prepublishOnly` refuses a version not
on npm, or one whose `parts/index.json` on jsDelivr is not this checkout's: the weekly refresh
(`.github/workflows/libraries.yml`) bumps the pack on a branch and stamps the player — version
Expand Down Expand Up @@ -2028,8 +2030,8 @@ SPA-fallback trap described above cannot happen there.
turns the default `connect-src 'self' https:` into exactly the hosts given, and `config.js`
hands the same list to the page, which refuses an `src` or a library URL off it by name
before fetching anything. (Until 2026-10-10 it also refused `hub` unless `https://api.h5p.org`
was listed; `hub` now means `pack`, and a site without its own pack needs
`https://cdn.jsdelivr.net` listed for it.) The page checks only the address it is
was listed; a site without its own pack needs `https://cdn.jsdelivr.net` listed for `pack`, and
`hub`, which meant `pack` from then to embed 0.8, is no longer a token.) The page checks only the address it is
given; `connect-src` also applies to every redirect, so a listed host that redirects off the
list fails as a plain network error — the README says to list every host on the way. With a list, a listed host plays as
it is, never untrusted: the host vouched for it when it wrote the site. Without one, a package
Expand Down Expand Up @@ -2514,8 +2516,8 @@ deliberate additions to it, not drift:
it; the GPL on the H5P core made that `(MIT AND GPL-3.0-only)`, and from 0.5 the runtime and
the frame's boot script are `@missing-elements/h5p-runtime` so the player can be MIT alone.
- A published library bundle, and `libraries` as an ordered list. The design has `hub` or one
URL; the hub's move put a bundle the project controls first, and since 0.6 `hub` means that
bundle and the hub is not asked at all. Since 0.10 the pack is split, a package downloads only
URL; the hub's move put a bundle the project controls first, and since 0.6 the hub is not asked
at all (`hub` meant the pack until it went before 1.0). Since 0.10 the pack is split, a package downloads only
the parts it needs, and the pack's newer versions play in place of the older ones a package
carries: the design plays what a package carries.
- Content written for an older minor than the one served is upgraded in the frame with the
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,11 @@ allows one Service Worker, or must work offline, mounts the player in its own wo
| `jobs` | attribute | The background worker's URL, for a CSP without `blob:` in `worker-src`. Default: `blob:`, then `h5p-jobs.js` beside the element |
| `runtime` | property | The `runtime` export of `@missing-elements/h5p-runtime`. Set before `src` |
| `assets-base` | attribute | Where the runtime's `dist/` is served. Default: `frame-assets/` beside the element |
| `libraries` | attribute | Sources for a package's libraries, tried in order: `pack`, the library pack, of which only the libraries a package needs are downloaded, and whose newer versions play in place of those a package carries; a `.h5p` URL, which only fills gaps; or both. `hub` means `pack`. Default: a package plays as it is, and one without its libraries is refused |
| `libraries` | attribute | Sources for a package's libraries, tried in order: `pack`, the library pack, of which only the libraries a package needs are downloaded, and whose newer versions play in place of those a package carries; a `.h5p` URL, which only fills gaps; or both. Default: a package plays as it is, and one without its libraries is refused |
| `pack-base` | attribute | Where `pack` is: the folder of a copy of `@missing-elements/h5p-libraries`' `parts/` on your site. Default: jsDelivr, at the version this release pins |
| `pack` | property | The `pack` export of `@missing-elements/h5p-libraries`, as a bundler emits it; wins over `pack-base`. Set before `src` |
| `refresh-libraries` | attribute | `off` plays the libraries a package carries as exported, and `pack` only fills gaps. Default: the pack's newer versions play |
| `untrusted` | attribute, property | Plays a package you did not make as data: its own library folders are ignored and the libraries come from `libraries` (default `pack`), `content/` serves only H5P's own file types, and the frame's script policy names exact paths. Its content is checked against each library's `semantics.json`, as the H5P CMS plugins check it. A package that needs its own copy of a library is refused, with `missingLibraries.carried` set. Needs the runtime at 0.2 or later |
| `untrusted` | attribute, property | Plays a package you did not make as data: its own library folders are ignored and the libraries come from `libraries` (default `pack`), `content/` serves only H5P's own file types, and the frame's script policy names exact paths. Its content is checked against each library's `semantics.json`, as the H5P CMS plugins check it. A package that needs its own copy of a library is refused, with `missingLibraries.carried` set |
| `allow-origins` | attribute | Extra origins for the frame's CSP, space separated |
| `frame`, `copyright`, `export`, `icon`, `embed` | attribute | Show the H5P action bar and its buttons. `export` needs `download-url` or a package URL, `embed` needs `embed-code` |
| `fullscreen` | attribute | `off` removes the fullscreen button |
Expand All @@ -196,7 +196,7 @@ allows one Service Worker, or must work offline, mounts the player in its own wo
| `auto-resize` | attribute | `off` to size the element yourself. Default: follows the content's height |
| `preload` | attribute, property | `auto` pulls large deflated media before it is asked for. Default: `none` |
| `resume` | attribute, property | `device` keeps the content's saved state on this device; `host` hands it to the page. Default: `off` |
| `save-interval` | attribute, property | Seconds between saves while the content runs, with `resume`; also saved after each completion and as the page closes. Default: `10` |
| `save-interval` | attribute, property (`saveInterval`) | Seconds between saves while the content runs, with `resume`; also saved after each completion and as the page closes. Default: `10` |
| `userData` | property | Under `resume="host"`, the state to restore, as `userdata` events carried it. Set before `src`. Not checked against the build: compare the `revision` you stored |
| `state` | read-only | `idle`, `probing`, `downloading`, `indexing`, `ready` or `error` |
| `pkgId`, `revision` | read-only | The loaded package's id and build fingerprint |
Expand Down
4 changes: 2 additions & 2 deletions apps/demo/demo/setup.html
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,11 @@ <h2>Attributes</h2>
<tr><td><code>jobs</code></td><td>Where the background worker script is, for a page whose Content Security Policy refuses <code>blob:</code> workers: copy <code>dist/h5p-jobs.js</code> beside <code>h5p-sw.js</code> and point this at it. Default: a <code>blob:</code> URL, then <code>h5p-jobs.js</code> next to the element. Must be same-origin.</td></tr>
<tr><td><code>runtime</code></td><td>A property: the <code>runtime</code> export of <code>@missing-elements/h5p-runtime</code>, for a bundler to emit the files. Set it before <code>src</code>; it wins over <code>assets-base</code>.</td></tr>
<tr><td><code>assets-base</code></td><td>The folder the runtime's <code>dist/</code> is served from. Default: <code>frame-assets/</code> next to the element. May be a CDN.</td></tr>
<tr><td><code>libraries</code></td><td>Where a package's libraries come from: <code>pack</code> for <a href="https://www.npmjs.com/package/@missing-elements/h5p-libraries" rel="noopener"><code>@missing-elements/h5p-libraries</code></a>, every H5P hub content type's libraries, one archive per library, of which only those a package needs are downloaded, from jsDelivr, and whose newer versions play in place of those a package carries; or the URL of a <code>.h5p</code> that carries library folders, which only fills gaps; or several separated by spaces, tried in order. <code>hub</code> means <code>pack</code>. Downloaded once and cached. Default: a package plays as it is, and one without its libraries is refused, with the missing libraries named.</td></tr>
<tr><td><code>libraries</code></td><td>Where a package's libraries come from: <code>pack</code> for <a href="https://www.npmjs.com/package/@missing-elements/h5p-libraries" rel="noopener"><code>@missing-elements/h5p-libraries</code></a>, every H5P hub content type's libraries, one archive per library, of which only those a package needs are downloaded, from jsDelivr, and whose newer versions play in place of those a package carries; or the URL of a <code>.h5p</code> that carries library folders, which only fills gaps; or several separated by spaces, tried in order. Downloaded once and cached. Default: a package plays as it is, and one without its libraries is refused, with the missing libraries named.</td></tr>
<tr><td><code>pack-base</code></td><td>The folder of your own copy of the pack's <code>parts/</code>, with its <code>libraries.txt</code> of licences beside it, for <code>pack</code> to come from instead of jsDelivr. With a bundler, set the <code>pack</code> property to the package's <code>pack</code> export instead.</td></tr>
<tr><td><code>refresh-libraries</code></td><td><code>off</code> plays the libraries a package carries as exported; <code>pack</code> then only fills gaps.</td></tr>
<tr><td><code>preload</code></td><td><code>auto</code> pulls large compressed media once the content is up, instead of when the learner presses play.</td></tr>
<tr><td><code>untrusted</code></td><td>Plays a package you did not make as data: none of its own code runs. Its library folders are ignored and the libraries come from <code>libraries</code>, <code>pack</code> when that is not set; <code>content/</code> serves only H5P's own file types; the frame's script policy names exact paths instead of your origin; the content is checked against each library's <code>semantics.json</code>, as the H5P CMS plugins check it. A package that needs its own copy of a library is refused, and the error's <code>missingLibraries.carried</code> says so. Needs the runtime at 0.2 or later.</td></tr>
<tr><td><code>untrusted</code></td><td>Plays a package you did not make as data: none of its own code runs. Its library folders are ignored and the libraries come from <code>libraries</code>, <code>pack</code> when that is not set; <code>content/</code> serves only H5P's own file types; the frame's script policy names exact paths instead of your origin; the content is checked against each library's <code>semantics.json</code>, as the H5P CMS plugins check it. A package that needs its own copy of a library is refused, and the error's <code>missingLibraries.carried</code> says so.</td></tr>
<tr><td><code>allow-origins</code></td><td>Extra origins the frame's Content Security Policy should permit, space separated: a tenant's video host, an in-house CDN.</td></tr>
<tr><td><code>resume</code></td><td>Keep the content's own saved state &mdash; the slide reached, the answers so far, the position in a video &mdash; in the browser's storage on that device, and pick up there on the next load of the package. <code>host</code> keeps nothing on the device: the page gets a <code>userdata</code> event on every save and hands the state back through <code>userData</code> before the next load. Default: off, nothing is kept. Nothing is sent in either case.</td></tr>
<tr><td><code>save-interval</code></td><td>Seconds between saves while the content runs, with <code>resume</code>. The content is also saved shortly after each completion and as the page closes. Default: 10; the H5P plugins use 30, which suits a host that sends each save to a server.</td></tr>
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/embed.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<span class="spinner" aria-hidden="true"></span>
<span class="loader-text">Loading…</span>
</div>
<h5p-player auto-resize></h5p-player>
<h5p-player></h5p-player>
<p class="notice" id="notice" hidden></p>
</body>
</html>
2 changes: 1 addition & 1 deletion apps/demo/scripts/build-demo-content.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { download, hubBundle as cachedBundle } from './lib/hub.mjs'
* Builds the packages the hosted demo plays, from `demo/content/src/<name>/` into
* `demo/content/<name>.h5p`. Each source is our own `content.json` plus a `manifest.json`
* naming the content type; the libraries come from the H5P hub's bundle for that content type,
* exactly as `libraries="hub"` fetches them at runtime, and the result is a self-contained
* as `libraries="hub"` fetched them at runtime up to player 0.5, and the result is a self-contained
* package run through the normalizer, so it is also an example of what the normalizer produces.
* A manifest with `"stripped": true` is written the way h5p.com and h5p.org export: `h5p.json`
* naming the main library alone, `content/`, and no library folders — the package the player
Expand Down
3 changes: 1 addition & 2 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ warning can come in any release.
## Deprecation

A renamed attribute, value or option keeps working through the rest of its major and goes only
in a major. Two are in that state now: `libraries="hub"`, read as `pack` with a console note, and
a bare `auto-resize`, read as the default.
in a major. None is in that state now.

## Browsers

Expand Down
6 changes: 3 additions & 3 deletions docs/libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ the ones before it lack. The pack refreshes wherever it is in the list, and supp
missing once the sources before it have been tried. A `.h5p` URL only supplies what is missing.
Nothing is on by default: a package is played as it is unless `libraries` names a source.

`hub` means `pack`. Up to player 0.5 it asked the H5P hub itself, but the only hub host that
answers a browser serves an outdated catalogue, so current exports could not get their libraries
there anyway.
The H5P hub itself is not asked: the only hub host that answers a browser serves an outdated
catalogue, so current exports could not get their libraries there anyway. Up to player 0.10,
`hub` was read as `pack`; it is now a word like any other, a URL relative to the page.

## When a source cannot be reached

Expand Down
2 changes: 1 addition & 1 deletion docs/privacy.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ the [README](https://github.com/missing-elements/h5p-offline-player#which-setup)
|---|---|---|
| The package | The host in `src` | Every load from a URL. A picked file is not sent anywhere |
| The player and runtime files | Your site, or a CDN if `assets-base` names one (jsDelivr in the README's example) | Every load |
| Library pack | `cdn.jsdelivr.net` for `pack` (and `hub`, which means the same), unless `pack-base` or the `pack` property names your own copy | With `libraries="pack"`: the libraries a package lacks, and those the pack has newer versions of than the package carries, once each per device. The requests show which libraries, so which content types, are played. Serve the pack from your own domain to keep this on your hosting |
| Library pack | `cdn.jsdelivr.net` for `pack`, unless `pack-base` or the `pack` property names your own copy | With `libraries="pack"`: the libraries a package lacks, and those the pack has newer versions of than the package carries, once each per device. The requests show which libraries, so which content types, are played. Serve the pack from your own domain to keep this on your hosting |
| Library bundle | The `.h5p` URL in `libraries` | A package without its own libraries, once per bundle |
| MathJax | `cdn.jsdelivr.net` or `cdnjs.cloudflare.com` | Only a package carrying an old `H5P.MathDisplay` that loads MathJax from a CDN. The pack's MathDisplay renders from its own folder, and sends nothing |
| Web fonts | `ajax.googleapis.com`, `fonts.googleapis.com`, `fonts.gstatic.com` (Google) | Only content types that load Google Fonts |
Expand Down
2 changes: 1 addition & 1 deletion packages/embed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ To update, run the command again with `--force` and redeploy.
| Parameter | Effect |
|---|---|
| `src=<url>` | The package, required. Encode `&`, `#`, `+`, `%` and spaces |
| `libraries=pack`, `<url>` or `none` | Where libraries come from; several are tried in order. `pack` is this domain's copy, or jsDelivr's without one: a package gets the libraries it lacks from it, and those it carries at the pack's newer versions. `hub` means `pack`; `none` turns off `--default-libraries` |
| `libraries=pack`, `<url>` or `none` | Where libraries come from; several are tried in order. `pack` is this domain's copy, or jsDelivr's without one: a package gets the libraries it lacks from it, and those it carries at the pack's newer versions. `none` turns off `--default-libraries` |
| `refresh-libraries=off` | Play the libraries a package carries as exported; `pack` then only fills gaps |
| `frame`, `copyright`, `export`, `icon`, `reporting` | H5P's action bar and its buttons |
| `fullscreen=off` | No fullscreen button |
Expand Down
10 changes: 5 additions & 5 deletions packages/embed/lib/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const PACK_CDN_ORIGIN = 'https://cdn.jsdelivr.net'

/**
* A `libraries` value for addresses that name none, checked against the site it goes into: the
* tokens the page understands (`pack`, `hub`, `none`, https URLs), and with a host list, nothing
* tokens the page understands (`pack`, `none`, https URLs), and with a host list, nothing
* the page would then refuse on every load. Returns the value normalised to single spaces.
*
* @param {string | null | undefined} value
Expand All @@ -74,19 +74,19 @@ export function parseDefaultLibraries(value, { packages = null, libraries = true
return null
}
// An exact match on a parsed origin, not a substring of a URL. Without its own copy, `pack` is
// the copy on jsDelivr that the element names; `hub` has meant `pack` since player 0.6.
// the copy on jsDelivr that the element names.
const cdnAllowed = !packages || packages.some((origin) => origin === PACK_CDN_ORIGIN)
for (const token of tokens) {
if (token === 'pack' || token === 'hub') {
if (token === 'pack') {
if (!libraries && !cdnAllowed) {
throw new EmbedError(`--default-libraries names ${token}, but the site has no pack (--no-libraries) and --packages does not list ${PACK_CDN_ORIGIN}.`)
throw new EmbedError(`--default-libraries names pack, but the site has no pack (--no-libraries) and --packages does not list ${PACK_CDN_ORIGIN}.`)
}
} else {
let url
try {
url = new URL(token)
} catch {
throw new EmbedError(`--default-libraries: not pack, hub, none or a URL: ${token}`)
throw new EmbedError(`--default-libraries: not pack, none or a URL: ${token}`)
}
if (url.protocol !== 'https:' && !['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) {
throw new EmbedError(`--default-libraries: not an https URL: ${token}`)
Expand Down
Loading
Loading