@missing-elements/h5p-embed 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -8
- package/lib/build.mjs +13 -9
- package/package.json +5 -5
- package/site/embed.js +29 -16
- package/site/main.js +2 -2
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ a subdomain shares your site's cookies.
|
|
|
30
30
|
| `--packages <origins>` | Play packages and fetch library bundles only from these origins (the site's own is always allowed). Add `https://cdn.jsdelivr.net` for `libraries=pack` on a site built with `--no-libraries`. Default: any https host |
|
|
31
31
|
| `--ancestors <origins>` | Only these sites may frame the page (`frame-ancestors`, header only). Default: any site |
|
|
32
32
|
| `--default-libraries <sources>` | The `libraries` value for addresses that name none: `pack` or URLs. Checked against `--packages`. Default: none, so exports without libraries are refused unless the address asks |
|
|
33
|
-
| `--no-libraries` | Leave out the 13.
|
|
33
|
+
| `--no-libraries` | Leave out the library pack, 13.8 MB in `libraries/`, one archive per library, of which a learner's browser fetches only those a package needs; `libraries=pack` then fetches them from jsDelivr, where allowed |
|
|
34
34
|
| `--force` | Write into a non-empty folder, replacing only this tool's files |
|
|
35
35
|
| `-h`, `--help` / `-v`, `--version` | |
|
|
36
36
|
|
|
@@ -65,7 +65,7 @@ The headers themselves, for a host not listed:
|
|
|
65
65
|
|---|---|
|
|
66
66
|
| every path | `Content-Security-Policy` (the policy the command prints, with `frame-ancestors` from `--ancestors`), `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin` |
|
|
67
67
|
| `/h5p-sw.js` | `Cache-Control: no-cache`, so an update reaches learners on their next visit |
|
|
68
|
-
| `/frame-assets/*`, `/resizer.js`, `/libraries
|
|
68
|
+
| `/frame-assets/*`, `/resizer.js`, `/libraries/*` | `Cache-Control: public, max-age=3600, stale-while-revalidate=86400` |
|
|
69
69
|
|
|
70
70
|
To update, run the command again with `--force` and redeploy.
|
|
71
71
|
|
|
@@ -74,7 +74,8 @@ To update, run the command again with `--force` and redeploy.
|
|
|
74
74
|
| Parameter | Effect |
|
|
75
75
|
|---|---|
|
|
76
76
|
| `src=<url>` | The package, required. Encode `&`, `#`, `+`, `%` and spaces |
|
|
77
|
-
| `libraries=pack`, `<url>` or `none` |
|
|
77
|
+
| `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` |
|
|
78
|
+
| `refresh-libraries=off` | Play the libraries a package carries as exported; `pack` then only fills gaps |
|
|
78
79
|
| `frame`, `copyright`, `export`, `icon`, `reporting` | H5P's action bar and its buttons |
|
|
79
80
|
| `fullscreen=off` | No fullscreen button |
|
|
80
81
|
| `preload=auto` | Start fetching media at once |
|
|
@@ -112,7 +113,7 @@ Whatever `xapi=` says, the frame also posts to its parent:
|
|
|
112
113
|
{ context: 'h5p-offline-player', action: 'report',
|
|
113
114
|
source, // { type: 'range-http' | 'chunked' | 'file', size }
|
|
114
115
|
metadata, // { title, license, licenseVersion, authors, mainLibrary }, from h5p.json; show as text
|
|
115
|
-
libraryBundle, // { url, origin, fromCache }, or null
|
|
116
|
+
libraryBundle, // { url, origin, fromCache, refreshed? }, or null; refreshed: [{ library, from, to }]
|
|
116
117
|
elapsedMs }
|
|
117
118
|
{ context: 'h5p-offline-player', action: 'error', code, message } // code 'refused', 'no-src' or the load's
|
|
118
119
|
```
|
|
@@ -127,19 +128,21 @@ await buildSite({ out: 'public/player', packages: ['https://cdn.example.org'] })
|
|
|
127
128
|
`buildSite` takes `out`, `libraries` (default `true`), `packages`, `ancestors`, `defaultLibraries`
|
|
128
129
|
and `force`, as the flags. `@missing-elements/h5p-embed/embed.js` exports `startEmbed()`, the
|
|
129
130
|
page's script, which reads the page's `<h5p-player>`, `#loader` and `#notice` (see
|
|
130
|
-
`site/index.html` and `site/embed.css`). Its options: `
|
|
131
|
-
|
|
131
|
+
`site/index.html` and `site/embed.css`). Its options: `pack` (the `@missing-elements/h5p-libraries`
|
|
132
|
+
export, which `libraries=pack` then plays from) or `packBase` (the folder of a copy of its
|
|
133
|
+
`parts/`), `packages` (allowed origins, `null` for any), `defaultLibraries`, `runtime` (the
|
|
132
134
|
`@missing-elements/h5p-runtime` export, for a bundled element) and `askInOwnFrame` (default
|
|
133
135
|
`true`; `false` plays a package from another origin as it is when a same-origin page frames it).
|
|
136
|
+
`librariesPack`, the URL of a whole `libraries.h5p`, still works, as a bundle that only fills gaps.
|
|
134
137
|
|
|
135
138
|
```js
|
|
136
139
|
import '@missing-elements/h5p-offline-player'
|
|
137
140
|
import { runtime } from '@missing-elements/h5p-runtime'
|
|
138
|
-
import
|
|
141
|
+
import { pack } from '@missing-elements/h5p-libraries'
|
|
139
142
|
import '@missing-elements/h5p-embed/embed.css'
|
|
140
143
|
import { startEmbed } from '@missing-elements/h5p-embed/embed.js'
|
|
141
144
|
|
|
142
|
-
startEmbed({ runtime,
|
|
145
|
+
startEmbed({ runtime, pack, defaultLibraries: 'pack' })
|
|
143
146
|
```
|
|
144
147
|
|
|
145
148
|
## Privacy
|
package/lib/build.mjs
CHANGED
|
@@ -7,8 +7,9 @@ import { fileURLToPath } from 'node:url'
|
|
|
7
7
|
* Writes the embed page as a static site: one folder, deployable to any static host, that a site
|
|
8
8
|
* frames from a player domain of its own. Everything comes from installed packages — the element
|
|
9
9
|
* and its workers from the player's `dist/`, the H5P runtime from its own package as
|
|
10
|
-
* `frame-assets/`, the library pack from `@missing-elements/h5p-libraries`
|
|
11
|
-
* the ones this package was published with, and nothing is
|
|
10
|
+
* `frame-assets/`, the library pack's parts from `@missing-elements/h5p-libraries` as
|
|
11
|
+
* `libraries/` — so the versions are the ones this package was published with, and nothing is
|
|
12
|
+
* fetched.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
export class EmbedError extends Error {}
|
|
@@ -149,7 +150,7 @@ function headerRules(csp) {
|
|
|
149
150
|
{ path: '/h5p-sw.js', headers: { 'Cache-Control': 'no-cache' } },
|
|
150
151
|
{ path: '/frame-assets/*', headers: { 'Cache-Control': revalidate } },
|
|
151
152
|
{ path: '/resizer.js', headers: { 'Cache-Control': revalidate } },
|
|
152
|
-
{ path: '/libraries
|
|
153
|
+
{ path: '/libraries/*', headers: { 'Cache-Control': revalidate } }
|
|
153
154
|
]
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -255,8 +256,9 @@ function notice({ version, libraries }) {
|
|
|
255
256
|
frame-assets/ @missing-elements/h5p-runtime, the H5P core runtime: GPL-3.0-only. Its
|
|
256
257
|
LICENSE.txt and NOTICE.txt say what it is and where its source is; keep them
|
|
257
258
|
with it.
|
|
258
|
-
${libraries ? ` libraries
|
|
259
|
-
own licence, listed in libraries.txt.
|
|
259
|
+
${libraries ? ` libraries/ @missing-elements/h5p-libraries, the H5P hub's libraries, one archive per
|
|
260
|
+
library and index.json, each under its own licence, listed in libraries.txt.
|
|
261
|
+
The player downloads only the ones a package needs.
|
|
260
262
|
` : ''}
|
|
261
263
|
Serve it from a domain that holds nothing else, and frame it:
|
|
262
264
|
|
|
@@ -287,7 +289,7 @@ async function occupied(dir) {
|
|
|
287
289
|
*
|
|
288
290
|
* @param {object} options
|
|
289
291
|
* @param {string} options.out
|
|
290
|
-
* @param {boolean} [options.libraries] include the library pack, for `libraries=pack` (default true)
|
|
292
|
+
* @param {boolean} [options.libraries] include the library pack's parts, for `libraries=pack` (default true)
|
|
291
293
|
* @param {string[] | string | null} [options.packages] the only origins packages may come from
|
|
292
294
|
* @param {string[] | string | null} [options.ancestors] the only origins that may frame the page
|
|
293
295
|
* @param {string | null} [options.defaultLibraries] the `libraries` value for addresses that name none
|
|
@@ -308,7 +310,7 @@ export async function buildSite({ out, libraries = true, packages = null, ancest
|
|
|
308
310
|
|
|
309
311
|
const player = dirname(installed('@missing-elements/h5p-offline-player/dist/h5p-player.js', 'Run `pnpm build` in the workspace, or reinstall this package.'))
|
|
310
312
|
const runtime = dirname(installed('@missing-elements/h5p-runtime/dist/h5p.css', 'Run `pnpm build` in the workspace, or reinstall this package.'))
|
|
311
|
-
const pack = libraries ? installed('@missing-elements/h5p-libraries/
|
|
313
|
+
const pack = libraries ? dirname(installed('@missing-elements/h5p-libraries/parts/index.json', 'Reinstall this package, or pass --no-libraries.')) : null
|
|
312
314
|
const version = (await readFile(join(player, 'VERSION'), 'utf8').catch(() => 'unknown')).trim()
|
|
313
315
|
|
|
314
316
|
await mkdir(target, { recursive: true })
|
|
@@ -316,11 +318,13 @@ export async function buildSite({ out, libraries = true, packages = null, ancest
|
|
|
316
318
|
for (const name of PLAYER_FILES) await cp(join(player, name), join(target, name))
|
|
317
319
|
await rm(join(target, 'frame-assets'), { recursive: true, force: true })
|
|
318
320
|
await cp(runtime, join(target, 'frame-assets'), { recursive: true })
|
|
321
|
+
// A site written before the pack was split carried it whole; that file is gone either way.
|
|
322
|
+
await rm(join(target, 'libraries.h5p'), { force: true })
|
|
323
|
+
await rm(join(target, 'libraries'), { recursive: true, force: true })
|
|
319
324
|
if (pack) {
|
|
320
|
-
await cp(pack, join(target, 'libraries
|
|
325
|
+
await cp(pack, join(target, 'libraries'), { recursive: true })
|
|
321
326
|
await cp(join(dirname(pack), 'libraries.txt'), join(target, 'libraries.txt'))
|
|
322
327
|
} else {
|
|
323
|
-
await rm(join(target, 'libraries.h5p'), { force: true })
|
|
324
328
|
await rm(join(target, 'libraries.txt'), { force: true })
|
|
325
329
|
}
|
|
326
330
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@missing-elements/h5p-embed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Self-hosted H5P embeds: writes the H5P embed page as a static site for a player domain of your own, then any site frames it with an iframe and one script line. No H5P server, no third-party service; optional allowlists for package hosts and embedding sites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"cli"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@missing-elements/h5p-libraries": "^0.
|
|
47
|
-
"@missing-elements/h5p-
|
|
48
|
-
"@missing-elements/h5p-
|
|
46
|
+
"@missing-elements/h5p-libraries": "^0.3.0",
|
|
47
|
+
"@missing-elements/h5p-runtime": "^0.2.5",
|
|
48
|
+
"@missing-elements/h5p-offline-player": "^0.10.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^26.6.5",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"vitest": "^5.0.3"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
|
-
"node": ">=
|
|
58
|
+
"node": ">=22"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"test": "pnpm --filter @missing-elements/h5p-offline-player build && vitest run",
|
package/site/embed.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* `h5p-embed` was told when it wrote the site, and the demo's `/embed` with the demo's copy of the
|
|
6
6
|
* library pack.
|
|
7
7
|
*
|
|
8
|
-
* ?src=<package url>[&libraries=pack|<url> …|none][&
|
|
9
|
-
* [&frame][©right][&export][&icon][&reporting][&fullscreen=off]
|
|
8
|
+
* ?src=<package url>[&libraries=pack|<url> …|none][&refresh-libraries=off][&preload=auto]
|
|
9
|
+
* [&xapi=<parent origin>][&frame][©right][&export][&icon][&reporting][&fullscreen=off]
|
|
10
10
|
* [&activity-id=<IRI>][&custom-css=<stylesheet url>][&lang=<language tag>]
|
|
11
11
|
*
|
|
12
12
|
* Upward it speaks H5P's own resizer protocol — the `hello` / `resize` exchange that h5p.org's
|
|
@@ -23,8 +23,12 @@ const PACK_CDN_ORIGIN = 'https://cdn.jsdelivr.net'
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* @param {object} [options]
|
|
26
|
-
* @param {string | null} [options.
|
|
27
|
-
*
|
|
26
|
+
* @param {string | null} [options.packBase] the folder of this site's copy of the library pack's
|
|
27
|
+
* parts (`parts/` of `@missing-elements/h5p-libraries`), which `libraries=pack` plays from
|
|
28
|
+
* @param {{ libraries: object[] } | null} [options.pack] the same, as the `pack` export a bundler
|
|
29
|
+
* resolves; it wins over `packBase`. Without either, `pack` is the element's copy on jsDelivr
|
|
30
|
+
* @param {string | null} [options.librariesPack] the URL of a whole `libraries.h5p` on this site,
|
|
31
|
+
* for a page written before the pack was split: played as any bundle, with no refresh
|
|
28
32
|
* @param {string[] | null} [options.packages] the origins packages and library bundles may come
|
|
29
33
|
* from, besides this page's own; `null` plays any, one from another origin `untrusted` when the
|
|
30
34
|
* storage is this origin's
|
|
@@ -38,11 +42,15 @@ const PACK_CDN_ORIGIN = 'https://cdn.jsdelivr.net'
|
|
|
38
42
|
* page of its own origin, whose storage it shares. A site whose own preview frames the page for
|
|
39
43
|
* a package the visitor just chose passes `false`
|
|
40
44
|
*/
|
|
41
|
-
export function startEmbed({ librariesPack = null, packages = null, defaultLibraries = null, runtime = null, askInOwnFrame = true } = {}) {
|
|
45
|
+
export function startEmbed({ packBase = null, pack = null, librariesPack = null, packages = null, defaultLibraries = null, runtime = null, askInOwnFrame = true } = {}) {
|
|
42
46
|
const params = new URLSearchParams(location.search)
|
|
43
47
|
const player = document.querySelector('h5p-player')
|
|
44
|
-
// Before `src`: the element resolves the runtime's files
|
|
48
|
+
// Before `src`: the element resolves the runtime's files, and reads where the pack is, when a
|
|
49
|
+
// package is set.
|
|
45
50
|
if (runtime) player.runtime = runtime
|
|
51
|
+
if (pack) player.pack = pack
|
|
52
|
+
else if (packBase) player.setAttribute('pack-base', packBase)
|
|
53
|
+
const ownPack = Boolean(pack || packBase)
|
|
46
54
|
const notice = document.querySelector('#notice')
|
|
47
55
|
const loader = document.querySelector('#loader')
|
|
48
56
|
const framed = window.parent !== window
|
|
@@ -237,12 +245,13 @@ export function startEmbed({ librariesPack = null, packages = null, defaultLibra
|
|
|
237
245
|
if (value === 'none') return { value: '' }
|
|
238
246
|
for (const token of value.split(/\s+/).filter(Boolean)) {
|
|
239
247
|
if (token === 'pack' || token === 'hub') {
|
|
240
|
-
// This site's copy of the pack
|
|
241
|
-
// jsDelivr, where that may be reached, so a snippet
|
|
242
|
-
// a rebuild with --no-libraries. `hub` has meant
|
|
243
|
-
// host that answers a browser serves a
|
|
244
|
-
|
|
245
|
-
|
|
248
|
+
// This site's copy of the pack, which the element was pointed at above; without one, the
|
|
249
|
+
// element's own `pack`, the copy on jsDelivr, where that may be reached, so a snippet
|
|
250
|
+
// written for `pack` keeps playing after a rebuild with --no-libraries. `hub` has meant
|
|
251
|
+
// `pack` since player 0.6: the one H5P hub host that answers a browser serves a
|
|
252
|
+
// catalogue older than current exports need.
|
|
253
|
+
if (ownPack || (!librariesPack && permitted(PACK_CDN_ORIGIN))) sources.push('pack')
|
|
254
|
+
else if (librariesPack) sources.push(librariesPack)
|
|
246
255
|
else return { error: 'This player was set up without the library pack, so libraries=pack is not available here.' }
|
|
247
256
|
} else {
|
|
248
257
|
const origin = urlOrigin(token)
|
|
@@ -256,7 +265,8 @@ export function startEmbed({ librariesPack = null, packages = null, defaultLibra
|
|
|
256
265
|
/**
|
|
257
266
|
* The element's display options, by their attribute names, for the embedding page to ask for:
|
|
258
267
|
* `&frame©right&export` shows H5P's action bar with those buttons, `&fullscreen=off` takes
|
|
259
|
-
* that one away, `&
|
|
268
|
+
* that one away, `&refresh-libraries=off` plays a package's own libraries even where the pack
|
|
269
|
+
* has newer ones, `&activity-id=` names the statements' object and `&custom-css=` restyles the
|
|
260
270
|
* content to the embedding site's taste. `&lang=` is the embedding page's language, which this
|
|
261
271
|
* page cannot read across origins: H5P's own buttons and dialogs follow it. Not `custom-js`,
|
|
262
272
|
* `embed-code` or `user`: a script is a capability on this origin that a link should not hand
|
|
@@ -267,6 +277,9 @@ export function startEmbed({ librariesPack = null, packages = null, defaultLibra
|
|
|
267
277
|
if (params.has(name) && params.get(name) !== 'off') player.setAttribute(name, '')
|
|
268
278
|
}
|
|
269
279
|
if (params.get('fullscreen') === 'off') player.setAttribute('fullscreen', 'off')
|
|
280
|
+
// The pack's newer versions of the libraries a package carries, unless the embedding page
|
|
281
|
+
// needs the package exactly as exported.
|
|
282
|
+
if (params.get('refresh-libraries') === 'off') player.setAttribute('refresh-libraries', 'off')
|
|
270
283
|
for (const name of ['activity-id', 'custom-css']) {
|
|
271
284
|
const value = params.get(name)?.trim()
|
|
272
285
|
if (value) player.setAttribute(name, value)
|
|
@@ -358,11 +371,11 @@ export function startEmbed({ librariesPack = null, packages = null, defaultLibra
|
|
|
358
371
|
// as the host's own: an untrusted package plays with this site's sources alone, the page's
|
|
359
372
|
// default or its pack, and the address's only once the click has trusted it.
|
|
360
373
|
const own = defaultLibraries?.trim() ? librarySources(defaultLibraries.trim()).value : ''
|
|
361
|
-
const
|
|
362
|
-
if (!
|
|
374
|
+
const sitesSources = own || librarySources('pack').value
|
|
375
|
+
if (!sitesSources) {
|
|
363
376
|
askToTrust()
|
|
364
377
|
return
|
|
365
378
|
}
|
|
366
379
|
player.untrusted = true
|
|
367
|
-
start(src,
|
|
380
|
+
start(src, sitesSources)
|
|
368
381
|
}
|
package/site/main.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/*! @missing-elements/h5p-embed. MIT. */
|
|
2
2
|
// The page's entry: `config.js` is what `h5p-embed` was told when it wrote this site — whether
|
|
3
|
-
// the library pack
|
|
3
|
+
// the library pack's parts are here, and which hosts packages may come from.
|
|
4
4
|
import config from './config.js'
|
|
5
5
|
import { startEmbed } from './embed.js'
|
|
6
6
|
|
|
7
7
|
startEmbed({
|
|
8
|
-
|
|
8
|
+
packBase: config.libraries ? new URL('./libraries/', import.meta.url).href : null,
|
|
9
9
|
packages: config.packages,
|
|
10
10
|
defaultLibraries: config.defaultLibraries
|
|
11
11
|
})
|