@missing-elements/h5p-offline-player 0.1.0

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +273 -0
  3. package/dist/VERSION +1 -0
  4. package/dist/frame-assets/MANIFEST.json +4 -0
  5. package/dist/frame-assets/fonts/h5p-core-30.woff2 +0 -0
  6. package/dist/frame-assets/fonts/h5p-hub-publish.woff +0 -0
  7. package/dist/frame-assets/fonts/h5p-hub-publish.woff2 +0 -0
  8. package/dist/frame-assets/fonts/h5p-theme.woff2 +0 -0
  9. package/dist/frame-assets/fonts/inter/Inter-ExtraBold.woff2 +0 -0
  10. package/dist/frame-assets/fonts/inter/Inter-ExtraBoldItalic.woff2 +0 -0
  11. package/dist/frame-assets/fonts/inter/LICENSE.txt +92 -0
  12. package/dist/frame-assets/fonts/inter/inter-v18-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-600.woff2 +0 -0
  13. package/dist/frame-assets/fonts/inter/inter-v18-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-600italic.woff2 +0 -0
  14. package/dist/frame-assets/fonts/inter/inter-v18-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-italic.woff2 +0 -0
  15. package/dist/frame-assets/fonts/inter/inter-v18-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-regular.woff2 +0 -0
  16. package/dist/frame-assets/fonts/open-sans/OFL.txt +88 -0
  17. package/dist/frame-assets/fonts/open-sans/open-sans-v40-cyrillic_cyrillic-ext_greek_greek-ext_hebrew_latin_latin-ext_math_symbols_vietnamese-600.woff2 +0 -0
  18. package/dist/frame-assets/fonts/open-sans/open-sans-v40-cyrillic_cyrillic-ext_greek_greek-ext_hebrew_latin_latin-ext_math_symbols_vietnamese-600italic.woff2 +0 -0
  19. package/dist/frame-assets/fonts/open-sans/open-sans-v40-cyrillic_cyrillic-ext_greek_greek-ext_hebrew_latin_latin-ext_math_symbols_vietnamese-700.woff2 +0 -0
  20. package/dist/frame-assets/fonts/open-sans/open-sans-v40-cyrillic_cyrillic-ext_greek_greek-ext_hebrew_latin_latin-ext_math_symbols_vietnamese-700italic.woff2 +0 -0
  21. package/dist/frame-assets/fonts/open-sans/open-sans-v40-cyrillic_cyrillic-ext_greek_greek-ext_hebrew_latin_latin-ext_math_symbols_vietnamese-italic.woff2 +0 -0
  22. package/dist/frame-assets/fonts/open-sans/open-sans-v40-cyrillic_cyrillic-ext_greek_greek-ext_hebrew_latin_latin-ext_math_symbols_vietnamese-regular.woff2 +0 -0
  23. package/dist/frame-assets/frame.bundle.js +1 -0
  24. package/dist/frame-assets/images/h5p.svg +16 -0
  25. package/dist/frame-assets/images/throbber.gif +0 -0
  26. package/dist/frame-assets/main.bundle.js +2 -0
  27. package/dist/frame-assets/styles/h5p-fonts.css +103 -0
  28. package/dist/frame-assets/styles/h5p.css +1643 -0
  29. package/dist/h5p-player.js +1780 -0
  30. package/dist/h5p-sw-mount.js +88 -0
  31. package/dist/h5p-sw.js +88 -0
  32. package/package.json +81 -0
  33. package/types/h5p-offline-player.d.ts +154 -0
  34. package/types/jobs/chunk-writer.d.ts +16 -0
  35. package/types/jobs/jobs-worker.d.ts +1 -0
  36. package/types/node/tsconfig.node.tsbuildinfo +1 -0
  37. package/types/node/vite.config.d.ts +6 -0
  38. package/types/node/vite.demo.config.d.ts +2 -0
  39. package/types/node/vite.plugins.d.ts +19 -0
  40. package/types/shared/chunk-store.d.ts +104 -0
  41. package/types/shared/constants.d.ts +67 -0
  42. package/types/shared/entry-names.d.ts +29 -0
  43. package/types/shared/eviction.d.ts +16 -0
  44. package/types/shared/forward-index.d.ts +85 -0
  45. package/types/shared/idb.d.ts +9 -0
  46. package/types/shared/locks.d.ts +14 -0
  47. package/types/shared/mime.d.ts +7 -0
  48. package/types/shared/mp4.d.ts +41 -0
  49. package/types/shared/pkg-id.d.ts +21 -0
  50. package/types/shared/protocol.d.ts +232 -0
  51. package/types/shared/range.d.ts +16 -0
  52. package/types/shared/source.d.ts +57 -0
  53. package/types/shared/stream-utils.d.ts +7 -0
  54. package/types/sw/frame-document.d.ts +19 -0
  55. package/types/sw/mount.d.ts +17 -0
  56. package/types/sw/package-reader.d.ts +242 -0
  57. package/types/sw/routes.d.ts +26 -0
  58. package/types/sw/stream-utils.d.ts +9 -0
  59. package/types/sw/sw-entry.d.ts +1 -0
@@ -0,0 +1,242 @@
1
+ import { type FileEntry } from '@zip.js/zip.js';
2
+ import { type MissingLibraries, type PrefetchEntry } from '../shared/protocol';
3
+ import { type SourceHandle } from '../shared/source';
4
+ import type { ByteRange } from '../shared/range';
5
+ import type { ForwardIndexSnapshot } from '../shared/forward-index';
6
+ /**
7
+ * The package reader: a zip central directory turned into a name → entry index, plus the decision
8
+ * of how each entry gets served. It does not walk dependencies — h5p-standalone resolves
9
+ * `preloadedDependencies` itself and asks for the files it needs by name.
10
+ */
11
+ export type Strategy =
12
+ /** Small, or text the runtime has to parse whole: inflate once into the cache, then serve. */
13
+ {
14
+ kind: 'inline';
15
+ }
16
+ /** Large and stored (method 0): the bytes are already flat in the archive, so slice them. */
17
+ | {
18
+ kind: 'slice';
19
+ }
20
+ /** Large and deflated (method 8): the Jobs worker inflates into chunks, served progressively. */
21
+ | {
22
+ kind: 'chunked';
23
+ };
24
+ /** The parts of `h5p.json` the player needs. Everything in it comes from an untrusted archive. */
25
+ export interface PackageManifest {
26
+ title?: string;
27
+ mainLibrary?: string;
28
+ preloadedDependencies?: LibraryDependency[];
29
+ }
30
+ export interface LibraryDependency {
31
+ machineName: string;
32
+ majorVersion: string | number;
33
+ minorVersion: string | number;
34
+ }
35
+ export interface IndexedEntry {
36
+ /** Normalised name, the form the virtual server routes on. */
37
+ name: string;
38
+ size: number;
39
+ compressedSize: number;
40
+ method: number;
41
+ strategy: Strategy;
42
+ /** The zip.js entry, for an index read from the central directory. Absent for a forward entry. */
43
+ zip?: FileEntry;
44
+ /** Where the compressed bytes begin, when the local header has already been read. */
45
+ dataStart?: number;
46
+ encrypted?: boolean;
47
+ }
48
+ /**
49
+ * An entry together with the archive it lives in. A package may be served from more than one:
50
+ * content from the one the user opened, libraries from a bundle attached to fill its gaps.
51
+ */
52
+ export interface LocatedEntry {
53
+ reader: PackageReader;
54
+ entry: IndexedEntry;
55
+ }
56
+ export declare class PackageReader {
57
+ private readonly handle;
58
+ readonly pkgId: string;
59
+ readonly entries: Map<string, IndexedEntry>;
60
+ readonly rejected: string[];
61
+ manifest: PackageManifest;
62
+ /**
63
+ * Built from the forward index of an archive still downloading. Entries arrive as the download
64
+ * does — `absorb()` takes them in — and every answer is provisional until the archive is whole
65
+ * and a reader from its central directory replaces this one.
66
+ */
67
+ readonly partial: boolean;
68
+ /** For a partial reader: how far the archive is accounted for, and whether its index is complete. */
69
+ parsedTo: number;
70
+ forwardDone: boolean;
71
+ private forwardStopped;
72
+ private absorbed;
73
+ /** Top-level folder of the newest entry: the one folder that may still be receiving files. */
74
+ private lastTopFolder;
75
+ /** Archives consulted, in order, for an entry this one does not have. */
76
+ private readonly fallbacks;
77
+ /** Library folders reachable from here, built on first use and dropped when `use()` adds more. */
78
+ private librariesByName;
79
+ /** Data offsets of stored entries, each read from its local header once. */
80
+ private readonly dataRanges;
81
+ private constructor();
82
+ get title(): string | undefined;
83
+ static open(pkgId: string, handle: SourceHandle, options?: {
84
+ requireLibraries?: boolean;
85
+ }): Promise<PackageReader>;
86
+ /**
87
+ * A reader over an archive that is still downloading, from the entries its local headers have
88
+ * given up so far. Nothing is asserted here: what is missing may simply not have arrived.
89
+ */
90
+ static fromForwardIndex(pkgId: string, handle: SourceHandle, snapshot: ForwardIndexSnapshot): Promise<PackageReader>;
91
+ /**
92
+ * Takes in the entries a newer snapshot has and this reader does not. Snapshots are cumulative
93
+ * and in archive order, so only the tail is new; the same first-occurrence rule as the full
94
+ * index applies, so a later duplicate can never shadow an entry already served.
95
+ */
96
+ absorb(snapshot: ForwardIndexSnapshot): Promise<boolean>;
97
+ /**
98
+ * For a partial reader: whether the runtime could boot from what has arrived — `h5p.json`, and
99
+ * for every dependency it declares a folder that is present and finished arriving. A folder has
100
+ * finished once an entry of a later folder has been seen, since exporters write a folder's files
101
+ * together; only the newest folder may still be growing. An index the scanner had to give up on
102
+ * never says ready: what it could not see must wait for the central directory.
103
+ */
104
+ bootReady(): boolean;
105
+ /**
106
+ * For a partial reader: a miss that can be answered without waiting for more of the archive.
107
+ * True when the entry's top-level folder has already finished arriving, or when the request
108
+ * is h5p-standalone's versioned probe on a package that keeps that library unversioned.
109
+ */
110
+ provablyAbsent(name: string): boolean;
111
+ private folderComplete;
112
+ private hasFolder;
113
+ /** Adds an archive to consult for entries this one does not carry. */
114
+ use(reader: PackageReader): void;
115
+ /**
116
+ * Looks an entry up in this archive, then in each attached one.
117
+ *
118
+ * A miss on a library path is retried against a compatible version of the same library, so
119
+ * content asking for `H5P.Text-1.0/...` is served from `H5P.Text-1.1/...` when that is what
120
+ * the attached bundle happens to ship.
121
+ */
122
+ get(name: string): LocatedEntry | undefined;
123
+ /** Exact lookup, this archive first and then each attached one, in order. */
124
+ private locate;
125
+ private locateCompatible;
126
+ /**
127
+ * Library folders reachable from here, this archive's own taking precedence.
128
+ *
129
+ * Memoised, because this runs on the miss path and misses are not rare: h5p-standalone probes
130
+ * `<Library>-<major>.<minor>/library.json` on purpose to learn whether a package uses versioned
131
+ * folders, so every load walks through here several times. Rebuilding a map over every entry
132
+ * name — two thousand of them in a real package — on each of those is work that never changes
133
+ * its answer. The entry set is fixed after `open`; only `use()` can change the result, and it
134
+ * drops the cache.
135
+ */
136
+ private availableLibraries;
137
+ /** Every entry name reachable through this reader, including the attached archives. */
138
+ entryNames(): Set<string>;
139
+ /** What `h5p.json` declares but nothing reachable from here provides. */
140
+ missingLibraries(): MissingLibraries | null;
141
+ /** True when another archive is supplying entries this one does not have. */
142
+ get hasFallbacks(): boolean;
143
+ /**
144
+ * The manifest as the runtime should see it once a library bundle is attached.
145
+ *
146
+ * A content-only export does not just drop the library folders, it also strips
147
+ * `preloadedDependencies` down to the main library — so the runtime would load Interactive
148
+ * Video and none of the interaction types used inside it, and the first interaction would fail
149
+ * with "Unable to find constructor". The bundle's own manifest is the list the content type
150
+ * was published with, so the two are merged: a dependency is kept only if its folder is
151
+ * actually reachable, and the content's own version wins when both archives have one.
152
+ */
153
+ mergedManifest(): PackageManifest;
154
+ assertLibrariesPresent(): void;
155
+ /**
156
+ * Entries that cannot be served cold without a background inflate, in archive order.
157
+ *
158
+ * Everything else is reachable on demand: a stored entry is already flat in the archive and
159
+ * gets sliced, and a small one is cheap to inflate when asked for. Only a large deflated entry
160
+ * makes the runtime wait on a job, so only these are worth starting early.
161
+ *
162
+ * Archive order, not largest first: the point is to have an entry before the learner reaches
163
+ * it, and the order the packager wrote them in tracks the order the content uses them far
164
+ * better than size does. The biggest video is not usually the first one on screen.
165
+ */
166
+ prefetchable(): PrefetchEntry[];
167
+ /**
168
+ * Inflates an entry into a stream. The stream is handed straight to `cache.put()` or to a
169
+ * `Response`, so the entry never sits in memory as a whole.
170
+ *
171
+ * An entry from the central directory goes through zip.js. A forward entry has no zip.js object
172
+ * behind it and is inflated directly: its compressed span is known from its local header, and
173
+ * `DecompressionStream` does the rest — the same codec zip.js would have used.
174
+ */
175
+ inflate(entry: IndexedEntry): ReadableStream<Uint8Array>;
176
+ /**
177
+ * Byte range of an entry's compressed data inside the archive. The central directory records
178
+ * where the *local* header starts, and the local header's own name and extra fields are what
179
+ * stand between it and the bytes, so the header has to be read to find them — unless a forward
180
+ * entry already knows, having come from that very header.
181
+ *
182
+ * Read once per entry and kept as a promise: a media element opens with a burst of range
183
+ * requests, and every one of them used to pay a round trip for the same thirty bytes — over
184
+ * HTTP, a second request on top of the one for the data — and every seek after that paid it
185
+ * again. Now the burst shares one read and a seek pays none.
186
+ */
187
+ dataRange(entry: IndexedEntry): Promise<ByteRange>;
188
+ private inflateDirect;
189
+ private readDataRange;
190
+ /** Streams a range of an entry's compressed bytes straight out of the source. No storage. */
191
+ sliceStream(entry: IndexedEntry, range: ByteRange): Promise<ReadableStream<Uint8Array>>;
192
+ }
193
+ /**
194
+ * Serving strategy for one entry. Size decides first, then compression method: only a large
195
+ * deflated entry is worth the cost of a background extraction.
196
+ */
197
+ export declare function chooseStrategy(name: string, entry: {
198
+ uncompressedSize: number;
199
+ compressionMethod: number;
200
+ }): Strategy;
201
+ export interface LibraryFolder {
202
+ machineName: string;
203
+ major: number;
204
+ minor: number;
205
+ folder: string;
206
+ }
207
+ /** Splits `H5P.Text-1.1` into its parts, or returns `null` for a folder that is not a library. */
208
+ export declare function parseLibraryFolder(folder: string): LibraryFolder | null;
209
+ /** Every library folder in a set of entry names, grouped by machine name. */
210
+ export declare function indexLibraryFolders(entryNames: Iterable<string>): Map<string, LibraryFolder[]>;
211
+ /**
212
+ * Picks the folder that satisfies a dependency, by H5P's own compatibility rule: the same major
213
+ * version, and a minor at least as high as the one asked for. Libraries are backward compatible
214
+ * within a major, which is why a platform installs one version per major and content authored
215
+ * against an older minor keeps working.
216
+ *
217
+ * This matters whenever libraries come from somewhere other than the package: a hub bundle ships
218
+ * the current `H5P.Text-1.1` while content authored earlier asks for `H5P.Text-1.0`, and without
219
+ * this every such request is a 404.
220
+ *
221
+ * The lowest satisfying version wins, as the closest to what the content was written against.
222
+ */
223
+ export declare function resolveLibraryFolder(available: Map<string, LibraryFolder[]>, dependency: {
224
+ machineName: string;
225
+ major: number;
226
+ minor: number;
227
+ }): string | undefined;
228
+ /**
229
+ * The dependencies `h5p.json` declares that the archive does not actually carry. Pure, so the
230
+ * rule can be checked without building a zip.
231
+ */
232
+ export declare function findMissingLibraries(entryNames: ReadonlySet<string>, manifest: PackageManifest): LibraryDependency[];
233
+ /**
234
+ * What a package declares but does not carry, or `null` when it carries everything.
235
+ *
236
+ * Exports from h5p.com and h5p.org routinely leave the libraries out: the platform they came from
237
+ * already has them, so bundling them would be waste. Played anywhere else, such an archive is
238
+ * just a `content/` folder — the runtime asks for `<MainLibrary>/library.json`, gets a 404, and
239
+ * fails with nothing to go on.
240
+ */
241
+ export declare function describeMissingLibraries(entryNames: ReadonlySet<string>, manifest: PackageManifest): MissingLibraries | null;
242
+ export declare function explainMissingLibraries(missing: MissingLibraries): string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * URL shape of the virtual routes. Our own registration uses the scope `<swDir>h5p/`, so routes
3
+ * sit directly under it; `mountH5P` puts them at `<hostScope>h5p/`. Both spell the same thing —
4
+ * `…/h5p/virtual/…` and `…/h5p/frame/…` — so everything downstream works off one base.
5
+ */
6
+ export interface Routes {
7
+ /** Absolute URL prefix, always ending in `h5p/`. */
8
+ base: string;
9
+ virtual: string;
10
+ frame: string;
11
+ ping: string;
12
+ }
13
+ export declare function routesFor(scope: string): Routes;
14
+ export type RouteMatch = {
15
+ kind: 'ping';
16
+ } | {
17
+ kind: 'frame';
18
+ pkgId: string;
19
+ } | {
20
+ kind: 'entry';
21
+ pkgId: string;
22
+ path: string;
23
+ } | {
24
+ kind: 'none';
25
+ };
26
+ export declare function matchRoute(routes: Routes, url: string): RouteMatch;
@@ -0,0 +1,9 @@
1
+ import type { ByteRange } from '../shared/range';
2
+ /**
3
+ * Restricts a byte stream to an inclusive range without buffering it. Used to answer a `Range`
4
+ * request from an entry the cache holds in one piece: the body is sliced as it flows rather than
5
+ * read into memory and cut.
6
+ */
7
+ export declare function sliceStream(source: ReadableStream<Uint8Array>, range: ByteRange): ReadableStream<Uint8Array>;
8
+ /** Counts bytes as they pass, reporting progress without holding on to any of them. */
9
+ export declare function tapProgress(source: ReadableStream<Uint8Array>, onBytes: (total: number) => void): ReadableStream<Uint8Array>;
@@ -0,0 +1 @@
1
+ export {};