mutts 1.0.1 → 1.0.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 +24 -2
- package/dist/chunks/_tslib-C-cuVLvZ.js +73 -0
- package/dist/chunks/_tslib-C-cuVLvZ.js.map +1 -0
- package/dist/chunks/_tslib-CMEnd0VE.esm.js +68 -0
- package/dist/chunks/_tslib-CMEnd0VE.esm.js.map +1 -0
- package/dist/chunks/{decorator-8qjFb7dw.js → decorator-D4DU97Zg.js} +70 -4
- package/dist/chunks/decorator-D4DU97Zg.js.map +1 -0
- package/dist/chunks/{decorator-AbRkXM5O.esm.js → decorator-GnHw1Az7.esm.js} +67 -5
- package/dist/chunks/decorator-GnHw1Az7.esm.js.map +1 -0
- package/dist/chunks/index-DBScoeCX.esm.js +1960 -0
- package/dist/chunks/index-DBScoeCX.esm.js.map +1 -0
- package/dist/chunks/index-DOTmXL89.js +1983 -0
- package/dist/chunks/index-DOTmXL89.js.map +1 -0
- package/dist/decorator.d.ts +57 -0
- package/dist/decorator.esm.js +1 -1
- package/dist/decorator.js +1 -1
- package/dist/destroyable.d.ts +42 -0
- package/dist/destroyable.esm.js +19 -1
- package/dist/destroyable.esm.js.map +1 -1
- package/dist/destroyable.js +19 -1
- package/dist/destroyable.js.map +1 -1
- package/dist/eventful.d.ts +10 -1
- package/dist/eventful.esm.js +5 -27
- package/dist/eventful.esm.js.map +1 -1
- package/dist/eventful.js +15 -37
- package/dist/eventful.js.map +1 -1
- package/dist/index.d.ts +52 -3
- package/dist/index.esm.js +3 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +17 -3
- package/dist/index.js.map +1 -1
- package/dist/indexable.d.ts +26 -0
- package/dist/indexable.esm.js +6 -0
- package/dist/indexable.esm.js.map +1 -1
- package/dist/indexable.js +6 -0
- package/dist/indexable.js.map +1 -1
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/promiseChain.d.ts +10 -0
- package/dist/promiseChain.esm.js +6 -0
- package/dist/promiseChain.esm.js.map +1 -1
- package/dist/promiseChain.js +6 -0
- package/dist/promiseChain.js.map +1 -1
- package/dist/reactive.d.ts +255 -18
- package/dist/reactive.esm.js +4 -1458
- package/dist/reactive.esm.js.map +1 -1
- package/dist/reactive.js +29 -1471
- package/dist/reactive.js.map +1 -1
- package/dist/std-decorators.d.ts +35 -0
- package/dist/std-decorators.esm.js +36 -1
- package/dist/std-decorators.esm.js.map +1 -1
- package/dist/std-decorators.js +36 -1
- package/dist/std-decorators.js.map +1 -1
- package/docs/mixin.md +229 -0
- package/docs/reactive.md +7739 -882
- package/package.json +1 -1
- package/dist/chunks/decorator-8qjFb7dw.js.map +0 -1
- package/dist/chunks/decorator-AbRkXM5O.esm.js.map +0 -1
package/docs/mixin.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Mixin System
|
|
2
|
+
|
|
3
|
+
The mixin system provides a powerful way to create reusable functionality that can be applied to classes either as base classes or as mixin functions.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `mixin()` function creates a mixin that can be used in two ways:
|
|
8
|
+
1. **As a base class** - `class MyClass extends MyMixin`
|
|
9
|
+
2. **As a mixin function** - `class MyClass extends MyMixin(SomeBase)`
|
|
10
|
+
|
|
11
|
+
## Basic Usage
|
|
12
|
+
|
|
13
|
+
### Creating a Mixin
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { mixin } from 'mutts/mixin'
|
|
17
|
+
|
|
18
|
+
const CounterMixin = mixin((base) => {
|
|
19
|
+
return class extends base {
|
|
20
|
+
count = 0
|
|
21
|
+
|
|
22
|
+
increment() {
|
|
23
|
+
this.count++
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getCount() {
|
|
27
|
+
return this.count
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Using as Base Class
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
class MyClass extends CounterMixin {
|
|
37
|
+
name = 'MyClass'
|
|
38
|
+
|
|
39
|
+
greet() {
|
|
40
|
+
return `Hello, I'm ${this.name} with count ${this.count}`
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const instance = new MyClass()
|
|
45
|
+
instance.increment()
|
|
46
|
+
console.log(instance.greet()) // "Hello, I'm MyClass with count 1"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Using as Mixin Function
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
class BaseClass {
|
|
53
|
+
value = 42
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class MyClass extends CounterMixin(BaseClass) {
|
|
57
|
+
name = 'MyClass'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const instance = new MyClass()
|
|
61
|
+
console.log(instance.value) // 42 (from BaseClass)
|
|
62
|
+
console.log(instance.count) // 0 (from CounterMixin)
|
|
63
|
+
instance.increment()
|
|
64
|
+
console.log(instance.getCount()) // 1
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Advanced Examples
|
|
68
|
+
|
|
69
|
+
### Eventful Mixin
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const EventfulMixin = mixin((base) => {
|
|
73
|
+
return class extends base {
|
|
74
|
+
#events = new Map()
|
|
75
|
+
|
|
76
|
+
on(event: string, callback: Function) {
|
|
77
|
+
if (!this.#events.has(event)) {
|
|
78
|
+
this.#events.set(event, [])
|
|
79
|
+
}
|
|
80
|
+
this.#events.get(event)!.push(callback)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
emit(event: string, ...args: any[]) {
|
|
84
|
+
const callbacks = this.#events.get(event) || []
|
|
85
|
+
callbacks.forEach(cb => cb(...args))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
off(event: string, callback?: Function) {
|
|
89
|
+
if (!callback) {
|
|
90
|
+
this.#events.delete(event)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
const callbacks = this.#events.get(event) || []
|
|
94
|
+
this.#events.set(event, callbacks.filter(cb => cb !== callback))
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Use as base class
|
|
100
|
+
class EventEmitter extends EventfulMixin {
|
|
101
|
+
constructor(public name: string) {
|
|
102
|
+
super()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Use as mixin
|
|
107
|
+
class GameObject extends EventfulMixin(BaseClass) {
|
|
108
|
+
constructor(public id: string) {
|
|
109
|
+
super()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Combining Mixins
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
class BaseModel {
|
|
118
|
+
id = Math.random().toString(36)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class UserModel extends EventfulMixin(CounterMixin(BaseModel)) {
|
|
122
|
+
name = 'User'
|
|
123
|
+
email = 'user@example.com'
|
|
124
|
+
|
|
125
|
+
updateProfile(newName: string) {
|
|
126
|
+
this.name = newName
|
|
127
|
+
this.emit('profileUpdated', { name: newName })
|
|
128
|
+
this.increment()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const user = new UserModel()
|
|
133
|
+
user.on('profileUpdated', (data) => {
|
|
134
|
+
console.log('Profile updated:', data)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
user.updateProfile('John Doe')
|
|
138
|
+
// Output: "Profile updated: { name: 'John Doe' }"
|
|
139
|
+
// user.count is now 1
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Caching
|
|
143
|
+
|
|
144
|
+
The mixin system automatically caches results to ensure that the same base class always returns the same mixed class. This provides:
|
|
145
|
+
|
|
146
|
+
- **Performance**: No repeated class creation
|
|
147
|
+
- **Identity**: Same base class always produces the same mixed class
|
|
148
|
+
- **Memory efficiency**: Automatic cleanup when base classes are garbage collected
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const MixinA = mixin((base) => class extends base { prop = 'A' })
|
|
152
|
+
const MixinB = mixin((base) => class extends base { prop = 'B' })
|
|
153
|
+
|
|
154
|
+
class BaseClass {}
|
|
155
|
+
|
|
156
|
+
const Mixed1 = MixinA(BaseClass)
|
|
157
|
+
const Mixed2 = MixinA(BaseClass)
|
|
158
|
+
|
|
159
|
+
console.log(Mixed1 === Mixed2) // true - same reference due to caching
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Error Handling
|
|
163
|
+
|
|
164
|
+
The mixin system provides clear error messages for invalid usage:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const MyMixin = mixin((base) => class extends base {})
|
|
168
|
+
|
|
169
|
+
// ❌ Error: Mixin requires a base class
|
|
170
|
+
MyMixin()
|
|
171
|
+
|
|
172
|
+
// ❌ Error: Mixin requires a constructor function
|
|
173
|
+
MyMixin('not a function')
|
|
174
|
+
|
|
175
|
+
// ✅ Valid usage
|
|
176
|
+
MyMixin(SomeClass)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Type Safety
|
|
180
|
+
|
|
181
|
+
The mixin system preserves TypeScript type information:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
interface User {
|
|
185
|
+
name: string
|
|
186
|
+
email: string
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const UserMixin = mixin((base) => {
|
|
190
|
+
return class extends base {
|
|
191
|
+
validateEmail(): boolean {
|
|
192
|
+
return this.email.includes('@')
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
class UserModel extends UserMixin {
|
|
198
|
+
name = ''
|
|
199
|
+
email = ''
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const user = new UserModel()
|
|
203
|
+
user.validateEmail() // TypeScript knows this method exists
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Best Practices
|
|
207
|
+
|
|
208
|
+
1. **Keep mixins focused**: Each mixin should provide a single, cohesive piece of functionality
|
|
209
|
+
2. **Use descriptive names**: Make it clear what functionality the mixin provides
|
|
210
|
+
3. **Document behavior**: Include JSDoc comments for mixin methods
|
|
211
|
+
4. **Consider composition**: Combine multiple small mixins rather than creating one large mixin
|
|
212
|
+
5. **Test thoroughly**: Mixins can have complex interactions, so comprehensive testing is important
|
|
213
|
+
|
|
214
|
+
## Integration with MutTs
|
|
215
|
+
|
|
216
|
+
The mixin system is designed to work seamlessly with other MutTs features:
|
|
217
|
+
|
|
218
|
+
- **ReactiveBase**: Can be used as a mixin to add reactivity to any class
|
|
219
|
+
- **Eventful**: Can be used as a mixin to add event handling to any class
|
|
220
|
+
- **Destroyable**: Can be used as a mixin to add cleanup functionality to any class
|
|
221
|
+
|
|
222
|
+
This allows for powerful combinations like:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Reactive + Eventful + Destroyable
|
|
226
|
+
class GameEntity extends Destroyable(Eventful(ReactiveBase(BaseClass))) {
|
|
227
|
+
// Your game entity with all capabilities
|
|
228
|
+
}
|
|
229
|
+
```
|