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.
Files changed (82) hide show
  1. package/README.md +150 -0
  2. package/dist/chunks/decorator-BXsign4Z.js +176 -0
  3. package/dist/chunks/decorator-BXsign4Z.js.map +1 -0
  4. package/dist/chunks/decorator-CPbZNnsX.esm.js +168 -0
  5. package/dist/chunks/decorator-CPbZNnsX.esm.js.map +1 -0
  6. package/dist/decorator.d.ts +50 -0
  7. package/dist/decorator.esm.js +2 -0
  8. package/dist/decorator.esm.js.map +1 -0
  9. package/dist/decorator.js +11 -0
  10. package/dist/decorator.js.map +1 -0
  11. package/dist/destroyable.d.ts +48 -0
  12. package/dist/destroyable.esm.js +91 -0
  13. package/dist/destroyable.esm.js.map +1 -0
  14. package/dist/destroyable.js +98 -0
  15. package/dist/destroyable.js.map +1 -0
  16. package/dist/eventful.d.ts +11 -0
  17. package/dist/eventful.esm.js +88 -0
  18. package/dist/eventful.esm.js.map +1 -0
  19. package/dist/eventful.js +90 -0
  20. package/dist/eventful.js.map +1 -0
  21. package/dist/index.d.ts +15 -0
  22. package/dist/index.esm.js +7 -0
  23. package/dist/index.esm.js.map +1 -0
  24. package/dist/index.js +52 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/indexable.d.ts +31 -0
  27. package/dist/indexable.esm.js +85 -0
  28. package/dist/indexable.esm.js.map +1 -0
  29. package/dist/indexable.js +89 -0
  30. package/dist/indexable.js.map +1 -0
  31. package/dist/mutts.umd.js +2 -0
  32. package/dist/mutts.umd.js.map +1 -0
  33. package/dist/mutts.umd.min.js +2 -0
  34. package/dist/mutts.umd.min.js.map +1 -0
  35. package/dist/promiseChain.d.ts +11 -0
  36. package/dist/promiseChain.esm.js +72 -0
  37. package/dist/promiseChain.esm.js.map +1 -0
  38. package/dist/promiseChain.js +74 -0
  39. package/dist/promiseChain.js.map +1 -0
  40. package/dist/reactive.d.ts +114 -0
  41. package/dist/reactive.esm.js +1455 -0
  42. package/dist/reactive.esm.js.map +1 -0
  43. package/dist/reactive.js +1472 -0
  44. package/dist/reactive.js.map +1 -0
  45. package/dist/std-decorators.d.ts +17 -0
  46. package/dist/std-decorators.esm.js +161 -0
  47. package/dist/std-decorators.esm.js.map +1 -0
  48. package/dist/std-decorators.js +169 -0
  49. package/dist/std-decorators.js.map +1 -0
  50. package/docs/decorator.md +300 -0
  51. package/docs/destroyable.md +294 -0
  52. package/docs/events.md +225 -0
  53. package/docs/indexable.md +561 -0
  54. package/docs/promiseChain.md +218 -0
  55. package/docs/reactive.md +2072 -0
  56. package/docs/std-decorators.md +558 -0
  57. package/package.json +132 -0
  58. package/src/decorator.test.ts +495 -0
  59. package/src/decorator.ts +205 -0
  60. package/src/destroyable.test.ts +155 -0
  61. package/src/destroyable.ts +158 -0
  62. package/src/eventful.test.ts +380 -0
  63. package/src/eventful.ts +69 -0
  64. package/src/index.ts +7 -0
  65. package/src/indexable.test.ts +388 -0
  66. package/src/indexable.ts +124 -0
  67. package/src/promiseChain.test.ts +201 -0
  68. package/src/promiseChain.ts +99 -0
  69. package/src/reactive/array.test.ts +923 -0
  70. package/src/reactive/array.ts +352 -0
  71. package/src/reactive/core.test.ts +1663 -0
  72. package/src/reactive/core.ts +866 -0
  73. package/src/reactive/index.ts +28 -0
  74. package/src/reactive/interface.test.ts +1477 -0
  75. package/src/reactive/interface.ts +231 -0
  76. package/src/reactive/map.test.ts +866 -0
  77. package/src/reactive/map.ts +162 -0
  78. package/src/reactive/set.test.ts +289 -0
  79. package/src/reactive/set.ts +142 -0
  80. package/src/std-decorators.test.ts +679 -0
  81. package/src/std-decorators.ts +182 -0
  82. package/src/utils.ts +52 -0
@@ -0,0 +1,495 @@
1
+ // Test the decorator system with all decorator types
2
+ import { decorator } from './decorator'
3
+
4
+ describe('Decorator System', () => {
5
+ describe('Method Decorators', () => {
6
+ it('should wrap method calls', () => {
7
+ const methodDecorator = decorator({
8
+ method(original, _name) {
9
+ return function (this: any, ...args: any[]) {
10
+ return `wrapped: ${original.apply(this, args)}`
11
+ }
12
+ },
13
+ })
14
+
15
+ class TestClass {
16
+ @methodDecorator
17
+ greet(name: string) {
18
+ return `Hello ${name}`
19
+ }
20
+ }
21
+
22
+ const instance = new TestClass()
23
+ expect(instance.greet('World')).toBe('wrapped: Hello World')
24
+ })
25
+
26
+ it('should work with multiple methods', () => {
27
+ const methodDecorator = decorator({
28
+ method(original, name) {
29
+ return function (this: any, ...args: any[]) {
30
+ return `${String(name)}: ${original.apply(this, args)}`
31
+ }
32
+ },
33
+ })
34
+
35
+ class TestClass {
36
+ @methodDecorator
37
+ first() {
38
+ return 'first'
39
+ }
40
+
41
+ @methodDecorator
42
+ second() {
43
+ return 'second'
44
+ }
45
+ }
46
+
47
+ const instance = new TestClass()
48
+ expect(instance.first()).toBe('first: first')
49
+ expect(instance.second()).toBe('second: second')
50
+ })
51
+ })
52
+
53
+ describe('Class Decorators', () => {
54
+ it('should modify class behavior', () => {
55
+ const classDecorator = decorator({
56
+ class: (target) => {
57
+ // Add a static property to the class
58
+ ;(target as any).decorated = true
59
+ return target
60
+ },
61
+ })
62
+
63
+ @classDecorator
64
+ class TestClass {
65
+ static original = 'original'
66
+ }
67
+
68
+ expect((TestClass as any).decorated).toBe(true)
69
+ expect(TestClass.original).toBe('original')
70
+ })
71
+
72
+ it('should work with class inheritance', () => {
73
+ const classDecorator = decorator({
74
+ class: (target) => {
75
+ ;(target as any).decorated = true
76
+ return target
77
+ },
78
+ })
79
+
80
+ @classDecorator
81
+ class BaseClass {}
82
+
83
+ class ExtendedClass extends BaseClass {}
84
+
85
+ expect((BaseClass as any).decorated).toBe(true)
86
+ // In legacy decorators, the decorator is applied to the class itself
87
+ // so ExtendedClass should also have the decorated property
88
+ expect((ExtendedClass as any).decorated).toBe(true)
89
+ })
90
+ })
91
+
92
+ describe('Getter Decorators', () => {
93
+ it('should wrap getter calls', () => {
94
+ const getterDecorator = decorator({
95
+ getter(original, _name) {
96
+ return function (this: any) {
97
+ return `wrapped: ${original.call(this)}`
98
+ }
99
+ },
100
+ })
101
+
102
+ class TestClass {
103
+ private _value = 'test'
104
+
105
+ @getterDecorator
106
+ get value() {
107
+ return this._value
108
+ }
109
+ }
110
+
111
+ const instance = new TestClass()
112
+ expect(instance.value).toBe('wrapped: test')
113
+ })
114
+
115
+ it('should work with multiple getters', () => {
116
+ const getterDecorator = decorator({
117
+ getter(original, name) {
118
+ return function (this: any) {
119
+ return `${String(name)}: ${original.call(this)}`
120
+ }
121
+ },
122
+ })
123
+
124
+ class TestClass {
125
+ private _first = 'first'
126
+ private _second = 'second'
127
+
128
+ @getterDecorator
129
+ get first() {
130
+ return this._first
131
+ }
132
+
133
+ @getterDecorator
134
+ get second() {
135
+ return this._second
136
+ }
137
+ }
138
+
139
+ const instance = new TestClass()
140
+ expect(instance.first).toBe('first: first')
141
+ expect(instance.second).toBe('second: second')
142
+ })
143
+ })
144
+
145
+ describe('Setter Decorators', () => {
146
+ it('should wrap setter calls', () => {
147
+ const setterDecorator = decorator({
148
+ setter(original, _name) {
149
+ return function (this: any, value: any) {
150
+ return original.call(this, `wrapped: ${value}`)
151
+ }
152
+ },
153
+ })
154
+
155
+ class TestClass {
156
+ private _value = ''
157
+
158
+ @setterDecorator
159
+ set value(v: string) {
160
+ this._value = v
161
+ }
162
+
163
+ get value() {
164
+ return this._value
165
+ }
166
+ }
167
+
168
+ const instance = new TestClass()
169
+ instance.value = 'test'
170
+ expect(instance.value).toBe('wrapped: test')
171
+ })
172
+
173
+ it('should work with multiple setters', () => {
174
+ const setterDecorator = decorator({
175
+ setter(original, name) {
176
+ return function (this: any, value: any) {
177
+ return original.call(this, `${String(name)}: ${value}`)
178
+ }
179
+ },
180
+ })
181
+
182
+ class TestClass {
183
+ private _first = ''
184
+ private _second = ''
185
+
186
+ @setterDecorator
187
+ set first(v: string) {
188
+ this._first = v
189
+ }
190
+
191
+ @setterDecorator
192
+ set second(v: string) {
193
+ this._second = v
194
+ }
195
+
196
+ get first() {
197
+ return this._first
198
+ }
199
+
200
+ get second() {
201
+ return this._second
202
+ }
203
+ }
204
+
205
+ const instance = new TestClass()
206
+ instance.first = 'test1'
207
+ instance.second = 'test2'
208
+ expect(instance.first).toBe('first: test1')
209
+ expect(instance.second).toBe('second: test2')
210
+ })
211
+ })
212
+
213
+ describe('Combined Decorators', () => {
214
+ it('should work with method and class decorators together', () => {
215
+ const myDecorator = decorator({
216
+ class: (target) => {
217
+ ;(target as any).decorated = true
218
+ return target
219
+ },
220
+ method(original, _name) {
221
+ return function (this: any, ...args: any[]) {
222
+ return `method: ${original.apply(this, args)}`
223
+ }
224
+ },
225
+ })
226
+
227
+ @myDecorator
228
+ class TestClass {
229
+ @myDecorator
230
+ greet(name: string) {
231
+ return `Hello ${name}`
232
+ }
233
+ }
234
+
235
+ expect((TestClass as any).decorated).toBe(true)
236
+ const instance = new TestClass()
237
+ expect(instance.greet('World')).toBe('method: Hello World')
238
+ })
239
+
240
+ it('should work with getter and setter decorators on different properties', () => {
241
+ const myDecorator = decorator({
242
+ getter(original, _name) {
243
+ return function (this: any) {
244
+ return `get: ${original.call(this)}`
245
+ }
246
+ },
247
+ setter(original, _name) {
248
+ return function (this: any, value: any) {
249
+ return original.call(this, `set: ${value}`)
250
+ }
251
+ },
252
+ })
253
+
254
+ class TestClass {
255
+ private _value1 = ''
256
+ private _value2 = ''
257
+
258
+ @myDecorator
259
+ get value1() {
260
+ return this._value1
261
+ }
262
+
263
+ @myDecorator
264
+ set value2(v: string) {
265
+ this._value2 = v
266
+ }
267
+ //@ts-ignore: The end-user should put a decorator here if modern, and not if legacy
268
+ @myDecorator
269
+ get value2() {
270
+ return this._value2
271
+ }
272
+ }
273
+
274
+ const instance = new TestClass()
275
+ instance.value2 = 'test'
276
+ expect(instance.value1).toBe('get: ')
277
+ expect(instance.value2).toBe('get: set: test')
278
+ })
279
+
280
+ it('should work with all decorator types together', () => {
281
+ const myDecorator = decorator({
282
+ class: (target) => {
283
+ ;(target as any).decorated = true
284
+ return target
285
+ },
286
+ method(original, _name) {
287
+ return function (this: any, ...args: any[]) {
288
+ return `method: ${original.apply(this, args)}`
289
+ }
290
+ },
291
+ getter(original, _name) {
292
+ return function (this: any) {
293
+ return `get: ${original.call(this)}`
294
+ }
295
+ },
296
+ default(...args) {
297
+ return args.length
298
+ },
299
+ })
300
+
301
+ @myDecorator
302
+ class TestClass {
303
+ value = 'initial'
304
+
305
+ @myDecorator
306
+ greet(name: string) {
307
+ return `Hello ${name}`
308
+ }
309
+
310
+ private _data = ''
311
+
312
+ @myDecorator
313
+ get data() {
314
+ return this._data
315
+ }
316
+
317
+ set data(v: string) {
318
+ this._data = v
319
+ }
320
+ }
321
+
322
+ // Test class decoration
323
+ expect((TestClass as any).decorated).toBe(true)
324
+
325
+ const instance = new TestClass()
326
+
327
+ // Test method decoration
328
+ expect(instance.greet('World')).toBe('method: Hello World')
329
+
330
+ // Test accessor decoration
331
+ instance.data = 'test'
332
+ expect(instance.data).toBe('get: test')
333
+ expect(myDecorator(1, 2, 3)).toBe(3)
334
+ })
335
+
336
+ it('should call all decorator types without changing behavior', () => {
337
+ const callLog: string[] = []
338
+
339
+ const noOpDecorator = decorator({
340
+ class(original) {
341
+ callLog.push('class decorator called')
342
+ return original // Return unchanged
343
+ },
344
+ method(original, name) {
345
+ callLog.push(`method decorator called for ${String(name)}`)
346
+ return original // Return unchanged
347
+ },
348
+ getter(original, name) {
349
+ callLog.push(`getter decorator called for ${String(name)}`)
350
+ return original // Return unchanged
351
+ },
352
+ setter(original, name) {
353
+ callLog.push(`setter decorator called for ${String(name)}`)
354
+ return original // Return unchanged
355
+ },
356
+ })
357
+
358
+ @noOpDecorator
359
+ class TestClass {
360
+ value = 'initial'
361
+
362
+ @noOpDecorator
363
+ greet(name: string) {
364
+ return `Hello ${name}`
365
+ }
366
+
367
+ private _data = ''
368
+
369
+ @noOpDecorator
370
+ get data() {
371
+ return this._data
372
+ }
373
+
374
+ set data(v: string) {
375
+ this._data = v
376
+ }
377
+ }
378
+
379
+ // Verify all decorators were called
380
+ expect(callLog).toContain('class decorator called')
381
+ expect(callLog).toContain('method decorator called for greet')
382
+ expect(callLog).toContain('getter decorator called for data')
383
+
384
+ const instance = new TestClass()
385
+
386
+ // Verify behavior is unchanged (no wrapping occurred)
387
+ expect(instance.greet('World')).toBe('Hello World') // No "method:" prefix
388
+ expect(instance.value).toBe('initial')
389
+
390
+ instance.data = 'test'
391
+ expect(instance.data).toBe('test') // No "get:" prefix
392
+ })
393
+
394
+ it('should call setter decorator without changing behavior', () => {
395
+ const callLog: string[] = []
396
+
397
+ const noOpDecorator = decorator({
398
+ setter(original, name) {
399
+ callLog.push(`setter decorator called for ${String(name)}`)
400
+ return original // Return unchanged
401
+ },
402
+ })
403
+
404
+ class TestClass {
405
+ private _value = ''
406
+
407
+ @noOpDecorator
408
+ set value(v: string) {
409
+ this._value = v
410
+ }
411
+
412
+ get value() {
413
+ return this._value
414
+ }
415
+ }
416
+
417
+ // Verify setter decorator was called
418
+ expect(callLog).toContain('setter decorator called for value')
419
+
420
+ const instance = new TestClass()
421
+
422
+ // Verify behavior is unchanged (no wrapping occurred)
423
+ instance.value = 'test'
424
+ expect(instance.value).toBe('test') // No wrapping
425
+ })
426
+ })
427
+
428
+ describe('Error Handling', () => {
429
+ it('should throw error when decorator is applied to wrong target', () => {
430
+ const methodOnlyDecorator = decorator({
431
+ method(original, _name) {
432
+ return original
433
+ },
434
+ })
435
+
436
+ expect(() => {
437
+ class TestClass {
438
+ // @ts-ignore
439
+ @methodOnlyDecorator
440
+ value = 'test'
441
+ }
442
+ void new TestClass()
443
+ }).toThrow('Decorator cannot be applied to a field')
444
+ })
445
+
446
+ it('should throw error when class decorator is applied to method', () => {
447
+ const classOnlyDecorator = decorator({
448
+ class: (target) => target,
449
+ })
450
+
451
+ expect(() => {
452
+ class TestClass {
453
+ // @ts-ignore
454
+ @classOnlyDecorator
455
+ method() {}
456
+ }
457
+ void new TestClass()
458
+ }).toThrow('Decorator cannot be applied to a method')
459
+ })
460
+
461
+ it('should throw error when getter decorator is applied to method', () => {
462
+ const getterOnlyDecorator = decorator({
463
+ getter(original, _name) {
464
+ return original
465
+ },
466
+ })
467
+
468
+ expect(() => {
469
+ class TestClass {
470
+ // @ts-ignore
471
+ @getterOnlyDecorator
472
+ method() {}
473
+ }
474
+ void new TestClass()
475
+ }).toThrow('Decorator cannot be applied to a method')
476
+ })
477
+
478
+ it('should throw error when decorating a field', () => {
479
+ const anyDecorator = decorator({
480
+ method(original, _name) {
481
+ return original
482
+ },
483
+ })
484
+
485
+ expect(() => {
486
+ class TestClass {
487
+ // @ts-ignore
488
+ @anyDecorator
489
+ field = 'value'
490
+ }
491
+ void new TestClass()
492
+ }).toThrow('Decorator cannot be applied to a field')
493
+ })
494
+ })
495
+ })
@@ -0,0 +1,205 @@
1
+ // biome-ignore-all lint/suspicious/noConfusingVoidType: We *love* voids
2
+ // Standardized decorator system that works with both Legacy and Modern decorators
3
+
4
+ import { isConstructor } from './utils'
5
+
6
+ export class DecoratorError extends Error {
7
+ constructor(message: string) {
8
+ super(message)
9
+ this.name = 'DecoratorException'
10
+ }
11
+ }
12
+ //#region all decorator types
13
+
14
+ // Used for get/set and method decorators
15
+ export type LegacyPropertyDecorator<T> = (
16
+ target: T,
17
+ name: string | symbol,
18
+ descriptor: PropertyDescriptor
19
+ ) => any
20
+
21
+ export type LegacyClassDecorator<T> = (target: T) => any
22
+
23
+ export type ModernMethodDecorator<T> = (target: T, context: ClassMethodDecoratorContext) => any
24
+
25
+ export type ModernGetterDecorator<T> = (target: T, context: ClassGetterDecoratorContext) => any
26
+
27
+ export type ModernSetterDecorator<T> = (target: T, context: ClassSetterDecoratorContext) => any
28
+
29
+ export type ModernAccessorDecorator<T> = (target: T, context: ClassAccessorDecoratorContext) => any
30
+
31
+ export type ModernClassDecorator<T> = (target: T, context: ClassDecoratorContext) => any
32
+
33
+ //#endregion
34
+
35
+ type DDMethod<T> = (
36
+ original: (this: T, ...args: any[]) => any,
37
+ name: PropertyKey
38
+ ) => ((this: T, ...args: any[]) => any) | void
39
+
40
+ type DDGetter<T> = (original: (this: T) => any, name: PropertyKey) => ((this: T) => any) | void
41
+
42
+ type DDSetter<T> = (
43
+ original: (this: T, value: any) => void,
44
+ name: PropertyKey
45
+ ) => ((this: T, value: any) => void) | void
46
+
47
+ type DDClass<T> = <Ctor extends new (...args: any[]) => T = new (...args: any[]) => T>(
48
+ target: Ctor
49
+ ) => Ctor | void
50
+ export interface DecoratorDescription<T> {
51
+ method?: DDMethod<T>
52
+ class?: DDClass<T>
53
+ getter?: DDGetter<T>
54
+ setter?: DDSetter<T>
55
+ default?: (...args: any[]) => any
56
+ }
57
+
58
+ export type Decorator<T, Description extends DecoratorDescription<T>> = (Description extends {
59
+ method: DDMethod<T>
60
+ }
61
+ ? LegacyPropertyDecorator<T> & ModernMethodDecorator<T>
62
+ : unknown) &
63
+ (Description extends { class: DDClass<new (...args: any[]) => T> }
64
+ ? LegacyClassDecorator<new (...args: any[]) => T> &
65
+ ModernClassDecorator<new (...args: any[]) => T>
66
+ : unknown) &
67
+ (Description extends { getter: DDGetter<T> }
68
+ ? LegacyPropertyDecorator<T> & ModernGetterDecorator<T> & ModernAccessorDecorator<T>
69
+ : unknown) &
70
+ (Description extends { setter: DDSetter<T> }
71
+ ? LegacyPropertyDecorator<T> & ModernSetterDecorator<T> & ModernAccessorDecorator<T>
72
+ : unknown) &
73
+ (Description extends { default: infer Signature } ? Signature : unknown)
74
+
75
+ export type DecoratorFactory<T> = <Description extends DecoratorDescription<T>>(
76
+ description: Description
77
+ ) => (Description extends { method: DDMethod<T> }
78
+ ? LegacyPropertyDecorator<T> & ModernMethodDecorator<T>
79
+ : unknown) &
80
+ (Description extends { class: DDClass<new (...args: any[]) => T> }
81
+ ? LegacyClassDecorator<new (...args: any[]) => T> &
82
+ ModernClassDecorator<new (...args: any[]) => T>
83
+ : unknown) &
84
+ (Description extends { getter: DDGetter<T> }
85
+ ? LegacyPropertyDecorator<T> & ModernGetterDecorator<T> & ModernAccessorDecorator<T>
86
+ : unknown) &
87
+ (Description extends { setter: DDSetter<T> }
88
+ ? LegacyPropertyDecorator<T> & ModernSetterDecorator<T> & ModernAccessorDecorator<T>
89
+ : unknown) &
90
+ (Description extends { default: infer Signature } ? Signature : unknown)
91
+
92
+ export function legacyDecorator<T = any>(description: DecoratorDescription<T>): any {
93
+ return function (
94
+ target: any,
95
+ propertyKey?: PropertyKey,
96
+ descriptor?: PropertyDescriptor,
97
+ ...args: any[]
98
+ ) {
99
+ if (propertyKey === undefined) {
100
+ if (isConstructor(target)) {
101
+ if (!('class' in description)) throw new Error('Decorator cannot be applied to a class')
102
+ return description.class?.(target)
103
+ }
104
+ } else if (typeof target === 'object' && ['string', 'symbol'].includes(typeof propertyKey)) {
105
+ if (!descriptor) throw new Error('Decorator cannot be applied to a field')
106
+ else if (typeof descriptor === 'object' && 'configurable' in descriptor) {
107
+ if ('get' in descriptor || 'set' in descriptor) {
108
+ if (!('getter' in description || 'setter' in description))
109
+ throw new Error('Decorator cannot be applied to a getter or setter')
110
+ if ('getter' in description) {
111
+ const newGetter = description.getter?.(descriptor.get, propertyKey)
112
+ if (newGetter) descriptor.get = newGetter
113
+ }
114
+ if ('setter' in description) {
115
+ const newSetter = description.setter?.(descriptor.set, propertyKey)
116
+ if (newSetter) descriptor.set = newSetter
117
+ }
118
+ return descriptor
119
+ } else if (typeof descriptor.value === 'function') {
120
+ if (!('method' in description)) throw new Error('Decorator cannot be applied to a method')
121
+ const newMethod = description.method?.(descriptor.value, propertyKey)
122
+ if (newMethod) descriptor.value = newMethod
123
+ return descriptor
124
+ }
125
+ }
126
+ }
127
+ if (!('default' in description))
128
+ throw new Error('Decorator do not have a default implementation')
129
+ return description.default.call(this, target, propertyKey, descriptor, ...args)
130
+ }
131
+ }
132
+
133
+ export function modernDecorator<T = any>(description: DecoratorDescription<T>): any {
134
+ return function (target: any, context?: DecoratorContext, ...args: any[]) {
135
+ if (!context?.kind || typeof context.kind !== 'string') {
136
+ if (!('default' in description))
137
+ throw new Error('Decorator do not have a default implementation')
138
+ return description.default.call(this, target, context, ...args)
139
+ }
140
+ switch (context.kind) {
141
+ case 'class':
142
+ if (!('class' in description)) throw new Error('Decorator cannot be applied to a class')
143
+ return description.class?.(target)
144
+ case 'field':
145
+ throw new Error('Decorator cannot be applied to a field')
146
+ case 'getter':
147
+ if (!('getter' in description)) throw new Error('Decorator cannot be applied to a getter')
148
+ return description.getter?.(target, context.name)
149
+ case 'setter':
150
+ if (!('setter' in description)) throw new Error('Decorator cannot be applied to a setter')
151
+ return description.setter?.(target, context.name)
152
+ case 'method':
153
+ if (!('method' in description)) throw new Error('Decorator cannot be applied to a method')
154
+ return description.method?.(target, context.name)
155
+ case 'accessor': {
156
+ if (!('getter' in description || 'setter' in description))
157
+ throw new Error('Decorator cannot be applied to a getter or setter')
158
+ const rv: Partial<ClassAccessorDecoratorResult<any, any>> = {}
159
+ if ('getter' in description) {
160
+ const newGetter = description.getter?.(target.get, context.name)
161
+ if (newGetter) rv.get = newGetter
162
+ }
163
+ if ('setter' in description) {
164
+ const newSetter = description.setter?.(target.set, context.name)
165
+ if (newSetter) rv.set = newSetter
166
+ }
167
+ return rv
168
+ }
169
+ //return description.accessor?.(target, context.name, target)
170
+ }
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Detects if the decorator is being called in modern (Modern) or legacy (Legacy) mode
176
+ * based on the arguments passed to the decorator function
177
+ */
178
+ function detectDecoratorMode(
179
+ _target: any,
180
+ contextOrKey?: any,
181
+ _descriptor?: any
182
+ ): 'modern' | 'legacy' {
183
+ // Modern decorators have a context object as the second parameter
184
+ // Legacy decorators have a string/symbol key as the second parameter
185
+ if (
186
+ typeof contextOrKey === 'object' &&
187
+ contextOrKey !== null &&
188
+ typeof contextOrKey.kind === 'string'
189
+ ) {
190
+ return 'modern'
191
+ }
192
+ return 'legacy'
193
+ }
194
+
195
+ export const decorator: DecoratorFactory<any> = (description: DecoratorDescription<any>) => {
196
+ return ((target: any, contextOrKey?: any, ...args: any[]) => {
197
+ const mode = detectDecoratorMode(target, contextOrKey, args[0])
198
+ return mode === 'modern'
199
+ ? modernDecorator(description)(target, contextOrKey, ...args)
200
+ : legacyDecorator(description)(target, contextOrKey, ...args)
201
+ }) as any
202
+ }
203
+
204
+ export type GenericClassDecorator<T> = LegacyClassDecorator<new (...args: any[]) => T> &
205
+ ModernClassDecorator<new (...args: any[]) => T>