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,166 @@
1
+ # 🗄️ TinyMediaCache
2
+
3
+ `TinyMediaCache` is the shared memory cache used by every media loader. It stores blobs and their object URLs, reference-counts the consumers and releases the underlying `Blob` only when the last cache that knows about it is gone.
4
+
5
+ ---
6
+
7
+ ## ✨ Highlights
8
+
9
+ * **Shared registry** — the same URL is downloaded once, no matter how many loaders ask for it.
10
+ * **Reference counting** — an object URL is only revoked when the last consumer releases it.
11
+ * **Three cleanup strategies** — `auto`, `manual` and `ttl`.
12
+ * **Budgets** — limit the cache by number of entries (`maxItems`) and by total size (`maxBytes`).
13
+ * **Parent/child links** — derived entries (e.g. animation frames) can be attached to the entry that owns them.
14
+ * **Statistics** — hits, misses, evictions, bytes and links.
15
+
16
+ ---
17
+
18
+ ## 🚀 Quick start
19
+
20
+ ```js
21
+ import TinyMediaCache from 'tiny-essentials/libs/html/loader/utils/TinyMediaCache';
22
+
23
+ const cache = new TinyMediaCache({
24
+ maxItems: 100,
25
+ maxBytes: 128 * 1024 * 1024,
26
+ ttl: 5 * 60 * 1000,
27
+ strategy: TinyMediaCache.Strategy.TTL,
28
+ });
29
+ ```
30
+
31
+ ---
32
+
33
+ ## ⚙️ Options
34
+
35
+ | Option | Type | Default | Description |
36
+ | :--- | :--- | :--- | :--- |
37
+ | `maxItems` | `number` | `Infinity` | Maximum number of entries. |
38
+ | `maxBytes` | `number` | `Infinity` | Maximum cache size in bytes. |
39
+ | `ttl` | `number` | `0` | Entry lifetime in milliseconds (`0` = never expires). |
40
+ | `strategy` | `'auto' \| 'manual' \| 'ttl'` | `'manual'` | Cleanup strategy applied when an entry reaches zero references. |
41
+ | `ignoreSearch` | `boolean` | `false` | Ignores the query string when building keys. |
42
+ | `sweepInterval` | `number` | `0` | Interval in milliseconds for active TTL sweeping (`0` = disabled). |
43
+
44
+ ---
45
+
46
+ ## 🧩 Properties
47
+
48
+ | Getter | Type | Description |
49
+ | :--- | :--- | :--- |
50
+ | `size` | `number` | Number of entries in this cache. |
51
+ | `bytes` | `number` | Total size in bytes held by this cache. |
52
+ | `links` | `number` | Number of entries that own at least one child. |
53
+ | `maxItems` | `number` | The configured item budget. |
54
+ | `maxBytes` | `number` | The configured byte budget. |
55
+ | `strategy` | `string` | The active cleanup strategy. |
56
+ | `evictions` | `number` | Number of evicted entries. |
57
+ | `destroyed` | `boolean` | `true` after `destroy()` ran. |
58
+ | `stats` | `MediaCacheStats` | A snapshot of every counter. |
59
+
60
+ ---
61
+
62
+ ## 🛠️ Methods
63
+
64
+ ### `set(url, blob, pinned?, metadata?, parent?)`
65
+
66
+ Stores a blob and returns the created entry.
67
+
68
+ ```js
69
+ cache.set('https://cdn/a.png', blob, true, { width: 100 }, null);
70
+ ```
71
+
72
+ * **Throws:** `TypeError` when `blob` is not a `Blob`, when `pinned` is not a boolean, when `metadata` is not a plain object, or when `parent` is neither a string nor `null`.
73
+
74
+ ### `get(url)` / `has(url)`
75
+
76
+ Reads an entry without changing its reference count.
77
+
78
+ ### `peek(url)`
79
+
80
+ Reads an entry without changing its reference count **and** without touching the `hits`/`misses` counters. Expired entries are reported as missing but are not removed, so a read-only probe never mutates the cache.
81
+
82
+ ```js
83
+ const entry = cache.peek('https://cdn/a.png');
84
+ if (entry) console.log(entry.size, entry.type);
85
+ ```
86
+
87
+ * **Returns:** `MediaCacheEntry | null` — the live entry, or `null` when absent or expired.
88
+ * **Throws:** `TypeError` when `url` is not a string; `Error` when the cache was destroyed.
89
+
90
+ > Use `peek()` to decide *whether* to load a resource. Never use it as a replacement for `get()`/`acquire()`, which are the methods that keep the reference count correct.
91
+
92
+ ### `acquire(url)` / `release(url)`
93
+
94
+ Increments / decrements the reference count. When the count reaches zero the configured strategy is applied.
95
+
96
+ ### `delete(url)`
97
+
98
+ Removes an entry. When the removed entry was the last child of a parent, the parent is removed as well.
99
+
100
+ ### `children(url)`
101
+
102
+ Returns the normalized keys of the entries owned by `url`.
103
+
104
+ ```js
105
+ cache.set(frameUrl, frameBlob, false, {}, 'https://cdn/anim.gif');
106
+ console.log(cache.children('https://cdn/anim.gif')); // [frameUrl]
107
+ ```
108
+
109
+ ### `entries()`
110
+
111
+ Returns a defensive copy of every entry, including a shallow copy of each `metadata` object.
112
+
113
+ ### `pin(url, value?)`
114
+
115
+ Protects (or unprotects) an entry from eviction.
116
+
117
+ ### `prune()` / `clear()` / `destroy()`
118
+
119
+ Removes expired entries, empties the cache, or tears the instance down and releases the shared registry.
120
+
121
+ ---
122
+
123
+ ## 🧠 Strategies
124
+
125
+ | Strategy | Behavior |
126
+ | :--- | :--- |
127
+ | `auto` | The entry is deleted as soon as its reference count reaches zero. |
128
+ | `manual` | The entry stays until `delete`, `clear` or eviction. |
129
+ | `ttl` | The entry expires after `ttl` milliseconds. |
130
+
131
+ ---
132
+
133
+ ## 📊 Statistics
134
+
135
+ ```js
136
+ const { size, bytes, hits, misses, evictions, links, instances, blobs } = cache.stats;
137
+ ```
138
+
139
+ | Field | Description |
140
+ | :--- | :--- |
141
+ | `size` | Entries in this cache. |
142
+ | `bytes` | Total size in bytes held by this cache. |
143
+ | `refs` | Total number of active references. |
144
+ | `hits` / `misses` | Lookup counters. |
145
+ | `evictions` | Entries evicted by the limits. |
146
+ | `links` | Entries that own at least one child. |
147
+ | `instances` | Live cache instances. |
148
+ | `blobs` | Blobs in the shared registry. |
149
+
150
+ ---
151
+
152
+ ## 🔗 Parent / child links
153
+
154
+ Derived blobs (animation frames, thumbnails, transcoded variants) can be attached to the entry that produced them:
155
+
156
+ ```js
157
+ cache.set(originalUrl, originalBlob);
158
+
159
+ frames.forEach((frame, index) => {
160
+ cache.set(`${originalUrl}#frame-${index}`, frame, false, { index }, originalUrl);
161
+ });
162
+
163
+ console.log(cache.links); // 1
164
+ console.log(cache.children(originalUrl).length); // frames.length
165
+ ```
166
+ Deleting the last child also deletes the parent, which keeps the cache consistent without any manual bookkeeping.
@@ -0,0 +1,300 @@
1
+ # 📦 TinyMediaLoader
2
+
3
+ `TinyMediaLoader` is the abstract base class that powers every media loader in the library. It owns the whole lifecycle of a media element — download, cache, progress, state machine and cleanup — while subclasses only describe **how** a specific kind of media is created and decoded.
4
+
5
+ > **Note:** this class cannot be instantiated directly. Use [`TinyImageLoader`](./TinyImageLoader.md), [`TinyFetchLoader`](./TinyFetchLoader.md) or extend it with your own `_createElement` / `_startLoad` implementation.
6
+
7
+ ---
8
+
9
+ ## ✨ Highlights
10
+
11
+ * **Full state machine** — `idle` → `loading` → `loaded` / `error` → `destroyed`.
12
+ * **Streaming with progress** — every chunk is measured, so you get speed, ETA and byte counts for free.
13
+ * **Shared cache** — plug a [`TinyMediaCache`](./TinyMediaCache.md) and the same URL is never downloaded twice.
14
+ * **Mirror registry** — concurrent loads of the same URL share a single network request.
15
+ * **Progressive rendering** — the element is updated while the bytes are still arriving.
16
+ * **Optional `HEAD` pre-flight** — read the response headers before downloading the body.
17
+ * **Typed events** — the entire event surface is described by `MediaLoaderEvents`.
18
+
19
+ ---
20
+
21
+ ## 🚀 Quick start
22
+
23
+ ```js
24
+ import TinyImageLoader from 'tiny-essentials/libs/html/loader/formats/TinyImageLoader';
25
+ import TinyMediaCache from 'tiny-essentials/libs/html/loader/utils/TinyMediaCache';
26
+
27
+ const cache = new TinyMediaCache({ maxItems: 50, maxBytes: 64 * 1024 * 1024 });
28
+
29
+ const loader = new TinyImageLoader({
30
+ src: 'https://picsum.photos/1200/800',
31
+ cache,
32
+ timeout: 30_000,
33
+ });
34
+
35
+ loader.on('progress', (p) => console.log(`${p.percent.toFixed(1)}%`));
36
+ loader.on('error', (error) => console.error(error));
37
+
38
+ await loader.load();
39
+ loader.mount(document.body);
40
+ ```
41
+
42
+ ---
43
+
44
+ ## ⚙️ Options
45
+
46
+ Every option is optional.
47
+
48
+ | Option | Type | Default | Description |
49
+ | :--- | :--- | :--- | :--- |
50
+ | `src` | `string` | `''` | The media source URL. |
51
+ | `timeout` | `number` | `Infinity` | Maximum time in milliseconds before the load is aborted. |
52
+ | `element` | `HTMLElement \| null` | `null` | An existing DOM element to adopt instead of creating a new one. |
53
+ | `autoReload` | `boolean` | `false` | Reloads automatically when the `src` attribute changes externally. |
54
+ | `originalSrc` | `boolean` | `false` | Mirrors the original URL into the `original-src` attribute. |
55
+ | `earlyDecode` | `boolean` | `true` | Decodes dimensions from the response headers using WebCodecs. |
56
+ | `progressive` | `boolean` | `true` | Renders the media while it is still downloading. |
57
+ | `useHeadRequest` | `boolean` | `false` | Performs a `HEAD` request to read the headers before downloading. |
58
+ | `mirror` | `boolean` | `true` | Shares a single download between loaders that request the same URL. |
59
+ | `cache` | `TinyMediaCache \| null` | `null` | A shared memory cache. |
60
+ | `body` | `BodyInit \| undefined` | `undefined` | The request body forwarded to the transport. Ignored by `GET`/`HEAD`, which the Fetch spec forbids from carrying a body. |
61
+
62
+ > **Note:** `body` is only sent when the request is not a `GET`/`HEAD` (for example when a subclass sets `_requestInit.method` to `'POST'`). When `_requestInit.body` is already set, it takes precedence over the `body` option.
63
+
64
+ ---
65
+
66
+ ## 🧩 Properties
67
+
68
+ ### State
69
+
70
+ | Getter | Type | Description |
71
+ | :--- | :--- | :--- |
72
+ | `state` | `string` | The current state machine value. |
73
+ | `isLoading` | `boolean` | `true` while a load is in flight. |
74
+ | `isLoaded` | `boolean` | `true` after a successful load. |
75
+ | `hasError` | `boolean` | `true` when the last load failed. |
76
+ | `aborted` | `boolean` | `true` when the last load was aborted. |
77
+
78
+ ### Source & element
79
+
80
+ | Getter | Type | Description |
81
+ | :--- | :--- | :--- |
82
+ | `src` | `string` | The current source URL (settable). |
83
+ | `internalSrc` | `string` | The URL currently assigned to the element (may be an object URL). |
84
+ | `objectUrl` | `string \| null` | The object URL created for a streamed blob. |
85
+ | `element` | `HTMLElement \| null` | The managed DOM element. |
86
+
87
+ ### Network
88
+
89
+ | Getter | Type | Description |
90
+ | :--- | :--- | :--- |
91
+ | `response` | `Response \| null` | The `GET` response, when one was received. |
92
+ | `headResponse` | `Response \| null` | The `HEAD` response, when `useHeadRequest` is enabled. |
93
+ | `useHeadRequest` | `boolean` | Whether the `HEAD` pre-flight is enabled. |
94
+ | `progressive` | `boolean` | Whether progressive rendering is enabled. |
95
+ | `mirror` | `boolean` | Whether download mirroring is enabled. |
96
+ | `mirrored` | `boolean` | `true` when the payload was shared by another loader. |
97
+ | `signal` | `AbortSignal` | The signal of the current request. |
98
+ | `body` | `BodyInit \| undefined` | The request body forwarded to the transport (settable). |
99
+ | `pending` | `boolean` | `true` while another loader is downloading the same source (mirror registry). |
100
+ | `cached` | `boolean` | `true` when the current source is already available in the shared cache. |
101
+
102
+ ### Metrics
103
+
104
+ | Getter | Type | Description |
105
+ | :--- | :--- | :--- |
106
+ | `progress` | `MediaProgressSnapshot \| null` | The latest progress snapshot. |
107
+ | `metadata` | `MediaMetadata` | The metadata of the last successful load. |
108
+ | `resourceSize` | `number` | The resource size in bytes. |
109
+ | `startTime` | `number` | Timestamp of when the load started. |
110
+ | `endTime` | `number` | Timestamp of when the load finished. |
111
+ | `cacheHint` | `boolean` | Whether the resource is expected to come from the cache. |
112
+ | `cacheAcquired` | `boolean` | Whether the cache entry is currently acquired. |
113
+
114
+ ---
115
+
116
+ ## 🛠️ Methods
117
+
118
+ ### `load()`
119
+
120
+ Starts the download and resolves with the final `MediaMetadata`.
121
+
122
+ ```js
123
+ const metadata = await loader.load();
124
+ console.log(metadata.width, metadata.height, metadata.size);
125
+ ```
126
+
127
+ * **Throws:** `Error` when the loader was destroyed, is already loading, or has no source.
128
+
129
+ ### `reload()`
130
+
131
+ Resets the state machine and loads the current source again.
132
+
133
+ ### `abort()`
134
+
135
+ Cancels the in-flight load.
136
+
137
+ * **Returns:** `boolean` — `true` when a load was cancelled.
138
+
139
+ ### `mount(container)`
140
+
141
+ Appends the managed element to `container`.
142
+
143
+ ```js
144
+ loader.mount(document.querySelector('#stage'));
145
+ ```
146
+
147
+ ### `destroy()`
148
+
149
+ Aborts the request, releases the cache entry, detaches every listener and revokes the object URLs. The instance cannot be reused afterwards.
150
+
151
+ ### `isCached(src = this.src)`
152
+
153
+ Checks whether a URL already has a live payload in the shared cache, without acquiring a reference and without touching the network. The lookup is strictly read-only: it never increments `hits`/`misses`, never revives an expired entry and never creates an object URL.
154
+
155
+ ```js
156
+ if (loader.isCached('https://cdn/a.png')) {
157
+ console.log('no network request needed');
158
+ }
159
+ ```
160
+
161
+ * **Returns:** `boolean` — `true` when the cache holds a live entry.
162
+ * **Throws:** `TypeError` when `src` is not a string; `Error` when the cache was destroyed.
163
+
164
+ ### `peekCache(src = this.src)`
165
+
166
+ Reads the cache entry of a URL without acquiring a reference, so it can be inspected (size, type, metadata) before deciding to load it.
167
+
168
+ ```js
169
+ const entry = loader.peekCache('https://cdn/a.png');
170
+ if (entry) console.log(entry.size, entry.type, entry.metadata);
171
+ ```
172
+
173
+ * **Returns:** `MediaCacheEntry | null` — the cached entry, or `null`.
174
+ * **Throws:** `TypeError` when `src` is not a string; `Error` when the cache was destroyed.
175
+
176
+ ---
177
+
178
+ ## 📡 Events
179
+
180
+ | Event | Payload | Description |
181
+ | :--- | :--- | :--- |
182
+ | `loadstart` | `{ src }` | A load just started. |
183
+ | `progress` | `MediaProgressSnapshot` | Download progress. |
184
+ | `chunk` | `{ chunk, loaded, total }` | A binary chunk was received. |
185
+ | `metadata` | `(metadata, type)` | Partial or full metadata was resolved. |
186
+ | `response` | `Response` | The `GET` response was received. |
187
+ | `headResponse` | `Response` | The `HEAD` response was received. |
188
+ | `cachemiss` | `MediaCacheEntry` | The resource was not in the cache. |
189
+ | `cachehit` | `MediaCacheEntry` | The resource was served from the cache. |
190
+ | `mirror` | `MediaMirrorPayload` | The download is being shared with another loader. |
191
+ | `statechange` | `{ state, previous, src }` | The state machine transitioned. |
192
+ | `srcchange` | `{ src, previousSrc }` | The source URL changed. |
193
+ | `srctransition` | `{ src, internalSrc }` | The element swapped between a network and an object URL. |
194
+ | `timeout` | `{ src, timeout }` | The configured timeout was reached. |
195
+ | `abort` | `{ src }` | The load was aborted. |
196
+ | `loaded` | `MediaMetadata` | The media was fully loaded. |
197
+ | `loadend` | `{ src, state }` | The load finished, successfully or not. |
198
+ | `error` | `Error` | An unrecoverable error occurred. |
199
+ | `destroy` | — | The loader was destroyed. |
200
+
201
+ ---
202
+
203
+ ## 🧬 Metadata
204
+
205
+ ```ts
206
+ type MediaMetadata = {
207
+ src: string;
208
+ type: string;
209
+ width: number;
210
+ height: number;
211
+ duration: number;
212
+ size: number;
213
+ fromCache: boolean;
214
+ loadTime: number;
215
+ timestamp: number;
216
+ animation: AnimationProbeData | null;
217
+ httpHeaders?: Record<string, string>;
218
+ };
219
+ ```
220
+
221
+ ---
222
+
223
+ ## 📤 Request body
224
+
225
+ The `body` option forwards a payload to the transport, which is required by endpoints that only accept `POST`, `PUT` or `PATCH` requests. It is ignored by `GET` and `HEAD`, because the Fetch specification forbids those methods from carrying a body.
226
+
227
+ ```js
228
+ const loader = new TinyFetchLoader({
229
+ src: 'https://api.example.com/render',
230
+ init: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
231
+ body: JSON.stringify({ width: 800, height: 450 }),
232
+ responseType: 'blob',
233
+ });
234
+
235
+ await loader.load();
236
+ ```
237
+
238
+ * When `init.body` is already set, it takes precedence over the `body` option.
239
+ * The value is exposed through the `body` getter/setter, so it can be replaced between loads:
240
+
241
+ ```js
242
+ loader.body = JSON.stringify({ width: 1920, height: 1080 });
243
+ await loader.reload();
244
+ ```
245
+
246
+ ---
247
+
248
+ ## 🪞 Mirror registry
249
+
250
+ When two loaders request the same URL at the same time, only one connection is opened. The second loader attaches to the running download, replays the events emitted so far and receives the very same `Blob` instance.
251
+
252
+ ```js
253
+ const a = new TinyImageLoader({ src, mirror: true });
254
+ const b = new TinyImageLoader({ src, mirror: true });
255
+
256
+ b.on('mirror', (info) => console.log(`sharing with ${info.mirrors} loader(s)`));
257
+
258
+ await Promise.all([a.load(), b.load()]);
259
+ console.log(b.mirrored); // true
260
+ ```
261
+
262
+ Set `mirror: false` to always open a dedicated request.
263
+
264
+ ---
265
+
266
+ ## 🧩 Extending
267
+
268
+ Subclasses must implement the protected hooks below.
269
+
270
+ | Hook | Description |
271
+ | :--- | :--- |
272
+ | `static get tagName` | The uppercase tag name of the managed element. |
273
+ | `_createElement()` | Creates the DOM element. |
274
+ | `_startLoad()` | Performs the download and resolves when the media is ready. |
275
+ | `_getMetadataDetails()` | Returns the media specific metadata fields. |
276
+ | `_abort()` | Cancels the underlying operation. |
277
+ | `_cleanup()` | Releases the subclass resources. |
278
+
279
+ ```js
280
+ import TinyMediaLoader from 'tiny-essentials/libs/html/loader/TinyMediaLoader';
281
+
282
+ class TinyJsonLoader extends TinyMediaLoader {
283
+ static get tagName() {
284
+ return 'DIV';
285
+ }
286
+
287
+ async _startLoad() {
288
+ const blob = await this._downloadStream(this.src);
289
+ this.data = JSON.parse(await blob.text());
290
+ }
291
+
292
+ _getMetadataDetails() {
293
+ return { type: 'application/json', width: 0, height: 0, duration: 0, size: 0 };
294
+ }
295
+
296
+ _createElement() {
297
+ return document.createElement('div');
298
+ }
299
+ }
300
+ ```
@@ -228,6 +228,9 @@ To ensure a valid custom message object, you must provide all the following keys
228
228
  | `itemValid` | Message for a successful check. |
229
229
  | `itemInvalid` | Message for a failed check. |
230
230
  | `itemMissing` | Message showing the missing path. |
231
+ | `itemSkipped` | Message for a `null` target (excluded subpath). |
232
+ | `itemPattern` | Message for a successful wildcard match. |
233
+ | `itemInvalidTarget` | Message for an invalid target, with the reason code. |
231
234
  | `successHeader` | Final message on total success. |
232
235
  | `failureHeader` | Final message on failure. |
233
236
  | `errorLoad` | Error if `package.json` isn't loaded. |
@@ -271,8 +274,60 @@ FAILURE: 1 export(s) are missing or invalid.
271
274
  ## ⚠️ Error Handling
272
275
 
273
276
  The validator provides specific feedback for common issues:
277
+
274
278
  1. **`errorLoad`**: Attempted to validate before calling `.start()`.
275
279
  2. **`noExports`**: The `package.json` exists, but the `"exports"` field is missing.
276
280
  3. **`errorNotFound`**: The file at `packageJsonPath` does not exist.
277
281
  4. **`errorParse`**: The `package.json` file contains syntax errors.
278
282
  5. **`errorUnexpected`**: Any other error (e.g., permission issues).
283
+
284
+ ---
285
+
286
+ ## 🧭 Node.js `exports` rules
287
+
288
+ Starting with version 1.29.2 the validator follows the [official Node.js documentation](https://nodejs.org/api/packages.html#package-entry-points) for the `exports` field.
289
+
290
+ ### Wildcard targets
291
+
292
+ A target that contains `*` is expanded against the file system. The validation succeeds when at least one file matches the pattern:
293
+
294
+ ```json
295
+ {
296
+ "exports": {
297
+ "./css/*.css": "./dist/v1/css/*.css"
298
+ }
299
+ }
300
+ ```
301
+
302
+ ### `null` targets
303
+
304
+ A `null` target excludes a subpath from a broader pattern. It is reported as `itemSkipped` and never fails the validation:
305
+
306
+ ```json
307
+ {
308
+ "exports": {
309
+ "./internal/*": null
310
+ }
311
+ }
312
+ ```
313
+
314
+ ### Fallback arrays
315
+
316
+ An array is treated as an ordered list of fallbacks. Every entry is validated, and an empty array is reported as `EMPTY_ARRAY`.
317
+
318
+ ### Rejected targets
319
+
320
+ | Reason | Description |
321
+ | :--- | :--- |
322
+ | `EMPTY_TARGET` | The target is an empty string. |
323
+ | `INVALID_SEGMENT` | The target contains `\`, `.` or `..` segments. |
324
+ | `INVALID_CHARACTER` | The target contains `?` or `#`. |
325
+ | `URL_SCHEME_NOT_ALLOWED` | The target looks like a URL (`node:fs`, `https://…`). |
326
+ | `TARGET_NOT_RELATIVE` | The target does not start with `./`. |
327
+ | `INVALID_PATTERN` | The target contains `**`. |
328
+ | `PATTERN_NO_MATCH` | The wildcard pattern matched no file. |
329
+ | `DIRECTORY_NOT_FOUND` | The directory that holds the pattern does not exist. |
330
+ | `NESTED_SUBPATH_KEY` | A subpath key was declared inside another subpath. |
331
+ | `INVALID_SUBPATH_KEY` | A key starts with `.` but is not `.` or `./…`. |
332
+ | `INVALID_CONDITION` | A condition key contains `,`, `/` or `\`. |
333
+ | `EMPTY_KEY` | A key is an empty string. |
@@ -2,10 +2,6 @@
2
2
 
3
3
  > Extract solid colors from PSD layers, generate separated images per filter, and get pixel statistics — in **Node.js** and the **Browser**, with the exact same API.
4
4
 
5
- [![Node.js](https://img.shields.io/badge/Node.js-%E2%89%A518-339933?logo=node.js&logoColor=white)](#-nodejs-usage)
6
- [![Browser](https://img.shields.io/badge/Browser-ESM-4285F4?logo=googlechrome&logoColor=white)](#-browser-usage)
7
- [![ag-psd](https://img.shields.io/badge/built%20on-ag--psd-8A2BE2)](#-how-it-works)
8
-
9
5
  ---
10
6
 
11
7
  ## 📖 Table of Contents