mutts 1.0.8 → 1.0.9
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 +19 -2
- package/dist/browser.cjs +1030 -4406
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +310 -573
- package/dist/browser.esm.js +18 -24
- package/dist/browser.esm.js.map +1 -1
- package/dist/chunks/{async-browser-CA0jPWIi.cjs → async-browser-lvzLOCgk.cjs} +17 -24
- package/dist/chunks/async-browser-lvzLOCgk.cjs.map +1 -0
- package/dist/chunks/{async-core-UqHzvJ-S.cjs → async-core-CRLKP3l-.cjs} +8 -4
- package/dist/chunks/async-core-CRLKP3l-.cjs.map +1 -0
- package/dist/chunks/{async-node-BYHuGTni.cjs → async-node-C3DeIb0y.cjs} +5 -12
- package/dist/chunks/async-node-C3DeIb0y.cjs.map +1 -0
- package/dist/chunks/index-2vea86wD.esm.js +3011 -0
- package/dist/chunks/index-2vea86wD.esm.js.map +1 -0
- package/dist/chunks/index-CtA2AWl3.esm.js +4376 -0
- package/dist/chunks/index-CtA2AWl3.esm.js.map +1 -0
- package/dist/chunks/index-VTO-b2vR.cjs +4461 -0
- package/dist/chunks/index-VTO-b2vR.cjs.map +1 -0
- package/dist/{reactive → debug}/debug.d.ts +19 -8
- package/dist/debug/debug.d.ts.map +1 -0
- package/dist/debug/index.d.ts +4 -0
- package/dist/debug/index.d.ts.map +1 -0
- package/dist/debug/lineage-panel.d.ts +5 -0
- package/dist/debug/lineage-panel.d.ts.map +1 -0
- package/dist/debug/lineage.d.ts +79 -0
- package/dist/debug/lineage.d.ts.map +1 -0
- package/dist/debug.cjs +35 -0
- package/dist/debug.cjs.map +1 -0
- package/dist/debug.d.ts +209 -0
- package/dist/debug.esm.js +2 -0
- package/dist/debug.esm.js.map +1 -0
- package/dist/index.d.ts +1 -13
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/node.cjs +65 -63
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.esm.js +5 -11
- package/dist/node.esm.js.map +1 -1
- package/dist/{async → src/async}/browser.d.ts.map +1 -1
- package/dist/{async → src/async}/index.d.ts +2 -1
- package/dist/src/async/index.d.ts.map +1 -0
- package/dist/{async → src/async}/node.d.ts.map +1 -1
- package/dist/src/decorator.d.ts.map +1 -0
- package/dist/src/destroyable.d.ts.map +1 -0
- package/dist/src/entry-browser.d.ts.map +1 -0
- package/dist/src/entry-node.d.ts.map +1 -0
- package/dist/src/eventful.d.ts +20 -0
- package/dist/src/eventful.d.ts.map +1 -0
- package/dist/src/flavored.d.ts +33 -0
- package/dist/src/flavored.d.ts.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/indexable.d.ts.map +1 -0
- package/dist/{introspection.d.ts → src/introspection.d.ts} +1 -1
- package/dist/src/introspection.d.ts.map +1 -0
- package/dist/src/iterableWeak.d.ts.map +1 -0
- package/dist/src/mixins.d.ts.map +1 -0
- package/dist/src/promiseChain.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/array.d.ts +9 -10
- package/dist/src/reactive/array.d.ts.map +1 -0
- package/dist/src/reactive/buffer.d.ts +120 -0
- package/dist/src/reactive/buffer.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/change.d.ts +2 -2
- package/dist/src/reactive/change.d.ts.map +1 -0
- package/dist/src/reactive/deep-touch.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/deep-watch-state.d.ts +3 -3
- package/dist/src/reactive/deep-watch-state.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/deep-watch.d.ts +2 -1
- package/dist/src/reactive/deep-watch.d.ts.map +1 -0
- package/dist/src/reactive/describe.d.ts +12 -0
- package/dist/src/reactive/describe.d.ts.map +1 -0
- package/dist/src/reactive/effect-context.d.ts +34 -0
- package/dist/src/reactive/effect-context.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/effects.d.ts +31 -18
- package/dist/src/reactive/effects.d.ts.map +1 -0
- package/dist/src/reactive/index.d.ts +19 -0
- package/dist/src/reactive/index.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/map.d.ts +0 -2
- package/dist/src/reactive/map.d.ts.map +1 -0
- package/dist/src/reactive/memoize.d.ts +28 -0
- package/dist/src/reactive/memoize.d.ts.map +1 -0
- package/dist/src/reactive/non-reactive-state.d.ts.map +1 -0
- package/dist/src/reactive/non-reactive.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/project.d.ts +1 -2
- package/dist/src/reactive/project.d.ts.map +1 -0
- package/dist/src/reactive/proxy-state.d.ts.map +1 -0
- package/dist/src/reactive/proxy.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/record.d.ts +2 -3
- package/dist/src/reactive/record.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/register.d.ts +61 -0
- package/dist/src/reactive/register.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/registry.d.ts +6 -5
- package/dist/src/reactive/registry.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/set.d.ts +0 -2
- package/dist/src/reactive/set.d.ts.map +1 -0
- package/dist/src/reactive/tracking.d.ts.map +1 -0
- package/dist/{reactive → src/reactive}/types.d.ts +69 -21
- package/dist/src/reactive/types.d.ts.map +1 -0
- package/dist/{reactive/interface.d.ts → src/reactive/watch.d.ts} +7 -23
- package/dist/src/reactive/watch.d.ts.map +1 -0
- package/dist/{std-decorators.d.ts → src/std-decorators.d.ts} +1 -6
- package/dist/src/std-decorators.d.ts.map +1 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/{zone.d.ts → src/zone.d.ts} +2 -2
- package/dist/src/zone.d.ts.map +1 -0
- package/dist/types-DaHFfhlN.d.ts +414 -0
- package/docs/ai/api-reference.md +1 -1
- package/docs/ai/manual.md +443 -17
- package/docs/events.md +57 -2
- package/docs/flavored.md +210 -0
- package/docs/reactive/advanced.md +2 -3
- package/docs/reactive/attend.md +143 -0
- package/docs/reactive/collections.md +55 -0
- package/docs/reactive/core.md +120 -4
- package/docs/reactive/debugging.md +8 -10
- package/docs/reactive/describe.md +85 -0
- package/docs/reactive/error-handling.md +121 -0
- package/docs/reactive/project.md +1 -1
- package/docs/reactive/scan.md +215 -0
- package/docs/reactive.md +2 -0
- package/docs/std-decorators.md +102 -51
- package/docs/zone.md +3 -7
- package/package.json +6 -1
- package/src/async/browser.ts +110 -106
- package/src/async/index.ts +15 -11
- package/src/async/node.ts +5 -17
- package/src/eventful.ts +82 -49
- package/src/flavored.ts +106 -0
- package/src/index.d.ts +12 -12
- package/src/index.ts +2 -2
- package/src/introspection.ts +1 -1
- package/src/reactive/array.ts +29 -36
- package/src/reactive/buffer.ts +187 -27
- package/src/reactive/change.ts +16 -23
- package/src/reactive/deep-touch.ts +19 -14
- package/src/reactive/deep-watch-state.ts +3 -3
- package/src/reactive/deep-watch.ts +7 -4
- package/src/reactive/describe.ts +39 -0
- package/src/reactive/effect-context.ts +69 -4
- package/src/reactive/effects.ts +445 -393
- package/src/reactive/index.ts +12 -15
- package/src/reactive/map.ts +9 -25
- package/src/reactive/memoize.ts +26 -3
- package/src/reactive/project.md +107 -0
- package/src/reactive/project.ts +48 -71
- package/src/reactive/proxy.ts +21 -3
- package/src/reactive/record.ts +30 -45
- package/src/reactive/register.ts +138 -19
- package/src/reactive/registry.ts +22 -9
- package/src/reactive/set.ts +8 -21
- package/src/reactive/tracking.ts +9 -12
- package/src/reactive/types.ts +104 -24
- package/src/reactive/{interface.ts → watch.ts} +29 -59
- package/src/std-decorators.ts +56 -17
- package/src/utils.ts +2 -3
- package/src/zone.ts +28 -24
- package/dist/async/index.d.ts.map +0 -1
- package/dist/chunks/async-browser-CA0jPWIi.cjs.map +0 -1
- package/dist/chunks/async-core-UqHzvJ-S.cjs.map +0 -1
- package/dist/chunks/async-node-BYHuGTni.cjs.map +0 -1
- package/dist/chunks/index-DhaOVusv.esm.js +0 -6411
- package/dist/chunks/index-DhaOVusv.esm.js.map +0 -1
- package/dist/decorator.d.ts.map +0 -1
- package/dist/destroyable.d.ts.map +0 -1
- package/dist/entry-browser.d.ts.map +0 -1
- package/dist/entry-node.d.ts.map +0 -1
- package/dist/eventful.d.ts +0 -18
- package/dist/eventful.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/indexable.d.ts.map +0 -1
- package/dist/introspection.d.ts.map +0 -1
- package/dist/iterableWeak.d.ts.map +0 -1
- package/dist/mixins.d.ts.map +0 -1
- package/dist/promiseChain.d.ts.map +0 -1
- package/dist/reactive/array.d.ts.map +0 -1
- package/dist/reactive/buffer.d.ts +0 -44
- package/dist/reactive/buffer.d.ts.map +0 -1
- package/dist/reactive/change.d.ts.map +0 -1
- package/dist/reactive/debug.d.ts.map +0 -1
- package/dist/reactive/deep-touch.d.ts.map +0 -1
- package/dist/reactive/deep-watch-state.d.ts.map +0 -1
- package/dist/reactive/deep-watch.d.ts.map +0 -1
- package/dist/reactive/effect-context.d.ts +0 -7
- package/dist/reactive/effect-context.d.ts.map +0 -1
- package/dist/reactive/effects.d.ts.map +0 -1
- package/dist/reactive/index.d.ts +0 -20
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/reactive/interface.d.ts.map +0 -1
- package/dist/reactive/map.d.ts.map +0 -1
- package/dist/reactive/memoize.d.ts +0 -5
- package/dist/reactive/memoize.d.ts.map +0 -1
- package/dist/reactive/non-reactive-state.d.ts.map +0 -1
- package/dist/reactive/non-reactive.d.ts.map +0 -1
- package/dist/reactive/project.d.ts.map +0 -1
- package/dist/reactive/proxy-state.d.ts.map +0 -1
- package/dist/reactive/proxy.d.ts.map +0 -1
- package/dist/reactive/record.d.ts.map +0 -1
- package/dist/reactive/register.d.ts.map +0 -1
- package/dist/reactive/registry.d.ts.map +0 -1
- package/dist/reactive/set.d.ts.map +0 -1
- package/dist/reactive/tracking.d.ts.map +0 -1
- package/dist/reactive/types.d.ts.map +0 -1
- package/dist/std-decorators.d.ts.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/zone.d.ts.map +0 -1
- package/src/reactive/debug.ts +0 -517
- /package/dist/{async → src/async}/browser.d.ts +0 -0
- /package/dist/{async → src/async}/node.d.ts +0 -0
- /package/dist/{decorator.d.ts → src/decorator.d.ts} +0 -0
- /package/dist/{destroyable.d.ts → src/destroyable.d.ts} +0 -0
- /package/dist/{entry-browser.d.ts → src/entry-browser.d.ts} +0 -0
- /package/dist/{entry-node.d.ts → src/entry-node.d.ts} +0 -0
- /package/dist/{indexable.d.ts → src/indexable.d.ts} +0 -0
- /package/dist/{iterableWeak.d.ts → src/iterableWeak.d.ts} +0 -0
- /package/dist/{mixins.d.ts → src/mixins.d.ts} +0 -0
- /package/dist/{promiseChain.d.ts → src/promiseChain.d.ts} +0 -0
- /package/dist/{reactive → src/reactive}/deep-touch.d.ts +0 -0
- /package/dist/{reactive → src/reactive}/non-reactive-state.d.ts +0 -0
- /package/dist/{reactive → src/reactive}/non-reactive.d.ts +0 -0
- /package/dist/{reactive → src/reactive}/proxy-state.d.ts +0 -0
- /package/dist/{reactive → src/reactive}/proxy.d.ts +0 -0
- /package/dist/{reactive → src/reactive}/tracking.d.ts +0 -0
- /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
package/src/async/node.ts
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { createHook } from 'node:async_hooks'
|
|
2
|
-
import {
|
|
2
|
+
import { hooks, type Restorer } from '.'
|
|
3
3
|
|
|
4
4
|
// 1. Generic async_hooks implementation for Hooks
|
|
5
5
|
// This maintains support for 'asyncHooks.addHook' for generic use cases.
|
|
6
6
|
|
|
7
|
-
const hooks = new Set<Hook>()
|
|
8
|
-
asyncHooks.addHook = function (hook: Hook) {
|
|
9
|
-
hooks.add(hook)
|
|
10
|
-
return () => {
|
|
11
|
-
hooks.delete(hook)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
7
|
const contexts = new Map<number, Restorer[]>()
|
|
16
8
|
const activeUndoers = new Map<number, (() => void)[]>()
|
|
17
9
|
|
|
@@ -47,7 +39,7 @@ function wrap<Args extends any[], R>(fn: ((...args: Args) => R) | null | undefin
|
|
|
47
39
|
}
|
|
48
40
|
|
|
49
41
|
const hook = createHook({
|
|
50
|
-
init(asyncId,
|
|
42
|
+
init(asyncId, _type, _triggerId, _resource) {
|
|
51
43
|
// Used for native resources like Timers
|
|
52
44
|
const restorers = captureRestorers()
|
|
53
45
|
if (restorers.length > 0) contexts.set(asyncId, restorers)
|
|
@@ -88,17 +80,13 @@ const originalMethods = {
|
|
|
88
80
|
all: OriginalPromise.all,
|
|
89
81
|
}
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
|
|
93
83
|
// Patch prototype
|
|
94
|
-
OriginalPromise.prototype.then = function(onFulfilled, onRejected) {
|
|
84
|
+
OriginalPromise.prototype.then = function (onFulfilled, onRejected) {
|
|
95
85
|
return originalMethods.then.call(this, wrap(onFulfilled), wrap(onRejected))
|
|
96
86
|
} as any
|
|
97
|
-
OriginalPromise.prototype.catch = function(onRejected) {
|
|
87
|
+
OriginalPromise.prototype.catch = function (onRejected) {
|
|
98
88
|
return originalMethods.catch.call(this, wrap(onRejected))
|
|
99
89
|
} as any
|
|
100
|
-
OriginalPromise.prototype.finally = function(onFinally) {
|
|
90
|
+
OriginalPromise.prototype.finally = function (onFinally) {
|
|
101
91
|
return originalMethods.finally.call(this, wrap(onFinally))
|
|
102
92
|
} as any
|
|
103
|
-
|
|
104
|
-
|
package/src/eventful.ts
CHANGED
|
@@ -2,76 +2,109 @@
|
|
|
2
2
|
* Base type for event maps - all event handlers must be functions
|
|
3
3
|
*/
|
|
4
4
|
export type EventsBase = Record<string, (...args: any[]) => void>
|
|
5
|
-
/**
|
|
6
|
-
* A type-safe event system that provides a clean API for event handling
|
|
7
|
-
* @template Events - The event map defining event names and their handler signatures
|
|
8
|
-
*/
|
|
9
|
-
export class Eventful<Events extends EventsBase> {
|
|
10
|
-
readonly #events = new Map<keyof Events, ((...args: any[]) => void)[]>()
|
|
11
|
-
readonly #hooks = [] as ((...args: any[]) => void)[]
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
event: EventType,
|
|
16
|
-
...args: Parameters<Events[EventType]>
|
|
17
|
-
) => void
|
|
18
|
-
): () => void {
|
|
19
|
-
if (!this.#hooks.includes(cb)) this.#hooks.push(cb)
|
|
20
|
-
return () => {
|
|
21
|
-
this.#hooks.splice(this.#hooks.indexOf(cb), 1)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
6
|
+
const events = Symbol('events')
|
|
7
|
+
const hooks = Symbol('hooks')
|
|
24
8
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
cb?: Events[EventType]
|
|
9
|
+
const eventBehavior = {
|
|
10
|
+
on<EventType extends keyof EventsBase>(
|
|
11
|
+
eventOrEvents: EventType | Partial<EventsBase>,
|
|
12
|
+
cb?: EventsBase[EventType]
|
|
30
13
|
): () => void {
|
|
31
14
|
if (typeof eventOrEvents === 'object') {
|
|
32
|
-
for (const e of Object.keys(eventOrEvents) as (keyof
|
|
15
|
+
for (const e of Object.keys(eventOrEvents) as (keyof EventsBase)[]) {
|
|
33
16
|
this.on(e, eventOrEvents[e]!)
|
|
34
17
|
}
|
|
35
18
|
} else if (cb !== undefined) {
|
|
36
|
-
|
|
37
|
-
if (!callbacks)
|
|
38
|
-
|
|
39
|
-
this.#events.set(eventOrEvents, callbacks)
|
|
40
|
-
}
|
|
41
|
-
callbacks.push(cb)
|
|
19
|
+
const callbacks = this[events].get(eventOrEvents) ?? new Set<EventsBase[EventType]>()
|
|
20
|
+
if (!callbacks.has(cb)) callbacks.add(cb)
|
|
21
|
+
this[events].set(eventOrEvents, callbacks)
|
|
42
22
|
}
|
|
43
|
-
// @ts-expect-error Generic case leads to generic case
|
|
44
23
|
return () => this.off(eventOrEvents, cb)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
eventOrEvents: EventType | Partial<Events>,
|
|
50
|
-
cb?: Events[EventType]
|
|
24
|
+
},
|
|
25
|
+
off<EventType extends keyof EventsBase>(
|
|
26
|
+
eventOrEvents: EventType | Partial<EventsBase>,
|
|
27
|
+
cb?: EventsBase[EventType]
|
|
51
28
|
): void {
|
|
52
29
|
if (typeof eventOrEvents === 'object') {
|
|
53
|
-
for (const e of Object.keys(eventOrEvents) as (keyof
|
|
30
|
+
for (const e of Object.keys(eventOrEvents) as (keyof EventsBase)[]) {
|
|
54
31
|
this.off(e, eventOrEvents[e])
|
|
55
32
|
}
|
|
56
33
|
} else if (cb !== null && cb !== undefined) {
|
|
57
|
-
const callbacks = this
|
|
34
|
+
const callbacks = this[events].get(eventOrEvents)
|
|
58
35
|
if (callbacks) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
callbacks.filter((c) => c !== cb)
|
|
62
|
-
)
|
|
36
|
+
callbacks.delete(cb)
|
|
37
|
+
if (!callbacks.size) this[events].delete(eventOrEvents)
|
|
63
38
|
}
|
|
64
39
|
} else {
|
|
65
40
|
// Remove all listeners for this event
|
|
66
|
-
this
|
|
41
|
+
this[events].delete(eventOrEvents)
|
|
67
42
|
}
|
|
68
|
-
}
|
|
69
|
-
|
|
43
|
+
},
|
|
44
|
+
emit<EventType extends keyof EventsBase>(
|
|
70
45
|
event: EventType,
|
|
71
|
-
...args: Parameters<
|
|
46
|
+
...args: Parameters<EventsBase[EventType]>
|
|
72
47
|
) {
|
|
73
|
-
const callbacks = this
|
|
48
|
+
const callbacks = this[events].get(event)
|
|
74
49
|
if (callbacks) for (const cb of callbacks) cb.apply(this, args)
|
|
75
|
-
for (const cb of this
|
|
50
|
+
for (const cb of this[hooks]) cb.call(this, event, ...args)
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function perEvent(
|
|
55
|
+
eventful: Eventful<any>,
|
|
56
|
+
fct: (event: string, ...args: any[]) => void,
|
|
57
|
+
use?: 'use'
|
|
58
|
+
) {
|
|
59
|
+
const cache = new Map<string, (...args: any[]) => any>()
|
|
60
|
+
return new Proxy(fct, {
|
|
61
|
+
get(target, prop: PropertyKey) {
|
|
62
|
+
if (typeof prop !== 'string') return target[prop]
|
|
63
|
+
if (use && !eventful[events].has(prop) && !eventful[hooks].size) return () => {}
|
|
64
|
+
|
|
65
|
+
// Return cached function or create and cache
|
|
66
|
+
let cached = cache.get(prop)
|
|
67
|
+
if (!cached) {
|
|
68
|
+
cached = (...args: any[]) => fct.apply(eventful, [prop, ...args])
|
|
69
|
+
cache.set(prop, cached)
|
|
70
|
+
}
|
|
71
|
+
return cached
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A type-safe event system that provides a clean API for event handling
|
|
78
|
+
* @template Events - The event map defining event names and their handler signatures
|
|
79
|
+
*/
|
|
80
|
+
export class Eventful<Events extends EventsBase> {
|
|
81
|
+
private readonly [events] = new Map<keyof Events, Set<(...args: any[]) => void>>()
|
|
82
|
+
private readonly [hooks] = new Set<(...args: any[]) => void>()
|
|
83
|
+
|
|
84
|
+
public hook(
|
|
85
|
+
cb: <EventType extends keyof Events>(
|
|
86
|
+
event: EventType,
|
|
87
|
+
...args: Parameters<Events[EventType]>
|
|
88
|
+
) => void
|
|
89
|
+
): () => void {
|
|
90
|
+
this[hooks].add(cb)
|
|
91
|
+
return () => {
|
|
92
|
+
this[hooks].delete(cb)
|
|
93
|
+
}
|
|
76
94
|
}
|
|
95
|
+
|
|
96
|
+
public on = perEvent(this, eventBehavior.on) as ((events: Partial<Events>) => void) &
|
|
97
|
+
(<EventType extends keyof Events>(event: EventType, cb: Events[EventType]) => () => void) & {
|
|
98
|
+
[event in keyof Events]: (cb: Events[event]) => () => void
|
|
99
|
+
}
|
|
100
|
+
public off = perEvent(this, eventBehavior.off) as ((events: Partial<Events>) => void) &
|
|
101
|
+
(<EventType extends keyof Events>(event: EventType, cb?: Events[EventType]) => void) & {
|
|
102
|
+
[event in keyof Events]: (cb?: Events[event]) => void
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public emit = perEvent(this, eventBehavior.emit, 'use') as (<EventType extends keyof Events>(
|
|
106
|
+
event: EventType,
|
|
107
|
+
...args: Parameters<Events[EventType]>
|
|
108
|
+
) => void) &
|
|
109
|
+
Events
|
|
77
110
|
}
|
package/src/flavored.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a flavored (extensible) version of a function with chainable property modifiers.
|
|
3
|
+
*
|
|
4
|
+
* Each property defined in `flavors` returns a new flavored function that transforms
|
|
5
|
+
* how the original function is called. This enables a fluent API where properties
|
|
6
|
+
* create specialized variants of the base function.
|
|
7
|
+
*
|
|
8
|
+
* @param fn - The base function to flavor
|
|
9
|
+
* @param flavors - Object defining the flavor properties (getters or methods)
|
|
10
|
+
* @returns A proxy of the function with the flavor properties attached
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* function greet(name: string, options?: { loud?: boolean }) {
|
|
15
|
+
* const greeting = `Hello, ${name}!`
|
|
16
|
+
* return options?.loud ? greeting.toUpperCase() : greeting
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* const flavoredGreet = flavored(greet, {
|
|
20
|
+
* get loud() {
|
|
21
|
+
* return createFlavor(this, (name, opts) => [name, { ...opts, loud: true }])
|
|
22
|
+
* }
|
|
23
|
+
* })
|
|
24
|
+
*
|
|
25
|
+
* flavoredGreet('World') // "Hello, World!"
|
|
26
|
+
* flavoredGreet.loud('World') // "HELLO, WORLD!"
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
import { named } from './utils'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a flavored (extensible) version of a function with chainable property modifiers.
|
|
33
|
+
*/
|
|
34
|
+
export function flavored<T extends (...args: any[]) => any, F extends Record<string, any>>(
|
|
35
|
+
fn: T,
|
|
36
|
+
flavors: F
|
|
37
|
+
): T & F {
|
|
38
|
+
// Store flavors for recursive flavoring
|
|
39
|
+
;(fn as any).flavors = flavors
|
|
40
|
+
|
|
41
|
+
return new Proxy(fn, {
|
|
42
|
+
get(target, prop, receiver) {
|
|
43
|
+
if (prop in flavors) {
|
|
44
|
+
return Reflect.get(flavors, prop, receiver)
|
|
45
|
+
}
|
|
46
|
+
return (target as any)[prop]
|
|
47
|
+
},
|
|
48
|
+
}) as T & F
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates a new flavored function that transforms arguments before calling the base.
|
|
53
|
+
*
|
|
54
|
+
* @param fn - The base flavored function
|
|
55
|
+
* @param transform - Function that receives the original arguments and returns transformed arguments
|
|
56
|
+
* @returns A new flavored function with the transformation applied
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const loudGreet = createFlavor(greet, (name, opts) => [name, { ...opts, loud: true }])
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function createFlavor<T extends (...args: any[]) => any>(
|
|
64
|
+
fn: T,
|
|
65
|
+
transform: (...args: Parameters<T>) => Parameters<T>,
|
|
66
|
+
name?: string
|
|
67
|
+
): T {
|
|
68
|
+
const fct = function flavorWrapper(this: any, ...args: Parameters<T>) {
|
|
69
|
+
return fn.apply(this, transform(...args))
|
|
70
|
+
}
|
|
71
|
+
if (name) named(name, fct)
|
|
72
|
+
|
|
73
|
+
return flavored(fct as any as T, (fn as any).flavors || {})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates a new flavored function that merges options objects.
|
|
78
|
+
* Useful when the last argument is an options object that should be merged.
|
|
79
|
+
*
|
|
80
|
+
* @param fn - The base flavored function
|
|
81
|
+
* @param defaultOptions - Options to merge with the provided options
|
|
82
|
+
* @returns A new flavored function with merged options
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const opaqueEffect = flavorOptions(effect, { opaque: true })
|
|
87
|
+
* const namedEffect = flavorOptions(effect, { name: 'myEffect' })
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function flavorOptions<T extends (...args: any[]) => any>(
|
|
91
|
+
fn: T,
|
|
92
|
+
defaultOptions: Record<string, any>,
|
|
93
|
+
name?: string
|
|
94
|
+
): T {
|
|
95
|
+
const fct = function flavorOptionsWrapper(this: any, ...args: any[]) {
|
|
96
|
+
const lastArg = args[args.length - 1]
|
|
97
|
+
const mergedArgs =
|
|
98
|
+
lastArg !== undefined && typeof lastArg === 'object'
|
|
99
|
+
? [...args.slice(0, -1), { ...defaultOptions, ...lastArg }]
|
|
100
|
+
: [...args, defaultOptions]
|
|
101
|
+
return fn.apply(this, mergedArgs)
|
|
102
|
+
}
|
|
103
|
+
if (name) named(name, fct)
|
|
104
|
+
|
|
105
|
+
return flavored(fct as any as T, (fn as any).flavors || {})
|
|
106
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export * from './decorator'
|
|
2
|
-
export * from './destroyable'
|
|
3
|
-
export * from './eventful'
|
|
4
|
-
export * from './indexable'
|
|
5
|
-
export * from './iterableWeak'
|
|
6
|
-
export * from './mixins'
|
|
7
|
-
export * from './promiseChain'
|
|
8
|
-
export * from './reactive'
|
|
9
|
-
export * from './std-decorators'
|
|
10
|
-
export * from './utils'
|
|
11
|
-
export * from './zone'
|
|
12
|
-
export * from './async'
|
|
1
|
+
export * from './decorator'
|
|
2
|
+
export * from './destroyable'
|
|
3
|
+
export * from './eventful'
|
|
4
|
+
export * from './indexable'
|
|
5
|
+
export * from './iterableWeak'
|
|
6
|
+
export * from './mixins'
|
|
7
|
+
export * from './promiseChain'
|
|
8
|
+
export * from './reactive'
|
|
9
|
+
export * from './std-decorators'
|
|
10
|
+
export * from './utils'
|
|
11
|
+
export * from './zone'
|
|
12
|
+
export * from './async'
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
export * from './async'
|
|
1
2
|
export * from './decorator'
|
|
2
3
|
export * from './destroyable'
|
|
3
4
|
export * from './eventful'
|
|
5
|
+
export * from './flavored'
|
|
4
6
|
export * from './indexable'
|
|
5
7
|
export * from './iterableWeak'
|
|
6
8
|
export * from './mixins'
|
|
@@ -9,7 +11,6 @@ export * from './reactive'
|
|
|
9
11
|
export * from './std-decorators'
|
|
10
12
|
export * from './utils'
|
|
11
13
|
export * from './zone'
|
|
12
|
-
export * from './async'
|
|
13
14
|
|
|
14
15
|
import pkg from '../package.json'
|
|
15
16
|
|
|
@@ -31,7 +32,6 @@ if (globalScope) {
|
|
|
31
32
|
let source = 'mutts/index'
|
|
32
33
|
const viteEval = eval
|
|
33
34
|
try {
|
|
34
|
-
// @ts-ignore
|
|
35
35
|
if (typeof __filename !== 'undefined') source = __filename
|
|
36
36
|
else {
|
|
37
37
|
// Using eval to avoid SyntaxError in CJS environments where import.meta is not allowed
|
package/src/introspection.ts
CHANGED
package/src/reactive/array.ts
CHANGED
|
@@ -25,29 +25,18 @@ export abstract class Indexer extends Array {
|
|
|
25
25
|
dependant(this, i)
|
|
26
26
|
return reactive(this[i])
|
|
27
27
|
}
|
|
28
|
+
// Returns undefined intentionally: signals the proxy handler that notifications
|
|
29
|
+
// were already dispatched via touched(), preventing double notification
|
|
28
30
|
set(i: number, value: any) {
|
|
29
31
|
const added = i >= this.length
|
|
30
32
|
this[i] = value
|
|
31
33
|
touched(this, { type: 'set', prop: i }, index(i, { length: added }))
|
|
32
34
|
}
|
|
33
|
-
getLength() {
|
|
34
|
-
dependant(this, 'length')
|
|
35
|
-
return this.length
|
|
36
|
-
}
|
|
37
|
-
setLength(value: number) {
|
|
38
|
-
const oldLength = this.length
|
|
39
|
-
try {
|
|
40
|
-
this.length = value
|
|
41
|
-
} finally {
|
|
42
|
-
touched(this, { type: 'set', prop: 'length' }, range(oldLength, value, { length: true }))
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
35
|
}
|
|
46
36
|
const indexLess = { get: FoolProof.get, set: FoolProof.set }
|
|
47
37
|
Object.assign(FoolProof, {
|
|
48
38
|
get(obj: any, prop: any, receiver: any) {
|
|
49
39
|
if (obj instanceof Array && typeof prop === 'string') {
|
|
50
|
-
if (prop === 'length') return Indexer.prototype.getLength.call(obj)
|
|
51
40
|
const index = parseInt(prop)
|
|
52
41
|
if (!Number.isNaN(index)) return Indexer.prototype.get.call(obj, index)
|
|
53
42
|
}
|
|
@@ -55,7 +44,6 @@ Object.assign(FoolProof, {
|
|
|
55
44
|
},
|
|
56
45
|
set(obj: any, prop: any, value: any, receiver: any) {
|
|
57
46
|
if (obj instanceof Array && typeof prop === 'string') {
|
|
58
|
-
if (prop === 'length') return Indexer.prototype.setLength.call(obj, value)
|
|
59
47
|
const index = parseInt(prop)
|
|
60
48
|
if (!Number.isNaN(index)) return Indexer.prototype.set.call(obj, index, value)
|
|
61
49
|
}
|
|
@@ -67,8 +55,18 @@ export abstract class ReactiveArray extends Array {
|
|
|
67
55
|
toJSON() {
|
|
68
56
|
return this
|
|
69
57
|
}
|
|
70
|
-
get
|
|
71
|
-
|
|
58
|
+
get length(): number {
|
|
59
|
+
dependant(this, 'length')
|
|
60
|
+
return this.length
|
|
61
|
+
}
|
|
62
|
+
set length(value: number) {
|
|
63
|
+
const oldLength = this.length
|
|
64
|
+
if (oldLength === value) return
|
|
65
|
+
try {
|
|
66
|
+
this.length = value
|
|
67
|
+
} finally {
|
|
68
|
+
touched(this, { type: 'set', prop: 'length' }, range(oldLength, value, { length: true }))
|
|
69
|
+
}
|
|
72
70
|
}
|
|
73
71
|
// Safe array access with negative indices
|
|
74
72
|
at(index: number): any {
|
|
@@ -103,17 +101,17 @@ export abstract class ReactiveArray extends Array {
|
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
// Iterator methods with reactivity tracking
|
|
106
|
-
entries() {
|
|
104
|
+
entries(): any {
|
|
107
105
|
dependant(this)
|
|
108
106
|
return makeReactiveEntriesIterator(this.entries())
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
keys() {
|
|
109
|
+
keys(): any {
|
|
112
110
|
dependant(this, 'length')
|
|
113
111
|
return this.keys()
|
|
114
112
|
}
|
|
115
113
|
|
|
116
|
-
values() {
|
|
114
|
+
values(): any {
|
|
117
115
|
dependant(this)
|
|
118
116
|
return makeReactiveIterator(this.values())
|
|
119
117
|
}
|
|
@@ -140,7 +138,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
140
138
|
const length = this.length
|
|
141
139
|
let i = fromIndex === undefined ? 0 : fromIndex
|
|
142
140
|
if (i < 0) i = Math.max(length + i, 0)
|
|
143
|
-
|
|
141
|
+
|
|
144
142
|
const unwrappedSearch = unwrap(searchElement)
|
|
145
143
|
|
|
146
144
|
for (; i < length; i++) {
|
|
@@ -171,7 +169,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
171
169
|
}
|
|
172
170
|
}
|
|
173
171
|
|
|
174
|
-
// If we scanned the whole relevant part and didn't find it, we depend on length
|
|
172
|
+
// If we scanned the whole relevant part and didn't find it, we depend on length
|
|
175
173
|
// (because adding elements might shift indices or add the element)
|
|
176
174
|
// Actually for lastIndexOf, if we start from end, length dependency is implicit in the start index calculation?
|
|
177
175
|
// But if we return -1, it means we didn't find it.
|
|
@@ -360,7 +358,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
360
358
|
return false
|
|
361
359
|
}
|
|
362
360
|
// Side-effectful
|
|
363
|
-
push(...items: any[]) {
|
|
361
|
+
push(...items: any[]): number {
|
|
364
362
|
const oldLength = this.length
|
|
365
363
|
try {
|
|
366
364
|
return this.push(...items)
|
|
@@ -373,7 +371,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
373
371
|
}
|
|
374
372
|
}
|
|
375
373
|
|
|
376
|
-
pop() {
|
|
374
|
+
pop(): any {
|
|
377
375
|
if (this.length === 0) return undefined
|
|
378
376
|
try {
|
|
379
377
|
return reactive(this.pop())
|
|
@@ -382,7 +380,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
382
380
|
}
|
|
383
381
|
}
|
|
384
382
|
|
|
385
|
-
shift() {
|
|
383
|
+
shift(): any {
|
|
386
384
|
if (this.length === 0) return undefined
|
|
387
385
|
try {
|
|
388
386
|
return reactive(this.shift())
|
|
@@ -391,7 +389,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
391
389
|
}
|
|
392
390
|
}
|
|
393
391
|
|
|
394
|
-
unshift(...items: any[]) {
|
|
392
|
+
unshift(...items: any[]): number {
|
|
395
393
|
try {
|
|
396
394
|
return this.unshift(...items)
|
|
397
395
|
} finally {
|
|
@@ -403,7 +401,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
403
401
|
}
|
|
404
402
|
}
|
|
405
403
|
|
|
406
|
-
splice(start: number, deleteCount?: number, ...items: any[]) {
|
|
404
|
+
splice(start: number, deleteCount?: number, ...items: any[]): any[] {
|
|
407
405
|
const oldLength = this.length
|
|
408
406
|
|
|
409
407
|
// Normalize start index
|
|
@@ -435,7 +433,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
435
433
|
}
|
|
436
434
|
}
|
|
437
435
|
|
|
438
|
-
reverse() {
|
|
436
|
+
reverse(): any[] {
|
|
439
437
|
try {
|
|
440
438
|
return this.reverse()
|
|
441
439
|
} finally {
|
|
@@ -443,7 +441,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
443
441
|
}
|
|
444
442
|
}
|
|
445
443
|
|
|
446
|
-
sort(compareFn?: (a: any, b: any) => number) {
|
|
444
|
+
sort(compareFn?: (a: any, b: any) => number): this {
|
|
447
445
|
compareFn = compareFn || ((a, b) => a.toString().localeCompare(b.toString()))
|
|
448
446
|
try {
|
|
449
447
|
return this.sort((a, b) => compareFn(reactive(a), reactive(b))) as any
|
|
@@ -452,7 +450,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
452
450
|
}
|
|
453
451
|
}
|
|
454
452
|
|
|
455
|
-
fill(value: any, start?: number, end?: number) {
|
|
453
|
+
fill(value: any, start?: number, end?: number): this {
|
|
456
454
|
const len = this.length
|
|
457
455
|
let k = start === undefined ? 0 : start
|
|
458
456
|
if (k < 0) k = Math.max(len + k, 0)
|
|
@@ -473,7 +471,7 @@ export abstract class ReactiveArray extends Array {
|
|
|
473
471
|
}
|
|
474
472
|
}
|
|
475
473
|
|
|
476
|
-
copyWithin(target: number, start: number, end?: number) {
|
|
474
|
+
copyWithin(target: number, start: number, end?: number): this {
|
|
477
475
|
try {
|
|
478
476
|
if (end === undefined) return this.copyWithin(target, start) as any
|
|
479
477
|
return this.copyWithin(target, start, end) as any
|
|
@@ -495,13 +493,8 @@ export abstract class ReactiveArray extends Array {
|
|
|
495
493
|
const count = Math.min(final - from, len - to)
|
|
496
494
|
|
|
497
495
|
if (count > 0) {
|
|
498
|
-
touched(
|
|
499
|
-
this,
|
|
500
|
-
{ type: 'bunch', method: 'copyWithin' },
|
|
501
|
-
range(to, to + count - 1)
|
|
502
|
-
)
|
|
496
|
+
touched(this, { type: 'bunch', method: 'copyWithin' }, range(to, to + count - 1))
|
|
503
497
|
}
|
|
504
|
-
|
|
505
498
|
}
|
|
506
499
|
}
|
|
507
500
|
}
|