tiny-essentials 1.26.3 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.github/workflows/node.js.yml +1 -1
  2. package/README.md +0 -1
  3. package/changelog/1/26/4.md +10 -0
  4. package/changelog/1/27/0.md +24 -0
  5. package/dist/v1/ColorSafeStringify.min.js +1 -1
  6. package/dist/v1/TinyAdvancedRaffle.min.js +1 -1
  7. package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
  8. package/dist/v1/TinyAnalogClock.min.js +1 -1
  9. package/dist/v1/TinyArrayComparator.min.js +1 -1
  10. package/dist/v1/TinyArrayPaginator.min.js +1 -1
  11. package/dist/v1/TinyBasicsEs.min.js +1 -1
  12. package/dist/v1/TinyClassManager.min.js +1 -1
  13. package/dist/v1/TinyClipboard.min.js +1 -1
  14. package/dist/v1/TinyColorConverter.min.js +1 -1
  15. package/dist/v1/TinyColorValidator.min.js +1 -1
  16. package/dist/v1/TinyCookieConsent.min.js +1 -1
  17. package/dist/v1/TinyDayNightCycle.min.js +1 -1
  18. package/dist/v1/TinyDomReadyManager.min.js +1 -1
  19. package/dist/v1/TinyDragDropDetector.min.js +1 -1
  20. package/dist/v1/TinyDragger.min.js +1 -1
  21. package/dist/v1/TinyElementObserver.min.js +1 -1
  22. package/dist/v1/TinyEssentials.min.js +1 -1
  23. package/dist/v1/TinyEvents.min.js +1 -1
  24. package/dist/v1/TinyGamepad.min.js +1 -1
  25. package/dist/v1/TinyHtml.min.js +1 -1
  26. package/dist/v1/TinyI18.min.js +1 -1
  27. package/dist/v1/TinyIframeEvents.min.js +1 -1
  28. package/dist/v1/TinyInventory.min.js +1 -1
  29. package/dist/v1/TinyInventoryTrader.min.js +1 -1
  30. package/dist/v1/TinyLevelUp.min.js +1 -1
  31. package/dist/v1/TinyLoadingScreen.min.js +1 -1
  32. package/dist/v1/TinyLocalStorage.min.js +1 -1
  33. package/dist/v1/TinyMaInSys.min.js +1 -1
  34. package/dist/v1/TinyMediaPlayer.min.js +1 -0
  35. package/dist/v1/TinyNeedBar.min.js +1 -1
  36. package/dist/v1/TinyNewWinEvents.min.js +1 -1
  37. package/dist/v1/TinyNotifications.min.js +1 -1
  38. package/dist/v1/TinyNotifyCenter.min.js +1 -1
  39. package/dist/v1/TinyPromiseQueue.min.js +1 -1
  40. package/dist/v1/TinyRadioFm.min.js +1 -0
  41. package/dist/v1/TinyRateLimiter.min.js +1 -1
  42. package/dist/v1/TinySimpleDice.min.js +1 -1
  43. package/dist/v1/TinySmartScroller.min.js +1 -1
  44. package/dist/v1/TinyTextDiffer.min.js +1 -1
  45. package/dist/v1/TinyTextRangeEditor.min.js +1 -1
  46. package/dist/v1/TinyTextarea.min.js +1 -1
  47. package/dist/v1/TinyTimeout.min.js +1 -1
  48. package/dist/v1/TinyToastNotify.min.js +1 -1
  49. package/dist/v1/TinyUploadClicker.min.js +1 -1
  50. package/dist/v1/UltraRandomMsgGen.min.js +1 -1
  51. package/dist/v1/basics/index.cjs +5 -0
  52. package/dist/v1/basics/index.d.mts +5 -1
  53. package/dist/v1/basics/index.mjs +2 -1
  54. package/dist/v1/basics/mediaContent.cjs +644 -0
  55. package/dist/v1/basics/mediaContent.d.mts +296 -0
  56. package/dist/v1/basics/mediaContent.mjs +557 -0
  57. package/dist/v1/build/TinyMediaPlayer.cjs +7 -0
  58. package/dist/v1/build/TinyMediaPlayer.d.mts +3 -0
  59. package/dist/v1/build/TinyMediaPlayer.mjs +2 -0
  60. package/dist/v1/build/TinyRadioFm.cjs +7 -0
  61. package/dist/v1/build/TinyRadioFm.d.mts +3 -0
  62. package/dist/v1/build/TinyRadioFm.mjs +2 -0
  63. package/dist/v1/index.cjs +9 -0
  64. package/dist/v1/index.d.mts +7 -1
  65. package/dist/v1/index.mjs +5 -2
  66. package/dist/v1/libs/ColorSafeStringify.d.mts +1 -1
  67. package/dist/v1/libs/TinyAfterScrollWatcher.cjs +16 -3
  68. package/dist/v1/libs/TinyAfterScrollWatcher.d.mts +7 -3
  69. package/dist/v1/libs/TinyAfterScrollWatcher.mjs +13 -3
  70. package/dist/v1/libs/TinyArrayComparator.cjs +14 -11
  71. package/dist/v1/libs/TinyArrayComparator.d.mts +22 -19
  72. package/dist/v1/libs/TinyArrayComparator.mjs +14 -11
  73. package/dist/v1/libs/TinyArrayPaginator.cjs +23 -16
  74. package/dist/v1/libs/TinyArrayPaginator.d.mts +57 -30
  75. package/dist/v1/libs/TinyArrayPaginator.mjs +22 -16
  76. package/dist/v1/libs/TinyClassManager/TinyPluginInliner.cjs +265 -0
  77. package/dist/v1/libs/TinyClassManager/TinyPluginInliner.d.mts +92 -0
  78. package/dist/v1/libs/TinyClassManager/TinyPluginInliner.mjs +223 -0
  79. package/dist/v1/libs/TinyClassManager.cjs +124 -23
  80. package/dist/v1/libs/TinyClassManager.d.mts +33 -13
  81. package/dist/v1/libs/TinyClassManager.mjs +108 -23
  82. package/dist/v1/libs/TinyColorValidator.d.mts +16 -16
  83. package/dist/v1/libs/TinyDragDropDetector.cjs +9 -34
  84. package/dist/v1/libs/TinyDragDropDetector.d.mts +7 -21
  85. package/dist/v1/libs/TinyDragDropDetector.mjs +9 -24
  86. package/dist/v1/libs/TinyDragger.cjs +5 -5
  87. package/dist/v1/libs/TinyDragger.d.mts +4 -3
  88. package/dist/v1/libs/TinyDragger.mjs +5 -5
  89. package/dist/v1/libs/TinyElementObserver.cjs +6 -21
  90. package/dist/v1/libs/TinyElementObserver.d.mts +6 -15
  91. package/dist/v1/libs/TinyElementObserver.mjs +6 -20
  92. package/dist/v1/libs/TinyEvents.cjs +7 -0
  93. package/dist/v1/libs/TinyEvents.d.mts +4 -0
  94. package/dist/v1/libs/TinyEvents.mjs +6 -0
  95. package/dist/v1/libs/TinyGamepad.cjs +4 -6
  96. package/dist/v1/libs/TinyGamepad.d.mts +1 -1
  97. package/dist/v1/libs/TinyHtml/TinyHtmlIcon.d.mts +1 -1
  98. package/dist/v1/libs/TinyHtml/TinyHtmlTemplate.d.mts +1 -1
  99. package/dist/v1/libs/TinyHtml/TinyHtmlTextarea.d.mts +1 -1
  100. package/dist/v1/libs/TinyHtml/index.d.mts +1 -1
  101. package/dist/v1/libs/TinyHtml.cjs +72 -98
  102. package/dist/v1/libs/TinyHtml.d.mts +15 -32
  103. package/dist/v1/libs/TinyHtml.mjs +53 -77
  104. package/dist/v1/libs/TinyInventory.d.mts +1 -1
  105. package/dist/v1/libs/TinyMediaPlayer.cjs +844 -0
  106. package/dist/v1/libs/TinyMediaPlayer.d.mts +306 -0
  107. package/dist/v1/libs/TinyMediaPlayer.mjs +755 -0
  108. package/dist/v1/libs/TinyRadioFm.cjs +1378 -0
  109. package/dist/v1/libs/TinyRadioFm.d.mts +554 -0
  110. package/dist/v1/libs/TinyRadioFm.mjs +1137 -0
  111. package/dist/v1/libs/TinySmartScroller.cjs +39 -26
  112. package/dist/v1/libs/TinySmartScroller.d.mts +16 -62
  113. package/dist/v1/libs/TinySmartScroller.mjs +35 -26
  114. package/dist/v1/libs/UltraRandomMsgGen.d.mts +7 -7
  115. package/dist/v1/libs/examples/TinyClassManager.cjs +43 -41
  116. package/dist/v1/libs/examples/TinyClassManager.mjs +37 -37
  117. package/docs/v1/README.md +6 -0
  118. package/docs/v1/basics/mediaContent.md +131 -0
  119. package/docs/v1/libs/TinyClassManager/TinyPluginInliner.md +107 -0
  120. package/docs/v1/libs/TinyClassManager.md +69 -70
  121. package/docs/v1/libs/TinyDragDropDetector.md +5 -9
  122. package/docs/v1/libs/TinyElementObserver.md +3 -6
  123. package/docs/v1/libs/TinyEvents.md +1 -1
  124. package/docs/v1/libs/TinyHtml.md +0 -29
  125. package/docs/v1/libs/TinyMediaPlayer.md +100 -0
  126. package/docs/v1/libs/TinyRadioFm.md +141 -0
  127. package/docs/v1/libs/TinySmartScroller.md +3 -3
  128. package/package.json +40 -22
@@ -10,51 +10,51 @@ class EntityCore {
10
10
  }
11
11
  }
12
12
  // 2. Creating Dependent DLCs
13
- const Health = {
14
- name: 'Health',
15
- dependencies: [], // No dependencies, works just with the Core
16
- /** @param {typeof EntityCore} Base */
17
- apply: (Base) => class HealthModule extends Base {
18
- /** @param {string} id */
19
- constructor(id) {
20
- super(id);
21
- this.hp = 100;
22
- }
23
- /**
24
- * Deducts health from the entity.
25
- * @param {number} amount - The amount of raw damage to deal.
26
- */
27
- takeDamage(amount) {
28
- this.hp -= amount;
29
- console.log(`Took ${amount} damage. HP left: ${this.hp}`);
30
- }
31
- },
13
+ /** @param {typeof EntityCore} Base */
14
+ const applyHealth = (Base) => class HealthModule extends Base {
15
+ static _tinyDepName = 'Health';
16
+ /** @type {string[]} */
17
+ static _tinyDeps = []; // No dependencies, works just with the Core
18
+ /** @param {string} id */
19
+ constructor(id) {
20
+ super(id);
21
+ this.hp = 100;
22
+ }
23
+ /**
24
+ * Deducts health from the entity.
25
+ * @param {number} amount - The amount of raw damage to deal.
26
+ */
27
+ takeDamage(amount) {
28
+ this.hp -= amount;
29
+ console.log(`Took ${amount} damage. HP left: ${this.hp}`);
30
+ }
32
31
  };
33
32
  // 3. Creating Independent DLCs
34
- const Armor = {
35
- name: 'Armor',
36
- dependencies: ['Health'], // Strictly requires the Health module to be installed first
37
- /** @param {ReturnType<typeof Health.apply>} Base */
38
- apply: (Base) => class ArmorModule extends Base {
39
- /** @param {string} id */
40
- constructor(id) {
41
- super(id);
42
- this.armor = 50;
43
- }
33
+ /** @param {ReturnType<typeof applyHealth>} Base */
34
+ const applyArmor = (Base) => class ArmorModule extends Base {
35
+ static _tinyDepName = 'Armor';
36
+ static _tinyDeps = ['Health']; // Strictly requires the Health module to be installed first
37
+ /** @param {string} id */
38
+ constructor(id) {
39
+ super(id);
40
+ this.armor = 50;
41
+ }
42
+ /**
43
+ * Deducts health, factoring in damage mitigation provided by armor.
44
+ * @param {number} amount - The initial damage before armor reduction.
45
+ */
46
+ takeDamage(amount) {
44
47
  /**
45
- * Deducts health, factoring in damage mitigation provided by armor.
46
- * @param {number} amount - The initial damage before armor reduction.
48
+ * Calculated damage after armor mitigation is applied, ensuring it never drops below 0.
47
49
  */
48
- takeDamage(amount) {
49
- const reducedDamage = Math.max(0, amount - this.armor * 0.1);
50
- super.takeDamage(reducedDamage);
51
- }
52
- },
50
+ const reducedDamage = Math.max(0, amount - this.armor * 0.1);
51
+ super.takeDamage(reducedDamage);
52
+ }
53
53
  };
54
54
  // 4. Building the final class
55
55
  try {
56
56
  const CoreTest = new TinyClassManager(EntityCore);
57
- const FullyArmoredEntity = CoreTest.use(Health).use(Armor).build();
57
+ const FullyArmoredEntity = CoreTest.insert(applyHealth).insert(applyArmor).build();
58
58
  const myEntity = new FullyArmoredEntity('Player_1');
59
59
  myEntity.takeDamage(30);
60
60
  console.log(`Armor value: ${myEntity.armor}`);
package/docs/v1/README.md CHANGED
@@ -20,6 +20,7 @@ Here, you can navigate through different sections of the project. Below are the
20
20
  - 🖼️ **[Html](./basics/html.md)** — Utilities for handling DOM element interactions like collision detection and basic element manipulation.
21
21
  - 📺 **[FullScreen](./basics/fullScreen.md)** — A complete fullscreen API manager with detection, event handling, and cross-browser compatibility.
22
22
  - 🧱 **[Collision](./basics/collision.md)** — Full-featured rectangle collision detection system with directional analysis, depth calculation, and center offset metrics.
23
+ - 🎵 **[MediaContent](./basics/mediaContent.md)** — A comprehensive media processing module for downloading audio, extracting rich ID3 metadata, and managing image blobs for album art.
23
24
 
24
25
  ### 2. **`libs/`**
25
26
  - 🗂️ **[TinyPromiseQueue](./libs/TinyPromiseQueue.md)** — A class that allows sequential execution of asynchronous tasks, supporting task delays, cancellation, and queue management.
@@ -62,7 +63,12 @@ Here, you can navigate through different sections of the project. Below are the
62
63
  * 🕒 **[TinyAnalogClock](./libs/TinyAnalogClock.md)** — A lightweight analog clock engine for managing time-based rotations, supporting custom offsets, smooth transitions, and easy binding to CSS variables rendering.
63
64
  * 🔍 **[TinyArrayComparator](./libs/TinyArrayComparator.md)** — A lightweight, highly optimized JavaScript utility class designed to compare two arrays and efficiently detect which items were **added** or **deleted**.
64
65
  * 🧠 **[TinyMamdaniInferenceSystem](./libs/TinyMamdaniInferenceSystem.md)** — A implementation of a Mamdani Inference System, allowing you to model logic using trapezoidal membership functions.
66
+ * ⏯️ **[TinyMediaPlayer](./libs/TinyMediaPlayer.md)** — A universal media player architecture utilizing an adapter pattern to orchestrate multiple platform APIs, featuring robust playlist management, playback controls, and weighted random selection.
67
+
68
+ ### 2.1. **`libs/TinyClassManager`**
69
+
65
70
  * ⚙️ **[TinyClassManager](./libs/TinyClassManager.md)** — A lightweight, immutable manager designed to linearly compose a base class with multiple modular plugins, featuring automatic dependency verification and duplicate conflict protection.
71
+ * 🛠️ **[TinyPluginInliner](./libs/TinyClassManager/TinyPluginInliner.md)** — A powerful build-time utility that inlines plugin code into a single bundle, automatically rewriting import paths and hoisting dependencies for seamless deployment.
66
72
 
67
73
  ### 3. **`fileManager/`**
68
74
  * 📁 **[Main](./fileManager/main.md)** — A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
@@ -0,0 +1,131 @@
1
+ # 🎵 Media Content Module
2
+
3
+ Welcome to the documentation for the media content module! This module is the heart of the media processing pipeline, responsible for downloading audio, extracting rich metadata (ID3 tags), managing memory for image blobs, and preparing finalized `MediaContent` objects for the radio system. 🚀
4
+
5
+ ---
6
+
7
+ ## 📦 Data Structures & Types
8
+
9
+ These definitions ensure type safety and provide a clear blueprint for how media information is structured throughout the application.
10
+
11
+ ### 🎼 Core Metadata Types
12
+
13
+ | Type | Description |
14
+ | :--- | :--- |
15
+ | `MediaContent` | **The Final Object.** A combination of `MediaContentBase` and `MediaContentMetadata`. This is what the system uses for playback. |
16
+ | `MediaContentBase` | The essential properties: `id`, `title`, `artist`, `duration`, `url`, and `weight`. |
17
+ | `MediaContentMetadata` | Rich metadata modeled after `music-metadata`. Includes `album`, `genre`, `year`, `track` info, etc. |
18
+ | `MediaNumber` | A structure for indexing: `{ no: number \| null, of: number \| null }`. |
19
+ | `UnknownArtistGetter` | A type that can be a `string` or a function returning a `string`. |
20
+
21
+ ### 🖼️ Image & Picture Types
22
+
23
+ | Type | Description |
24
+ | :--- | :--- |
25
+ | `IPicture` | Represents an image attachment (like album art). |
26
+ | `IPictureTemplate<T>` | A generic template for pictures, where `T` is the data type (`Uint8Array` or `string`). |
27
+
28
+ ### 🚦 Progress & Error Types
29
+
30
+ | Type | Description |
31
+ | :--- | :--- |
32
+ | `LoadingMediaProgress` | Tracks the current state: `status` ('loading' or 'success'), `stage`, and the `url`. |
33
+ | `LoadingProgressStage` | Stages: `INITIALIZING` ➡️ `DOWNLOADING` ➡️ `METADATA_LOADED` ➡️ `EXTRACTING_ID3` ➡️ `COMPLETE`. |
34
+ | `LoadingErrorStage` | Error stages: `INITIALIZING`, `DOWNLOADING`, `METADATA`, `ID3`, or `UNKNOWN`. |
35
+
36
+ ---
37
+
38
+ ## ❌ Custom Errors
39
+
40
+ ### `MediaLoadingError`
41
+ Used to provide specific context when something goes wrong during the media preparation lifecycle.
42
+
43
+ * **Properties:**
44
+ * `message`: Human-readable error description.
45
+ * `url`: The URL that caused the failure.
46
+ * `stage`: The `LoadingErrorStage` where the error occurred.
47
+
48
+ ---
49
+
50
+ ## 🛠️ Utility Functions
51
+
52
+ ### Factory & Validation
53
+
54
+ | Function | Description |
55
+ | :--- | :--- |
56
+ | `getMediaContentBase` | Returns a fresh, empty `MediaContentBase` object. |
57
+ | `getMediaContentMetadata` | Returns a fresh, empty `MediaContentMetadata` object. |
58
+ | `valMediaContentMetadata` | Performs strict validation on a full `MediaContentMetadata` object. |
59
+ | `valMediaContentMetadataPartial` | Performs validation on a partial metadata object (useful for patches/updates). |
60
+
61
+ ### Data & Memory Management
62
+
63
+ | Function | Description |
64
+ | :--- | :--- |
65
+ | `generateSimpleHash` | Generates a deterministic 8-character ID from a string using SHA-1. |
66
+ | `convertToBlobUrl` | Converts `Uint8Array` or Base64 strings into high-performance Blob URLs. It also tracks usage to prevent memory leaks. |
67
+ | `blobUrlToBase64` | Converts a `blob:` URL back into a Base64 Data URL. Useful for exporting data. |
68
+ | `revokeContentUrls` | Safely cleans up memory by revoking Blob URLs associated with a `MediaContent` object. **Call this when media is no longer needed!** 🧹 |
69
+
70
+ ---
71
+
72
+ ## 🚀 Main Operations
73
+
74
+ ### `extractMediaId3Tags`
75
+ Downloads an audio file and uses a provided parser to extract its internal metadata.
76
+
77
+ * **Parameters:**
78
+ * `url` (string): The full URL of the audio file.
79
+ * `parseFile` (Function): A callback that accepts a `Blob` and returns a promise with metadata.
80
+ * **Returns:** `Promise<MediaContentMetadata>`
81
+ * **Throws:** `TypeError` for invalid arguments or `Error` for network/parsing failures.
82
+
83
+ ---
84
+
85
+ ### `parseMediaMetadata`
86
+ **The Primary Factory Method.** This is the main entry point for turning a URL or an Audio element into a fully prepared `MediaContent` object. It handles the heavy lifting: downloading, metadata extraction, fallback logic, and progress reporting.
87
+
88
+ #### 📝 Parameters
89
+
90
+ | Parameter | Type | Description |
91
+ | :--- | :--- | :--- |
92
+ | `source` | `string \| HTMLMediaElement` | The audio source (URL or Audio object). |
93
+ | `defaultMetadata` | `Partial<MediaContent>` | Default data for the metadata. |
94
+ | `metadata` | `Partial<MediaContent>` | Manual overrides for the metadata (highest priority). |
95
+ | `parseFile` | `ParseMediaContentMetadata` | The library function used to parse the file. |
96
+ | `callbacks` | `Object` | Contains `onProgress` and `onError` listeners. |
97
+ | `unknownArtist` | `UnknownArtistGetter` | Logic to determine the artist if none is found. |
98
+
99
+ #### 💡 Usage Examples
100
+
101
+ **1. Using a simple URL with manual overrides:**
102
+ ```javascript
103
+ import { parseMediaMetadata } from './mediaContent.mjs';
104
+ import { parseBlob } from 'music-metadata';
105
+
106
+ // Note: We pass empty objects for default/manual if we want to jump straight to the parser
107
+ const track = await parseMediaMetadata(
108
+ '/assets/song.mp3',
109
+ {},
110
+ { title: 'Manual Override' },
111
+ parseBlob
112
+ );
113
+
114
+ console.log(track.title); // "Manual Override"
115
+ ```
116
+
117
+ **2. Using an existing Audio object with progress tracking:**
118
+ ```javascript
119
+ const audio = new Audio('/assets/song.mp3');
120
+
121
+ const track = await parseMediaMetadata(
122
+ audio,
123
+ {},
124
+ {},
125
+ parseBlob,
126
+ {
127
+ onProgress: (p) => console.log(`[${p.stage}] ${p.status}`),
128
+ onError: (e) => console.error(`Error at ${e.stage}: ${e.error.message}`)
129
+ }
130
+ );
131
+ ```
@@ -0,0 +1,107 @@
1
+ # 🛠️ TinyPluginInliner
2
+
3
+ `TinyPluginInliner` is a powerful **build-time utility** designed specifically to complement the `TinyClassManager`.
4
+
5
+ When using `TinyClassManager`, you compose classes using a mixin pattern via the `.insert()` method. While this is incredibly flexible for development, it creates a challenge for deployment: you end up with many small, interconnected files and complex import paths.
6
+
7
+ **`TinyPluginInliner` solves this by "inlining" your plugins directly into a single, self-contained bundle.** It automates the process of fetching plugin code, rewriting their internal import paths, and hoisting dependencies to the top of your final file. 🚀
8
+
9
+ ---
10
+
11
+ ## ✨ Key Features
12
+
13
+ * **📦 Automatic Inlining:** It scans your entry file for `.insert(PluginName)` calls and replaces them with the actual source code of the plugin.
14
+ * **🔗 Smart Path Rewriting:** It automatically corrects `import` statements inside your plugins so they point to the correct relative locations in your `dist` folder.
15
+ * **📚 JSDoc Support:** It doesn't just fix code; it also rewrites `import()` calls and `@typedef` paths inside JSDoc comments, ensuring your IDE (like VS Code) keeps providing perfect autocomplete in the bundled version.
16
+ * **🌐 NPM Integration:** It can reach into your `node_modules`, grab an NPM package used by a plugin, and inline it into your bundle, even fixing that package's internal relative paths!
17
+ * **🔄 Root Remapping:** Through `rootReplacement`, you can tell the inliner to automatically swap directory names (e.g., changing all `src/` imports to `dist/` imports).
18
+ * **🔝 Dependency Hoisting:** It collects all necessary imports and type definitions from all plugins and moves them to the very top of the generated file for a clean, valid structure.
19
+
20
+ ---
21
+
22
+ ## ⚙️ Configuration
23
+
24
+ When creating an instance of `TinyPluginInliner`, you provide an `InlinerConfig` object:
25
+
26
+ | Property | Type | Description |
27
+ | :--- | :--- | :--- |
28
+ | `entryPoint` | `string` | The absolute or relative path to your main file (where you call `.insert()`). |
29
+ | `outDir` | `string` | The directory where the final bundle will be saved. |
30
+ | `outFileName` | `string` | The name of the generated bundle file (e.g., `bundle.mjs`). |
31
+ | `rootReplacement` | `Object` | *(Optional)* An object `{ from: string, to: string }` to remap project roots. |
32
+
33
+ ---
34
+
35
+ ## 🚀 How to Use
36
+
37
+ ### 1. Develop your Plugins
38
+ Your plugins must follow the `TinyClassManager` requirement of having static properties for identification.
39
+
40
+ ```javascript
41
+ // src/plugins/LoggerPlugin.mjs
42
+ export class LoggerPlugin {
43
+ static _tinyDepName = 'Logger';
44
+ static _tinyDeps = [];
45
+
46
+ constructor(Base) {
47
+ super();
48
+ this.log = (msg) => console.log(`[LOG]: ${msg}`);
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### 2. Create your Entry Point
54
+ This is the file that uses the `TinyClassManager` to compose your final class.
55
+
56
+ ```javascript
57
+ // src/main.mjs
58
+ import TinyClassManager from './libs/TinyClassManager.mjs';
59
+ import { LoggerPlugin } from './plugins/LoggerPlugin.mjs';
60
+
61
+ class BaseUser {}
62
+
63
+ // The Inliner will look for this specific pattern!
64
+ const User = new TinyClassManager(BaseUser)
65
+ .insert(LoggerPlugin)
66
+ .build();
67
+
68
+ export default User;
69
+ ```
70
+
71
+ ### 3. Run the Inliner
72
+ Create a build script (e.g., `build.mjs`) to trigger the process.
73
+
74
+ ```javascript
75
+ import TinyPluginInliner from './libs/TinyClassManager/TinyPluginInliner.mjs';
76
+
77
+ const builder = new TinyPluginInliner({
78
+ entryPoint: 'src/main.mjs',
79
+ outDir: 'dist',
80
+ outFileName: 'app.bundle.mjs',
81
+ // This ensures that if a plugin imports from 'src/utils',
82
+ // it will be rewritten to 'dist/utils' in the bundle.
83
+ rootReplacement: {
84
+ from: 'src',
85
+ to: 'dist',
86
+ },
87
+ });
88
+
89
+ builder.build();
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 🔄 Before vs. After
95
+
96
+ ### 📝 Before (Development)
97
+ Your project is a web of files:
98
+ * `src/main.mjs` $\rightarrow$ imports `LoggerPlugin`
99
+ * `src/plugins/LoggerPlugin.mjs` $\rightarrow$ imports `src/utils/helper.mjs`
100
+
101
+ ### 📦 After (Production Bundle)
102
+ The `dist/app.bundle.mjs` is a single, massive, but perfectly valid file:
103
+ 1. **Top:** All imports from `main.mjs`, `LoggerPlugin.mjs`, and `helper.mjs` are hoisted here.
104
+ 2. **Middle:** All `@typedef` definitions are gathered here.
105
+ 3. **Bottom:** The logic of `main.mjs` runs, but the `.insert(LoggerPlugin)` call has been replaced by the actual code of the `LoggerPlugin` class, which now correctly references the hoisted helper functions.
106
+
107
+ **Everything is ready to be shipped!** 🚢💨
@@ -6,9 +6,10 @@ Welcome to **TinyClassManager**! This is a lightweight, linear mixin manager des
6
6
 
7
7
  ## 🚀 Key Features
8
8
 
9
- * **🔒 Immutable Chain State:** Each `.use()` call consumes the previous manager, preventing unwanted side effects or weird mutations.
9
+ * **🔒 Immutable Chain State:** Each `.insert()` call consumes the previous manager, preventing unwanted side effects or weird mutations.
10
10
  * **🧩 Dependency Verification:** Automatically ensures that a required plugin is loaded before the dependent plugin is applied.
11
11
  * **🛑 Conflict Protection:** Prevents you from installing the exact same plugin twice on the same class chain.
12
+ * **⚡ Clean Architecture:** Uses static class properties to manage metadata, keeping your plugin functions pure and direct.
12
13
 
13
14
  ---
14
15
 
@@ -25,8 +26,9 @@ Welcome to **TinyClassManager**! This is a lightweight, linear mixin manager des
25
26
 
26
27
  #### Methods
27
28
 
28
- * **`constructor(coreClass)`**: Initialises the manager with your foundational core class.
29
- * **`use(plugin)`**: Applies a new plugin to the chain. Returns a *new* `TinyClassManager` instance containing the extended class.
29
+ * **`constructor(coreClass)`**: Initializes the manager with your foundational core class.
30
+ * **`insert(applyFn)`**: Applies a new plugin to the chain by passing a function that returns the extended class. The returned class **must** define a `static _tinyDepName` string, and can optionally define a `static _tinyDeps` array of strings. Returns a *new* `TinyClassManager` instance.
31
+ * **`use(plugin)`**: *[Deprecated]* Legacy method for applying plugins using a definition object. Use `insert` instead.
30
32
  * **`build()`**: Finalizes the composition chain, marks the instance as consumed, and returns the fully compiled class ready for instantiation.
31
33
 
32
34
  ---
@@ -57,84 +59,81 @@ class EntityCore {
57
59
 
58
60
  ### Step 2: Create an Independent Module 🏷️
59
61
 
60
- Now, let's create our first module. A plugin is just a plain object with three specific keys: `name`, `dependencies`, and `apply`. This module doesn't depend on anything else, so its dependencies array is empty.
62
+ Now, let's create our first module. A plugin is a function that receives the base class and returns an extended version of it. We use static properties (`_tinyDepName` and `_tinyDeps`) to tell the manager what this plugin is and what it needs. This module doesn't depend on anything else, so its dependencies array is empty.
61
63
 
62
64
  ```javascript
63
- const Health = {
64
- name: 'Health',
65
- dependencies: [], // Works fine straight out of the box with the Core
66
-
67
- /**
68
- * @param {typeof EntityCore} Base - The incoming base class to extend.
69
- */
70
- apply: (Base) =>
71
- class HealthModule extends Base {
72
- /**
73
- * @param {string} id
74
- */
75
- constructor(id) {
76
- super(id); // Always remember to pass arguments down!
77
-
78
- /** @type {number} */
79
- this.hp = 100;
80
- }
81
-
82
- /**
83
- * Deducts health from the entity.
84
- * @param {number} amount - The amount of raw damage to deal.
85
- */
86
- takeDamage(amount) {
87
- this.hp -= amount;
88
- console.log(`Took ${amount} damage. HP left: ${this.hp}`);
89
- }
90
- },
91
- };
65
+ /**
66
+ * @param {typeof EntityCore} Base - The incoming base class to extend.
67
+ */
68
+ const applyHealth = (Base) =>
69
+ class HealthModule extends Base {
70
+ static _tinyDepName = 'Health';
71
+ /** @type {string[]} */
72
+ static _tinyDeps = []; // Works fine straight out of the box with the Core
73
+
74
+ /**
75
+ * @param {string} id
76
+ */
77
+ constructor(id) {
78
+ super(id); // Always remember to pass arguments down!
79
+
80
+ /** @type {number} */
81
+ this.hp = 100;
82
+ }
83
+
84
+ /**
85
+ * Deducts health from the entity.
86
+ * @param {number} amount - The amount of raw damage to deal.
87
+ */
88
+ takeDamage(amount) {
89
+ this.hp -= amount;
90
+ console.log(`Took ${amount} damage. HP left: ${this.hp}`);
91
+ }
92
+ };
92
93
 
93
94
  ```
94
95
 
95
96
  ### Step 3: Create a Dependent Module 🛡️
96
97
 
97
- Here is where the magic happens! The `Armor` module strictly requires the `Health` module to be loaded first because it relies on overriding the `takeDamage` logic. We enforce this relationship inside the `dependencies` array.
98
+ Here is where the magic happens! The `Armor` module strictly requires the `Health` module to be loaded first because it relies on overriding the `takeDamage` logic. We enforce this relationship inside the `static _tinyDeps` array.
98
99
 
99
100
  ```javascript
100
- const Armor = {
101
- name: 'Armor',
102
- dependencies: ['Health'], // Strictly requires the "Health" module!
103
-
104
- /**
105
- * @param {ReturnType<typeof Health.apply>} Base - The class containing Health features.
106
- */
107
- apply: (Base) =>
108
- class ArmorModule extends Base {
109
- /**
110
- * @param {string} id
111
- */
112
- constructor(id) {
113
- super(id);
114
-
115
- /** @type {number} */
116
- this.armor = 50;
117
- }
118
-
119
- /**
120
- * Deducts health, factoring in damage mitigation provided by armor.
121
- * @param {number} amount - The initial damage before armor reduction.
122
- */
123
- takeDamage(amount) {
124
- // Mitigates damage using the armor stat
125
- const reducedDamage = Math.max(0, amount - this.armor * 0.1);
126
-
127
- // Pass the mitigated damage up to the Health layer
128
- super.takeDamage(reducedDamage);
129
- }
130
- },
131
- };
101
+ /**
102
+ * @param {ReturnType<typeof applyHealth>} Base - The class containing Health features.
103
+ */
104
+ const applyArmor = (Base) =>
105
+ class ArmorModule extends Base {
106
+ static _tinyDepName = 'Armor';
107
+ static _tinyDeps = ['Health']; // Strictly requires the "Health" module!
108
+
109
+ /**
110
+ * @param {string} id
111
+ */
112
+ constructor(id) {
113
+ super(id);
114
+
115
+ /** @type {number} */
116
+ this.armor = 50;
117
+ }
118
+
119
+ /**
120
+ * Deducts health, factoring in damage mitigation provided by armor.
121
+ * @param {number} amount - The initial damage before armor reduction.
122
+ */
123
+ takeDamage(amount) {
124
+ // Mitigates damage using the armor stat
125
+ const reducedDamage = Math.max(0, amount - this.armor * 0.1);
126
+
127
+ // Pass the mitigated damage up to the Health layer
128
+ super.takeDamage(reducedDamage);
129
+ }
130
+ };
132
131
 
133
132
  ```
134
133
 
135
134
  ### Step 4: Chain and Build Your Custom Class 🏗️
136
135
 
137
- Finally, use the manager to safely piece your modules together. Remember that `TinyClassManager` uses an explosive state pattern: once you run `.use()` or `.build()`, that specific instance is closed, forcing you to use the new returned chain instance.
136
+ Finally, use the manager to safely piece your modules together. Remember that `TinyClassManager` uses an explosive state pattern: once you run `.insert()` or `.build()`, that specific instance is closed, forcing you to use the new returned chain instance.
138
137
 
139
138
  ```javascript
140
139
  try {
@@ -142,8 +141,8 @@ try {
142
141
  const CoreTest = new TinyClassManager(EntityCore);
143
142
 
144
143
  // 2. Chain your modules and compile!
145
- // If you try to swap the order (.use(Armor).use(Health)), it will throw an explicit dependency error.
146
- const FullyArmoredEntity = CoreTest.use(Health).use(Armor).build();
144
+ // If you try to swap the order (.insert(applyArmor).insert(applyHealth)), it will throw an explicit dependency error.
145
+ const FullyArmoredEntity = CoreTest.insert(applyHealth).insert(applyArmor).build();
147
146
 
148
147
  // 3. Create your custom instance!
149
148
  const myEntity = new FullyArmoredEntity('Player_1');
@@ -170,5 +169,5 @@ try {
170
169
  When you want to build a new set of mechanics for your game or application using this architecture, always remember to follow this loop:
171
170
 
172
171
  1. **Define your Base State**: Keep it clean and focused on global data configurations.
173
- 2. **Declare Plugins as Pure Objects**: Always structure them with `name`, `dependencies` (an array of strings matching other plugin names), and an `apply` method.
172
+ 2. **Declare Plugins as Functions**: Create a function that accepts a `Base` class and returns an extended class. Equip it with `static _tinyDepName` (string) and `static _tinyDeps` (array of strings).
174
173
  3. **Use `super` Inheritances**: Inside your plugin classes, always call `super(...)` in constructors and use `super.methodName(...)` if you are overriding an existing skill or function to allow all mixed behaviors to fire smoothly down the pipeline!
@@ -21,7 +21,6 @@ A lightweight JavaScript utility for detecting drag-and-drop file operations on
21
21
  import TinyDragDropDetector from './TinyDragDropDetector.js';
22
22
 
23
23
  const dnd = new TinyDragDropDetector({
24
- fullscreen: false,
25
24
  target: document.getElementById('drop-area'),
26
25
  hoverClass: 'hover-effect',
27
26
  onDrop: (files, event) => {
@@ -50,19 +49,19 @@ A drag-and-drop detector instance.
50
49
  ### Constructor
51
50
 
52
51
  ```javascript
53
- new TinyDragDropDetector(options)
52
+ new TinyDragDropDetector(target, options)
54
53
  ```
55
54
 
56
55
  #### Parameters:
57
56
 
58
- | Name | Type | Default | Description |
59
- | --------- | ------------------------------------------- | ------- | --------------------- |
60
- | `options` | [`DragAndDropOptions`](#draganddropoptions) | `{}` | Configuration object. |
57
+ | Name | Type | Description |
58
+ | --------- | ------------------------------------------- | ------------------------------------------------------- |
59
+ | `target` | `HTMLElement` | The DOM element where drag listeners will be attached. |
60
+ | `options` | [`DragAndDropOptions`](#draganddropoptions) | Configuration object. |
61
61
 
62
62
  #### Throws:
63
63
 
64
64
  * `TypeError` if `target` is not an `HTMLElement`.
65
- * `TypeError` if `fullscreen` is not a boolean.
66
65
  * `TypeError` if `hoverClass` is not a string.
67
66
  * `TypeError` if `onDrop` is not a function.
68
67
  * `TypeError` if `onEnter` is defined but not a function.
@@ -76,7 +75,6 @@ new TinyDragDropDetector(options)
76
75
  | ----------------- | ------------- | ------------------------------------------------------------------------ |
77
76
  | `getTarget()` | `HTMLElement` | Returns the DOM element where listeners are attached. |
78
77
  | `getHoverClass()` | `string` | Returns the CSS class applied during drag hover. |
79
- | `isFullScreen()` | `boolean` | Indicates whether the detector is in fullscreen mode (`document.body`). |
80
78
  | `isDragging()` | `boolean` | Returns whether a drag operation is currently active over the target. |
81
79
  | `bound()` | `boolean` | Returns whether the event listeners are currently bound to the target. |
82
80
  | `destroy()` | `void` | Destroys the detector, unbinding all events and cleaning up CSS classes. |
@@ -98,8 +96,6 @@ new TinyDragDropDetector(options)
98
96
  ```javascript
99
97
  /**
100
98
  * @typedef {Object} DragAndDropOptions
101
- * @property {HTMLElement} [target=document.body] - The DOM element where drag listeners will be attached. Defaults to `document.body` if `fullscreen` is true or no target is provided.
102
- * @property {boolean} [fullscreen=true] - If true, listeners are attached to the entire page (`document.body`). If false, the `target` must be specified.
103
99
  * @property {string} [hoverClass="dnd-hover"] - CSS class applied to the target element while files are being dragged over it.
104
100
  * @property {(files: FileList, event: DragEvent) => void} onDrop - Callback function executed when files are dropped onto the target.
105
101
  * @property {(event: DragEvent) => void} [onEnter] - Optional callback triggered when dragging enters the target area.
@@ -22,14 +22,13 @@ type ElementDetectorsFn = (
22
22
  ## 🔹 Constructor
23
23
 
24
24
  ```ts
25
- new TinyElementObserver({
26
- el?: Element,
25
+ new TinyElementObserver(el, {
27
26
  initDetectors?: Array<[string, ElementDetectorsFn]>,
28
27
  initCfg?: MutationObserverInit
29
28
  })
30
29
  ```
31
30
 
32
- * **`el`** *(optional)* → Initial element to observe 🏷️
31
+ * **`el`** → Initial element to observe 🏷️
33
32
  * **`initDetectors`** *(optional)* → Array of detectors to register on creation 🎛️
34
33
  * **`initCfg`** *(optional)* → Initial `MutationObserverInit` configuration ⚙️
35
34
 
@@ -39,7 +38,6 @@ new TinyElementObserver({
39
38
 
40
39
  | Property | Type | Description |
41
40
  | ----------- | ------------------------------------- | --------------------------------------------------------- |
42
- | `el` | `Element \| undefined` | The DOM element being observed. Can only be set once. 🏗️ |
43
41
  | `settings` | `MutationObserverInit` | Observer configuration object. 🔧 |
44
42
  | `observer` | `MutationObserver \| null` | Internal observer instance. |
45
43
  | `detectors` | `Array<[string, ElementDetectorsFn]>` | List of registered detectors. |
@@ -85,8 +83,7 @@ const logDetector = (mutation, index, mutations) => {
85
83
  };
86
84
 
87
85
  // Create instance
88
- const observer = new TinyElementObserver({
89
- el: document.querySelector('#target'),
86
+ const observer = new TinyElementObserver(document.querySelector('#target'), {
90
87
  initDetectors: [['logger', logDetector]],
91
88
  initCfg: { childList: true, subtree: true }
92
89
  });
@@ -111,7 +111,7 @@ Removes a specific listener from an event.
111
111
 
112
112
  Removes all listeners for one or more events.
113
113
 
114
- #### `offAllTypes(): void`
114
+ #### `offAllTypes(): void` and `removeAllListeners(): void`
115
115
 
116
116
  Removes **all listeners** for **all events**.
117
117