tiny-essentials 1.29.1 → 1.29.2
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/changelog/1/29/2.md +82 -0
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyFetchLoader.min.js +1 -0
- package/dist/v1/TinyImageLoader.min.js +1 -0
- package/dist/v1/TinyMediaCache.min.js +1 -0
- package/dist/v1/TinyMediaLoader.min.js +1 -0
- package/dist/v1/basics/index.cjs +5 -0
- package/dist/v1/basics/index.d.mts +2 -1
- package/dist/v1/basics/index.d.mts.map +1 -1
- package/dist/v1/basics/index.mjs +2 -1
- package/dist/v1/basics/media/youtube.cjs +275 -0
- package/dist/v1/basics/media/youtube.d.mts +107 -0
- package/dist/v1/basics/media/youtube.d.mts.map +1 -0
- package/dist/v1/basics/media/youtube.mjs +214 -0
- package/dist/v1/index.cjs +13 -0
- package/dist/v1/index.d.mts +6 -1
- package/dist/v1/index.d.mts.map +1 -1
- package/dist/v1/index.mjs +7 -2
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.cjs +0 -1
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts +0 -1
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts.map +1 -1
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.mjs +0 -1
- package/dist/v1/libs/fs/plugins/OPFS/index.cjs +0 -8
- package/dist/v1/libs/fs/plugins/OPFS/index.d.mts +0 -8
- package/dist/v1/libs/fs/plugins/OPFS/index.d.mts.map +1 -1
- package/dist/v1/libs/fs/plugins/OPFS/index.mjs +0 -8
- package/dist/v1/libs/html/TinyHtml.cjs +1 -1
- package/dist/v1/libs/html/TinyHtml.d.mts +2 -2
- package/dist/v1/libs/html/TinyHtml.d.mts.map +1 -1
- package/dist/v1/libs/html/TinyHtml.mjs +1 -1
- package/dist/v1/libs/html/loader/TinyMediaLoader.cjs +1999 -0
- package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts +999 -0
- package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/TinyMediaLoader.mjs +1852 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.cjs +77 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts +103 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.mjs +67 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.cjs +630 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts +138 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.mjs +576 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.cjs +70 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts +100 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.mjs +60 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.cjs +946 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts +67 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.mjs +896 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.cjs +225 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts +149 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.mjs +197 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.cjs +1454 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts +200 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.mjs +1317 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.cjs +668 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts +83 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.mjs +626 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.cjs +277 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts +118 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.mjs +247 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.cjs +128 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts +49 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.mjs +116 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.cjs +796 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts +354 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.mjs +734 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.cjs +302 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts +256 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.mjs +280 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.cjs +194 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts +138 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.mjs +175 -0
- package/dist/v1/libs/tools/TinyPkgExportValidator.cjs +359 -55
- package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts +64 -6
- package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts.map +1 -1
- package/dist/v1/libs/tools/TinyPkgExportValidator.mjs +320 -57
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.cjs +0 -1
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts +0 -1
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts.map +1 -1
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.mjs +0 -1
- package/dist/v1/webTemplates/bootstrap/5.3/scss/background/_background-grid.scss +15 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-contrast.scss +208 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-ghost.scss +89 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_cube-icon.scss +65 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_sphere-pulse.scss +39 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_badges.scss +90 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_bootstrap-overrides.scss +495 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_buttons.scss +390 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_colors.scss +213 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_effects.scss +153 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms-tones.scss +69 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms.scss +323 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_global.scss +31 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_links.scss +47 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tables.scss +25 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_theme.scss +21 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tokens.scss +161 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_utilities.scss +81 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_variables.scss +63 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_form-tone-mixins.scss +236 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_sizing.scss +68 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_tone-mixins.scss +392 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_typography.scss +112 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/background/_tiny_gradient_1.scss +131 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/colors/_pink.scss +47 -0
- package/docs/v1/Exports.md +33 -2
- package/docs/v1/README.md +9 -0
- package/docs/v1/basics/youtube.md +204 -0
- package/docs/v1/libs/html/loader/TinyFetchLoader.md +114 -0
- package/docs/v1/libs/html/loader/TinyImageLoader.md +141 -0
- package/docs/v1/libs/html/loader/TinyMediaCache.md +166 -0
- package/docs/v1/libs/html/loader/TinyMediaLoader.md +300 -0
- package/docs/v1/libs/tools/TinyPkgExportValidator.md +55 -0
- package/docs/v1/webTemplates/bootstrap/5.3/DeepStrap.md +177 -0
- package/package.json +244 -8
- /package/dist/v1/webTemplates/bootstrap/5.3/scss/input/{Placeholder-Colors-Fix.scss → _placeholder-colors-fix.scss} +0 -0
- /package/dist/v1/webTemplates/bootstrap/5.3/scss/spacing/{Spacing-Plus.scss → _spacing-plus.scss} +0 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Registry of in-flight media downloads.
|
|
5
|
+
*
|
|
6
|
+
* Two loaders that request the same URL at the same time would normally open
|
|
7
|
+
* two connections and decode the same picture twice. This registry lets the
|
|
8
|
+
* second loader attach to the download that is already running, so the bytes
|
|
9
|
+
* travel through the network exactly once and every consumer receives the very
|
|
10
|
+
* same {@link Blob} instance.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Describes a single in-flight download.
|
|
15
|
+
* @template {string} Name
|
|
16
|
+
* @template {unknown[]} Args
|
|
17
|
+
* @typedef {Object} MediaMirrorEntry
|
|
18
|
+
* @property {string} key - The normalized registry key.
|
|
19
|
+
* @property {string} src - The original source URL.
|
|
20
|
+
* @property {number} startedAt - The epoch timestamp, in milliseconds, when the download started.
|
|
21
|
+
* @property {number} mirrors - The number of loaders attached to this download.
|
|
22
|
+
* @property {boolean} settled - Whether the download already produced a payload.
|
|
23
|
+
* @property {number} sequence - The sequence number assigned to the next published event.
|
|
24
|
+
* @property {MediaMirrorEvent<Name, Args>[]} events - Every event published so far, in order.
|
|
25
|
+
* @property {Set<MediaMirrorSubscriber<Name, Args>>} subscribers - The loaders attached to this download.
|
|
26
|
+
* @property {Promise<Blob>} promise - Resolves with the shared blob.
|
|
27
|
+
* @property {(blob: Blob) => void} resolve - Resolves the shared promise.
|
|
28
|
+
* @property {(error: Error) => void} reject - Rejects the shared promise.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A read-only snapshot of the registry counters.
|
|
33
|
+
* @typedef {Object} MediaMirrorStats
|
|
34
|
+
* @property {number} pending - The number of downloads currently in flight.
|
|
35
|
+
* @property {number} mirrors - The total number of attached loaders.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A read-only snapshot of a single in-flight download.
|
|
40
|
+
* @typedef {Object} MediaMirrorSnapshot
|
|
41
|
+
* @property {string} key - The normalized registry key.
|
|
42
|
+
* @property {string} src - The original source URL.
|
|
43
|
+
* @property {number} startedAt - The epoch timestamp, in milliseconds, when the download started.
|
|
44
|
+
* @property {number} mirrors - The number of loaders attached to this download.
|
|
45
|
+
* @property {boolean} settled - Whether the download already produced a payload.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A single event captured from the leading loader.
|
|
50
|
+
* @template {string} Name
|
|
51
|
+
* @template {unknown[]} Args
|
|
52
|
+
* @typedef {Object} MediaMirrorEvent
|
|
53
|
+
* @property {number} seq - The zero-based position of the event in the stream.
|
|
54
|
+
* @property {Name} name - The event name.
|
|
55
|
+
* @property {Args} args - The arguments the event was emitted with.
|
|
56
|
+
* @property {number} timestamp - The high resolution timestamp of the capture.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Handles a broadcast event.
|
|
61
|
+
* @template {string} Name
|
|
62
|
+
* @template {unknown[]} Args
|
|
63
|
+
* @callback MediaMirrorSubscriber
|
|
64
|
+
* @param {MediaMirrorEvent<Name, Args>} event - The published event.
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Deduplicates concurrent downloads that target the same URL.
|
|
70
|
+
*/
|
|
71
|
+
class TinyMediaMirror {
|
|
72
|
+
/** @type {Map<string, MediaMirrorEntry<string, unknown[]>>} */
|
|
73
|
+
static #pending = new Map();
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The number of downloads currently in flight.
|
|
77
|
+
* @returns {number} The number of registered downloads.
|
|
78
|
+
*/
|
|
79
|
+
static get size() {
|
|
80
|
+
return TinyMediaMirror.#pending.size;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The key of every in-flight download.
|
|
85
|
+
* @returns {string[]} A new array with the registered keys.
|
|
86
|
+
*/
|
|
87
|
+
static get keys() {
|
|
88
|
+
return Array.from(TinyMediaMirror.#pending.keys());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A read-only snapshot of the registry counters.
|
|
93
|
+
* @returns {MediaMirrorStats} The current counters.
|
|
94
|
+
*/
|
|
95
|
+
static get stats() {
|
|
96
|
+
let mirrors = 0;
|
|
97
|
+
for (const entry of TinyMediaMirror.#pending.values()) {
|
|
98
|
+
mirrors += entry.mirrors;
|
|
99
|
+
}
|
|
100
|
+
return { pending: TinyMediaMirror.#pending.size, mirrors };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Checks whether a download is already in flight for the given key.
|
|
105
|
+
* @param {string} key - The normalized registry key.
|
|
106
|
+
* @returns {boolean} True when a download is in flight.
|
|
107
|
+
*/
|
|
108
|
+
static has(key) {
|
|
109
|
+
return TinyMediaMirror.#pending.has(key);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Reads an in-flight download without attaching to it.
|
|
114
|
+
* @param {string} key - The normalized registry key.
|
|
115
|
+
* @returns {MediaMirrorEntry<string, unknown[]>|null} The shared entry, or null when nothing is in flight.
|
|
116
|
+
*/
|
|
117
|
+
static get(key) {
|
|
118
|
+
return TinyMediaMirror.#pending.get(key) ?? null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A read-only snapshot of every in-flight download.
|
|
123
|
+
* @returns {MediaMirrorSnapshot[]} One record per in-flight download.
|
|
124
|
+
*/
|
|
125
|
+
static entries() {
|
|
126
|
+
return Array.from(TinyMediaMirror.#pending.values(), (entry) => ({
|
|
127
|
+
key: entry.key,
|
|
128
|
+
src: entry.src,
|
|
129
|
+
startedAt: entry.startedAt,
|
|
130
|
+
mirrors: entry.mirrors,
|
|
131
|
+
settled: entry.settled,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Registers a new in-flight download. When the key is already taken the
|
|
137
|
+
* existing entry is returned, so a live download is never overwritten.
|
|
138
|
+
* @param {string} key - The normalized registry key.
|
|
139
|
+
* @param {string} src - The original source URL.
|
|
140
|
+
* @returns {MediaMirrorEntry<string, unknown[]>} The registered entry.
|
|
141
|
+
* @throws {TypeError} If `key` is not a string.
|
|
142
|
+
* @throws {TypeError} If `src` is not a string.
|
|
143
|
+
*/
|
|
144
|
+
static open(key, src) {
|
|
145
|
+
if (typeof key !== 'string') {
|
|
146
|
+
throw new TypeError('The "key" argument must be a string.');
|
|
147
|
+
}
|
|
148
|
+
if (typeof src !== 'string') {
|
|
149
|
+
throw new TypeError('The "src" argument must be a string.');
|
|
150
|
+
}
|
|
151
|
+
const existing = TinyMediaMirror.#pending.get(key);
|
|
152
|
+
if (existing) {
|
|
153
|
+
return existing;
|
|
154
|
+
}
|
|
155
|
+
/** @type {MediaMirrorEntry<string, unknown[]>} */
|
|
156
|
+
const entry = {
|
|
157
|
+
key,
|
|
158
|
+
src,
|
|
159
|
+
startedAt: Date.now(),
|
|
160
|
+
mirrors: 0,
|
|
161
|
+
settled: false,
|
|
162
|
+
sequence: 0,
|
|
163
|
+
events: [],
|
|
164
|
+
subscribers: new Set(),
|
|
165
|
+
promise: /** @type {Promise<Blob>} */ (/** @type {unknown} */ (null)),
|
|
166
|
+
resolve: () => {},
|
|
167
|
+
reject: () => {},
|
|
168
|
+
};
|
|
169
|
+
entry.promise = new Promise((resolve, reject) => {
|
|
170
|
+
entry.resolve = (blob) => {
|
|
171
|
+
entry.settled = true;
|
|
172
|
+
resolve(blob);
|
|
173
|
+
};
|
|
174
|
+
entry.reject = (error) => {
|
|
175
|
+
entry.settled = true;
|
|
176
|
+
reject(error);
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
// A download without mirrors must never surface as an unhandled rejection.
|
|
180
|
+
entry.promise.catch(() => {});
|
|
181
|
+
TinyMediaMirror.#pending.set(key, entry);
|
|
182
|
+
return entry;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Attaches a new consumer to an in-flight download.
|
|
187
|
+
* @param {string} key - The normalized registry key.
|
|
188
|
+
* @returns {MediaMirrorEntry<string, unknown[]>|null} The shared entry, or null when nothing is in flight.
|
|
189
|
+
*/
|
|
190
|
+
static attach(key) {
|
|
191
|
+
const entry = TinyMediaMirror.#pending.get(key);
|
|
192
|
+
if (!entry) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
entry.mirrors += 1;
|
|
196
|
+
return entry;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Removes a finished download from the registry. A download that never
|
|
201
|
+
* produced a payload rejects its waiters instead of hanging them forever.
|
|
202
|
+
* @param {string} key - The normalized registry key.
|
|
203
|
+
* @returns {boolean} True when an entry was removed.
|
|
204
|
+
*/
|
|
205
|
+
static close(key) {
|
|
206
|
+
const entry = TinyMediaMirror.#pending.get(key);
|
|
207
|
+
if (!entry) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
if (!entry.settled) {
|
|
211
|
+
entry.reject(new Error(`The mirrored download for "${entry.src}" ended without a payload.`));
|
|
212
|
+
}
|
|
213
|
+
return TinyMediaMirror.#pending.delete(key);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Rejects every in-flight download and empties the registry.
|
|
218
|
+
* @returns {void}
|
|
219
|
+
*/
|
|
220
|
+
static clear() {
|
|
221
|
+
for (const entry of TinyMediaMirror.#pending.values()) {
|
|
222
|
+
if (!entry.settled) {
|
|
223
|
+
entry.reject(new Error('The mirror registry was cleared.'));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
TinyMediaMirror.#pending.clear();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Broadcasts an event to every loader attached to an in-flight download. The
|
|
231
|
+
* event is appended to the history so a loader that attaches later still
|
|
232
|
+
* receives the complete stream, in its original order.
|
|
233
|
+
* @param {string} key - The normalized registry key.
|
|
234
|
+
* @param {string} name - The event name.
|
|
235
|
+
* @param {unknown[]} args - The arguments the event was emitted with.
|
|
236
|
+
* @returns {number} The number of subscribers that received the event.
|
|
237
|
+
* @throws {TypeError} If `key` is not a string.
|
|
238
|
+
* @throws {TypeError} If `name` is not a string.
|
|
239
|
+
* @throws {TypeError} If `args` is not an array.
|
|
240
|
+
*/
|
|
241
|
+
static publish(key, name, args) {
|
|
242
|
+
if (typeof key !== 'string') {
|
|
243
|
+
throw new TypeError('The "key" argument must be a string.');
|
|
244
|
+
}
|
|
245
|
+
if (typeof name !== 'string') {
|
|
246
|
+
throw new TypeError('The "name" argument must be a string.');
|
|
247
|
+
}
|
|
248
|
+
if (!Array.isArray(args)) {
|
|
249
|
+
throw new TypeError('The "args" argument must be an array.');
|
|
250
|
+
}
|
|
251
|
+
const entry = TinyMediaMirror.#pending.get(key);
|
|
252
|
+
if (!entry) {
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
const event = { seq: entry.sequence, name, args, timestamp: performance.now() };
|
|
256
|
+
entry.sequence += 1;
|
|
257
|
+
entry.events.push(event);
|
|
258
|
+
for (const subscriber of entry.subscribers) {
|
|
259
|
+
subscriber(event);
|
|
260
|
+
}
|
|
261
|
+
return entry.subscribers.size;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Attaches a subscriber to an in-flight download. The entire history is
|
|
266
|
+
* replayed synchronously, in its original order, before the handler starts
|
|
267
|
+
* receiving live events. Because JavaScript never preempts a running
|
|
268
|
+
* function, no live event can slip between the replay and the subscription,
|
|
269
|
+
* so the handler observes exactly the same sequence the leader produced.
|
|
270
|
+
* @param {string} key - The normalized registry key.
|
|
271
|
+
* @param {MediaMirrorSubscriber<string, unknown[]>} handler - The callback invoked for every event.
|
|
272
|
+
* @returns {(() => void)|null} A function that detaches the subscriber, or null when nothing is in flight.
|
|
273
|
+
* @throws {TypeError} If `handler` is not a function.
|
|
274
|
+
*/
|
|
275
|
+
static subscribe(key, handler) {
|
|
276
|
+
if (typeof handler !== 'function') {
|
|
277
|
+
throw new TypeError('The "handler" argument must be a function.');
|
|
278
|
+
}
|
|
279
|
+
const entry = TinyMediaMirror.#pending.get(key);
|
|
280
|
+
if (!entry) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
const { events } = entry;
|
|
284
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
285
|
+
handler(events[index]);
|
|
286
|
+
}
|
|
287
|
+
entry.subscribers.add(handler);
|
|
288
|
+
return () => entry.subscribers.delete(handler);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The ordered event history of an in-flight download. The array is live and
|
|
293
|
+
* must be treated as read-only.
|
|
294
|
+
* @param {string} key - The normalized registry key.
|
|
295
|
+
* @returns {MediaMirrorEvent<string, unknown[]>[]} The captured events, oldest first.
|
|
296
|
+
*/
|
|
297
|
+
static history(key) {
|
|
298
|
+
return TinyMediaMirror.#pending.get(key)?.events ?? [];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
module.exports = TinyMediaMirror;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Registry of in-flight media downloads.
|
|
3
|
+
*
|
|
4
|
+
* Two loaders that request the same URL at the same time would normally open
|
|
5
|
+
* two connections and decode the same picture twice. This registry lets the
|
|
6
|
+
* second loader attach to the download that is already running, so the bytes
|
|
7
|
+
* travel through the network exactly once and every consumer receives the very
|
|
8
|
+
* same {@link Blob} instance.
|
|
9
|
+
*/
|
|
10
|
+
export type MediaMirrorEntry<Name extends string, Args extends unknown[]> = {
|
|
11
|
+
/**
|
|
12
|
+
* - The normalized registry key.
|
|
13
|
+
*/
|
|
14
|
+
key: string;
|
|
15
|
+
/**
|
|
16
|
+
* - The original source URL.
|
|
17
|
+
*/
|
|
18
|
+
src: string;
|
|
19
|
+
/**
|
|
20
|
+
* - The epoch timestamp, in milliseconds, when the download started.
|
|
21
|
+
*/
|
|
22
|
+
startedAt: number;
|
|
23
|
+
/**
|
|
24
|
+
* - The number of loaders attached to this download.
|
|
25
|
+
*/
|
|
26
|
+
mirrors: number;
|
|
27
|
+
/**
|
|
28
|
+
* - Whether the download already produced a payload.
|
|
29
|
+
*/
|
|
30
|
+
settled: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* - The sequence number assigned to the next published event.
|
|
33
|
+
*/
|
|
34
|
+
sequence: number;
|
|
35
|
+
/**
|
|
36
|
+
* - Every event published so far, in order.
|
|
37
|
+
*/
|
|
38
|
+
events: MediaMirrorEvent<Name, Args>[];
|
|
39
|
+
/**
|
|
40
|
+
* - The loaders attached to this download.
|
|
41
|
+
*/
|
|
42
|
+
subscribers: Set<MediaMirrorSubscriber<Name, Args>>;
|
|
43
|
+
/**
|
|
44
|
+
* - Resolves with the shared blob.
|
|
45
|
+
*/
|
|
46
|
+
promise: Promise<Blob>;
|
|
47
|
+
/**
|
|
48
|
+
* - Resolves the shared promise.
|
|
49
|
+
*/
|
|
50
|
+
resolve: (blob: Blob) => void;
|
|
51
|
+
/**
|
|
52
|
+
* - Rejects the shared promise.
|
|
53
|
+
*/
|
|
54
|
+
reject: (error: Error) => void;
|
|
55
|
+
};
|
|
56
|
+
export type MediaMirrorStats = {
|
|
57
|
+
/**
|
|
58
|
+
* - The number of downloads currently in flight.
|
|
59
|
+
*/
|
|
60
|
+
pending: number;
|
|
61
|
+
/**
|
|
62
|
+
* - The total number of attached loaders.
|
|
63
|
+
*/
|
|
64
|
+
mirrors: number;
|
|
65
|
+
};
|
|
66
|
+
export type MediaMirrorSnapshot = {
|
|
67
|
+
/**
|
|
68
|
+
* - The normalized registry key.
|
|
69
|
+
*/
|
|
70
|
+
key: string;
|
|
71
|
+
/**
|
|
72
|
+
* - The original source URL.
|
|
73
|
+
*/
|
|
74
|
+
src: string;
|
|
75
|
+
/**
|
|
76
|
+
* - The epoch timestamp, in milliseconds, when the download started.
|
|
77
|
+
*/
|
|
78
|
+
startedAt: number;
|
|
79
|
+
/**
|
|
80
|
+
* - The number of loaders attached to this download.
|
|
81
|
+
*/
|
|
82
|
+
mirrors: number;
|
|
83
|
+
/**
|
|
84
|
+
* - Whether the download already produced a payload.
|
|
85
|
+
*/
|
|
86
|
+
settled: boolean;
|
|
87
|
+
};
|
|
88
|
+
export type MediaMirrorEvent<Name extends string, Args extends unknown[]> = {
|
|
89
|
+
/**
|
|
90
|
+
* - The zero-based position of the event in the stream.
|
|
91
|
+
*/
|
|
92
|
+
seq: number;
|
|
93
|
+
/**
|
|
94
|
+
* - The event name.
|
|
95
|
+
*/
|
|
96
|
+
name: Name;
|
|
97
|
+
/**
|
|
98
|
+
* - The arguments the event was emitted with.
|
|
99
|
+
*/
|
|
100
|
+
args: Args;
|
|
101
|
+
/**
|
|
102
|
+
* - The high resolution timestamp of the capture.
|
|
103
|
+
*/
|
|
104
|
+
timestamp: number;
|
|
105
|
+
};
|
|
106
|
+
export type MediaMirrorSubscriber<Name extends string, Args extends unknown[]> = (event: MediaMirrorEvent<Name, Args>) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Describes a single in-flight download.
|
|
109
|
+
* @template {string} Name
|
|
110
|
+
* @template {unknown[]} Args
|
|
111
|
+
* @typedef {Object} MediaMirrorEntry
|
|
112
|
+
* @property {string} key - The normalized registry key.
|
|
113
|
+
* @property {string} src - The original source URL.
|
|
114
|
+
* @property {number} startedAt - The epoch timestamp, in milliseconds, when the download started.
|
|
115
|
+
* @property {number} mirrors - The number of loaders attached to this download.
|
|
116
|
+
* @property {boolean} settled - Whether the download already produced a payload.
|
|
117
|
+
* @property {number} sequence - The sequence number assigned to the next published event.
|
|
118
|
+
* @property {MediaMirrorEvent<Name, Args>[]} events - Every event published so far, in order.
|
|
119
|
+
* @property {Set<MediaMirrorSubscriber<Name, Args>>} subscribers - The loaders attached to this download.
|
|
120
|
+
* @property {Promise<Blob>} promise - Resolves with the shared blob.
|
|
121
|
+
* @property {(blob: Blob) => void} resolve - Resolves the shared promise.
|
|
122
|
+
* @property {(error: Error) => void} reject - Rejects the shared promise.
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* A read-only snapshot of the registry counters.
|
|
126
|
+
* @typedef {Object} MediaMirrorStats
|
|
127
|
+
* @property {number} pending - The number of downloads currently in flight.
|
|
128
|
+
* @property {number} mirrors - The total number of attached loaders.
|
|
129
|
+
*/
|
|
130
|
+
/**
|
|
131
|
+
* A read-only snapshot of a single in-flight download.
|
|
132
|
+
* @typedef {Object} MediaMirrorSnapshot
|
|
133
|
+
* @property {string} key - The normalized registry key.
|
|
134
|
+
* @property {string} src - The original source URL.
|
|
135
|
+
* @property {number} startedAt - The epoch timestamp, in milliseconds, when the download started.
|
|
136
|
+
* @property {number} mirrors - The number of loaders attached to this download.
|
|
137
|
+
* @property {boolean} settled - Whether the download already produced a payload.
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* A single event captured from the leading loader.
|
|
141
|
+
* @template {string} Name
|
|
142
|
+
* @template {unknown[]} Args
|
|
143
|
+
* @typedef {Object} MediaMirrorEvent
|
|
144
|
+
* @property {number} seq - The zero-based position of the event in the stream.
|
|
145
|
+
* @property {Name} name - The event name.
|
|
146
|
+
* @property {Args} args - The arguments the event was emitted with.
|
|
147
|
+
* @property {number} timestamp - The high resolution timestamp of the capture.
|
|
148
|
+
*/
|
|
149
|
+
/**
|
|
150
|
+
* Handles a broadcast event.
|
|
151
|
+
* @template {string} Name
|
|
152
|
+
* @template {unknown[]} Args
|
|
153
|
+
* @callback MediaMirrorSubscriber
|
|
154
|
+
* @param {MediaMirrorEvent<Name, Args>} event - The published event.
|
|
155
|
+
* @returns {void}
|
|
156
|
+
*/
|
|
157
|
+
/**
|
|
158
|
+
* Deduplicates concurrent downloads that target the same URL.
|
|
159
|
+
*/
|
|
160
|
+
declare class TinyMediaMirror {
|
|
161
|
+
#private;
|
|
162
|
+
/**
|
|
163
|
+
* The number of downloads currently in flight.
|
|
164
|
+
* @returns {number} The number of registered downloads.
|
|
165
|
+
*/
|
|
166
|
+
static get size(): number;
|
|
167
|
+
/**
|
|
168
|
+
* The key of every in-flight download.
|
|
169
|
+
* @returns {string[]} A new array with the registered keys.
|
|
170
|
+
*/
|
|
171
|
+
static get keys(): string[];
|
|
172
|
+
/**
|
|
173
|
+
* A read-only snapshot of the registry counters.
|
|
174
|
+
* @returns {MediaMirrorStats} The current counters.
|
|
175
|
+
*/
|
|
176
|
+
static get stats(): MediaMirrorStats;
|
|
177
|
+
/**
|
|
178
|
+
* Checks whether a download is already in flight for the given key.
|
|
179
|
+
* @param {string} key - The normalized registry key.
|
|
180
|
+
* @returns {boolean} True when a download is in flight.
|
|
181
|
+
*/
|
|
182
|
+
static has(key: string): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Reads an in-flight download without attaching to it.
|
|
185
|
+
* @param {string} key - The normalized registry key.
|
|
186
|
+
* @returns {MediaMirrorEntry<string, unknown[]>|null} The shared entry, or null when nothing is in flight.
|
|
187
|
+
*/
|
|
188
|
+
static get(key: string): MediaMirrorEntry<string, unknown[]> | null;
|
|
189
|
+
/**
|
|
190
|
+
* A read-only snapshot of every in-flight download.
|
|
191
|
+
* @returns {MediaMirrorSnapshot[]} One record per in-flight download.
|
|
192
|
+
*/
|
|
193
|
+
static entries(): MediaMirrorSnapshot[];
|
|
194
|
+
/**
|
|
195
|
+
* Registers a new in-flight download. When the key is already taken the
|
|
196
|
+
* existing entry is returned, so a live download is never overwritten.
|
|
197
|
+
* @param {string} key - The normalized registry key.
|
|
198
|
+
* @param {string} src - The original source URL.
|
|
199
|
+
* @returns {MediaMirrorEntry<string, unknown[]>} The registered entry.
|
|
200
|
+
* @throws {TypeError} If `key` is not a string.
|
|
201
|
+
* @throws {TypeError} If `src` is not a string.
|
|
202
|
+
*/
|
|
203
|
+
static open(key: string, src: string): MediaMirrorEntry<string, unknown[]>;
|
|
204
|
+
/**
|
|
205
|
+
* Attaches a new consumer to an in-flight download.
|
|
206
|
+
* @param {string} key - The normalized registry key.
|
|
207
|
+
* @returns {MediaMirrorEntry<string, unknown[]>|null} The shared entry, or null when nothing is in flight.
|
|
208
|
+
*/
|
|
209
|
+
static attach(key: string): MediaMirrorEntry<string, unknown[]> | null;
|
|
210
|
+
/**
|
|
211
|
+
* Removes a finished download from the registry. A download that never
|
|
212
|
+
* produced a payload rejects its waiters instead of hanging them forever.
|
|
213
|
+
* @param {string} key - The normalized registry key.
|
|
214
|
+
* @returns {boolean} True when an entry was removed.
|
|
215
|
+
*/
|
|
216
|
+
static close(key: string): boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Rejects every in-flight download and empties the registry.
|
|
219
|
+
* @returns {void}
|
|
220
|
+
*/
|
|
221
|
+
static clear(): void;
|
|
222
|
+
/**
|
|
223
|
+
* Broadcasts an event to every loader attached to an in-flight download. The
|
|
224
|
+
* event is appended to the history so a loader that attaches later still
|
|
225
|
+
* receives the complete stream, in its original order.
|
|
226
|
+
* @param {string} key - The normalized registry key.
|
|
227
|
+
* @param {string} name - The event name.
|
|
228
|
+
* @param {unknown[]} args - The arguments the event was emitted with.
|
|
229
|
+
* @returns {number} The number of subscribers that received the event.
|
|
230
|
+
* @throws {TypeError} If `key` is not a string.
|
|
231
|
+
* @throws {TypeError} If `name` is not a string.
|
|
232
|
+
* @throws {TypeError} If `args` is not an array.
|
|
233
|
+
*/
|
|
234
|
+
static publish(key: string, name: string, args: unknown[]): number;
|
|
235
|
+
/**
|
|
236
|
+
* Attaches a subscriber to an in-flight download. The entire history is
|
|
237
|
+
* replayed synchronously, in its original order, before the handler starts
|
|
238
|
+
* receiving live events. Because JavaScript never preempts a running
|
|
239
|
+
* function, no live event can slip between the replay and the subscription,
|
|
240
|
+
* so the handler observes exactly the same sequence the leader produced.
|
|
241
|
+
* @param {string} key - The normalized registry key.
|
|
242
|
+
* @param {MediaMirrorSubscriber<string, unknown[]>} handler - The callback invoked for every event.
|
|
243
|
+
* @returns {(() => void)|null} A function that detaches the subscriber, or null when nothing is in flight.
|
|
244
|
+
* @throws {TypeError} If `handler` is not a function.
|
|
245
|
+
*/
|
|
246
|
+
static subscribe(key: string, handler: MediaMirrorSubscriber<string, unknown[]>): (() => void) | null;
|
|
247
|
+
/**
|
|
248
|
+
* The ordered event history of an in-flight download. The array is live and
|
|
249
|
+
* must be treated as read-only.
|
|
250
|
+
* @param {string} key - The normalized registry key.
|
|
251
|
+
* @returns {MediaMirrorEvent<string, unknown[]>[]} The captured events, oldest first.
|
|
252
|
+
*/
|
|
253
|
+
static history(key: string): MediaMirrorEvent<string, unknown[]>[];
|
|
254
|
+
}
|
|
255
|
+
export default TinyMediaMirror;
|
|
256
|
+
//# sourceMappingURL=TinyMediaMirror.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TinyMediaMirror.d.mts","sourceRoot":"","sources":["../../../../../../src/v1/libs/html/loader/utils/TinyMediaMirror.mjs"],"names":[],"mappings":"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"}
|