mutts 1.0.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.
- package/README.md +150 -0
- package/dist/chunks/decorator-BXsign4Z.js +176 -0
- package/dist/chunks/decorator-BXsign4Z.js.map +1 -0
- package/dist/chunks/decorator-CPbZNnsX.esm.js +168 -0
- package/dist/chunks/decorator-CPbZNnsX.esm.js.map +1 -0
- package/dist/decorator.d.ts +50 -0
- package/dist/decorator.esm.js +2 -0
- package/dist/decorator.esm.js.map +1 -0
- package/dist/decorator.js +11 -0
- package/dist/decorator.js.map +1 -0
- package/dist/destroyable.d.ts +48 -0
- package/dist/destroyable.esm.js +91 -0
- package/dist/destroyable.esm.js.map +1 -0
- package/dist/destroyable.js +98 -0
- package/dist/destroyable.js.map +1 -0
- package/dist/eventful.d.ts +11 -0
- package/dist/eventful.esm.js +88 -0
- package/dist/eventful.esm.js.map +1 -0
- package/dist/eventful.js +90 -0
- package/dist/eventful.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.esm.js +7 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/indexable.d.ts +31 -0
- package/dist/indexable.esm.js +85 -0
- package/dist/indexable.esm.js.map +1 -0
- package/dist/indexable.js +89 -0
- package/dist/indexable.js.map +1 -0
- package/dist/mutts.umd.js +2 -0
- package/dist/mutts.umd.js.map +1 -0
- package/dist/mutts.umd.min.js +2 -0
- package/dist/mutts.umd.min.js.map +1 -0
- package/dist/promiseChain.d.ts +11 -0
- package/dist/promiseChain.esm.js +72 -0
- package/dist/promiseChain.esm.js.map +1 -0
- package/dist/promiseChain.js +74 -0
- package/dist/promiseChain.js.map +1 -0
- package/dist/reactive.d.ts +114 -0
- package/dist/reactive.esm.js +1455 -0
- package/dist/reactive.esm.js.map +1 -0
- package/dist/reactive.js +1472 -0
- package/dist/reactive.js.map +1 -0
- package/dist/std-decorators.d.ts +17 -0
- package/dist/std-decorators.esm.js +161 -0
- package/dist/std-decorators.esm.js.map +1 -0
- package/dist/std-decorators.js +169 -0
- package/dist/std-decorators.js.map +1 -0
- package/docs/decorator.md +300 -0
- package/docs/destroyable.md +294 -0
- package/docs/events.md +225 -0
- package/docs/indexable.md +561 -0
- package/docs/promiseChain.md +218 -0
- package/docs/reactive.md +2072 -0
- package/docs/std-decorators.md +558 -0
- package/package.json +132 -0
- package/src/decorator.test.ts +495 -0
- package/src/decorator.ts +205 -0
- package/src/destroyable.test.ts +155 -0
- package/src/destroyable.ts +158 -0
- package/src/eventful.test.ts +380 -0
- package/src/eventful.ts +69 -0
- package/src/index.ts +7 -0
- package/src/indexable.test.ts +388 -0
- package/src/indexable.ts +124 -0
- package/src/promiseChain.test.ts +201 -0
- package/src/promiseChain.ts +99 -0
- package/src/reactive/array.test.ts +923 -0
- package/src/reactive/array.ts +352 -0
- package/src/reactive/core.test.ts +1663 -0
- package/src/reactive/core.ts +866 -0
- package/src/reactive/index.ts +28 -0
- package/src/reactive/interface.test.ts +1477 -0
- package/src/reactive/interface.ts +231 -0
- package/src/reactive/map.test.ts +866 -0
- package/src/reactive/map.ts +162 -0
- package/src/reactive/set.test.ts +289 -0
- package/src/reactive/set.ts +142 -0
- package/src/std-decorators.test.ts +679 -0
- package/src/std-decorators.ts +182 -0
- package/src/utils.ts +52 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
# Standard decorators
|
|
2
|
+
|
|
3
|
+
A TypeScript library that provides standard decorators that should stop being re-implemented for the 50th time
|
|
4
|
+
|
|
5
|
+
## Cached
|
|
6
|
+
|
|
7
|
+
The `cached` module provides a decorator that can be applied to getter methods to cache their results and detect circular dependencies. This is useful for expensive computations that should only be calculated once and for preventing infinite recursion in complex object graphs.
|
|
8
|
+
|
|
9
|
+
## API Reference
|
|
10
|
+
|
|
11
|
+
### `@cached`
|
|
12
|
+
|
|
13
|
+
A decorator that can be applied to getter methods to cache their results and detect circular dependencies.
|
|
14
|
+
|
|
15
|
+
**Target:** Getter methods only
|
|
16
|
+
|
|
17
|
+
**Returns:** Modified property descriptor with caching logic
|
|
18
|
+
|
|
19
|
+
**Throws:**
|
|
20
|
+
|
|
21
|
+
- `Error`: If applied to a non-getter method
|
|
22
|
+
- `Error`: If circular dependency is detected
|
|
23
|
+
|
|
24
|
+
### `isCached(object: Object, propertyKey: PropertyKey): boolean`
|
|
25
|
+
|
|
26
|
+
Checks if a property on an object has been cached.
|
|
27
|
+
|
|
28
|
+
**Parameters:**
|
|
29
|
+
|
|
30
|
+
- `object`: The object to check
|
|
31
|
+
- `propertyKey`: The property key to check
|
|
32
|
+
|
|
33
|
+
**Returns:** `true` if the property is cached, `false` otherwise
|
|
34
|
+
|
|
35
|
+
### `cache(object: Object, propertyKey: PropertyKey, value: any): void`
|
|
36
|
+
|
|
37
|
+
Manually caches a value for a property on an object.
|
|
38
|
+
|
|
39
|
+
**Parameters:**
|
|
40
|
+
|
|
41
|
+
- `object`: The object to cache the value on
|
|
42
|
+
- `propertyKey`: The property key to cache
|
|
43
|
+
- `value`: The value to cache
|
|
44
|
+
|
|
45
|
+
## Usage Examples
|
|
46
|
+
|
|
47
|
+
### Basic Caching
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { cached } from 'mutts/std-decorators'
|
|
51
|
+
|
|
52
|
+
class ExpensiveCalculator {
|
|
53
|
+
@cached
|
|
54
|
+
get expensiveValue() {
|
|
55
|
+
// This will only be calculated once
|
|
56
|
+
return this.performExpensiveCalculation()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private performExpensiveCalculation() {
|
|
60
|
+
// Simulate expensive computation
|
|
61
|
+
return Math.random() * 1000
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const calculator = new ExpensiveCalculator()
|
|
66
|
+
console.log(calculator.expensiveValue) // Calculated
|
|
67
|
+
console.log(calculator.expensiveValue) // Cached result
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Circular Dependency Detection
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { cached } from 'mutts/std-decorators'
|
|
74
|
+
|
|
75
|
+
class CircularObject {
|
|
76
|
+
@cached
|
|
77
|
+
get valueA() {
|
|
78
|
+
return this.valueB + 1 // This will cause circular dependency
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@cached
|
|
82
|
+
get valueB() {
|
|
83
|
+
return this.valueA + 1 // This will cause circular dependency
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const obj = new CircularObject()
|
|
88
|
+
// This will throw: "Circular dependency detected: CircularObject.valueA -> CircularObject.valueB -> again"
|
|
89
|
+
console.log(obj.valueA)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Complex Object Graph
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { cached } from 'mutts/std-decorators'
|
|
96
|
+
|
|
97
|
+
class Node {
|
|
98
|
+
constructor(public id: string, public children: Node[] = []) {}
|
|
99
|
+
|
|
100
|
+
@cached
|
|
101
|
+
get totalChildren() {
|
|
102
|
+
return this.children.reduce((total, child) => {
|
|
103
|
+
return total + 1 + child.totalChildren
|
|
104
|
+
}, 0)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@cached
|
|
108
|
+
get depth() {
|
|
109
|
+
if (this.children.length === 0) return 0
|
|
110
|
+
return Math.max(...this.children.map(child => child.depth)) + 1
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const root = new Node('root', [
|
|
115
|
+
new Node('child1', [new Node('grandchild1')]),
|
|
116
|
+
new Node('child2')
|
|
117
|
+
])
|
|
118
|
+
|
|
119
|
+
console.log(root.totalChildren) // 3
|
|
120
|
+
console.log(root.depth) // 2
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Manual Caching
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { cache, isCached } from 'mutts/std-decorators'
|
|
127
|
+
|
|
128
|
+
class ManualCache {
|
|
129
|
+
private _value: number | undefined
|
|
130
|
+
|
|
131
|
+
get value() {
|
|
132
|
+
if (this._value !== undefined) {
|
|
133
|
+
return this._value
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = this.calculateValue()
|
|
137
|
+
cache(this, 'value', result)
|
|
138
|
+
return result
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private calculateValue() {
|
|
142
|
+
return Math.random() * 100
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const obj = new ManualCache()
|
|
147
|
+
console.log(isCached(obj, 'value')) // false
|
|
148
|
+
console.log(obj.value) // Calculated and cached
|
|
149
|
+
console.log(isCached(obj, 'value')) // true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Implementation Details
|
|
153
|
+
|
|
154
|
+
### Thread Safety
|
|
155
|
+
|
|
156
|
+
The current implementation uses a global array for tracking circular dependencies, which means it's not thread-safe. In a multi-threaded environment (like Node.js with worker threads), you might need to use zone.js or a similar solution to avoid async re-entrance issues.
|
|
157
|
+
|
|
158
|
+
## Error Messages
|
|
159
|
+
|
|
160
|
+
### Circular Dependency Error
|
|
161
|
+
|
|
162
|
+
When a circular dependency is detected, the error message includes the full path of the circular reference:
|
|
163
|
+
|
|
164
|
+
```txt
|
|
165
|
+
Circular dependency detected: ClassA.propertyA -> ClassB.propertyB -> ClassC.propertyC -> again
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Invalid Usage Error
|
|
169
|
+
|
|
170
|
+
When the decorator is applied to a non-getter method:
|
|
171
|
+
|
|
172
|
+
```txt
|
|
173
|
+
@cached can only be used on getters
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Best Practices
|
|
177
|
+
|
|
178
|
+
1. **Use for expensive computations**: Only apply `@cached` to getters that perform expensive calculations
|
|
179
|
+
2. **Avoid side effects**: Cached getters should be pure functions without side effects
|
|
180
|
+
3. **Consider memory usage**: Cached values persist for the lifetime of the object
|
|
181
|
+
4. **Test for circular dependencies**: Always test complex object graphs for circular references
|
|
182
|
+
5. **Use manual caching for complex logic**: For more control, use the `cache()` function directly
|
|
183
|
+
|
|
184
|
+
## Limitations
|
|
185
|
+
|
|
186
|
+
1. **Global state**: The circular dependency detection uses global state
|
|
187
|
+
2. **Not thread-safe**: The current implementation is not safe for concurrent access
|
|
188
|
+
3. **Memory overhead**: Cached values are stored indefinitely
|
|
189
|
+
4. **Getter-only**: Can only be applied to getter methods, not setters or regular methods
|
|
190
|
+
|
|
191
|
+
## Describe
|
|
192
|
+
|
|
193
|
+
The `describe` decorator provides a clean, reusable way to configure property descriptors (enumerable, configurable, writable) for class properties. This decorator uses a functional approach that makes it easy to create reusable descriptor configurations.
|
|
194
|
+
|
|
195
|
+
## API Reference
|
|
196
|
+
|
|
197
|
+
### `describe(descriptor: PropertyDescriptor): <T>(...properties: (keyof T)[]) => GenericClassDecorator<T>`
|
|
198
|
+
|
|
199
|
+
A function that creates a decorator to configure property descriptors for specified properties.
|
|
200
|
+
|
|
201
|
+
**Parameters:**
|
|
202
|
+
- `descriptor`: An object with descriptor configuration
|
|
203
|
+
- `enumerable?: boolean` - Controls whether the property appears in enumerations
|
|
204
|
+
- `configurable?: boolean` - Controls whether the property descriptor can be changed
|
|
205
|
+
- `writable?: boolean` - Controls whether the property value can be changed
|
|
206
|
+
|
|
207
|
+
**Returns:** A function that takes property names and returns a class decorator
|
|
208
|
+
|
|
209
|
+
**Usage Pattern:**
|
|
210
|
+
```typescript
|
|
211
|
+
const readonly = describe({ writable: false })
|
|
212
|
+
const hidden = describe({ enumerable: false })
|
|
213
|
+
const locked = describe({ configurable: false })
|
|
214
|
+
|
|
215
|
+
@readonly('id', 'createdAt')
|
|
216
|
+
@hidden('_private')
|
|
217
|
+
@locked('critical')
|
|
218
|
+
class MyClass { }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Usage Examples
|
|
222
|
+
|
|
223
|
+
### Creating Reusable Decorators
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { describe } from 'mutts/std-decorators'
|
|
227
|
+
|
|
228
|
+
// Create reusable descriptor configurations
|
|
229
|
+
const readonly = describe({ writable: false })
|
|
230
|
+
const hidden = describe({ enumerable: false })
|
|
231
|
+
const locked = describe({ configurable: false })
|
|
232
|
+
|
|
233
|
+
// Use them on classes
|
|
234
|
+
@readonly('id', 'createdAt')
|
|
235
|
+
@hidden('_private', '_cache')
|
|
236
|
+
@locked('critical')
|
|
237
|
+
class User {
|
|
238
|
+
id: string = 'user-123'
|
|
239
|
+
name: string = 'John'
|
|
240
|
+
_private: string = 'secret'
|
|
241
|
+
_cache: Map<string, any> = new Map()
|
|
242
|
+
createdAt: Date = new Date()
|
|
243
|
+
critical: string = 'locked'
|
|
244
|
+
|
|
245
|
+
constructor(name: string) {
|
|
246
|
+
this.name = name
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const user = new User('Alice')
|
|
251
|
+
console.log(Object.keys(user)) // ['id', 'name', 'createdAt', 'critical'] - only enumerable properties
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Making Properties Non-Enumerable
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { describe } from 'mutts/std-decorators'
|
|
258
|
+
|
|
259
|
+
const hidden = describe({ enumerable: false })
|
|
260
|
+
|
|
261
|
+
@hidden('_internal', '_cache', 'debug')
|
|
262
|
+
class CacheManager {
|
|
263
|
+
public data: any[] = []
|
|
264
|
+
_internal: Map<string, any> = new Map()
|
|
265
|
+
_cache: WeakMap<object, any> = new WeakMap()
|
|
266
|
+
debug: boolean = false
|
|
267
|
+
|
|
268
|
+
getCached(key: string) {
|
|
269
|
+
return this._internal.get(key)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const cache = new CacheManager()
|
|
274
|
+
// Only public properties are enumerable
|
|
275
|
+
console.log(Object.keys(cache)) // ['data']
|
|
276
|
+
console.log(Object.getOwnPropertyNames(cache)) // ['data', '_internal', '_cache', 'debug']
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Read-Only Properties
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { describe } from 'mutts/std-decorators'
|
|
283
|
+
|
|
284
|
+
const readonly = describe({ writable: false })
|
|
285
|
+
const readonlyLocked = describe({ writable: false, configurable: false })
|
|
286
|
+
|
|
287
|
+
@readonly('createdAt', 'version')
|
|
288
|
+
@readonlyLocked('id')
|
|
289
|
+
class Document {
|
|
290
|
+
id: string
|
|
291
|
+
title: string
|
|
292
|
+
createdAt: Date
|
|
293
|
+
version: number = 1
|
|
294
|
+
|
|
295
|
+
constructor(id: string, title: string) {
|
|
296
|
+
this.id = id
|
|
297
|
+
this.title = title
|
|
298
|
+
this.createdAt = new Date()
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
updateTitle(newTitle: string) {
|
|
302
|
+
this.title = newTitle
|
|
303
|
+
this.version++
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const doc = new Document('doc-1', 'My Document')
|
|
308
|
+
// doc.id = 'new-id' // TypeError: Cannot assign to read only property 'id'
|
|
309
|
+
// doc.createdAt = new Date() // TypeError: Cannot assign to read only property 'createdAt'
|
|
310
|
+
doc.updateTitle('Updated Title') // This works
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Configuration Control
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { describe } from 'mutts/std-decorators'
|
|
317
|
+
|
|
318
|
+
const locked = describe({ configurable: false })
|
|
319
|
+
const frozen = describe({ configurable: false, writable: false })
|
|
320
|
+
|
|
321
|
+
@locked('_sealed')
|
|
322
|
+
@frozen('_frozen')
|
|
323
|
+
class SecureObject {
|
|
324
|
+
public data: any
|
|
325
|
+
_sealed: string = 'cannot be reconfigured'
|
|
326
|
+
_frozen: string = 'cannot be changed or reconfigured'
|
|
327
|
+
|
|
328
|
+
constructor(data: any) {
|
|
329
|
+
this.data = data
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const obj = new SecureObject({ key: 'value' })
|
|
334
|
+
// Object.defineProperty(obj, '_sealed', { value: 'new' }) // TypeError: Cannot redefine property
|
|
335
|
+
// Object.defineProperty(obj, '_frozen', { value: 'new' }) // TypeError: Cannot redefine property
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Implementation Details
|
|
339
|
+
|
|
340
|
+
### Functional Approach
|
|
341
|
+
|
|
342
|
+
The `describe` decorator uses a functional approach that separates descriptor configuration from property selection:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Create reusable configurations once
|
|
346
|
+
const readonly = describe({ writable: false })
|
|
347
|
+
const hidden = describe({ enumerable: false })
|
|
348
|
+
|
|
349
|
+
// Apply to multiple classes with different properties
|
|
350
|
+
@readonly('id', 'createdAt')
|
|
351
|
+
class User { }
|
|
352
|
+
|
|
353
|
+
@readonly('version', 'buildDate')
|
|
354
|
+
class Package { }
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Common Decorator Patterns
|
|
358
|
+
|
|
359
|
+
The functional approach makes it easy to create common decorator patterns:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { describe } from './std-decorators'
|
|
363
|
+
|
|
364
|
+
// Common reusable decorators
|
|
365
|
+
export const readonly = describe({ writable: false })
|
|
366
|
+
export const hidden = describe({ enumerable: false })
|
|
367
|
+
export const locked = describe({ configurable: false })
|
|
368
|
+
export const frozen = describe({ writable: false, configurable: false })
|
|
369
|
+
export const private = describe({ enumerable: false, configurable: false })
|
|
370
|
+
|
|
371
|
+
// Usage examples
|
|
372
|
+
@readonly('id', 'createdAt')
|
|
373
|
+
@hidden('_cache', '_internal')
|
|
374
|
+
@locked('critical')
|
|
375
|
+
class SecureData {
|
|
376
|
+
id: string = 'secure-1'
|
|
377
|
+
_cache: Map<string, any> = new Map()
|
|
378
|
+
_internal: any = {}
|
|
379
|
+
createdAt: Date = new Date()
|
|
380
|
+
critical: string = 'locked'
|
|
381
|
+
public: string = 'visible'
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Property Descriptor Merging
|
|
386
|
+
|
|
387
|
+
The decorator merges the provided descriptor configuration with the existing property descriptor, allowing you to override specific aspects while preserving others:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// Original property might have { enumerable: true, writable: true, configurable: true }
|
|
391
|
+
// After @readonly('prop') where readonly = describe({ writable: false })
|
|
392
|
+
// Final descriptor: { enumerable: true, writable: false, configurable: true }
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Constructor Timing
|
|
396
|
+
|
|
397
|
+
The property descriptor configuration is applied in the constructor after calling the parent constructor, ensuring that the properties are properly initialized before descriptor modification.
|
|
398
|
+
|
|
399
|
+
## Best Practices
|
|
400
|
+
|
|
401
|
+
1. **Create reusable configurations**: Define descriptor configurations once and reuse them
|
|
402
|
+
2. **Use descriptive names**: Name your descriptor configurations clearly (`readonly`, `hidden`, `locked`)
|
|
403
|
+
3. **Combine multiple decorators**: Stack multiple `describe` decorators for complex configurations
|
|
404
|
+
4. **Use for encapsulation**: Hide internal properties from enumeration
|
|
405
|
+
5. **Control immutability**: Make critical properties read-only
|
|
406
|
+
6. **Prevent reconfiguration**: Lock important properties from being modified
|
|
407
|
+
|
|
408
|
+
## Limitations
|
|
409
|
+
|
|
410
|
+
1. **Class-level only**: Cannot be applied to individual properties
|
|
411
|
+
2. **Constructor timing**: Properties must exist before descriptor modification
|
|
412
|
+
3. **No runtime changes**: Descriptor configuration is fixed at class definition time
|
|
413
|
+
4. **Type safety**: TypeScript doesn't enforce descriptor constraints at compile time
|
|
414
|
+
|
|
415
|
+
## Deprecated
|
|
416
|
+
|
|
417
|
+
The `deprecated` decorator provides a way to mark methods, getters, setters, or classes as deprecated with optional custom warning messages.
|
|
418
|
+
|
|
419
|
+
## API Reference
|
|
420
|
+
|
|
421
|
+
### `@deprecated` / `@deprecated(message: string)`
|
|
422
|
+
|
|
423
|
+
A decorator that can be applied to methods, getters, setters, or classes to mark them as deprecated.
|
|
424
|
+
|
|
425
|
+
**Target:** Methods, getters, setters, classes
|
|
426
|
+
|
|
427
|
+
**Parameters:**
|
|
428
|
+
- `message` (optional): Custom deprecation message to display
|
|
429
|
+
|
|
430
|
+
**Returns:** Modified method/getter/setter/class with deprecation warning
|
|
431
|
+
|
|
432
|
+
**Throws:**
|
|
433
|
+
- `Error`: If applied to an unsupported target
|
|
434
|
+
|
|
435
|
+
## Usage Examples
|
|
436
|
+
|
|
437
|
+
### Basic Deprecation
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { deprecated } from 'mutts/std-decorators'
|
|
441
|
+
|
|
442
|
+
class API {
|
|
443
|
+
@deprecated
|
|
444
|
+
oldMethod() {
|
|
445
|
+
return 'old implementation'
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
@deprecated
|
|
449
|
+
get oldValue() {
|
|
450
|
+
return 'deprecated value'
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const api = new API()
|
|
455
|
+
api.oldMethod() // Console: "API.oldMethod is deprecated"
|
|
456
|
+
api.oldValue // Console: "API.oldValue is deprecated"
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Custom Deprecation Messages
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { deprecated } from 'mutts/std-decorators'
|
|
463
|
+
|
|
464
|
+
class API {
|
|
465
|
+
@deprecated('Use newMethod() instead')
|
|
466
|
+
oldMethod() {
|
|
467
|
+
return 'old implementation'
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
@deprecated('This will be removed in v2.0')
|
|
471
|
+
get oldValue() {
|
|
472
|
+
return 'deprecated value'
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@deprecated('Use setNewValue() instead')
|
|
476
|
+
set oldValue(value: string) {
|
|
477
|
+
// deprecated setter
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const api = new API()
|
|
482
|
+
api.oldMethod() // Console: "API.oldMethod is deprecated: Use newMethod() instead"
|
|
483
|
+
api.oldValue // Console: "API.oldValue is deprecated: This will be removed in v2.0"
|
|
484
|
+
api.oldValue = 'test' // Console: "API.oldValue is deprecated: Use setNewValue() instead"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Class Deprecation
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import { deprecated } from './std-decorators'
|
|
491
|
+
|
|
492
|
+
@deprecated('Use NewClass instead')
|
|
493
|
+
class OldClass {
|
|
494
|
+
constructor() {}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
new OldClass() // Console: "OldClass.constructor is deprecated: Use NewClass instead"
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Multiple Deprecation Messages
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
import { deprecated } from './std-decorators'
|
|
504
|
+
|
|
505
|
+
class API {
|
|
506
|
+
@deprecated('This will be removed in v2.0')
|
|
507
|
+
method1() {}
|
|
508
|
+
|
|
509
|
+
@deprecated('Use the new API')
|
|
510
|
+
method2() {}
|
|
511
|
+
|
|
512
|
+
@deprecated('Legacy support only')
|
|
513
|
+
method3() {}
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## Implementation Details
|
|
518
|
+
|
|
519
|
+
### Warning Message Format
|
|
520
|
+
|
|
521
|
+
The default warning message format is:
|
|
522
|
+
```
|
|
523
|
+
{ClassName}.{methodName} is deprecated
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
With custom message:
|
|
527
|
+
```
|
|
528
|
+
{ClassName}.{methodName} is deprecated: {customMessage}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Console Output
|
|
532
|
+
|
|
533
|
+
The decorator uses `console.warn()` to display deprecation messages. You can override the warning behavior by modifying the `deprecated.warn` function:
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
import { deprecated } from './std-decorators'
|
|
537
|
+
|
|
538
|
+
// Override the warning function
|
|
539
|
+
deprecated.warn = (target, propertyKey, message?) => {
|
|
540
|
+
// Custom warning logic
|
|
541
|
+
alert(`${target.constructor.name}.${String(propertyKey)} is deprecated${message ? `: ${message}` : ''}`)
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
## Best Practices
|
|
546
|
+
|
|
547
|
+
1. **Provide clear alternatives**: Always include information about what to use instead
|
|
548
|
+
2. **Include version information**: Mention when the deprecated feature will be removed
|
|
549
|
+
3. **Use consistent messaging**: Follow a consistent format across your codebase
|
|
550
|
+
4. **Document migration paths**: Provide clear upgrade instructions
|
|
551
|
+
5. **Monitor usage**: Track which deprecated features are still being used
|
|
552
|
+
|
|
553
|
+
## Related
|
|
554
|
+
|
|
555
|
+
- [JavaScript Decorators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Decorators)
|
|
556
|
+
- [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
|
|
557
|
+
- [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor)
|
|
558
|
+
- [Zone.js](https://github.com/angular/zone.js) - For async re-entrance handling
|
package/package.json
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mutts",
|
|
3
|
+
"description": "Modern UTility TS: A collection of TypeScript utilities",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"source": "./src/index.ts",
|
|
12
|
+
"import": "./dist/index.esm.js",
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"script": "./dist/mutts.umd.min.js"
|
|
15
|
+
},
|
|
16
|
+
"./decorator": {
|
|
17
|
+
"types": "./dist/decorator.d.ts",
|
|
18
|
+
"source": "./src/decorator.ts",
|
|
19
|
+
"import": "./dist/decorator.esm.js",
|
|
20
|
+
"require": "./dist/decorator.js"
|
|
21
|
+
},
|
|
22
|
+
"./reactive": {
|
|
23
|
+
"types": "./dist/reactive.d.ts",
|
|
24
|
+
"source": "./src/reactive/index.ts",
|
|
25
|
+
"import": "./dist/reactive.esm.js",
|
|
26
|
+
"require": "./dist/reactive.js"
|
|
27
|
+
},
|
|
28
|
+
"./eventful": {
|
|
29
|
+
"types": "./dist/eventful.d.ts",
|
|
30
|
+
"source": "./src/eventful.ts",
|
|
31
|
+
"import": "./dist/eventful.esm.js",
|
|
32
|
+
"require": "./dist/eventful.js"
|
|
33
|
+
},
|
|
34
|
+
"./indexable": {
|
|
35
|
+
"types": "./dist/indexable.d.ts",
|
|
36
|
+
"source": "./src/indexable.ts",
|
|
37
|
+
"import": "./dist/indexable.esm.js",
|
|
38
|
+
"require": "./dist/indexable.js"
|
|
39
|
+
},
|
|
40
|
+
"./promiseChain": {
|
|
41
|
+
"types": "./dist/promiseChain.d.ts",
|
|
42
|
+
"source": "./src/promiseChain.ts",
|
|
43
|
+
"import": "./dist/promiseChain.esm.js",
|
|
44
|
+
"require": "./dist/promiseChain.js"
|
|
45
|
+
},
|
|
46
|
+
"./destroyable": {
|
|
47
|
+
"types": "./dist/destroyable.d.ts",
|
|
48
|
+
"source": "./src/destroyable.ts",
|
|
49
|
+
"import": "./dist/destroyable.esm.js",
|
|
50
|
+
"require": "./dist/destroyable.js"
|
|
51
|
+
},
|
|
52
|
+
"./std-decorators": {
|
|
53
|
+
"types": "./dist/std-decorators.d.ts",
|
|
54
|
+
"source": "./src/std-decorators.ts",
|
|
55
|
+
"import": "./dist/std-decorators.esm.js",
|
|
56
|
+
"require": "./dist/std-decorators.js"
|
|
57
|
+
},
|
|
58
|
+
"./src": {
|
|
59
|
+
"import": "./src/index.ts"
|
|
60
|
+
},
|
|
61
|
+
"./src/*": {
|
|
62
|
+
"import": "./src/*"
|
|
63
|
+
},
|
|
64
|
+
"./umd": {
|
|
65
|
+
"browser": "./dist/mutts.umd.js"
|
|
66
|
+
},
|
|
67
|
+
"./umd.min": {
|
|
68
|
+
"browser": "./dist/mutts.umd.min.js"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"files": [
|
|
72
|
+
"dist",
|
|
73
|
+
"src",
|
|
74
|
+
"README.md",
|
|
75
|
+
"docs"
|
|
76
|
+
],
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build:js": "rollup -c",
|
|
79
|
+
"build": "npm run build:js",
|
|
80
|
+
"build:watch": "rollup -c --watch",
|
|
81
|
+
"prepublishOnly": "npm run build",
|
|
82
|
+
"test": "node --expose-gc node_modules/.bin/jest",
|
|
83
|
+
"test:legacy": "TSCONFIG=tsconfig.legacy.json node node_modules/.bin/jest --detectOpenHandles --testPathPatterns=decorator",
|
|
84
|
+
"test:modern": "TSCONFIG=tsconfig.modern.json node node_modules/.bin/jest --detectOpenHandles --testPathPatterns=decorator",
|
|
85
|
+
"biome": "biome check --write src"
|
|
86
|
+
},
|
|
87
|
+
"keywords": [
|
|
88
|
+
"typescript",
|
|
89
|
+
"utilities",
|
|
90
|
+
"reactive",
|
|
91
|
+
"caching",
|
|
92
|
+
"events",
|
|
93
|
+
"promise",
|
|
94
|
+
"proxy",
|
|
95
|
+
"decorators",
|
|
96
|
+
"indexable",
|
|
97
|
+
"computed",
|
|
98
|
+
"effects",
|
|
99
|
+
"state-management"
|
|
100
|
+
],
|
|
101
|
+
"author": "eddow",
|
|
102
|
+
"license": "ISC",
|
|
103
|
+
"repository": {
|
|
104
|
+
"type": "git",
|
|
105
|
+
"url": "git+https://github.com/eddow/mutts.git"
|
|
106
|
+
},
|
|
107
|
+
"homepage": "https://github.com/eddow/mutts#readme",
|
|
108
|
+
"bugs": {
|
|
109
|
+
"url": "https://github.com/eddow/mutts/issues"
|
|
110
|
+
},
|
|
111
|
+
"type": "module",
|
|
112
|
+
"engines": {
|
|
113
|
+
"node": ">=16.0.0"
|
|
114
|
+
},
|
|
115
|
+
"devDependencies": {
|
|
116
|
+
"@biomejs/biome": "^2.0.6",
|
|
117
|
+
"@rollup/plugin-commonjs": "^28.0.6",
|
|
118
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
119
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
120
|
+
"@rollup/plugin-typescript": "^12.1.4",
|
|
121
|
+
"@types/jest": "^30.0.0",
|
|
122
|
+
"jest": "^30.0.4",
|
|
123
|
+
"rollup": "^4.52.2",
|
|
124
|
+
"rollup-plugin-dts": "^6.2.3",
|
|
125
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
126
|
+
"ts-jest": "^29.4.0",
|
|
127
|
+
"ts-node": "^10.9.2",
|
|
128
|
+
"tslib": "^2.8.1",
|
|
129
|
+
"tsx": "^4.20.4",
|
|
130
|
+
"typescript": "^5.8.3"
|
|
131
|
+
}
|
|
132
|
+
}
|