@sigx/reactivity 0.1.3 → 0.1.5
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/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type EffectFn = () => void;
|
|
2
|
+
export interface EffectRunner<T = void> {
|
|
3
|
+
(): T;
|
|
4
|
+
stop: () => void;
|
|
5
|
+
}
|
|
2
6
|
export interface ReactiveEffect extends EffectFn {
|
|
3
7
|
deps: Set<ReactiveEffect>[];
|
|
4
8
|
}
|
|
@@ -9,7 +13,7 @@ export type Signal<T> = T & {
|
|
|
9
13
|
export declare function detectAccess(selector: () => any): [any, string | symbol] | null;
|
|
10
14
|
export declare function untrack<T>(fn: () => T): T;
|
|
11
15
|
export declare function signal<T extends object>(target: T): Signal<T>;
|
|
12
|
-
export declare function effect(fn: EffectFn):
|
|
16
|
+
export declare function effect(fn: EffectFn): EffectRunner;
|
|
13
17
|
export type WatchSource<T = any> = T | (() => T);
|
|
14
18
|
export type WatchCallback<V = any, OV = any> = (value: V, oldValue: OV, onCleanup: (fn: () => void) => void) => any;
|
|
15
19
|
export interface WatchOptions<Immediate = boolean> {
|
|
@@ -19,9 +23,9 @@ export interface WatchOptions<Immediate = boolean> {
|
|
|
19
23
|
}
|
|
20
24
|
export interface WatchHandle {
|
|
21
25
|
(): void;
|
|
26
|
+
stop: () => void;
|
|
22
27
|
pause: () => void;
|
|
23
28
|
resume: () => void;
|
|
24
|
-
stop: () => void;
|
|
25
29
|
}
|
|
26
30
|
export declare function watch<T>(source: WatchSource<T>, cb: WatchCallback<T>, options?: WatchOptions): WatchHandle;
|
|
27
31
|
export type EffectScope = {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC5C,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;CAC/B;AAMD,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,QAcnC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,IAAI;IAClC,IAAI,CAAC,CAAC;IACN,IAAI,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC5C,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;CAC/B;AAMD,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,QAcnC;AA4CD,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG;IACxB,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;CAC/B,CAAC;AAIF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,CAe/E;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQzC;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CA2G7D;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,CAEjD;AAaD,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AACjD,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,GAAG,CAAC;AACpH,MAAM,WAAW,YAAY,CAAC,SAAS,GAAG,OAAO;IAC7C,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CAsD1G;AAED,MAAM,MAAM,WAAW,GAAG;IACtB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACpC,CAAA;AAED,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,WAAW,CAc3D"}
|
package/dist/index.js
CHANGED
|
@@ -16,15 +16,17 @@ function batch(fn) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
function runEffect(fn) {
|
|
19
|
-
const
|
|
20
|
-
cleanup(
|
|
21
|
-
activeEffect =
|
|
19
|
+
const effectFn = function() {
|
|
20
|
+
cleanup(effectFn);
|
|
21
|
+
activeEffect = effectFn;
|
|
22
22
|
fn();
|
|
23
23
|
activeEffect = null;
|
|
24
24
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
effectFn.deps = [];
|
|
26
|
+
effectFn();
|
|
27
|
+
const runner = (() => effectFn());
|
|
28
|
+
runner.stop = () => cleanup(effectFn);
|
|
29
|
+
return runner;
|
|
28
30
|
}
|
|
29
31
|
function cleanup(effect$1) {
|
|
30
32
|
if (!effect$1.deps) return;
|
|
@@ -158,8 +160,16 @@ function watch(source, cb, options) {
|
|
|
158
160
|
let oldValue;
|
|
159
161
|
let isFirst = true;
|
|
160
162
|
let cleanupFn = null;
|
|
163
|
+
let paused = false;
|
|
164
|
+
let pendingValue;
|
|
165
|
+
let hasPending = false;
|
|
161
166
|
const runner = effect(() => {
|
|
162
167
|
const newValue = typeof source === "function" ? source() : source;
|
|
168
|
+
if (paused) {
|
|
169
|
+
pendingValue = newValue;
|
|
170
|
+
hasPending = true;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
163
173
|
if (isFirst) {
|
|
164
174
|
if (options?.immediate) {
|
|
165
175
|
if (cleanupFn) cleanupFn();
|
|
@@ -173,14 +183,28 @@ function watch(source, cb, options) {
|
|
|
173
183
|
oldValue = newValue;
|
|
174
184
|
});
|
|
175
185
|
const stop = () => {
|
|
176
|
-
runner();
|
|
186
|
+
runner.stop();
|
|
177
187
|
if (cleanupFn) cleanupFn();
|
|
178
188
|
};
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
const pause = () => {
|
|
190
|
+
paused = true;
|
|
191
|
+
};
|
|
192
|
+
const resume = () => {
|
|
193
|
+
if (!paused) return;
|
|
194
|
+
paused = false;
|
|
195
|
+
if (hasPending && !Object.is(pendingValue, oldValue)) {
|
|
196
|
+
if (cleanupFn) cleanupFn();
|
|
197
|
+
cb(pendingValue, oldValue, (fn) => cleanupFn = fn);
|
|
198
|
+
oldValue = pendingValue;
|
|
199
|
+
}
|
|
200
|
+
hasPending = false;
|
|
201
|
+
pendingValue = void 0;
|
|
202
|
+
};
|
|
203
|
+
return Object.assign(stop, {
|
|
204
|
+
stop,
|
|
205
|
+
pause,
|
|
206
|
+
resume
|
|
207
|
+
});
|
|
184
208
|
}
|
|
185
209
|
function effectScope(detached) {
|
|
186
210
|
const effects = [];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["activeEffect: ReactiveEffect | null","effect","effect: ReactiveEffect","accessObserver: ((target: any, key: string | symbol) => void) | null","result: [any, string | symbol] | null","arrayInstrumentations: Record<string, Function>","oldValue: T | undefined","cleanupFn: (() => void) | null","effects: (() => void)[]"],"sources":["../src/index.ts"],"sourcesContent":["// signals.ts\r\n\r\nexport type EffectFn = () => void;\r\n\r\nexport interface ReactiveEffect extends EffectFn {\r\n deps: Set<ReactiveEffect>[];\r\n}\r\n\r\nlet activeEffect: ReactiveEffect | null = null;\r\nlet batchDepth = 0;\r\nconst pendingEffects = new Set<ReactiveEffect>();\r\n\r\nexport function batch(fn: () => void) {\r\n batchDepth++;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth--;\r\n if (batchDepth === 0) {\r\n const effects = Array.from(pendingEffects);\r\n pendingEffects.clear();\r\n for (const effect of effects) {\r\n effect();\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction runEffect(fn: EffectFn): () => void {\r\n const effect: ReactiveEffect = function () {\r\n cleanup(effect);\r\n activeEffect = effect;\r\n fn();\r\n activeEffect = null;\r\n } as ReactiveEffect;\r\n\r\n effect.deps = [];\r\n effect();\r\n\r\n // disposer\r\n return () => cleanup(effect);\r\n}\r\n\r\nfunction cleanup(effect: ReactiveEffect): void {\r\n if (!effect.deps) return;\r\n for (const dep of effect.deps) {\r\n dep.delete(effect);\r\n }\r\n effect.deps.length = 0;\r\n}\r\n\r\nfunction track(depSet: Set<ReactiveEffect>): void {\r\n if (!activeEffect) return;\r\n depSet.add(activeEffect);\r\n activeEffect.deps.push(depSet);\r\n}\r\n\r\nfunction trigger(depSet: Set<ReactiveEffect>): void {\r\n const effects = Array.from(depSet);\r\n for (const effect of effects) {\r\n if (batchDepth > 0) {\r\n pendingEffects.add(effect);\r\n } else {\r\n effect();\r\n }\r\n }\r\n}\r\n\r\nexport type Signal<T> = T & {\r\n $set: (newValue: T) => void;\r\n};\r\n\r\nlet accessObserver: ((target: any, key: string | symbol) => void) | null = null;\r\n\r\nexport function detectAccess(selector: () => any): [any, string | symbol] | null {\r\n let result: [any, string | symbol] | null = null;\r\n const prev = accessObserver;\r\n\r\n accessObserver = (target, key) => {\r\n result = [target, key];\r\n };\r\n\r\n try {\r\n selector();\r\n } finally {\r\n accessObserver = prev;\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport function untrack<T>(fn: () => T): T {\r\n const prev = activeEffect;\r\n activeEffect = null;\r\n try {\r\n return fn();\r\n } finally {\r\n activeEffect = prev;\r\n }\r\n}\r\n\r\nexport function signal<T extends object>(target: T): Signal<T> {\r\n const depsMap = new Map<string | symbol, Set<ReactiveEffect>>();\r\n const reactiveCache = new WeakMap<object, any>();\r\n\r\n return new Proxy(target, {\r\n get(obj, prop, receiver) {\r\n if (prop === '$set') {\r\n return (newValue: T) => {\r\n batch(() => {\r\n if (Array.isArray(obj) && Array.isArray(newValue)) {\r\n const len = newValue.length;\r\n for (let i = 0; i < len; i++) {\r\n Reflect.set(receiver, String(i), newValue[i]);\r\n }\r\n Reflect.set(receiver, 'length', len);\r\n } else {\r\n const newKeys = Object.keys(newValue);\r\n const oldKeys = Object.keys(obj);\r\n for (const key of newKeys) {\r\n Reflect.set(receiver, key, (newValue as any)[key]);\r\n }\r\n for (const key of oldKeys) {\r\n if (!(key in newValue)) {\r\n Reflect.deleteProperty(receiver, key);\r\n }\r\n }\r\n }\r\n });\r\n };\r\n }\r\n\r\n if (Array.isArray(obj) && typeof prop === 'string' && arrayInstrumentations.hasOwnProperty(prop)) {\r\n return arrayInstrumentations[prop];\r\n }\r\n\r\n const value = Reflect.get(obj, prop);\r\n\r\n if (accessObserver) {\r\n accessObserver(receiver, prop);\r\n }\r\n\r\n // Track this property access\r\n let dep = depsMap.get(prop);\r\n if (!dep) {\r\n dep = new Set<ReactiveEffect>();\r\n depsMap.set(prop, dep);\r\n }\r\n track(dep);\r\n\r\n // If the value is an object, make it reactive too (with caching)\r\n if (value && typeof value === 'object') {\r\n let cached = reactiveCache.get(value);\r\n if (!cached) {\r\n cached = signal(value);\r\n reactiveCache.set(value, cached);\r\n }\r\n return cached;\r\n }\r\n\r\n return value;\r\n },\r\n set(obj, prop, newValue) {\r\n const oldLength = Array.isArray(obj) ? obj.length : 0;\r\n const oldValue = Reflect.get(obj, prop);\r\n const result = Reflect.set(obj, prop, newValue);\r\n\r\n // Only trigger if value actually changed\r\n if (!Object.is(oldValue, newValue)) {\r\n const dep = depsMap.get(prop);\r\n if (dep) {\r\n trigger(dep);\r\n }\r\n\r\n // Special handling for Arrays\r\n if (Array.isArray(obj)) {\r\n // If we set an index and length changed, trigger length dependency\r\n if (prop !== 'length' && obj.length !== oldLength) {\r\n const lengthDep = depsMap.get('length');\r\n if (lengthDep) {\r\n trigger(lengthDep);\r\n }\r\n }\r\n // If we set length, trigger indices that are now out of bounds\r\n if (prop === 'length' && typeof newValue === 'number' && newValue < oldLength) {\r\n for (let i = newValue; i < oldLength; i++) {\r\n const idxDep = depsMap.get(String(i));\r\n if (idxDep) trigger(idxDep);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n },\r\n deleteProperty(obj, prop) {\r\n const hasKey = Object.prototype.hasOwnProperty.call(obj, prop);\r\n const result = Reflect.deleteProperty(obj, prop);\r\n\r\n if (result && hasKey) {\r\n const dep = depsMap.get(prop);\r\n if (dep) {\r\n trigger(dep);\r\n }\r\n }\r\n return result;\r\n }\r\n }) as Signal<T>;\r\n}\r\n\r\nexport function effect(fn: EffectFn): () => void {\r\n return runEffect(fn);\r\n}\r\n\r\nconst arrayInstrumentations: Record<string, Function> = {};\r\n['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {\r\n arrayInstrumentations[method] = function (this: any, ...args: any[]) {\r\n let res;\r\n batch(() => {\r\n res = (Array.prototype as any)[method].apply(this, args);\r\n });\r\n return res;\r\n };\r\n});\r\n\r\nexport type WatchSource<T = any> = T | (() => T);\r\nexport type WatchCallback<V = any, OV = any> = (value: V, oldValue: OV, onCleanup: (fn: () => void) => void) => any;\r\nexport interface WatchOptions<Immediate = boolean> {\r\n immediate?: Immediate;\r\n deep?: boolean | number;\r\n once?: boolean;\r\n}\r\n\r\nexport interface WatchHandle {\r\n (): void; // stop\r\n pause: () => void;\r\n resume: () => void;\r\n stop: () => void;\r\n}\r\n\r\nexport function watch<T>(source: WatchSource<T>, cb: WatchCallback<T>, options?: WatchOptions): WatchHandle {\r\n let oldValue: T | undefined;\r\n let isFirst = true;\r\n let cleanupFn: (() => void) | null = null;\r\n\r\n const runner = effect(() => {\r\n const newValue = typeof source === 'function' ? (source as () => T)() : source;\r\n\r\n if (isFirst) {\r\n if (options?.immediate) {\r\n if (cleanupFn) cleanupFn();\r\n cb(newValue, oldValue, (fn) => cleanupFn = fn);\r\n }\r\n isFirst = false;\r\n } else {\r\n if (cleanupFn) cleanupFn();\r\n cb(newValue, oldValue, (fn) => cleanupFn = fn);\r\n }\r\n oldValue = newValue;\r\n });\r\n\r\n const stop = () => {\r\n runner();\r\n if (cleanupFn) cleanupFn();\r\n };\r\n\r\n const handle = stop as unknown as WatchHandle;\r\n handle.stop = stop;\r\n handle.pause = () => { }; // Not implemented\r\n handle.resume = () => { }; // Not implemented\r\n\r\n return handle;\r\n}\r\n\r\nexport type EffectScope = {\r\n run<T>(fn: () => T): T | undefined;\r\n stop(fromParent?: boolean): void;\r\n}\r\n\r\nexport function effectScope(detached?: boolean): EffectScope {\r\n const effects: (() => void)[] = [];\r\n let active = true;\r\n\r\n return {\r\n run<T>(fn: () => T): T | undefined {\r\n if (!active) return undefined;\r\n return fn();\r\n },\r\n stop() {\r\n active = false;\r\n effects.forEach(e => e());\r\n }\r\n };\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["activeEffect: ReactiveEffect | null","effect","effectFn: ReactiveEffect","accessObserver: ((target: any, key: string | symbol) => void) | null","result: [any, string | symbol] | null","arrayInstrumentations: Record<string, Function>","oldValue: T | undefined","cleanupFn: (() => void) | null","pendingValue: T | undefined","effects: (() => void)[]"],"sources":["../src/index.ts"],"sourcesContent":["// signals.ts\r\n\r\nexport type EffectFn = () => void;\r\n\r\nexport interface EffectRunner<T = void> {\r\n (): T;\r\n stop: () => void;\r\n}\r\n\r\nexport interface ReactiveEffect extends EffectFn {\r\n deps: Set<ReactiveEffect>[];\r\n}\r\n\r\nlet activeEffect: ReactiveEffect | null = null;\r\nlet batchDepth = 0;\r\nconst pendingEffects = new Set<ReactiveEffect>();\r\n\r\nexport function batch(fn: () => void) {\r\n batchDepth++;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth--;\r\n if (batchDepth === 0) {\r\n const effects = Array.from(pendingEffects);\r\n pendingEffects.clear();\r\n for (const effect of effects) {\r\n effect();\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction runEffect(fn: EffectFn): EffectRunner {\r\n const effectFn: ReactiveEffect = function () {\r\n cleanup(effectFn);\r\n activeEffect = effectFn;\r\n fn();\r\n activeEffect = null;\r\n } as ReactiveEffect;\r\n\r\n effectFn.deps = [];\r\n effectFn();\r\n\r\n // Return the effect as a runner with a stop method\r\n const runner = (() => effectFn()) as EffectRunner;\r\n runner.stop = () => cleanup(effectFn);\r\n return runner;\r\n}\r\n\r\nfunction cleanup(effect: ReactiveEffect): void {\r\n if (!effect.deps) return;\r\n for (const dep of effect.deps) {\r\n dep.delete(effect);\r\n }\r\n effect.deps.length = 0;\r\n}\r\n\r\nfunction track(depSet: Set<ReactiveEffect>): void {\r\n if (!activeEffect) return;\r\n depSet.add(activeEffect);\r\n activeEffect.deps.push(depSet);\r\n}\r\n\r\nfunction trigger(depSet: Set<ReactiveEffect>): void {\r\n const effects = Array.from(depSet);\r\n for (const effect of effects) {\r\n if (batchDepth > 0) {\r\n pendingEffects.add(effect);\r\n } else {\r\n effect();\r\n }\r\n }\r\n}\r\n\r\nexport type Signal<T> = T & {\r\n $set: (newValue: T) => void;\r\n};\r\n\r\nlet accessObserver: ((target: any, key: string | symbol) => void) | null = null;\r\n\r\nexport function detectAccess(selector: () => any): [any, string | symbol] | null {\r\n let result: [any, string | symbol] | null = null;\r\n const prev = accessObserver;\r\n\r\n accessObserver = (target, key) => {\r\n result = [target, key];\r\n };\r\n\r\n try {\r\n selector();\r\n } finally {\r\n accessObserver = prev;\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport function untrack<T>(fn: () => T): T {\r\n const prev = activeEffect;\r\n activeEffect = null;\r\n try {\r\n return fn();\r\n } finally {\r\n activeEffect = prev;\r\n }\r\n}\r\n\r\nexport function signal<T extends object>(target: T): Signal<T> {\r\n const depsMap = new Map<string | symbol, Set<ReactiveEffect>>();\r\n const reactiveCache = new WeakMap<object, any>();\r\n\r\n return new Proxy(target, {\r\n get(obj, prop, receiver) {\r\n if (prop === '$set') {\r\n return (newValue: T) => {\r\n batch(() => {\r\n if (Array.isArray(obj) && Array.isArray(newValue)) {\r\n const len = newValue.length;\r\n for (let i = 0; i < len; i++) {\r\n Reflect.set(receiver, String(i), newValue[i]);\r\n }\r\n Reflect.set(receiver, 'length', len);\r\n } else {\r\n const newKeys = Object.keys(newValue);\r\n const oldKeys = Object.keys(obj);\r\n for (const key of newKeys) {\r\n Reflect.set(receiver, key, (newValue as any)[key]);\r\n }\r\n for (const key of oldKeys) {\r\n if (!(key in newValue)) {\r\n Reflect.deleteProperty(receiver, key);\r\n }\r\n }\r\n }\r\n });\r\n };\r\n }\r\n\r\n if (Array.isArray(obj) && typeof prop === 'string' && arrayInstrumentations.hasOwnProperty(prop)) {\r\n return arrayInstrumentations[prop];\r\n }\r\n\r\n const value = Reflect.get(obj, prop);\r\n\r\n if (accessObserver) {\r\n accessObserver(receiver, prop);\r\n }\r\n\r\n // Track this property access\r\n let dep = depsMap.get(prop);\r\n if (!dep) {\r\n dep = new Set<ReactiveEffect>();\r\n depsMap.set(prop, dep);\r\n }\r\n track(dep);\r\n\r\n // If the value is an object, make it reactive too (with caching)\r\n if (value && typeof value === 'object') {\r\n let cached = reactiveCache.get(value);\r\n if (!cached) {\r\n cached = signal(value);\r\n reactiveCache.set(value, cached);\r\n }\r\n return cached;\r\n }\r\n\r\n return value;\r\n },\r\n set(obj, prop, newValue) {\r\n const oldLength = Array.isArray(obj) ? obj.length : 0;\r\n const oldValue = Reflect.get(obj, prop);\r\n const result = Reflect.set(obj, prop, newValue);\r\n\r\n // Only trigger if value actually changed\r\n if (!Object.is(oldValue, newValue)) {\r\n const dep = depsMap.get(prop);\r\n if (dep) {\r\n trigger(dep);\r\n }\r\n\r\n // Special handling for Arrays\r\n if (Array.isArray(obj)) {\r\n // If we set an index and length changed, trigger length dependency\r\n if (prop !== 'length' && obj.length !== oldLength) {\r\n const lengthDep = depsMap.get('length');\r\n if (lengthDep) {\r\n trigger(lengthDep);\r\n }\r\n }\r\n // If we set length, trigger indices that are now out of bounds\r\n if (prop === 'length' && typeof newValue === 'number' && newValue < oldLength) {\r\n for (let i = newValue; i < oldLength; i++) {\r\n const idxDep = depsMap.get(String(i));\r\n if (idxDep) trigger(idxDep);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n },\r\n deleteProperty(obj, prop) {\r\n const hasKey = Object.prototype.hasOwnProperty.call(obj, prop);\r\n const result = Reflect.deleteProperty(obj, prop);\r\n\r\n if (result && hasKey) {\r\n const dep = depsMap.get(prop);\r\n if (dep) {\r\n trigger(dep);\r\n }\r\n }\r\n return result;\r\n }\r\n }) as Signal<T>;\r\n}\r\n\r\nexport function effect(fn: EffectFn): EffectRunner {\r\n return runEffect(fn);\r\n}\r\n\r\nconst arrayInstrumentations: Record<string, Function> = {};\r\n['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {\r\n arrayInstrumentations[method] = function (this: any, ...args: any[]) {\r\n let res;\r\n batch(() => {\r\n res = (Array.prototype as any)[method].apply(this, args);\r\n });\r\n return res;\r\n };\r\n});\r\n\r\nexport type WatchSource<T = any> = T | (() => T);\r\nexport type WatchCallback<V = any, OV = any> = (value: V, oldValue: OV, onCleanup: (fn: () => void) => void) => any;\r\nexport interface WatchOptions<Immediate = boolean> {\r\n immediate?: Immediate;\r\n deep?: boolean | number;\r\n once?: boolean;\r\n}\r\n\r\nexport interface WatchHandle {\r\n (): void; // callable to stop\r\n stop: () => void;\r\n pause: () => void;\r\n resume: () => void;\r\n}\r\n\r\nexport function watch<T>(source: WatchSource<T>, cb: WatchCallback<T>, options?: WatchOptions): WatchHandle {\r\n let oldValue: T | undefined;\r\n let isFirst = true;\r\n let cleanupFn: (() => void) | null = null;\r\n let paused = false;\r\n let pendingValue: T | undefined;\r\n let hasPending = false;\r\n\r\n const runner = effect(() => {\r\n const newValue = typeof source === 'function' ? (source as () => T)() : source;\r\n\r\n if (paused) {\r\n // Store pending value to process on resume\r\n pendingValue = newValue;\r\n hasPending = true;\r\n return;\r\n }\r\n\r\n if (isFirst) {\r\n if (options?.immediate) {\r\n if (cleanupFn) cleanupFn();\r\n cb(newValue, oldValue, (fn) => cleanupFn = fn);\r\n }\r\n isFirst = false;\r\n } else {\r\n if (cleanupFn) cleanupFn();\r\n cb(newValue, oldValue, (fn) => cleanupFn = fn);\r\n }\r\n oldValue = newValue;\r\n });\r\n\r\n const stop = () => {\r\n runner.stop();\r\n if (cleanupFn) cleanupFn();\r\n };\r\n\r\n const pause = () => {\r\n paused = true;\r\n };\r\n\r\n const resume = () => {\r\n if (!paused) return;\r\n paused = false;\r\n // If value changed while paused, trigger callback now\r\n if (hasPending && !Object.is(pendingValue, oldValue)) {\r\n if (cleanupFn) cleanupFn();\r\n cb(pendingValue as T, oldValue, (fn) => cleanupFn = fn);\r\n oldValue = pendingValue;\r\n }\r\n hasPending = false;\r\n pendingValue = undefined;\r\n };\r\n\r\n return Object.assign(stop, { stop, pause, resume });\r\n}\r\n\r\nexport type EffectScope = {\r\n run<T>(fn: () => T): T | undefined;\r\n stop(fromParent?: boolean): void;\r\n}\r\n\r\nexport function effectScope(detached?: boolean): EffectScope {\r\n const effects: (() => void)[] = [];\r\n let active = true;\r\n\r\n return {\r\n run<T>(fn: () => T): T | undefined {\r\n if (!active) return undefined;\r\n return fn();\r\n },\r\n stop() {\r\n active = false;\r\n effects.forEach(e => e());\r\n }\r\n };\r\n}\r\n"],"mappings":";AAaA,IAAIA,eAAsC;AAC1C,IAAI,aAAa;AACjB,MAAM,iCAAiB,IAAI,KAAqB;AAEhD,SAAgB,MAAM,IAAgB;AAClC;AACA,KAAI;AACA,MAAI;WACE;AACN;AACA,MAAI,eAAe,GAAG;GAClB,MAAM,UAAU,MAAM,KAAK,eAAe;AAC1C,kBAAe,OAAO;AACtB,QAAK,MAAMC,YAAU,QACjB,WAAQ;;;;AAMxB,SAAS,UAAU,IAA4B;CAC3C,MAAMC,WAA2B,WAAY;AACzC,UAAQ,SAAS;AACjB,iBAAe;AACf,MAAI;AACJ,iBAAe;;AAGnB,UAAS,OAAO,EAAE;AAClB,WAAU;CAGV,MAAM,gBAAgB,UAAU;AAChC,QAAO,aAAa,QAAQ,SAAS;AACrC,QAAO;;AAGX,SAAS,QAAQ,UAA8B;AAC3C,KAAI,CAACD,SAAO,KAAM;AAClB,MAAK,MAAM,OAAOA,SAAO,KACrB,KAAI,OAAOA,SAAO;AAEtB,UAAO,KAAK,SAAS;;AAGzB,SAAS,MAAM,QAAmC;AAC9C,KAAI,CAAC,aAAc;AACnB,QAAO,IAAI,aAAa;AACxB,cAAa,KAAK,KAAK,OAAO;;AAGlC,SAAS,QAAQ,QAAmC;CAChD,MAAM,UAAU,MAAM,KAAK,OAAO;AAClC,MAAK,MAAMA,YAAU,QACjB,KAAI,aAAa,EACb,gBAAe,IAAIA,SAAO;KAE1B,WAAQ;;AASpB,IAAIE,iBAAuE;AAE3E,SAAgB,aAAa,UAAoD;CAC7E,IAAIC,SAAwC;CAC5C,MAAM,OAAO;AAEb,mBAAkB,QAAQ,QAAQ;AAC9B,WAAS,CAAC,QAAQ,IAAI;;AAG1B,KAAI;AACA,YAAU;WACJ;AACN,mBAAiB;;AAGrB,QAAO;;AAGX,SAAgB,QAAW,IAAgB;CACvC,MAAM,OAAO;AACb,gBAAe;AACf,KAAI;AACA,SAAO,IAAI;WACL;AACN,iBAAe;;;AAIvB,SAAgB,OAAyB,QAAsB;CAC3D,MAAM,0BAAU,IAAI,KAA2C;CAC/D,MAAM,gCAAgB,IAAI,SAAsB;AAEhD,QAAO,IAAI,MAAM,QAAQ;EACrB,IAAI,KAAK,MAAM,UAAU;AACrB,OAAI,SAAS,OACT,SAAQ,aAAgB;AACpB,gBAAY;AACR,SAAI,MAAM,QAAQ,IAAI,IAAI,MAAM,QAAQ,SAAS,EAAE;MAC/C,MAAM,MAAM,SAAS;AACrB,WAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IACrB,SAAQ,IAAI,UAAU,OAAO,EAAE,EAAE,SAAS,GAAG;AAEjD,cAAQ,IAAI,UAAU,UAAU,IAAI;YACjC;MACH,MAAM,UAAU,OAAO,KAAK,SAAS;MACrC,MAAM,UAAU,OAAO,KAAK,IAAI;AAChC,WAAK,MAAM,OAAO,QACd,SAAQ,IAAI,UAAU,KAAM,SAAiB,KAAK;AAEtD,WAAK,MAAM,OAAO,QACd,KAAI,EAAE,OAAO,UACT,SAAQ,eAAe,UAAU,IAAI;;MAInD;;AAIV,OAAI,MAAM,QAAQ,IAAI,IAAI,OAAO,SAAS,YAAY,sBAAsB,eAAe,KAAK,CAC5F,QAAO,sBAAsB;GAGjC,MAAM,QAAQ,QAAQ,IAAI,KAAK,KAAK;AAEpC,OAAI,eACA,gBAAe,UAAU,KAAK;GAIlC,IAAI,MAAM,QAAQ,IAAI,KAAK;AAC3B,OAAI,CAAC,KAAK;AACN,0BAAM,IAAI,KAAqB;AAC/B,YAAQ,IAAI,MAAM,IAAI;;AAE1B,SAAM,IAAI;AAGV,OAAI,SAAS,OAAO,UAAU,UAAU;IACpC,IAAI,SAAS,cAAc,IAAI,MAAM;AACrC,QAAI,CAAC,QAAQ;AACT,cAAS,OAAO,MAAM;AACtB,mBAAc,IAAI,OAAO,OAAO;;AAEpC,WAAO;;AAGX,UAAO;;EAEX,IAAI,KAAK,MAAM,UAAU;GACrB,MAAM,YAAY,MAAM,QAAQ,IAAI,GAAG,IAAI,SAAS;GACpD,MAAM,WAAW,QAAQ,IAAI,KAAK,KAAK;GACvC,MAAM,SAAS,QAAQ,IAAI,KAAK,MAAM,SAAS;AAG/C,OAAI,CAAC,OAAO,GAAG,UAAU,SAAS,EAAE;IAChC,MAAM,MAAM,QAAQ,IAAI,KAAK;AAC7B,QAAI,IACA,SAAQ,IAAI;AAIhB,QAAI,MAAM,QAAQ,IAAI,EAAE;AAEpB,SAAI,SAAS,YAAY,IAAI,WAAW,WAAW;MAC/C,MAAM,YAAY,QAAQ,IAAI,SAAS;AACvC,UAAI,UACA,SAAQ,UAAU;;AAI1B,SAAI,SAAS,YAAY,OAAO,aAAa,YAAY,WAAW,UAChE,MAAK,IAAI,IAAI,UAAU,IAAI,WAAW,KAAK;MACvC,MAAM,SAAS,QAAQ,IAAI,OAAO,EAAE,CAAC;AACrC,UAAI,OAAQ,SAAQ,OAAO;;;;AAM3C,UAAO;;EAEX,eAAe,KAAK,MAAM;GACtB,MAAM,SAAS,OAAO,UAAU,eAAe,KAAK,KAAK,KAAK;GAC9D,MAAM,SAAS,QAAQ,eAAe,KAAK,KAAK;AAEhD,OAAI,UAAU,QAAQ;IAClB,MAAM,MAAM,QAAQ,IAAI,KAAK;AAC7B,QAAI,IACA,SAAQ,IAAI;;AAGpB,UAAO;;EAEd,CAAC;;AAGN,SAAgB,OAAO,IAA4B;AAC/C,QAAO,UAAU,GAAG;;AAGxB,MAAMC,wBAAkD,EAAE;AAC1D;CAAC;CAAQ;CAAO;CAAS;CAAW;CAAU;CAAQ;CAAU,CAAC,SAAQ,WAAU;AAC/E,uBAAsB,UAAU,SAAqB,GAAG,MAAa;EACjE,IAAI;AACJ,cAAY;AACR,SAAO,MAAM,UAAkB,QAAQ,MAAM,MAAM,KAAK;IAC1D;AACF,SAAO;;EAEb;AAiBF,SAAgB,MAAS,QAAwB,IAAsB,SAAqC;CACxG,IAAIC;CACJ,IAAI,UAAU;CACd,IAAIC,YAAiC;CACrC,IAAI,SAAS;CACb,IAAIC;CACJ,IAAI,aAAa;CAEjB,MAAM,SAAS,aAAa;EACxB,MAAM,WAAW,OAAO,WAAW,aAAc,QAAoB,GAAG;AAExE,MAAI,QAAQ;AAER,kBAAe;AACf,gBAAa;AACb;;AAGJ,MAAI,SAAS;AACT,OAAI,SAAS,WAAW;AACpB,QAAI,UAAW,YAAW;AAC1B,OAAG,UAAU,WAAW,OAAO,YAAY,GAAG;;AAElD,aAAU;SACP;AACH,OAAI,UAAW,YAAW;AAC1B,MAAG,UAAU,WAAW,OAAO,YAAY,GAAG;;AAElD,aAAW;GACb;CAEF,MAAM,aAAa;AACf,SAAO,MAAM;AACb,MAAI,UAAW,YAAW;;CAG9B,MAAM,cAAc;AAChB,WAAS;;CAGb,MAAM,eAAe;AACjB,MAAI,CAAC,OAAQ;AACb,WAAS;AAET,MAAI,cAAc,CAAC,OAAO,GAAG,cAAc,SAAS,EAAE;AAClD,OAAI,UAAW,YAAW;AAC1B,MAAG,cAAmB,WAAW,OAAO,YAAY,GAAG;AACvD,cAAW;;AAEf,eAAa;AACb,iBAAe;;AAGnB,QAAO,OAAO,OAAO,MAAM;EAAE;EAAM;EAAO;EAAQ,CAAC;;AAQvD,SAAgB,YAAY,UAAiC;CACzD,MAAMC,UAA0B,EAAE;CAClC,IAAI,SAAS;AAEb,QAAO;EACH,IAAO,IAA4B;AAC/B,OAAI,CAAC,OAAQ,QAAO;AACpB,UAAO,IAAI;;EAEf,OAAO;AACH,YAAS;AACT,WAAQ,SAAQ,MAAK,GAAG,CAAC;;EAEhC"}
|