tiny-essentials 1.29.0 → 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.
Files changed (131) hide show
  1. package/changelog/1/29/1.md +11 -0
  2. package/changelog/1/29/2.md +82 -0
  3. package/dist/v1/TinyBasicsEs.min.js +1 -1
  4. package/dist/v1/TinyEssentials.min.js +1 -1
  5. package/dist/v1/TinyFetchLoader.min.js +1 -0
  6. package/dist/v1/TinyImageLoader.min.js +1 -0
  7. package/dist/v1/TinyMediaCache.min.js +1 -0
  8. package/dist/v1/TinyMediaLoader.min.js +1 -0
  9. package/dist/v1/basics/index.cjs +5 -0
  10. package/dist/v1/basics/index.d.mts +2 -1
  11. package/dist/v1/basics/index.d.mts.map +1 -1
  12. package/dist/v1/basics/index.mjs +2 -1
  13. package/dist/v1/basics/media/youtube.cjs +275 -0
  14. package/dist/v1/basics/media/youtube.d.mts +107 -0
  15. package/dist/v1/basics/media/youtube.d.mts.map +1 -0
  16. package/dist/v1/basics/media/youtube.mjs +214 -0
  17. package/dist/v1/index.cjs +13 -0
  18. package/dist/v1/index.d.mts +6 -1
  19. package/dist/v1/index.d.mts.map +1 -1
  20. package/dist/v1/index.mjs +7 -2
  21. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.cjs +0 -1
  22. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts +0 -1
  23. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts.map +1 -1
  24. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.mjs +0 -1
  25. package/dist/v1/libs/fs/plugins/OPFS/index.cjs +0 -8
  26. package/dist/v1/libs/fs/plugins/OPFS/index.d.mts +0 -8
  27. package/dist/v1/libs/fs/plugins/OPFS/index.d.mts.map +1 -1
  28. package/dist/v1/libs/fs/plugins/OPFS/index.mjs +0 -8
  29. package/dist/v1/libs/html/TinyHtml.cjs +1 -1
  30. package/dist/v1/libs/html/TinyHtml.d.mts +2 -2
  31. package/dist/v1/libs/html/TinyHtml.d.mts.map +1 -1
  32. package/dist/v1/libs/html/TinyHtml.mjs +1 -1
  33. package/dist/v1/libs/html/loader/TinyMediaLoader.cjs +1999 -0
  34. package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts +999 -0
  35. package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts.map +1 -0
  36. package/dist/v1/libs/html/loader/TinyMediaLoader.mjs +1852 -0
  37. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.cjs +77 -0
  38. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts +103 -0
  39. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts.map +1 -0
  40. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.mjs +67 -0
  41. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.cjs +630 -0
  42. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts +138 -0
  43. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts.map +1 -0
  44. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.mjs +576 -0
  45. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.cjs +70 -0
  46. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts +100 -0
  47. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts.map +1 -0
  48. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.mjs +60 -0
  49. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.cjs +946 -0
  50. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts +67 -0
  51. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts.map +1 -0
  52. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.mjs +896 -0
  53. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.cjs +225 -0
  54. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts +149 -0
  55. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts.map +1 -0
  56. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.mjs +197 -0
  57. package/dist/v1/libs/html/loader/formats/TinyImageLoader.cjs +1454 -0
  58. package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts +200 -0
  59. package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts.map +1 -0
  60. package/dist/v1/libs/html/loader/formats/TinyImageLoader.mjs +1317 -0
  61. package/dist/v1/libs/html/loader/probe/TinyImageProbe.cjs +668 -0
  62. package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts +83 -0
  63. package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts.map +1 -0
  64. package/dist/v1/libs/html/loader/probe/TinyImageProbe.mjs +626 -0
  65. package/dist/v1/libs/html/loader/utils/TinyBufferReader.cjs +277 -0
  66. package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts +118 -0
  67. package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts.map +1 -0
  68. package/dist/v1/libs/html/loader/utils/TinyBufferReader.mjs +247 -0
  69. package/dist/v1/libs/html/loader/utils/TinyIsoBox.cjs +128 -0
  70. package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts +49 -0
  71. package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts.map +1 -0
  72. package/dist/v1/libs/html/loader/utils/TinyIsoBox.mjs +116 -0
  73. package/dist/v1/libs/html/loader/utils/TinyMediaCache.cjs +796 -0
  74. package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts +354 -0
  75. package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts.map +1 -0
  76. package/dist/v1/libs/html/loader/utils/TinyMediaCache.mjs +734 -0
  77. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.cjs +302 -0
  78. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts +256 -0
  79. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts.map +1 -0
  80. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.mjs +280 -0
  81. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.cjs +194 -0
  82. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts +138 -0
  83. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts.map +1 -0
  84. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.mjs +175 -0
  85. package/dist/v1/libs/tools/TinyPkgExportValidator.cjs +359 -55
  86. package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts +64 -6
  87. package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts.map +1 -1
  88. package/dist/v1/libs/tools/TinyPkgExportValidator.mjs +320 -57
  89. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.cjs +0 -1
  90. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts +0 -1
  91. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts.map +1 -1
  92. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.mjs +0 -1
  93. package/dist/v1/webTemplates/bootstrap/5.3/scss/background/_background-grid.scss +15 -0
  94. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-contrast.scss +208 -0
  95. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-ghost.scss +89 -0
  96. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_cube-icon.scss +65 -0
  97. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_sphere-pulse.scss +39 -0
  98. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_badges.scss +90 -0
  99. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_bootstrap-overrides.scss +495 -0
  100. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_buttons.scss +390 -0
  101. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_colors.scss +213 -0
  102. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_effects.scss +153 -0
  103. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms-tones.scss +69 -0
  104. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms.scss +323 -0
  105. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_global.scss +31 -0
  106. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_links.scss +47 -0
  107. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tables.scss +25 -0
  108. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_theme.scss +21 -0
  109. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tokens.scss +161 -0
  110. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_utilities.scss +81 -0
  111. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_variables.scss +63 -0
  112. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_form-tone-mixins.scss +236 -0
  113. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_sizing.scss +68 -0
  114. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_tone-mixins.scss +392 -0
  115. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_typography.scss +112 -0
  116. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/background/_tiny_gradient_1.scss +131 -0
  117. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/colors/_pink.scss +47 -0
  118. package/dist/v1/webTemplates/multiformats/14.0/IPFSHasher/index.cjs +2 -2
  119. package/docs/v1/Exports.md +33 -2
  120. package/docs/v1/README.md +9 -0
  121. package/docs/v1/basics/youtube.md +204 -0
  122. package/docs/v1/libs/html/loader/TinyFetchLoader.md +114 -0
  123. package/docs/v1/libs/html/loader/TinyImageLoader.md +141 -0
  124. package/docs/v1/libs/html/loader/TinyMediaCache.md +166 -0
  125. package/docs/v1/libs/html/loader/TinyMediaLoader.md +300 -0
  126. package/docs/v1/libs/tools/TinyPkgExportValidator.md +55 -0
  127. package/docs/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters.md +0 -4
  128. package/docs/v1/webTemplates/bootstrap/5.3/DeepStrap.md +177 -0
  129. package/package.json +281 -17
  130. /package/dist/v1/webTemplates/bootstrap/5.3/scss/input/{Placeholder-Colors-Fix.scss → _placeholder-colors-fix.scss} +0 -0
  131. /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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"}