mutts 1.0.0 → 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.
Files changed (85) hide show
  1. package/README.md +24 -2
  2. package/dist/chunks/_tslib-C-cuVLvZ.js +73 -0
  3. package/dist/chunks/_tslib-C-cuVLvZ.js.map +1 -0
  4. package/dist/chunks/_tslib-CMEnd0VE.esm.js +68 -0
  5. package/dist/chunks/_tslib-CMEnd0VE.esm.js.map +1 -0
  6. package/dist/chunks/{decorator-BXsign4Z.js → decorator-D4DU97Zg.js} +70 -4
  7. package/dist/chunks/decorator-D4DU97Zg.js.map +1 -0
  8. package/dist/chunks/{decorator-CPbZNnsX.esm.js → decorator-GnHw1Az7.esm.js} +67 -5
  9. package/dist/chunks/decorator-GnHw1Az7.esm.js.map +1 -0
  10. package/dist/chunks/index-DBScoeCX.esm.js +1960 -0
  11. package/dist/chunks/index-DBScoeCX.esm.js.map +1 -0
  12. package/dist/chunks/index-DOTmXL89.js +1983 -0
  13. package/dist/chunks/index-DOTmXL89.js.map +1 -0
  14. package/dist/decorator.d.ts +58 -1
  15. package/dist/decorator.esm.js +1 -1
  16. package/dist/decorator.js +1 -1
  17. package/dist/destroyable.d.ts +42 -0
  18. package/dist/destroyable.esm.js +19 -1
  19. package/dist/destroyable.esm.js.map +1 -1
  20. package/dist/destroyable.js +19 -1
  21. package/dist/destroyable.js.map +1 -1
  22. package/dist/eventful.d.ts +10 -1
  23. package/dist/eventful.esm.js +5 -27
  24. package/dist/eventful.esm.js.map +1 -1
  25. package/dist/eventful.js +15 -37
  26. package/dist/eventful.js.map +1 -1
  27. package/dist/index.d.ts +52 -3
  28. package/dist/index.esm.js +3 -2
  29. package/dist/index.esm.js.map +1 -1
  30. package/dist/index.js +18 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/indexable.d.ts +26 -0
  33. package/dist/indexable.esm.js +6 -0
  34. package/dist/indexable.esm.js.map +1 -1
  35. package/dist/indexable.js +6 -0
  36. package/dist/indexable.js.map +1 -1
  37. package/dist/mutts.umd.js +1 -1
  38. package/dist/mutts.umd.js.map +1 -1
  39. package/dist/mutts.umd.min.js +1 -1
  40. package/dist/mutts.umd.min.js.map +1 -1
  41. package/dist/promiseChain.d.ts +10 -0
  42. package/dist/promiseChain.esm.js +6 -0
  43. package/dist/promiseChain.esm.js.map +1 -1
  44. package/dist/promiseChain.js +6 -0
  45. package/dist/promiseChain.js.map +1 -1
  46. package/dist/reactive.d.ts +258 -20
  47. package/dist/reactive.esm.js +4 -1454
  48. package/dist/reactive.esm.js.map +1 -1
  49. package/dist/reactive.js +29 -1466
  50. package/dist/reactive.js.map +1 -1
  51. package/dist/std-decorators.d.ts +35 -0
  52. package/dist/std-decorators.esm.js +36 -1
  53. package/dist/std-decorators.esm.js.map +1 -1
  54. package/dist/std-decorators.js +36 -1
  55. package/dist/std-decorators.js.map +1 -1
  56. package/docs/mixin.md +229 -0
  57. package/docs/reactive.md +7931 -458
  58. package/package.json +1 -2
  59. package/dist/chunks/decorator-BXsign4Z.js.map +0 -1
  60. package/dist/chunks/decorator-CPbZNnsX.esm.js.map +0 -1
  61. package/src/decorator.test.ts +0 -495
  62. package/src/decorator.ts +0 -205
  63. package/src/destroyable.test.ts +0 -155
  64. package/src/destroyable.ts +0 -158
  65. package/src/eventful.test.ts +0 -380
  66. package/src/eventful.ts +0 -69
  67. package/src/index.ts +0 -7
  68. package/src/indexable.test.ts +0 -388
  69. package/src/indexable.ts +0 -124
  70. package/src/promiseChain.test.ts +0 -201
  71. package/src/promiseChain.ts +0 -99
  72. package/src/reactive/array.test.ts +0 -923
  73. package/src/reactive/array.ts +0 -352
  74. package/src/reactive/core.test.ts +0 -1663
  75. package/src/reactive/core.ts +0 -866
  76. package/src/reactive/index.ts +0 -28
  77. package/src/reactive/interface.test.ts +0 -1477
  78. package/src/reactive/interface.ts +0 -231
  79. package/src/reactive/map.test.ts +0 -866
  80. package/src/reactive/map.ts +0 -162
  81. package/src/reactive/set.test.ts +0 -289
  82. package/src/reactive/set.ts +0 -142
  83. package/src/std-decorators.test.ts +0 -679
  84. package/src/std-decorators.ts +0 -182
  85. package/src/utils.ts +0 -52
package/src/decorator.ts DELETED
@@ -1,205 +0,0 @@
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>
@@ -1,155 +0,0 @@
1
- import {
2
- allocated,
3
- allocatedValues,
4
- Destroyable,
5
- DestructionError,
6
- destructor,
7
- } from './destroyable'
8
-
9
- function tick(ms: number = 0) {
10
- return new Promise((resolve) => setTimeout(resolve, ms))
11
- }
12
-
13
- const gc = global.gc
14
-
15
- async function collectGarbages() {
16
- await tick()
17
- gc!()
18
- await tick()
19
- }
20
-
21
- describe('Destroyable', () => {
22
- describe('with base class and destructor object', () => {
23
- it('should create destroyable class with custom destructor', async () => {
24
- let receivedAllocated: any = null
25
-
26
- class MyClass extends Destroyable({
27
- destructor(allocated) {
28
- receivedAllocated = allocated
29
- },
30
- }) {
31
- constructor(public name: string) {
32
- super()
33
- this[allocatedValues].name = name
34
- }
35
- }
36
-
37
- ;(() => {
38
- const obj = new MyClass('test')
39
- expect(obj.name).toBe('test')
40
- })()
41
- await collectGarbages()
42
- expect(receivedAllocated.name).toBe('test')
43
- })
44
-
45
- it('should pass constructor arguments to base class', () => {
46
- class BaseClass {
47
- constructor(public value: number) {}
48
- }
49
-
50
- const DestroyableBaseClass = Destroyable(BaseClass, {
51
- destructor: () => {},
52
- })
53
-
54
- const obj = new DestroyableBaseClass(42)
55
- expect(obj.value).toBe(42)
56
- })
57
-
58
- it('should throw error when accessing destroyed object', () => {
59
- class MyClass {
60
- constructor(public name: string) {}
61
- }
62
-
63
- const DestroyableMyClass = Destroyable(MyClass, {
64
- destructor: () => {},
65
- })
66
-
67
- const obj = new DestroyableMyClass('test')
68
- DestroyableMyClass.destroy(obj)
69
-
70
- expect(() => obj.name).toThrow(DestructionError)
71
- expect(() => {
72
- obj.name = 'value'
73
- }).toThrow(DestructionError)
74
- })
75
- })
76
-
77
- describe('with destructor object only', () => {
78
- it('should create destroyable class from scratch', () => {
79
- let destructorCalled = false
80
-
81
- const DestroyableClass = Destroyable({
82
- destructor: () => {
83
- destructorCalled = true
84
- },
85
- })
86
-
87
- const obj = new DestroyableClass()
88
- expect(DestroyableClass.isDestroyable(obj)).toBe(true)
89
- expect(destructorCalled).toBe(false)
90
-
91
- const result = DestroyableClass.destroy(obj)
92
- expect(result).toBe(true)
93
- expect(destructorCalled).toBe(true)
94
- })
95
- })
96
-
97
- describe('with base class only', () => {
98
- it('should create destroyable class with default destructor', () => {
99
- class MyClass {
100
- constructor(public name: string) {}
101
- }
102
-
103
- const DestroyableMyClass = Destroyable(MyClass)
104
-
105
- expect(() => new DestroyableMyClass('test')).toThrow(DestructionError)
106
- })
107
- })
108
-
109
- describe('class with [destructor] method', () => {
110
- it('should call [destructor] method with allocated values', async () => {
111
- let receivedAllocated: any = null
112
-
113
- class MyClass extends Destroyable() {
114
- constructor(public name: string) {
115
- super()
116
- this[allocatedValues].name = name
117
- }
118
- [destructor](allocated: any) {
119
- receivedAllocated = allocated
120
- }
121
- }
122
-
123
- ;(() => {
124
- const obj = new MyClass('test')
125
- expect(obj.name).toBe('test')
126
- })()
127
- await collectGarbages()
128
- expect(receivedAllocated.name).toBe('test')
129
- })
130
- })
131
- describe('decorators usage', () => {
132
- it('should collect allocated from decorators', async () => {
133
- let receivedAllocated: any = null
134
-
135
- class MyClass extends Destroyable() {
136
- @allocated
137
- accessor name: string
138
- constructor(name: string) {
139
- super()
140
- this.name = name
141
- }
142
- [destructor](allocated: any) {
143
- receivedAllocated = allocated
144
- }
145
- }
146
-
147
- ;(() => {
148
- const obj = new MyClass('test')
149
- expect(obj.name).toBe('test')
150
- })()
151
- await collectGarbages()
152
- expect(receivedAllocated.name).toBe('test')
153
- })
154
- })
155
- })
@@ -1,158 +0,0 @@
1
- import { decorator } from './decorator'
2
-
3
- // Integrated with `using` statement via Symbol.dispose
4
- const fr = new FinalizationRegistry<() => void>((f) => f())
5
- export const destructor = Symbol('destructor')
6
- export const allocatedValues = Symbol('allocated')
7
- export class DestructionError extends Error {
8
- static throw<_T = void>(msg: string) {
9
- return () => {
10
- throw new DestructionError(msg)
11
- }
12
- }
13
- constructor(msg: string) {
14
- super(`Object is destroyed. ${msg}`)
15
- this.name = 'DestroyedAccessError'
16
- }
17
- }
18
- const destroyedHandler = {
19
- [Symbol.toStringTag]: 'MutTs Destroyable',
20
- get: DestructionError.throw('Cannot access destroyed object'),
21
- set: DestructionError.throw('Cannot access destroyed object'),
22
- } as const
23
-
24
- abstract class AbstractDestroyable<Allocated> {
25
- abstract [destructor](allocated: Allocated): void
26
- [Symbol.dispose](): void {
27
- this[destructor](this as unknown as Allocated)
28
- }
29
- }
30
-
31
- interface Destructor<Allocated> {
32
- destructor(allocated: Allocated): void
33
- }
34
-
35
- export function Destroyable<
36
- T extends new (
37
- ...args: any[]
38
- ) => any,
39
- Allocated extends Partial<typeof this>,
40
- >(
41
- base: T,
42
- destructorObj: Destructor<Allocated>
43
- ): (new (
44
- ...args: ConstructorParameters<T>
45
- ) => InstanceType<T> & { [allocatedValues]: Allocated }) & {
46
- destroy(obj: InstanceType<T>): boolean
47
- isDestroyable(obj: InstanceType<T>): boolean
48
- }
49
-
50
- export function Destroyable<Allocated extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
51
- destructorObj: Destructor<Allocated>
52
- ): (new () => { [allocatedValues]: Allocated }) & {
53
- destroy(obj: any): boolean
54
- isDestroyable(obj: any): boolean
55
- }
56
-
57
- export function Destroyable<
58
- T extends new (
59
- ...args: any[]
60
- ) => any,
61
- Allocated extends Record<PropertyKey, any> = Record<PropertyKey, any>,
62
- >(
63
- base: T
64
- ): (new (
65
- ...args: ConstructorParameters<T>
66
- ) => AbstractDestroyable<Allocated> & InstanceType<T> & { [allocatedValues]: Allocated }) & {
67
- destroy(obj: InstanceType<T>): boolean
68
- isDestroyable(obj: InstanceType<T>): boolean
69
- }
70
-
71
- export function Destroyable<
72
- Allocated extends Record<PropertyKey, any> = Record<PropertyKey, any>,
73
- >(): abstract new () => (AbstractDestroyable<Allocated> & {
74
- [allocatedValues]: Allocated
75
- }) & {
76
- destroy(obj: any): boolean
77
- isDestroyable(obj: any): boolean
78
- }
79
-
80
- export function Destroyable<
81
- T extends new (
82
- ...args: any[]
83
- ) => any,
84
- Allocated extends Record<PropertyKey, any> = Record<PropertyKey, any>,
85
- >(base?: T | Destructor<Allocated>, destructorObj?: Destructor<Allocated>) {
86
- if (base && typeof base !== 'function') {
87
- destructorObj = base as Destructor<Allocated>
88
- base = undefined
89
- }
90
- if (!base) {
91
- base = class {} as T
92
- }
93
-
94
- return class Destroyable extends (base as T) {
95
- static readonly destructors = new WeakMap<any, () => void>()
96
- static destroy(obj: Destroyable) {
97
- const destructor = Destroyable.destructors.get(obj)
98
- if (!destructor) return false
99
- fr.unregister(obj)
100
- Destroyable.destructors.delete(obj)
101
- Object.setPrototypeOf(obj, new Proxy({}, destroyedHandler))
102
- // Clear all own properties
103
- for (const key of Object.getOwnPropertyNames(obj)) {
104
- delete (obj as any)[key]
105
- }
106
- destructor()
107
- return true
108
- }
109
- static isDestroyable(obj: Destroyable) {
110
- return Destroyable.destructors.has(obj)
111
- }
112
-
113
- declare [forwardProperties]: PropertyKey[]
114
- readonly [allocatedValues]: Allocated
115
- constructor(...args: any[]) {
116
- super(...args)
117
- const allocated = {} as Allocated
118
- this[allocatedValues] = allocated
119
- // @ts-expect-error `this` is an AbstractDestroyable
120
- const myDestructor = destructorObj?.destructor ?? this[destructor]
121
- if (!myDestructor) {
122
- throw new DestructionError('Destructor is not defined')
123
- }
124
- function destruction() {
125
- myDestructor(allocated)
126
- }
127
- Destroyable.destructors.set(this, destruction)
128
- fr.register(this, destruction, this)
129
- }
130
- }
131
- }
132
-
133
- const forwardProperties = Symbol('forwardProperties')
134
- export const allocated = decorator({
135
- setter(original, propertyKey) {
136
- return function (value) {
137
- this[allocatedValues][propertyKey] = value
138
- return original.call(this, value)
139
- }
140
- },
141
- })
142
-
143
- export function callOnGC(cb: () => void) {
144
- let called = false
145
- const forward = () => {
146
- if (called) return
147
- called = true
148
- cb()
149
- }
150
- fr.register(forward, cb, cb)
151
- return forward
152
- }
153
-
154
- // Context Manager Protocol for `with` statement integration
155
- export interface ContextManager<T = any> {
156
- [Symbol.dispose](): void
157
- value?: T
158
- }