tiny-essentials 1.26.1 → 1.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/changelog/1/26/2.md +10 -0
- package/dist/v1/TinyClassManager.min.js +1 -0
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/build/TinyClassManager.cjs +7 -0
- package/dist/v1/build/TinyClassManager.d.mts +3 -0
- package/dist/v1/build/TinyClassManager.mjs +2 -0
- package/dist/v1/index.cjs +2 -0
- package/dist/v1/index.d.mts +2 -1
- package/dist/v1/index.mjs +2 -1
- package/dist/v1/libs/TinyClassManager.cjs +121 -0
- package/dist/v1/libs/TinyClassManager.d.mts +68 -0
- package/dist/v1/libs/TinyClassManager.mjs +103 -0
- package/dist/v1/libs/examples/TinyClassManager.cjs +76 -0
- package/dist/v1/libs/examples/TinyClassManager.d.mts +2 -0
- package/dist/v1/libs/examples/TinyClassManager.mjs +66 -0
- package/docs/v1/README.md +10 -5
- package/docs/v1/libs/TinyClassManager.md +174 -0
- package/package.json +5 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# 🛠️ TinyClassManager
|
|
2
|
+
|
|
3
|
+
Welcome to **TinyClassManager**! This is a lightweight, linear mixin manager designed to compose base classes with optional modules (like features or "DLCs") in a safe, predictable, and sequential way. It handles dependencies and prevents multiple class reuses automatically.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 Key Features
|
|
8
|
+
|
|
9
|
+
* **🔒 Immutable Chain State:** Each `.use()` call consumes the previous manager, preventing unwanted side effects or weird mutations.
|
|
10
|
+
* **🧩 Dependency Verification:** Automatically ensures that a required plugin is loaded before the dependent plugin is applied.
|
|
11
|
+
* **🛑 Conflict Protection:** Prevents you from installing the exact same plugin twice on the same class chain.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 📐 API Overview
|
|
16
|
+
|
|
17
|
+
### `TinyClassManager<T>`
|
|
18
|
+
|
|
19
|
+
#### Properties
|
|
20
|
+
|
|
21
|
+
* **`appliedPlugins`** (`string[]`): Returns an array of the names of all successfully installed plugins so far.
|
|
22
|
+
* **`size`** (`number`): The total count of applied plugins plus the core base class.
|
|
23
|
+
* **`currentClass`** (`T`): Accesses the current state of the class chain inside the manager.
|
|
24
|
+
* **`used`** (`boolean`): Indicates whether this specific manager instance has already been transitioned or finalized.
|
|
25
|
+
|
|
26
|
+
#### Methods
|
|
27
|
+
|
|
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.
|
|
30
|
+
* **`build()`**: Finalizes the composition chain, marks the instance as consumed, and returns the fully compiled class ready for instantiation.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📖 Detailed Usage Guide & Step-by-Step Example
|
|
35
|
+
|
|
36
|
+
Let's break down exactly how to design, extend, and instantiate a class structure using `TinyClassManager`. You can mirror this step-by-step blueprint for your own features!
|
|
37
|
+
|
|
38
|
+
### Step 1: Establish the Core Base 🧱
|
|
39
|
+
|
|
40
|
+
Every structure needs a solid foundation. The core class is the absolute minimum requirement and contains properties that every subsequent plugin might rely on.
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
import TinyClassManager from '../TinyClassManager.mjs';
|
|
44
|
+
|
|
45
|
+
// The foundational class
|
|
46
|
+
class EntityCore {
|
|
47
|
+
/**
|
|
48
|
+
* @param {string} id
|
|
49
|
+
*/
|
|
50
|
+
constructor(id) {
|
|
51
|
+
/** @type {string} */
|
|
52
|
+
this.id = id;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Step 2: Create an Independent Module 🏷️
|
|
59
|
+
|
|
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.
|
|
61
|
+
|
|
62
|
+
```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
|
+
};
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Step 3: Create a Dependent Module 🛡️
|
|
96
|
+
|
|
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
|
+
|
|
99
|
+
```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
|
+
};
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Step 4: Chain and Build Your Custom Class 🏗️
|
|
136
|
+
|
|
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.
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
try {
|
|
141
|
+
// 1. Wrap your core class
|
|
142
|
+
const CoreTest = new TinyClassManager(EntityCore);
|
|
143
|
+
|
|
144
|
+
// 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();
|
|
147
|
+
|
|
148
|
+
// 3. Create your custom instance!
|
|
149
|
+
const myEntity = new FullyArmoredEntity('Player_1');
|
|
150
|
+
|
|
151
|
+
// 4. Test out the behaviors
|
|
152
|
+
myEntity.takeDamage(30);
|
|
153
|
+
// Output: "Took 25 damage. HP left: 75" (30 damage - 5 armor reduction = 25 damage)
|
|
154
|
+
|
|
155
|
+
console.log(`Armor value: ${myEntity.armor}`); // Output: Armor value: 50
|
|
156
|
+
console.log(`Current HP: ${myEntity.hp}`); // Output: Current HP: 75
|
|
157
|
+
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error instanceof Error) {
|
|
160
|
+
console.error(error.message);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 💡 How to Mimic This Pattern
|
|
169
|
+
|
|
170
|
+
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
|
+
|
|
172
|
+
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.
|
|
174
|
+
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!
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-essentials",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.2",
|
|
4
4
|
"description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tiny-essentials-fork": "./TinyFork.mjs"
|
|
@@ -268,6 +268,10 @@
|
|
|
268
268
|
"require": "./dist/v1/libs/TinyAdvancedRaffle.cjs",
|
|
269
269
|
"import": "./dist/v1/libs/TinyAdvancedRaffle.mjs"
|
|
270
270
|
},
|
|
271
|
+
"./libs/TinyClassManager": {
|
|
272
|
+
"require": "./dist/v1/libs/TinyClassManager.cjs",
|
|
273
|
+
"import": "./dist/v1/libs/TinyClassManager.mjs"
|
|
274
|
+
},
|
|
271
275
|
"./libs/TinyMamdaniInferenceSystem": {
|
|
272
276
|
"require": "./dist/v1/libs/TinyMamdaniInferenceSystem.cjs",
|
|
273
277
|
"import": "./dist/v1/libs/TinyMamdaniInferenceSystem.mjs"
|