mutts 1.0.5 → 1.0.7

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 (114) hide show
  1. package/README.md +2 -1
  2. package/dist/browser.d.ts +2 -0
  3. package/dist/browser.esm.js +70 -0
  4. package/dist/browser.esm.js.map +1 -0
  5. package/dist/browser.js +161 -0
  6. package/dist/browser.js.map +1 -0
  7. package/dist/chunks/{index-Cvxdw6Ax.js → index-BFYK02LG.js} +5377 -4059
  8. package/dist/chunks/index-BFYK02LG.js.map +1 -0
  9. package/dist/chunks/{index-qiWwozOc.esm.js → index-CNR6QRUl.esm.js} +5247 -3963
  10. package/dist/chunks/index-CNR6QRUl.esm.js.map +1 -0
  11. package/dist/mutts.umd.js +1 -1
  12. package/dist/mutts.umd.js.map +1 -1
  13. package/dist/mutts.umd.min.js +1 -1
  14. package/dist/mutts.umd.min.js.map +1 -1
  15. package/dist/node.d.ts +2 -0
  16. package/dist/node.esm.js +45 -0
  17. package/dist/node.esm.js.map +1 -0
  18. package/dist/node.js +136 -0
  19. package/dist/node.js.map +1 -0
  20. package/docs/ai/api-reference.md +0 -2
  21. package/docs/ai/manual.md +14 -95
  22. package/docs/reactive/advanced.md +7 -111
  23. package/docs/reactive/collections.md +0 -125
  24. package/docs/reactive/core.md +27 -24
  25. package/docs/reactive/debugging.md +168 -0
  26. package/docs/reactive/project.md +1 -1
  27. package/docs/reactive/scan.md +78 -0
  28. package/docs/reactive.md +8 -6
  29. package/docs/std-decorators.md +1 -0
  30. package/docs/zone.md +88 -0
  31. package/package.json +47 -65
  32. package/src/async/browser.ts +87 -0
  33. package/src/async/index.ts +8 -0
  34. package/src/async/node.ts +46 -0
  35. package/src/decorator.ts +15 -9
  36. package/src/destroyable.ts +4 -4
  37. package/src/index.ts +54 -0
  38. package/src/indexable.ts +42 -0
  39. package/src/mixins.ts +2 -2
  40. package/src/reactive/array.ts +149 -141
  41. package/src/reactive/buffer.ts +168 -0
  42. package/src/reactive/change.ts +3 -3
  43. package/src/reactive/debug.ts +1 -1
  44. package/src/reactive/deep-touch.ts +1 -1
  45. package/src/reactive/deep-watch.ts +1 -1
  46. package/src/reactive/effect-context.ts +15 -91
  47. package/src/reactive/effects.ts +138 -170
  48. package/src/reactive/index.ts +10 -13
  49. package/src/reactive/interface.ts +20 -33
  50. package/src/reactive/map.ts +48 -61
  51. package/src/reactive/memoize.ts +87 -31
  52. package/src/reactive/project.ts +43 -22
  53. package/src/reactive/proxy.ts +18 -43
  54. package/src/reactive/record.ts +3 -3
  55. package/src/reactive/register.ts +5 -7
  56. package/src/reactive/registry.ts +59 -0
  57. package/src/reactive/set.ts +42 -56
  58. package/src/reactive/tracking.ts +5 -62
  59. package/src/reactive/types.ts +79 -19
  60. package/src/std-decorators.ts +9 -9
  61. package/src/utils.ts +203 -19
  62. package/src/zone.ts +127 -0
  63. package/dist/chunks/_tslib-BgjropY9.js +0 -81
  64. package/dist/chunks/_tslib-BgjropY9.js.map +0 -1
  65. package/dist/chunks/_tslib-Mzh1rNsX.esm.js +0 -75
  66. package/dist/chunks/_tslib-Mzh1rNsX.esm.js.map +0 -1
  67. package/dist/chunks/decorator-DLvrD0UF.js +0 -265
  68. package/dist/chunks/decorator-DLvrD0UF.js.map +0 -1
  69. package/dist/chunks/decorator-DqiszP7i.esm.js +0 -253
  70. package/dist/chunks/decorator-DqiszP7i.esm.js.map +0 -1
  71. package/dist/chunks/index-Cvxdw6Ax.js.map +0 -1
  72. package/dist/chunks/index-qiWwozOc.esm.js.map +0 -1
  73. package/dist/decorator.d.ts +0 -107
  74. package/dist/decorator.esm.js +0 -2
  75. package/dist/decorator.esm.js.map +0 -1
  76. package/dist/decorator.js +0 -11
  77. package/dist/decorator.js.map +0 -1
  78. package/dist/destroyable.d.ts +0 -90
  79. package/dist/destroyable.esm.js +0 -109
  80. package/dist/destroyable.esm.js.map +0 -1
  81. package/dist/destroyable.js +0 -116
  82. package/dist/destroyable.js.map +0 -1
  83. package/dist/eventful.d.ts +0 -20
  84. package/dist/eventful.esm.js +0 -66
  85. package/dist/eventful.esm.js.map +0 -1
  86. package/dist/eventful.js +0 -68
  87. package/dist/eventful.js.map +0 -1
  88. package/dist/index.d.ts +0 -19
  89. package/dist/index.esm.js +0 -8
  90. package/dist/index.esm.js.map +0 -1
  91. package/dist/index.js +0 -95
  92. package/dist/index.js.map +0 -1
  93. package/dist/indexable.d.ts +0 -243
  94. package/dist/indexable.esm.js +0 -285
  95. package/dist/indexable.esm.js.map +0 -1
  96. package/dist/indexable.js +0 -291
  97. package/dist/indexable.js.map +0 -1
  98. package/dist/promiseChain.d.ts +0 -21
  99. package/dist/promiseChain.esm.js +0 -78
  100. package/dist/promiseChain.esm.js.map +0 -1
  101. package/dist/promiseChain.js +0 -80
  102. package/dist/promiseChain.js.map +0 -1
  103. package/dist/reactive.d.ts +0 -885
  104. package/dist/reactive.esm.js +0 -5
  105. package/dist/reactive.esm.js.map +0 -1
  106. package/dist/reactive.js +0 -59
  107. package/dist/reactive.js.map +0 -1
  108. package/dist/std-decorators.d.ts +0 -52
  109. package/dist/std-decorators.esm.js +0 -196
  110. package/dist/std-decorators.esm.js.map +0 -1
  111. package/dist/std-decorators.js +0 -204
  112. package/dist/std-decorators.js.map +0 -1
  113. package/src/reactive/mapped.ts +0 -129
  114. package/src/reactive/zone.ts +0 -208
@@ -0,0 +1,87 @@
1
+ import { Hook, Restorer, asyncHooks } from '.'
2
+
3
+ const hooks = new Set<Hook>()
4
+
5
+ asyncHooks.addHook = function (hook: Hook) {
6
+ hooks.add(hook)
7
+ return () => {
8
+ hooks.delete(hook)
9
+ }
10
+ }
11
+
12
+ export * from '../index'
13
+
14
+ function wrap<Args extends any[], R>(fn: ((...args: Args) => R) | null | undefined) {
15
+ if (typeof fn !== 'function') return fn
16
+ const restorers = new Set<Restorer>()
17
+ for (const hook of hooks) restorers.add(hook())
18
+
19
+ return function (this: any, ...args: Args) {
20
+ const undoers = new Set<() => void>()
21
+ for (const restore of restorers) undoers.add(restore())
22
+ try {
23
+ return fn.apply(this, args)
24
+ } finally {
25
+ for (const undo of undoers) undo()
26
+ }
27
+ }
28
+ }
29
+
30
+ const originals = {
31
+ then: Promise.prototype.then,
32
+ catch: Promise.prototype.catch,
33
+ finally: Promise.prototype.finally,
34
+ setTimeout: globalThis.setTimeout,
35
+ setInterval: globalThis.setInterval,
36
+ setImmediate: globalThis.setImmediate,
37
+ requestAnimationFrame: globalThis.requestAnimationFrame,
38
+ queueMicrotask: globalThis.queueMicrotask,
39
+ }
40
+
41
+ Promise.prototype.then = function <T, R1, R2>(
42
+ this: Promise<T>,
43
+ onFulfilled?: ((value: T) => R1 | PromiseLike<R1>) | null,
44
+ onRejected?: ((reason: any) => R2 | PromiseLike<R2>) | null
45
+ ): Promise<R1 | R2> {
46
+ return originals.then.call(this, wrap(onFulfilled), wrap(onRejected))
47
+ }
48
+
49
+ Promise.prototype.catch = function <T>(
50
+ this: Promise<T>,
51
+ onRejected?: ((reason: any) => T | PromiseLike<T>) | null
52
+ ): Promise<T> {
53
+ return originals.catch.call(this, wrap(onRejected))
54
+ }
55
+
56
+ Promise.prototype.finally = function <T>(
57
+ this: Promise<T>,
58
+ onFinally?: (() => void) | null
59
+ ): Promise<T> {
60
+ return originals.finally.call(this, wrap(onFinally))
61
+ }
62
+
63
+ globalThis.setTimeout = ((callback: Function, ...args: any[]) => {
64
+ return originals.setTimeout.call(globalThis, wrap(callback as any), ...args)
65
+ }) as any
66
+
67
+ globalThis.setInterval = ((callback: Function, ...args: any[]) => {
68
+ return originals.setInterval.call(globalThis, wrap(callback as any), ...args)
69
+ }) as any
70
+
71
+ if (originals.setImmediate) {
72
+ globalThis.setImmediate = ((callback: Function, ...args: any[]) => {
73
+ return originals.setImmediate.call(globalThis, wrap(callback as any), ...args)
74
+ }) as any
75
+ }
76
+
77
+ if (originals.requestAnimationFrame) {
78
+ globalThis.requestAnimationFrame = (callback: FrameRequestCallback) => {
79
+ return originals.requestAnimationFrame.call(globalThis, wrap(callback))
80
+ }
81
+ }
82
+
83
+ if (originals.queueMicrotask) {
84
+ globalThis.queueMicrotask = (callback: VoidFunction): void => {
85
+ originals.queueMicrotask.call(globalThis, wrap(callback))
86
+ }
87
+ }
@@ -0,0 +1,8 @@
1
+ export type Restorer = () => () => void
2
+ export type Hook = () => Restorer
3
+
4
+ export const asyncHooks = {
5
+ addHook(_hook: Hook): () => void {
6
+ throw 'One must import the library from the server or the client side'
7
+ }
8
+ }
@@ -0,0 +1,46 @@
1
+ import { createHook } from 'node:async_hooks'
2
+ import { Hook, Restorer, asyncHooks } from '.'
3
+
4
+ const hooks = new Set<Hook>()
5
+ const restorersPerAsyncId = new Map<number, Set<Restorer>>()
6
+ const undoersPerAsyncId = new Map<number, Set<() => void>>()
7
+
8
+ createHook({
9
+ init(asyncId) {
10
+ const restorers = new Set<Restorer>()
11
+ for (const hook of hooks) {
12
+ restorers.add(hook())
13
+ }
14
+ restorersPerAsyncId.set(asyncId, restorers)
15
+ },
16
+ before(asyncId) {
17
+ const restorers = restorersPerAsyncId.get(asyncId)
18
+ if (restorers) {
19
+ const undoers = new Set<() => void>()
20
+ for (const restore of restorers) {
21
+ undoers.add(restore())
22
+ }
23
+ undoersPerAsyncId.set(asyncId, undoers)
24
+ }
25
+ },
26
+ after(asyncId) {
27
+ const undoers = undoersPerAsyncId.get(asyncId)
28
+ if (undoers) {
29
+ for (const undo of undoers) undo()
30
+ undoersPerAsyncId.delete(asyncId)
31
+ }
32
+ },
33
+ destroy(asyncId) {
34
+ restorersPerAsyncId.delete(asyncId)
35
+ undoersPerAsyncId.delete(asyncId)
36
+ }
37
+ }).enable()
38
+
39
+ asyncHooks.addHook = function (hook: Hook) {
40
+ hooks.add(hook)
41
+ return () => {
42
+ hooks.delete(hook)
43
+ }
44
+ }
45
+
46
+ export * from '../index'
package/src/decorator.ts CHANGED
@@ -58,13 +58,19 @@ export type ModernClassDecorator<T> = (target: T, context: ClassDecoratorContext
58
58
 
59
59
  type DDMethod<T> = (
60
60
  original: (this: T, ...args: any[]) => any,
61
+ target: any,
61
62
  name: PropertyKey
62
63
  ) => ((this: T, ...args: any[]) => any) | void
63
64
 
64
- type DDGetter<T> = (original: (this: T) => any, name: PropertyKey) => ((this: T) => any) | void
65
+ type DDGetter<T> = (
66
+ original: (this: T) => any,
67
+ target: any,
68
+ name: PropertyKey
69
+ ) => ((this: T) => any) | void
65
70
 
66
71
  type DDSetter<T> = (
67
72
  original: (this: T, value: any) => void,
73
+ target: any,
68
74
  name: PropertyKey
69
75
  ) => ((this: T, value: any) => void) | void
70
76
 
@@ -153,17 +159,17 @@ export function legacyDecorator<T = any>(description: DecoratorDescription<T>):
153
159
  if (!('getter' in description || 'setter' in description))
154
160
  throw new Error('Decorator cannot be applied to a getter or setter')
155
161
  if ('getter' in description) {
156
- const newGetter = description.getter!(descriptor.get as any, propertyKey)
162
+ const newGetter = description.getter!(descriptor.get as any, target, propertyKey)
157
163
  if (newGetter) descriptor.get = newGetter
158
164
  }
159
165
  if ('setter' in description) {
160
- const newSetter = description.setter!(descriptor.set as any, propertyKey)
166
+ const newSetter = description.setter!(descriptor.set as any, target, propertyKey)
161
167
  if (newSetter) descriptor.set = newSetter
162
168
  }
163
169
  return descriptor
164
170
  } else if (typeof descriptor.value === 'function') {
165
171
  if (!('method' in description)) throw new Error('Decorator cannot be applied to a method')
166
- const newMethod = description.method!(descriptor.value, propertyKey)
172
+ const newMethod = description.method!(descriptor.value, target, propertyKey)
167
173
  if (newMethod) descriptor.value = newMethod
168
174
  return descriptor
169
175
  }
@@ -196,23 +202,23 @@ export function modernDecorator<T = any>(description: DecoratorDescription<T>):
196
202
  throw new Error('Decorator cannot be applied to a field')
197
203
  case 'getter':
198
204
  if (!('getter' in description)) throw new Error('Decorator cannot be applied to a getter')
199
- return description.getter!(target, context.name)
205
+ return description.getter!(target, target, context.name)
200
206
  case 'setter':
201
207
  if (!('setter' in description)) throw new Error('Decorator cannot be applied to a setter')
202
- return description.setter!(target, context.name)
208
+ return description.setter!(target, target, context.name)
203
209
  case 'method':
204
210
  if (!('method' in description)) throw new Error('Decorator cannot be applied to a method')
205
- return description.method!(target, context.name)
211
+ return description.method!(target, target, context.name)
206
212
  case 'accessor': {
207
213
  if (!('getter' in description || 'setter' in description))
208
214
  throw new Error('Decorator cannot be applied to a getter or setter')
209
215
  const rv: Partial<ClassAccessorDecoratorResult<any, any>> = {}
210
216
  if ('getter' in description) {
211
- const newGetter = description.getter!(target.get, context.name)
217
+ const newGetter = description.getter!(target.get, target, context.name)
212
218
  if (newGetter) rv.get = newGetter
213
219
  }
214
220
  if ('setter' in description) {
215
- const newSetter = description.setter!(target.set, context.name)
221
+ const newSetter = description.setter!(target.set, target, context.name)
216
222
  if (newSetter) rv.set = newSetter
217
223
  }
218
224
  return rv
@@ -117,7 +117,7 @@ export function Destroyable<
117
117
  base = undefined
118
118
  }
119
119
  if (!base) {
120
- base = class { } as T
120
+ base = class {} as T
121
121
  }
122
122
 
123
123
  return class Destroyable extends (base as T) {
@@ -125,7 +125,7 @@ export function Destroyable<
125
125
  static destroy(obj: Destroyable) {
126
126
  const destructor = Destroyable.destructors.get(obj)
127
127
  if (!destructor) return false
128
- fr.unregister(obj)
128
+ fr.unregister(obj[allocatedValues])
129
129
  Destroyable.destructors.delete(obj)
130
130
  Object.setPrototypeOf(obj, new Proxy({}, destroyedHandler))
131
131
  // Clear all own properties
@@ -154,7 +154,7 @@ export function Destroyable<
154
154
  myDestructor(allocated)
155
155
  }
156
156
  Destroyable.destructors.set(this, destruction)
157
- fr.register(this, destruction, this)
157
+ fr.register(this, destruction, allocated)
158
158
  }
159
159
  }
160
160
  }
@@ -165,7 +165,7 @@ const forwardProperties = Symbol('forwardProperties')
165
165
  * Use with accessor properties or explicit get/set pairs
166
166
  */
167
167
  export const allocated = decorator({
168
- setter(original, propertyKey) {
168
+ setter(original, _target, propertyKey) {
169
169
  return function (value) {
170
170
  this[allocatedValues][propertyKey] = value
171
171
  return original.call(this, value)
package/src/index.ts CHANGED
@@ -4,6 +4,60 @@ export * from './eventful'
4
4
  export * from './indexable'
5
5
  export * from './iterableWeak'
6
6
  export * from './mixins'
7
+ export * from './promiseChain'
7
8
  export * from './reactive'
8
9
  export * from './std-decorators'
9
10
  export * from './utils'
11
+ export * from './zone'
12
+
13
+ import pkg from '../package.json'
14
+
15
+ const { version } = pkg
16
+
17
+ // Singleton verification
18
+ const GLOBAL_MUTTS_KEY = '__MUTTS_INSTANCE__'
19
+ const globalScope = (
20
+ typeof globalThis !== 'undefined'
21
+ ? globalThis
22
+ : typeof window !== 'undefined'
23
+ ? window
24
+ : typeof global !== 'undefined'
25
+ ? global
26
+ : false
27
+ ) as any
28
+ if (globalScope) {
29
+ // Detect the source of this instance safely across different environments
30
+ let source = 'mutts/index'
31
+ const viteEval = eval
32
+ try {
33
+ // @ts-ignore
34
+ if (typeof __filename !== 'undefined') source = __filename
35
+ else {
36
+ // Using eval to avoid SyntaxError in CJS environments where import.meta is not allowed
37
+ const meta = viteEval('import.meta')
38
+ if (meta && meta.url) source = meta.url
39
+ }
40
+ } catch (e) {
41
+ // Fallback for environments where neither is available or accessible
42
+ }
43
+
44
+ const currentSourceInfo = {
45
+ version,
46
+ source,
47
+ timestamp: Date.now(),
48
+ }
49
+
50
+ if (globalScope[GLOBAL_MUTTS_KEY]) {
51
+ const existing = globalScope[GLOBAL_MUTTS_KEY]
52
+ throw new Error(
53
+ `[Mutts] Multiple instances detected!\n` +
54
+ `Existing instance: ${JSON.stringify(existing, null, 2)}\n` +
55
+ `New instance: ${JSON.stringify(currentSourceInfo, null, 2)}\n` +
56
+ `This usually happens when 'mutts' is both installed as a dependency and bundled, ` +
57
+ `or when different versions are loaded. ` +
58
+ `Please check your build configuration (aliases, externals) to ensure a single source of truth.`
59
+ )
60
+ }
61
+
62
+ globalScope[GLOBAL_MUTTS_KEY] = currentSourceInfo
63
+ }
package/src/indexable.ts CHANGED
@@ -140,6 +140,48 @@ export function Indexable<Items, Base extends abstract new (...args: any[]) => a
140
140
  })
141
141
  return true
142
142
  },
143
+ has(target, prop) {
144
+ if (prop in target) return true
145
+ if (typeof prop === 'string') {
146
+ if (prop === 'length' && accessor.getLength) return true
147
+ const numProp = Number(prop)
148
+ if (!Number.isNaN(numProp)) return true
149
+ }
150
+ return false
151
+ },
152
+ ownKeys(target) {
153
+ const keys = Reflect.ownKeys(target)
154
+ if (accessor.getLength) {
155
+ keys.push('length')
156
+ const len = accessor.getLength.call(this as any)
157
+ for (let i = 0; i < len; i++) keys.push(String(i))
158
+ }
159
+ return keys
160
+ },
161
+ getOwnPropertyDescriptor(target, prop) {
162
+ if (prop in target) return Object.getOwnPropertyDescriptor(target, prop)
163
+ if (typeof prop === 'string') {
164
+ if (prop === 'length' && accessor.getLength) {
165
+ return {
166
+ enumerable: false,
167
+ configurable: true,
168
+ get: () => accessor.getLength!.call(this as any),
169
+ }
170
+ }
171
+ const numProp = Number(prop)
172
+ if (!Number.isNaN(numProp)) {
173
+ return {
174
+ enumerable: true,
175
+ configurable: true,
176
+ get: () => accessor.get!.call(this as any, numProp),
177
+ set: accessor.set
178
+ ? (v: any) => accessor.set!.call(this as any, numProp, v)
179
+ : undefined,
180
+ }
181
+ }
182
+ }
183
+ return undefined
184
+ },
143
185
  })
144
186
  )
145
187
  return Indexable
package/src/mixins.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { isConstructor, ReflectGet } from './utils'
1
+ import { FoolProof, isConstructor } from './utils'
2
2
 
3
3
  /**
4
4
  * A mixin function that takes a base class and returns a new class with mixed-in functionality
@@ -85,7 +85,7 @@ export function mixin<MixinFn extends (base: any) => new (...args: any[]) => any
85
85
  const originalPrototype = baseClass.prototype
86
86
  const proxiedPrototype = new Proxy(originalPrototype, {
87
87
  get(target, prop, receiver) {
88
- const value = ReflectGet(target, prop, receiver)
88
+ const value = FoolProof.get(target, prop, receiver)
89
89
 
90
90
  // Only wrap methods that are likely to access private fields
91
91
  // Skip symbols and special properties that the reactive system needs