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.
Files changed (128) hide show
  1. package/changelog/1/29/2.md +82 -0
  2. package/dist/v1/TinyBasicsEs.min.js +1 -1
  3. package/dist/v1/TinyEssentials.min.js +1 -1
  4. package/dist/v1/TinyFetchLoader.min.js +1 -0
  5. package/dist/v1/TinyImageLoader.min.js +1 -0
  6. package/dist/v1/TinyMediaCache.min.js +1 -0
  7. package/dist/v1/TinyMediaLoader.min.js +1 -0
  8. package/dist/v1/basics/index.cjs +5 -0
  9. package/dist/v1/basics/index.d.mts +2 -1
  10. package/dist/v1/basics/index.d.mts.map +1 -1
  11. package/dist/v1/basics/index.mjs +2 -1
  12. package/dist/v1/basics/media/youtube.cjs +275 -0
  13. package/dist/v1/basics/media/youtube.d.mts +107 -0
  14. package/dist/v1/basics/media/youtube.d.mts.map +1 -0
  15. package/dist/v1/basics/media/youtube.mjs +214 -0
  16. package/dist/v1/index.cjs +13 -0
  17. package/dist/v1/index.d.mts +6 -1
  18. package/dist/v1/index.d.mts.map +1 -1
  19. package/dist/v1/index.mjs +7 -2
  20. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.cjs +0 -1
  21. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts +0 -1
  22. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts.map +1 -1
  23. package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.mjs +0 -1
  24. package/dist/v1/libs/fs/plugins/OPFS/index.cjs +0 -8
  25. package/dist/v1/libs/fs/plugins/OPFS/index.d.mts +0 -8
  26. package/dist/v1/libs/fs/plugins/OPFS/index.d.mts.map +1 -1
  27. package/dist/v1/libs/fs/plugins/OPFS/index.mjs +0 -8
  28. package/dist/v1/libs/html/TinyHtml.cjs +1 -1
  29. package/dist/v1/libs/html/TinyHtml.d.mts +2 -2
  30. package/dist/v1/libs/html/TinyHtml.d.mts.map +1 -1
  31. package/dist/v1/libs/html/TinyHtml.mjs +1 -1
  32. package/dist/v1/libs/html/loader/TinyMediaLoader.cjs +1999 -0
  33. package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts +999 -0
  34. package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts.map +1 -0
  35. package/dist/v1/libs/html/loader/TinyMediaLoader.mjs +1852 -0
  36. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.cjs +77 -0
  37. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts +103 -0
  38. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts.map +1 -0
  39. package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.mjs +67 -0
  40. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.cjs +630 -0
  41. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts +138 -0
  42. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts.map +1 -0
  43. package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.mjs +576 -0
  44. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.cjs +70 -0
  45. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts +100 -0
  46. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts.map +1 -0
  47. package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.mjs +60 -0
  48. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.cjs +946 -0
  49. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts +67 -0
  50. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts.map +1 -0
  51. package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.mjs +896 -0
  52. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.cjs +225 -0
  53. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts +149 -0
  54. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts.map +1 -0
  55. package/dist/v1/libs/html/loader/formats/TinyFetchLoader.mjs +197 -0
  56. package/dist/v1/libs/html/loader/formats/TinyImageLoader.cjs +1454 -0
  57. package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts +200 -0
  58. package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts.map +1 -0
  59. package/dist/v1/libs/html/loader/formats/TinyImageLoader.mjs +1317 -0
  60. package/dist/v1/libs/html/loader/probe/TinyImageProbe.cjs +668 -0
  61. package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts +83 -0
  62. package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts.map +1 -0
  63. package/dist/v1/libs/html/loader/probe/TinyImageProbe.mjs +626 -0
  64. package/dist/v1/libs/html/loader/utils/TinyBufferReader.cjs +277 -0
  65. package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts +118 -0
  66. package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts.map +1 -0
  67. package/dist/v1/libs/html/loader/utils/TinyBufferReader.mjs +247 -0
  68. package/dist/v1/libs/html/loader/utils/TinyIsoBox.cjs +128 -0
  69. package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts +49 -0
  70. package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts.map +1 -0
  71. package/dist/v1/libs/html/loader/utils/TinyIsoBox.mjs +116 -0
  72. package/dist/v1/libs/html/loader/utils/TinyMediaCache.cjs +796 -0
  73. package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts +354 -0
  74. package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts.map +1 -0
  75. package/dist/v1/libs/html/loader/utils/TinyMediaCache.mjs +734 -0
  76. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.cjs +302 -0
  77. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts +256 -0
  78. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts.map +1 -0
  79. package/dist/v1/libs/html/loader/utils/TinyMediaMirror.mjs +280 -0
  80. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.cjs +194 -0
  81. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts +138 -0
  82. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts.map +1 -0
  83. package/dist/v1/libs/html/loader/utils/TinyMediaProgress.mjs +175 -0
  84. package/dist/v1/libs/tools/TinyPkgExportValidator.cjs +359 -55
  85. package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts +64 -6
  86. package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts.map +1 -1
  87. package/dist/v1/libs/tools/TinyPkgExportValidator.mjs +320 -57
  88. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.cjs +0 -1
  89. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts +0 -1
  90. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts.map +1 -1
  91. package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.mjs +0 -1
  92. package/dist/v1/webTemplates/bootstrap/5.3/scss/background/_background-grid.scss +15 -0
  93. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-contrast.scss +208 -0
  94. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-ghost.scss +89 -0
  95. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_cube-icon.scss +65 -0
  96. package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_sphere-pulse.scss +39 -0
  97. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_badges.scss +90 -0
  98. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_bootstrap-overrides.scss +495 -0
  99. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_buttons.scss +390 -0
  100. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_colors.scss +213 -0
  101. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_effects.scss +153 -0
  102. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms-tones.scss +69 -0
  103. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms.scss +323 -0
  104. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_global.scss +31 -0
  105. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_links.scss +47 -0
  106. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tables.scss +25 -0
  107. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_theme.scss +21 -0
  108. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tokens.scss +161 -0
  109. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_utilities.scss +81 -0
  110. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_variables.scss +63 -0
  111. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_form-tone-mixins.scss +236 -0
  112. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_sizing.scss +68 -0
  113. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_tone-mixins.scss +392 -0
  114. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_typography.scss +112 -0
  115. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/background/_tiny_gradient_1.scss +131 -0
  116. package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/colors/_pink.scss +47 -0
  117. package/docs/v1/Exports.md +33 -2
  118. package/docs/v1/README.md +9 -0
  119. package/docs/v1/basics/youtube.md +204 -0
  120. package/docs/v1/libs/html/loader/TinyFetchLoader.md +114 -0
  121. package/docs/v1/libs/html/loader/TinyImageLoader.md +141 -0
  122. package/docs/v1/libs/html/loader/TinyMediaCache.md +166 -0
  123. package/docs/v1/libs/html/loader/TinyMediaLoader.md +300 -0
  124. package/docs/v1/libs/tools/TinyPkgExportValidator.md +55 -0
  125. package/docs/v1/webTemplates/bootstrap/5.3/DeepStrap.md +177 -0
  126. package/package.json +244 -8
  127. /package/dist/v1/webTemplates/bootstrap/5.3/scss/input/{Placeholder-Colors-Fix.scss → _placeholder-colors-fix.scss} +0 -0
  128. /package/dist/v1/webTemplates/bootstrap/5.3/scss/spacing/{Spacing-Plus.scss → _spacing-plus.scss} +0 -0
@@ -0,0 +1,280 @@
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
+ /**
11
+ * Describes a single in-flight download.
12
+ * @template {string} Name
13
+ * @template {unknown[]} Args
14
+ * @typedef {Object} MediaMirrorEntry
15
+ * @property {string} key - The normalized registry key.
16
+ * @property {string} src - The original source URL.
17
+ * @property {number} startedAt - The epoch timestamp, in milliseconds, when the download started.
18
+ * @property {number} mirrors - The number of loaders attached to this download.
19
+ * @property {boolean} settled - Whether the download already produced a payload.
20
+ * @property {number} sequence - The sequence number assigned to the next published event.
21
+ * @property {MediaMirrorEvent<Name, Args>[]} events - Every event published so far, in order.
22
+ * @property {Set<MediaMirrorSubscriber<Name, Args>>} subscribers - The loaders attached to this download.
23
+ * @property {Promise<Blob>} promise - Resolves with the shared blob.
24
+ * @property {(blob: Blob) => void} resolve - Resolves the shared promise.
25
+ * @property {(error: Error) => void} reject - Rejects the shared promise.
26
+ */
27
+ /**
28
+ * A read-only snapshot of the registry counters.
29
+ * @typedef {Object} MediaMirrorStats
30
+ * @property {number} pending - The number of downloads currently in flight.
31
+ * @property {number} mirrors - The total number of attached loaders.
32
+ */
33
+ /**
34
+ * A read-only snapshot of a single in-flight download.
35
+ * @typedef {Object} MediaMirrorSnapshot
36
+ * @property {string} key - The normalized registry key.
37
+ * @property {string} src - The original source URL.
38
+ * @property {number} startedAt - The epoch timestamp, in milliseconds, when the download started.
39
+ * @property {number} mirrors - The number of loaders attached to this download.
40
+ * @property {boolean} settled - Whether the download already produced a payload.
41
+ */
42
+ /**
43
+ * A single event captured from the leading loader.
44
+ * @template {string} Name
45
+ * @template {unknown[]} Args
46
+ * @typedef {Object} MediaMirrorEvent
47
+ * @property {number} seq - The zero-based position of the event in the stream.
48
+ * @property {Name} name - The event name.
49
+ * @property {Args} args - The arguments the event was emitted with.
50
+ * @property {number} timestamp - The high resolution timestamp of the capture.
51
+ */
52
+ /**
53
+ * Handles a broadcast event.
54
+ * @template {string} Name
55
+ * @template {unknown[]} Args
56
+ * @callback MediaMirrorSubscriber
57
+ * @param {MediaMirrorEvent<Name, Args>} event - The published event.
58
+ * @returns {void}
59
+ */
60
+ /**
61
+ * Deduplicates concurrent downloads that target the same URL.
62
+ */
63
+ class TinyMediaMirror {
64
+ /** @type {Map<string, MediaMirrorEntry<string, unknown[]>>} */
65
+ static #pending = new Map();
66
+ /**
67
+ * The number of downloads currently in flight.
68
+ * @returns {number} The number of registered downloads.
69
+ */
70
+ static get size() {
71
+ return TinyMediaMirror.#pending.size;
72
+ }
73
+ /**
74
+ * The key of every in-flight download.
75
+ * @returns {string[]} A new array with the registered keys.
76
+ */
77
+ static get keys() {
78
+ return Array.from(TinyMediaMirror.#pending.keys());
79
+ }
80
+ /**
81
+ * A read-only snapshot of the registry counters.
82
+ * @returns {MediaMirrorStats} The current counters.
83
+ */
84
+ static get stats() {
85
+ let mirrors = 0;
86
+ for (const entry of TinyMediaMirror.#pending.values()) {
87
+ mirrors += entry.mirrors;
88
+ }
89
+ return { pending: TinyMediaMirror.#pending.size, mirrors };
90
+ }
91
+ /**
92
+ * Checks whether a download is already in flight for the given key.
93
+ * @param {string} key - The normalized registry key.
94
+ * @returns {boolean} True when a download is in flight.
95
+ */
96
+ static has(key) {
97
+ return TinyMediaMirror.#pending.has(key);
98
+ }
99
+ /**
100
+ * Reads an in-flight download without attaching to it.
101
+ * @param {string} key - The normalized registry key.
102
+ * @returns {MediaMirrorEntry<string, unknown[]>|null} The shared entry, or null when nothing is in flight.
103
+ */
104
+ static get(key) {
105
+ return TinyMediaMirror.#pending.get(key) ?? null;
106
+ }
107
+ /**
108
+ * A read-only snapshot of every in-flight download.
109
+ * @returns {MediaMirrorSnapshot[]} One record per in-flight download.
110
+ */
111
+ static entries() {
112
+ return Array.from(TinyMediaMirror.#pending.values(), (entry) => ({
113
+ key: entry.key,
114
+ src: entry.src,
115
+ startedAt: entry.startedAt,
116
+ mirrors: entry.mirrors,
117
+ settled: entry.settled,
118
+ }));
119
+ }
120
+ /**
121
+ * Registers a new in-flight download. When the key is already taken the
122
+ * existing entry is returned, so a live download is never overwritten.
123
+ * @param {string} key - The normalized registry key.
124
+ * @param {string} src - The original source URL.
125
+ * @returns {MediaMirrorEntry<string, unknown[]>} The registered entry.
126
+ * @throws {TypeError} If `key` is not a string.
127
+ * @throws {TypeError} If `src` is not a string.
128
+ */
129
+ static open(key, src) {
130
+ if (typeof key !== 'string') {
131
+ throw new TypeError('The "key" argument must be a string.');
132
+ }
133
+ if (typeof src !== 'string') {
134
+ throw new TypeError('The "src" argument must be a string.');
135
+ }
136
+ const existing = TinyMediaMirror.#pending.get(key);
137
+ if (existing) {
138
+ return existing;
139
+ }
140
+ /** @type {MediaMirrorEntry<string, unknown[]>} */
141
+ const entry = {
142
+ key,
143
+ src,
144
+ startedAt: Date.now(),
145
+ mirrors: 0,
146
+ settled: false,
147
+ sequence: 0,
148
+ events: [],
149
+ subscribers: new Set(),
150
+ promise: /** @type {Promise<Blob>} */ ( /** @type {unknown} */(null)),
151
+ resolve: () => { },
152
+ reject: () => { },
153
+ };
154
+ entry.promise = new Promise((resolve, reject) => {
155
+ entry.resolve = (blob) => {
156
+ entry.settled = true;
157
+ resolve(blob);
158
+ };
159
+ entry.reject = (error) => {
160
+ entry.settled = true;
161
+ reject(error);
162
+ };
163
+ });
164
+ // A download without mirrors must never surface as an unhandled rejection.
165
+ entry.promise.catch(() => { });
166
+ TinyMediaMirror.#pending.set(key, entry);
167
+ return entry;
168
+ }
169
+ /**
170
+ * Attaches a new consumer to an in-flight download.
171
+ * @param {string} key - The normalized registry key.
172
+ * @returns {MediaMirrorEntry<string, unknown[]>|null} The shared entry, or null when nothing is in flight.
173
+ */
174
+ static attach(key) {
175
+ const entry = TinyMediaMirror.#pending.get(key);
176
+ if (!entry) {
177
+ return null;
178
+ }
179
+ entry.mirrors += 1;
180
+ return entry;
181
+ }
182
+ /**
183
+ * Removes a finished download from the registry. A download that never
184
+ * produced a payload rejects its waiters instead of hanging them forever.
185
+ * @param {string} key - The normalized registry key.
186
+ * @returns {boolean} True when an entry was removed.
187
+ */
188
+ static close(key) {
189
+ const entry = TinyMediaMirror.#pending.get(key);
190
+ if (!entry) {
191
+ return false;
192
+ }
193
+ if (!entry.settled) {
194
+ entry.reject(new Error(`The mirrored download for "${entry.src}" ended without a payload.`));
195
+ }
196
+ return TinyMediaMirror.#pending.delete(key);
197
+ }
198
+ /**
199
+ * Rejects every in-flight download and empties the registry.
200
+ * @returns {void}
201
+ */
202
+ static clear() {
203
+ for (const entry of TinyMediaMirror.#pending.values()) {
204
+ if (!entry.settled) {
205
+ entry.reject(new Error('The mirror registry was cleared.'));
206
+ }
207
+ }
208
+ TinyMediaMirror.#pending.clear();
209
+ }
210
+ /**
211
+ * Broadcasts an event to every loader attached to an in-flight download. The
212
+ * event is appended to the history so a loader that attaches later still
213
+ * receives the complete stream, in its original order.
214
+ * @param {string} key - The normalized registry key.
215
+ * @param {string} name - The event name.
216
+ * @param {unknown[]} args - The arguments the event was emitted with.
217
+ * @returns {number} The number of subscribers that received the event.
218
+ * @throws {TypeError} If `key` is not a string.
219
+ * @throws {TypeError} If `name` is not a string.
220
+ * @throws {TypeError} If `args` is not an array.
221
+ */
222
+ static publish(key, name, args) {
223
+ if (typeof key !== 'string') {
224
+ throw new TypeError('The "key" argument must be a string.');
225
+ }
226
+ if (typeof name !== 'string') {
227
+ throw new TypeError('The "name" argument must be a string.');
228
+ }
229
+ if (!Array.isArray(args)) {
230
+ throw new TypeError('The "args" argument must be an array.');
231
+ }
232
+ const entry = TinyMediaMirror.#pending.get(key);
233
+ if (!entry) {
234
+ return 0;
235
+ }
236
+ const event = { seq: entry.sequence, name, args, timestamp: performance.now() };
237
+ entry.sequence += 1;
238
+ entry.events.push(event);
239
+ for (const subscriber of entry.subscribers) {
240
+ subscriber(event);
241
+ }
242
+ return entry.subscribers.size;
243
+ }
244
+ /**
245
+ * Attaches a subscriber to an in-flight download. The entire history is
246
+ * replayed synchronously, in its original order, before the handler starts
247
+ * receiving live events. Because JavaScript never preempts a running
248
+ * function, no live event can slip between the replay and the subscription,
249
+ * so the handler observes exactly the same sequence the leader produced.
250
+ * @param {string} key - The normalized registry key.
251
+ * @param {MediaMirrorSubscriber<string, unknown[]>} handler - The callback invoked for every event.
252
+ * @returns {(() => void)|null} A function that detaches the subscriber, or null when nothing is in flight.
253
+ * @throws {TypeError} If `handler` is not a function.
254
+ */
255
+ static subscribe(key, handler) {
256
+ if (typeof handler !== 'function') {
257
+ throw new TypeError('The "handler" argument must be a function.');
258
+ }
259
+ const entry = TinyMediaMirror.#pending.get(key);
260
+ if (!entry) {
261
+ return null;
262
+ }
263
+ const { events } = entry;
264
+ for (let index = 0; index < events.length; index += 1) {
265
+ handler(events[index]);
266
+ }
267
+ entry.subscribers.add(handler);
268
+ return () => entry.subscribers.delete(handler);
269
+ }
270
+ /**
271
+ * The ordered event history of an in-flight download. The array is live and
272
+ * must be treated as read-only.
273
+ * @param {string} key - The normalized registry key.
274
+ * @returns {MediaMirrorEvent<string, unknown[]>[]} The captured events, oldest first.
275
+ */
276
+ static history(key) {
277
+ return TinyMediaMirror.#pending.get(key)?.events ?? [];
278
+ }
279
+ }
280
+ export default TinyMediaMirror;
@@ -0,0 +1,194 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Tracks byte-level progress for a media download. It is a plain
5
+ * value object: the loader pushes chunks in and reads snapshots out.
6
+ */
7
+
8
+ /**
9
+ * A point-in-time snapshot of the download progress. It exposes both the raw
10
+ * byte counters and the derived metrics (rate, ETA and completion percentage)
11
+ * so that consumers can render progress without recalculating anything.
12
+ *
13
+ * @typedef {Object} MediaProgressSnapshot
14
+ * @property {number} loaded - The number of bytes downloaded so far.
15
+ * @property {number} total - The total number of bytes (0 when unknown).
16
+ * @property {number} remaining - The number of bytes left to download.
17
+ * @property {number} percent - The completion percentage between 0 and 100.
18
+ * @property {number} rate - The smoothed download rate in bytes per second.
19
+ * @property {number} elapsed - The elapsed time in milliseconds.
20
+ * @property {number} eta - The estimated time remaining in milliseconds.
21
+ * @property {number} chunks - The number of chunks received so far.
22
+ * @property {number} [width] - The intrinsic width of the media.
23
+ * @property {number} [height] - The intrinsic height of the media.
24
+ */
25
+
26
+ /**
27
+ * Accumulates download chunks and derives rate, ETA and percentage.
28
+ */
29
+ class TinyMediaProgress {
30
+ /** @type {number} */
31
+ #total;
32
+ /** @type {number} */
33
+ #loaded = 0;
34
+ /** @type {number} */
35
+ #chunks = 0;
36
+ /** @type {number} */
37
+ #startTime = 0;
38
+ /** @type {number} */
39
+ #lastTime = 0;
40
+ /** @type {number} */
41
+ #lastLoaded = 0;
42
+ /** @type {number} */
43
+ #rate = 0;
44
+
45
+ /**
46
+ * Initializes a new progress tracker.
47
+ * @param {number} [total] - The total number of bytes (0 when unknown).
48
+ * @throws {RangeError} If `total` is not a positive number.
49
+ */
50
+ constructor(total = 0) {
51
+ if (typeof total !== 'number' || Number.isNaN(total) || total < 0) {
52
+ throw new RangeError('The "total" argument must be a positive number.');
53
+ }
54
+ this.#total = total;
55
+ }
56
+
57
+ /**
58
+ * The total number of bytes (0 when unknown).
59
+ * @returns {number}
60
+ */
61
+ get total() {
62
+ return this.#total;
63
+ }
64
+
65
+ /**
66
+ * Updates the total size of the media being loaded.
67
+ * @param {number} value - The new total in bytes.
68
+ * @throws {RangeError} If `value` is not a positive number.
69
+ */
70
+ set total(value) {
71
+ if (typeof value !== 'number' || Number.isNaN(value) || value < 0) {
72
+ throw new RangeError('The "total" value must be a positive number.');
73
+ }
74
+ this.#total = value;
75
+ }
76
+
77
+ /**
78
+ * @returns {number} The number of bytes downloaded so far.
79
+ */
80
+ get loaded() {
81
+ return this.#loaded;
82
+ }
83
+
84
+ /**
85
+ * @returns {number} The number of bytes left to download.
86
+ */
87
+ get remaining() {
88
+ return this.#total > 0 ? Math.max(0, this.#total - this.#loaded) : 0;
89
+ }
90
+
91
+ /**
92
+ * @returns {number} The completion percentage between 0 and 100.
93
+ */
94
+ get percent() {
95
+ return this.#total > 0 ? Math.min(100, (this.#loaded / this.#total) * 100) : 0;
96
+ }
97
+
98
+ /**
99
+ * @returns {number} The smoothed download rate in bytes per second.
100
+ */
101
+ get rate() {
102
+ return this.#rate;
103
+ }
104
+
105
+ /**
106
+ * @returns {number} The number of chunks received so far.
107
+ */
108
+ get chunks() {
109
+ return this.#chunks;
110
+ }
111
+
112
+ /**
113
+ * @returns {number} The elapsed time in milliseconds.
114
+ */
115
+ get elapsed() {
116
+ return this.#startTime === 0 ? 0 : performance.now() - this.#startTime;
117
+ }
118
+
119
+ /**
120
+ * @returns {number} The estimated time remaining in milliseconds.
121
+ */
122
+ get eta() {
123
+ if (this.#rate <= 0 || this.#total <= 0) {
124
+ return 0;
125
+ }
126
+ return (this.remaining / this.#rate) * 1000;
127
+ }
128
+
129
+ /**
130
+ * Marks the start of the download.
131
+ * @returns {TinyMediaProgress} The current instance for chaining.
132
+ */
133
+ start() {
134
+ this.#startTime = performance.now();
135
+ this.#lastTime = this.#startTime;
136
+ return this;
137
+ }
138
+
139
+ /**
140
+ * Registers a new chunk and updates the smoothed rate.
141
+ * @param {number} bytes - The size of the chunk in bytes.
142
+ * @returns {TinyMediaProgress} The current instance for chaining.
143
+ * @throws {RangeError} If `bytes` is not a positive number.
144
+ */
145
+ push(bytes) {
146
+ if (typeof bytes !== 'number' || Number.isNaN(bytes) || bytes < 0) {
147
+ throw new RangeError('The "bytes" argument must be a positive number.');
148
+ }
149
+ const now = performance.now();
150
+ this.#loaded += bytes;
151
+ this.#chunks += 1;
152
+ const deltaTime = now - this.#lastTime;
153
+ if (deltaTime > 0) {
154
+ const instantRate = ((this.#loaded - this.#lastLoaded) / deltaTime) * 1000;
155
+ this.#rate = this.#rate === 0 ? instantRate : this.#rate * 0.7 + instantRate * 0.3;
156
+ }
157
+ this.#lastTime = now;
158
+ this.#lastLoaded = this.#loaded;
159
+ return this;
160
+ }
161
+
162
+ /**
163
+ * Resets every counter so the instance can be reused.
164
+ * @returns {TinyMediaProgress} The current instance for chaining.
165
+ */
166
+ reset() {
167
+ this.#loaded = 0;
168
+ this.#chunks = 0;
169
+ this.#rate = 0;
170
+ this.#lastLoaded = 0;
171
+ this.#startTime = 0;
172
+ this.#lastTime = 0;
173
+ return this;
174
+ }
175
+
176
+ /**
177
+ * Returns a read-only snapshot of the current progress and derived metrics.
178
+ * @returns {MediaProgressSnapshot} An immutable snapshot of the current state.
179
+ */
180
+ snapshot() {
181
+ return {
182
+ loaded: this.#loaded,
183
+ total: this.#total,
184
+ remaining: this.remaining,
185
+ percent: this.percent,
186
+ rate: this.#rate,
187
+ elapsed: this.elapsed,
188
+ eta: this.eta,
189
+ chunks: this.#chunks,
190
+ };
191
+ }
192
+ }
193
+
194
+ module.exports = TinyMediaProgress;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @fileoverview Tracks byte-level progress for a media download. It is a plain
3
+ * value object: the loader pushes chunks in and reads snapshots out.
4
+ */
5
+ export type MediaProgressSnapshot = {
6
+ /**
7
+ * - The number of bytes downloaded so far.
8
+ */
9
+ loaded: number;
10
+ /**
11
+ * - The total number of bytes (0 when unknown).
12
+ */
13
+ total: number;
14
+ /**
15
+ * - The number of bytes left to download.
16
+ */
17
+ remaining: number;
18
+ /**
19
+ * - The completion percentage between 0 and 100.
20
+ */
21
+ percent: number;
22
+ /**
23
+ * - The smoothed download rate in bytes per second.
24
+ */
25
+ rate: number;
26
+ /**
27
+ * - The elapsed time in milliseconds.
28
+ */
29
+ elapsed: number;
30
+ /**
31
+ * - The estimated time remaining in milliseconds.
32
+ */
33
+ eta: number;
34
+ /**
35
+ * - The number of chunks received so far.
36
+ */
37
+ chunks: number;
38
+ /**
39
+ * - The intrinsic width of the media.
40
+ */
41
+ width?: number;
42
+ /**
43
+ * - The intrinsic height of the media.
44
+ */
45
+ height?: number;
46
+ };
47
+ /**
48
+ * A point-in-time snapshot of the download progress. It exposes both the raw
49
+ * byte counters and the derived metrics (rate, ETA and completion percentage)
50
+ * so that consumers can render progress without recalculating anything.
51
+ *
52
+ * @typedef {Object} MediaProgressSnapshot
53
+ * @property {number} loaded - The number of bytes downloaded so far.
54
+ * @property {number} total - The total number of bytes (0 when unknown).
55
+ * @property {number} remaining - The number of bytes left to download.
56
+ * @property {number} percent - The completion percentage between 0 and 100.
57
+ * @property {number} rate - The smoothed download rate in bytes per second.
58
+ * @property {number} elapsed - The elapsed time in milliseconds.
59
+ * @property {number} eta - The estimated time remaining in milliseconds.
60
+ * @property {number} chunks - The number of chunks received so far.
61
+ * @property {number} [width] - The intrinsic width of the media.
62
+ * @property {number} [height] - The intrinsic height of the media.
63
+ */
64
+ /**
65
+ * Accumulates download chunks and derives rate, ETA and percentage.
66
+ */
67
+ declare class TinyMediaProgress {
68
+ #private;
69
+ /**
70
+ * Initializes a new progress tracker.
71
+ * @param {number} [total] - The total number of bytes (0 when unknown).
72
+ * @throws {RangeError} If `total` is not a positive number.
73
+ */
74
+ constructor(total?: number);
75
+ /**
76
+ * The total number of bytes (0 when unknown).
77
+ * @returns {number}
78
+ */
79
+ get total(): number;
80
+ /**
81
+ * Updates the total size of the media being loaded.
82
+ * @param {number} value - The new total in bytes.
83
+ * @throws {RangeError} If `value` is not a positive number.
84
+ */
85
+ set total(value: number);
86
+ /**
87
+ * @returns {number} The number of bytes downloaded so far.
88
+ */
89
+ get loaded(): number;
90
+ /**
91
+ * @returns {number} The number of bytes left to download.
92
+ */
93
+ get remaining(): number;
94
+ /**
95
+ * @returns {number} The completion percentage between 0 and 100.
96
+ */
97
+ get percent(): number;
98
+ /**
99
+ * @returns {number} The smoothed download rate in bytes per second.
100
+ */
101
+ get rate(): number;
102
+ /**
103
+ * @returns {number} The number of chunks received so far.
104
+ */
105
+ get chunks(): number;
106
+ /**
107
+ * @returns {number} The elapsed time in milliseconds.
108
+ */
109
+ get elapsed(): number;
110
+ /**
111
+ * @returns {number} The estimated time remaining in milliseconds.
112
+ */
113
+ get eta(): number;
114
+ /**
115
+ * Marks the start of the download.
116
+ * @returns {TinyMediaProgress} The current instance for chaining.
117
+ */
118
+ start(): TinyMediaProgress;
119
+ /**
120
+ * Registers a new chunk and updates the smoothed rate.
121
+ * @param {number} bytes - The size of the chunk in bytes.
122
+ * @returns {TinyMediaProgress} The current instance for chaining.
123
+ * @throws {RangeError} If `bytes` is not a positive number.
124
+ */
125
+ push(bytes: number): TinyMediaProgress;
126
+ /**
127
+ * Resets every counter so the instance can be reused.
128
+ * @returns {TinyMediaProgress} The current instance for chaining.
129
+ */
130
+ reset(): TinyMediaProgress;
131
+ /**
132
+ * Returns a read-only snapshot of the current progress and derived metrics.
133
+ * @returns {MediaProgressSnapshot} An immutable snapshot of the current state.
134
+ */
135
+ snapshot(): MediaProgressSnapshot;
136
+ }
137
+ export default TinyMediaProgress;
138
+ //# sourceMappingURL=TinyMediaProgress.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TinyMediaProgress.d.mts","sourceRoot":"","sources":["../../../../../../src/v1/libs/html/loader/utils/TinyMediaProgress.mjs"],"names":[],"mappings":"AAAA;;;GAGG;AAOA,YAAkB,qBAAqB,GACvC;;;;IAAmB,MAAM,EAAd,MAAM,CACjB;;;;IAAmB,KAAK,EAAb,MAAM,CACjB;;;;IAAmB,SAAS,EAAjB,MAAM,CACjB;;;;IAAmB,OAAO,EAAf,MAAM,CACjB;;;;IAAmB,IAAI,EAAZ,MAAM,CACjB;;;;IAAmB,OAAO,EAAf,MAAM,CACjB;;;;IAAmB,GAAG,EAAX,MAAM,CACjB;;;;IAAmB,MAAM,EAAd,MAAM,CACjB;;;;IAAoB,KAAK,AAAzB,CACA,EADW,MAAM,CACjB;;;;IAAoB,MAAM,AAA1B,CACF,EADa,MAAM,CACnB;CAAA,CAAA;AAhBD;;;;;;;;;;;;;;;;GAgBG;AAEH;;GAEG;AACH,cAAM,iBAAiB;;IAgBrB;;;;OAIG;IACH,YAAY,KAAK,AAHd,CACA,EADQ,MAGU,EAKpB;IAED;;;OAGG;IACH,IAAI,KAAK,IAFI,MAAM,CAIlB;IAED;;;;OAIG;IACH,IAAI,KAAK,CAAC,KAAK,EAHJ,MAGI,EAKd;IAED;;OAEG;IACH,IAAI,MAAM,IAFG,MAAM,CAIlB;IAED;;OAEG;IACH,IAAI,SAAS,IAFA,MAAM,CAIlB;IAED;;OAEG;IACH,IAAI,OAAO,IAFE,MAAM,CAIlB;IAED;;OAEG;IACH,IAAI,IAAI,IAFK,MAAM,CAIlB;IAED;;OAEG;IACH,IAAI,MAAM,IAFG,MAAM,CAIlB;IAED;;OAEG;IACH,IAAI,OAAO,IAFE,MAAM,CAIlB;IAED;;OAEG;IACH,IAAI,GAAG,IAFM,MAAM,CAOlB;IAED;;;OAGG;IACH,KAAK,IAFQ,iBAAiB,CAM7B;IAED;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAJC,MAID,GAHG,iBAAiB,CAkB7B;IAED;;;OAGG;IACH,KAAK,IAFQ,iBAAiB,CAU7B;IAED;;;OAGG;IACH,QAAQ,IAFK,qBAAqB,CAajC;CACF;eAEc,iBAAiB"}