@yiin/reactive-proxy-state 1.0.12 → 1.0.13
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/computed.d.ts +6 -0
- package/dist/constants.d.ts +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1474 -11
- package/dist/reactive.d.ts +13 -1
- package/dist/ref.d.ts +24 -7
- package/dist/state.d.ts +6 -0
- package/dist/types.d.ts +1 -1
- package/dist/watch-effect.d.ts +2 -1
- package/dist/wrap-array.d.ts +1 -1
- package/dist/wrap-map.d.ts +1 -1
- package/dist/wrap-set.d.ts +1 -1
- package/package.json +2 -2
- package/dist/computed.js +0 -58
- package/dist/reactive.js +0 -114
- package/dist/ref.js +0 -91
- package/dist/state.js +0 -211
- package/dist/types.js +0 -1
- package/dist/utils.js +0 -220
- package/dist/watch-effect.js +0 -154
- package/dist/watch.js +0 -44
- package/dist/watchEffect.d.ts +0 -54
- package/dist/watchEffect.js +0 -154
- package/dist/wrap-array.js +0 -237
- package/dist/wrap-map.js +0 -252
- package/dist/wrap-set.js +0 -182
- package/dist/wrapArray.d.ts +0 -2
- package/dist/wrapArray.js +0 -227
- package/dist/wrapMap.d.ts +0 -2
- package/dist/wrapMap.js +0 -230
- package/dist/wrapSet.d.ts +0 -2
- package/dist/wrapSet.js +0 -167
package/dist/reactive.d.ts
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
import { EmitFunction, Path } from './types';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Checks if an object is a reactive proxy
|
|
4
|
+
*/
|
|
5
|
+
export declare function isReactive(value: any): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Returns the raw, original object underlying a reactive proxy.
|
|
8
|
+
* If the input is not a proxy, returns the input itself.
|
|
9
|
+
*/
|
|
10
|
+
export declare function toRaw<T>(observed: T): T;
|
|
11
|
+
/**
|
|
12
|
+
* Create a reactive proxy for an object
|
|
13
|
+
*/
|
|
14
|
+
export declare function reactive<T extends object>(obj: T, emit?: EmitFunction, path?: Path, seen?: WeakMap<any, any>): T;
|
package/dist/ref.d.ts
CHANGED
|
@@ -4,26 +4,36 @@ export interface Ref<T = any> {
|
|
|
4
4
|
readonly [isRefSymbol]: true;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Creates a reactive reference object.
|
|
8
|
+
* The object has a single `.value` property.
|
|
9
|
+
* Reactivity is tracked on access and mutation of the `.value` property.
|
|
10
|
+
* If an object is passed as the initial value, the object itself is *not* made deeply reactive.
|
|
11
|
+
* Only the assignment to `.value` is tracked.
|
|
12
|
+
*
|
|
13
|
+
* @param value - The value to wrap in a ref
|
|
14
|
+
* @returns A ref object with the value
|
|
12
15
|
*/
|
|
13
16
|
export declare function ref<T>(value: T): Ref<T>;
|
|
14
17
|
export declare function ref<T = undefined>(): Ref<T | undefined>;
|
|
15
18
|
/**
|
|
16
|
-
*
|
|
19
|
+
* Checks if a value is a ref object.
|
|
20
|
+
*
|
|
21
|
+
* @param r - The value to check
|
|
22
|
+
* @returns true if the value is a ref, false otherwise
|
|
17
23
|
*/
|
|
18
24
|
export declare function isRef<T>(r: any): r is Ref<T>;
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
26
|
+
* Returns the inner value if the argument is a ref,
|
|
21
27
|
* otherwise returns the argument itself. this is a sugar for `isRef(val) ? val.value : val`.
|
|
28
|
+
*
|
|
29
|
+
* @param refValue - The value to unref
|
|
30
|
+
* @returns The inner value if the argument is a ref, otherwise the argument itself
|
|
22
31
|
*/
|
|
23
32
|
export declare function unref<T>(refValue: T | Ref<T>): T;
|
|
24
33
|
/**
|
|
25
34
|
* Converts an object's properties to reactive refs.
|
|
26
35
|
* This is useful when you want to destructure reactive objects but maintain reactivity.
|
|
36
|
+
*
|
|
27
37
|
* @param object The reactive object to convert to refs
|
|
28
38
|
* @returns An object with the same properties, where each property is a ref connected to the original object
|
|
29
39
|
*/
|
|
@@ -32,8 +42,15 @@ export declare function toRefs<T extends object>(object: T): {
|
|
|
32
42
|
};
|
|
33
43
|
/**
|
|
34
44
|
* Creates a ref that is connected to a property on an object.
|
|
45
|
+
*
|
|
35
46
|
* @param object The source object
|
|
36
47
|
* @param key The property key
|
|
37
48
|
* @returns A ref connected to the object's property
|
|
38
49
|
*/
|
|
39
50
|
export declare function toRef<T extends object, K extends keyof T>(object: T, key: K): Ref<T[K]>;
|
|
51
|
+
/**
|
|
52
|
+
* Basic triggerRef function
|
|
53
|
+
*
|
|
54
|
+
* @param ref - The ref object to trigger
|
|
55
|
+
*/
|
|
56
|
+
export declare function triggerRef(ref: Ref<any>): void;
|
package/dist/state.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import { StateEvent } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Applies a state change event to a plain javascript object/array (the target state)
|
|
4
|
+
*
|
|
5
|
+
* @param root - The root object/array to apply the event to
|
|
6
|
+
* @param event - The state change event to apply
|
|
7
|
+
*/
|
|
2
8
|
export declare function updateState(root: any, event: StateEvent): void;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type Path = (string | number | symbol)[];
|
|
2
|
-
export type ActionType = 'set' | 'delete' | 'array-push' | 'array-pop' | 'array-splice' | 'array-shift' | 'array-unshift' | 'map-set' | 'map-delete' | 'map-clear' | 'set-add' | 'set-delete' | 'set-clear';
|
|
2
|
+
export type ActionType = 'set' | 'delete' | 'array-push' | 'array-pop' | 'array-splice' | 'array-shift' | 'array-unshift' | 'map-set' | 'map-delete' | 'map-clear' | 'set-add' | 'set-delete' | 'set-clear' | 'replace';
|
|
3
3
|
export interface StateEvent {
|
|
4
4
|
action: ActionType;
|
|
5
5
|
path: Path;
|
package/dist/watch-effect.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export interface TrackedEffect<T = any> {
|
|
|
10
10
|
options?: WatchEffectOptions;
|
|
11
11
|
active?: boolean;
|
|
12
12
|
_rawCallback: EffectCallback<T>;
|
|
13
|
+
triggerDepth?: number;
|
|
13
14
|
}
|
|
14
15
|
export declare let activeEffect: TrackedEffect<any> | null;
|
|
15
16
|
export declare function setActiveEffect(effect: TrackedEffect<any> | null): void;
|
|
@@ -26,7 +27,7 @@ export declare function track(target: object, key: string | symbol): void;
|
|
|
26
27
|
/**
|
|
27
28
|
* triggers all active effects associated with a specific object property.
|
|
28
29
|
* called by proxy setters/deleters or ref setters.
|
|
29
|
-
*
|
|
30
|
+
* now batches effects to run in the same tick.
|
|
30
31
|
*/
|
|
31
32
|
export declare function trigger(target: object, key: string | symbol): void;
|
|
32
33
|
export interface WatchEffectOptions {
|
package/dist/wrap-array.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { EmitFunction, Path } from './types';
|
|
2
|
-
export declare function wrapArray<T extends any[]>(arr: T, emit
|
|
2
|
+
export declare function wrapArray<T extends any[]>(arr: T, emit?: EmitFunction, path?: Path, seen?: WeakMap<any, any>): T;
|
package/dist/wrap-map.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { EmitFunction, Path } from './types';
|
|
2
|
-
export declare function wrapMap<K, V>(map: Map<K, V>, emit
|
|
2
|
+
export declare function wrapMap<K, V>(map: Map<K, V>, emit?: EmitFunction, path?: Path, seen?: WeakMap<any, any>): Map<K, V>;
|
package/dist/wrap-set.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { EmitFunction, Path } from './types';
|
|
2
|
-
export declare function wrapSet<T>(set: Set<T>, emit
|
|
2
|
+
export declare function wrapSet<T>(set: Set<T>, emit?: EmitFunction, path?: Path, seen?: WeakMap<any, any>): Set<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yiin/reactive-proxy-state",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"author": "Yiin <stanislovas@yiin.lt>",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "tsc",
|
|
48
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm && tsc --emitDeclarationOnly --outDir dist",
|
|
49
49
|
"test": "bun test",
|
|
50
50
|
"test:watch": "bun test --watch",
|
|
51
51
|
"test:bench": "bun --bun ./benchmarks/state-sync.bench.ts",
|
package/dist/computed.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { watchEffect, track, trigger } from './watch-effect';
|
|
2
|
-
import { isRefSymbol } from './ref';
|
|
3
|
-
// symbol for identifying computed refs
|
|
4
|
-
const isComputedSymbol = Symbol('isComputed');
|
|
5
|
-
// implementation using a lazy watchEffect with a custom scheduler for caching
|
|
6
|
-
export function computed(getterOrOptions) {
|
|
7
|
-
let getter;
|
|
8
|
-
let setter;
|
|
9
|
-
const isGetter = typeof getterOrOptions === 'function';
|
|
10
|
-
if (isGetter) {
|
|
11
|
-
getter = getterOrOptions;
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
getter = getterOrOptions.get;
|
|
15
|
-
setter = getterOrOptions.set;
|
|
16
|
-
}
|
|
17
|
-
let _value;
|
|
18
|
-
let _dirty = true; // flag to track if the cached value is stale
|
|
19
|
-
let computedRef; // placeholder to allow self-reference in scheduler
|
|
20
|
-
// create a lazy effect; scheduler intercepts triggers to mark dirty instead of recomputing immediately
|
|
21
|
-
const stopHandle = watchEffect(getter, {
|
|
22
|
-
lazy: true,
|
|
23
|
-
scheduler: () => {
|
|
24
|
-
if (!_dirty) {
|
|
25
|
-
_dirty = true;
|
|
26
|
-
// trigger effects that depend on this computed ref
|
|
27
|
-
trigger(computedRef, 'value');
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
const effectRunner = stopHandle.effect;
|
|
32
|
-
computedRef = {
|
|
33
|
-
[isRefSymbol]: true,
|
|
34
|
-
[isComputedSymbol]: true,
|
|
35
|
-
get value() {
|
|
36
|
-
track(computedRef, 'value');
|
|
37
|
-
// if dirty, recompute value by running the getter
|
|
38
|
-
if (_dirty) {
|
|
39
|
-
_value = effectRunner.run();
|
|
40
|
-
_dirty = false; // mark as clean after successful run
|
|
41
|
-
}
|
|
42
|
-
return _value;
|
|
43
|
-
},
|
|
44
|
-
set value(newValue) {
|
|
45
|
-
if (setter) {
|
|
46
|
-
setter(newValue);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
console.warn('computed value is read-only');
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
// stop: stopHandle // potentially expose stop handle
|
|
53
|
-
};
|
|
54
|
-
return computedRef;
|
|
55
|
-
}
|
|
56
|
-
export function isComputed(c) {
|
|
57
|
-
return !!(c && c[isComputedSymbol]);
|
|
58
|
-
}
|
package/dist/reactive.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { deepEqual, globalSeen, getPathConcat, setPathConcat } from './utils';
|
|
2
|
-
import { wrapArray } from './wrap-array';
|
|
3
|
-
import { wrapMap } from './wrap-map';
|
|
4
|
-
import { wrapSet } from './wrap-set';
|
|
5
|
-
import { track, trigger } from './watch-effect';
|
|
6
|
-
// avoid repeated typeof checks
|
|
7
|
-
function isObject(v) {
|
|
8
|
-
return v && typeof v === 'object';
|
|
9
|
-
}
|
|
10
|
-
// create a reactive proxy for an object
|
|
11
|
-
export function reactive(obj, emit, path = [], seen = globalSeen) {
|
|
12
|
-
// prevent infinite recursion with circular references
|
|
13
|
-
if (seen.has(obj))
|
|
14
|
-
return seen.get(obj);
|
|
15
|
-
// helper to wrap nested values recursively
|
|
16
|
-
function wrapValue(val, subPath) {
|
|
17
|
-
if (!isObject(val))
|
|
18
|
-
return val; // primitives are returned directly
|
|
19
|
-
if (seen.has(val))
|
|
20
|
-
return seen.get(val); // handle cycles within nested structures
|
|
21
|
-
// delegate wrapping to specific functions based on type
|
|
22
|
-
if (Array.isArray(val))
|
|
23
|
-
return wrapArray(val, emit, subPath);
|
|
24
|
-
if (val instanceof Map)
|
|
25
|
-
return wrapMap(val, emit, subPath);
|
|
26
|
-
if (val instanceof Set)
|
|
27
|
-
return wrapSet(val, emit, subPath);
|
|
28
|
-
if (val instanceof Date)
|
|
29
|
-
return new Date(val.getTime()); // dates are not proxied, return copy
|
|
30
|
-
// default to reactive for plain objects
|
|
31
|
-
return reactive(val, emit, subPath, seen);
|
|
32
|
-
}
|
|
33
|
-
const proxy = new Proxy(obj, {
|
|
34
|
-
get(target, prop, receiver) {
|
|
35
|
-
const value = Reflect.get(target, prop, receiver);
|
|
36
|
-
// track property access for dependency tracking
|
|
37
|
-
track(target, prop);
|
|
38
|
-
// return non-objects directly without wrapping
|
|
39
|
-
if (!isObject(value))
|
|
40
|
-
return value;
|
|
41
|
-
// calculate the path for the nested property, using cache for performance
|
|
42
|
-
const propKey = String(prop);
|
|
43
|
-
const pathKey = path.length > 0 ? `${path.join('.')}.${propKey}` : propKey;
|
|
44
|
-
let newPath = getPathConcat(pathKey);
|
|
45
|
-
if (newPath === undefined) {
|
|
46
|
-
newPath = path.concat(propKey);
|
|
47
|
-
setPathConcat(pathKey, newPath);
|
|
48
|
-
}
|
|
49
|
-
// wrap the nested value if it's an object/collection
|
|
50
|
-
return wrapValue(value, newPath);
|
|
51
|
-
},
|
|
52
|
-
set(target, prop, value, receiver) {
|
|
53
|
-
const oldValue = target[prop];
|
|
54
|
-
// avoid unnecessary triggers if the value hasn't changed
|
|
55
|
-
// fast path for primitives
|
|
56
|
-
if (oldValue === value)
|
|
57
|
-
return true;
|
|
58
|
-
// deep equality check for objects/arrays
|
|
59
|
-
if (isObject(oldValue) && isObject(value) && deepEqual(oldValue, value, new WeakMap()))
|
|
60
|
-
return true; // use new WeakMap for deepEqual seen
|
|
61
|
-
const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
62
|
-
const result = Reflect.set(target, prop, value, receiver);
|
|
63
|
-
// only emit and trigger if the set was successful and wasn't intercepted by a setter
|
|
64
|
-
if (result && (!descriptor || !descriptor.set)) {
|
|
65
|
-
// calculate path, using cache
|
|
66
|
-
const propKey = String(prop);
|
|
67
|
-
const pathKey = path.length > 0 ? `${path.join('.')}.${propKey}` : propKey;
|
|
68
|
-
let newPath = getPathConcat(pathKey);
|
|
69
|
-
if (newPath === undefined) {
|
|
70
|
-
newPath = path.concat(propKey);
|
|
71
|
-
setPathConcat(pathKey, newPath);
|
|
72
|
-
}
|
|
73
|
-
const event = {
|
|
74
|
-
action: 'set',
|
|
75
|
-
path: newPath,
|
|
76
|
-
oldValue,
|
|
77
|
-
newValue: value
|
|
78
|
-
};
|
|
79
|
-
emit(event);
|
|
80
|
-
// notify effects watching this property
|
|
81
|
-
trigger(target, prop);
|
|
82
|
-
}
|
|
83
|
-
return result;
|
|
84
|
-
},
|
|
85
|
-
deleteProperty(target, prop) {
|
|
86
|
-
const oldValue = target[prop];
|
|
87
|
-
const hadProperty = Object.prototype.hasOwnProperty.call(target, prop);
|
|
88
|
-
const result = Reflect.deleteProperty(target, prop);
|
|
89
|
-
// only emit and trigger if the property existed and deletion was successful
|
|
90
|
-
if (hadProperty && result) {
|
|
91
|
-
// calculate path, using cache
|
|
92
|
-
const propKey = String(prop);
|
|
93
|
-
const pathKey = path.length > 0 ? `${path.join('.')}.${propKey}` : propKey;
|
|
94
|
-
let newPath = getPathConcat(pathKey);
|
|
95
|
-
if (newPath === undefined) {
|
|
96
|
-
newPath = path.concat(propKey);
|
|
97
|
-
setPathConcat(pathKey, newPath);
|
|
98
|
-
}
|
|
99
|
-
const event = {
|
|
100
|
-
action: 'delete',
|
|
101
|
-
path: newPath,
|
|
102
|
-
oldValue
|
|
103
|
-
};
|
|
104
|
-
emit(event);
|
|
105
|
-
// notify effects watching this property
|
|
106
|
-
trigger(target, prop);
|
|
107
|
-
}
|
|
108
|
-
return result;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
// cache the proxy to handle circular references and improve performance
|
|
112
|
-
seen.set(obj, proxy);
|
|
113
|
-
return proxy;
|
|
114
|
-
}
|
package/dist/ref.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { track, trigger } from './watch-effect';
|
|
2
|
-
// Removed reactive import as ref doesn't automatically make contained objects reactive
|
|
3
|
-
// import { reactive } from './reactive';
|
|
4
|
-
// symbol used to identify refs internally and via isRef()
|
|
5
|
-
export const isRefSymbol = Symbol('isRef');
|
|
6
|
-
export function ref(value) {
|
|
7
|
-
return createRef(value);
|
|
8
|
-
}
|
|
9
|
-
// internal factory for creating ref objects
|
|
10
|
-
function createRef(rawValue) {
|
|
11
|
-
// avoid wrapping if the value is already a ref
|
|
12
|
-
if (isRef(rawValue)) {
|
|
13
|
-
return rawValue;
|
|
14
|
-
}
|
|
15
|
-
// store the inner value
|
|
16
|
-
let _value = rawValue;
|
|
17
|
-
// create the ref object with a getter/setter on `.value`
|
|
18
|
-
const r = {
|
|
19
|
-
[isRefSymbol]: true, // mark as a ref using the symbol
|
|
20
|
-
get value() {
|
|
21
|
-
// track dependency when `.value` is accessed
|
|
22
|
-
// `r` (the ref object itself) is the target for tracking
|
|
23
|
-
track(r, 'value');
|
|
24
|
-
return _value;
|
|
25
|
-
},
|
|
26
|
-
set value(newValue) {
|
|
27
|
-
// only update and trigger if the value has actually changed
|
|
28
|
-
// this uses strict equality (===), so for objects, it checks reference equality
|
|
29
|
-
if (_value !== newValue) {
|
|
30
|
-
_value = newValue;
|
|
31
|
-
// trigger effects when `.value` is assigned a new value
|
|
32
|
-
// `r` (the ref object itself) is the target for triggering
|
|
33
|
-
trigger(r, 'value');
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
}; // cast to ensure the object conforms to the Ref interface
|
|
37
|
-
return r;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* checks if a value is a ref object.
|
|
41
|
-
*/
|
|
42
|
-
export function isRef(r) {
|
|
43
|
-
// check for the presence of the internal symbol
|
|
44
|
-
return !!(r && r[isRefSymbol]);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* returns the inner value if the argument is a ref,
|
|
48
|
-
* otherwise returns the argument itself. this is a sugar for `isRef(val) ? val.value : val`.
|
|
49
|
-
*/
|
|
50
|
-
export function unref(refValue) {
|
|
51
|
-
return isRef(refValue) ? refValue.value : refValue;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Converts an object's properties to reactive refs.
|
|
55
|
-
* This is useful when you want to destructure reactive objects but maintain reactivity.
|
|
56
|
-
* @param object The reactive object to convert to refs
|
|
57
|
-
* @returns An object with the same properties, where each property is a ref connected to the original object
|
|
58
|
-
*/
|
|
59
|
-
export function toRefs(object) {
|
|
60
|
-
const result = {};
|
|
61
|
-
for (const key in object) {
|
|
62
|
-
result[key] = toRef(object, key);
|
|
63
|
-
}
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Creates a ref that is connected to a property on an object.
|
|
68
|
-
* @param object The source object
|
|
69
|
-
* @param key The property key
|
|
70
|
-
* @returns A ref connected to the object's property
|
|
71
|
-
*/
|
|
72
|
-
export function toRef(object, key) {
|
|
73
|
-
return {
|
|
74
|
-
[isRefSymbol]: true,
|
|
75
|
-
get value() {
|
|
76
|
-
track(this, 'value');
|
|
77
|
-
return object[key];
|
|
78
|
-
},
|
|
79
|
-
set value(newVal) {
|
|
80
|
-
object[key] = newVal;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
// Basic triggerRef function (may need refinement if used)
|
|
85
|
-
/*
|
|
86
|
-
export function triggerRef(ref: Ref<any>): void {
|
|
87
|
-
trigger(ref, 'value');
|
|
88
|
-
}
|
|
89
|
-
*/
|
|
90
|
-
// TODO: Implement shallowRef if needed (uses createRef(value, true))
|
|
91
|
-
// TODO: Implement customRef if needed (more advanced)
|
package/dist/state.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { pathCache, setInPathCache } from './utils';
|
|
2
|
-
// helper to abstract getting a value from a map or object property
|
|
3
|
-
function getValue(obj, key) {
|
|
4
|
-
if (obj instanceof Map)
|
|
5
|
-
return obj.get(key);
|
|
6
|
-
return obj[key];
|
|
7
|
-
}
|
|
8
|
-
// helper to abstract setting a value on a map or object property
|
|
9
|
-
function setValue(obj, key, value) {
|
|
10
|
-
if (obj instanceof Map)
|
|
11
|
-
obj.set(key, value);
|
|
12
|
-
else
|
|
13
|
-
obj[key] = value;
|
|
14
|
-
}
|
|
15
|
-
// helper to abstract deleting a key from a map or object
|
|
16
|
-
function deleteValue(obj, key) {
|
|
17
|
-
if (obj instanceof Map)
|
|
18
|
-
obj.delete(key);
|
|
19
|
-
else
|
|
20
|
-
delete obj[key];
|
|
21
|
-
}
|
|
22
|
-
// dispatch table for applying state events based on action type
|
|
23
|
-
// this avoids a large switch statement and makes adding new actions easier
|
|
24
|
-
const actionHandlers = {
|
|
25
|
-
'set': function (parent, key, event) {
|
|
26
|
-
setValue(parent, key, event.newValue);
|
|
27
|
-
},
|
|
28
|
-
'delete': function (parent, key) {
|
|
29
|
-
deleteValue(parent, key);
|
|
30
|
-
},
|
|
31
|
-
'array-push': function (targetArray, _keyIgnored, event) {
|
|
32
|
-
if (!Array.isArray(targetArray)) {
|
|
33
|
-
console.warn(`expected array at path ${event.path.join('.')}`);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (!event.items) {
|
|
37
|
-
console.warn('array-push event missing items');
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
// event.key is the starting index, but push always adds to end
|
|
41
|
-
targetArray.push(...event.items);
|
|
42
|
-
},
|
|
43
|
-
'array-pop': function (targetArray, _keyIgnored, event) {
|
|
44
|
-
if (!Array.isArray(targetArray)) {
|
|
45
|
-
console.warn(`expected array at path ${event.path.join('.')}`);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
// event info (key, oldvalue) not needed to perform pop
|
|
49
|
-
if (targetArray.length > 0) {
|
|
50
|
-
targetArray.pop();
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
'array-splice': function (targetArray, _keyIgnored, event) {
|
|
54
|
-
if (!Array.isArray(targetArray)) {
|
|
55
|
-
console.warn(`expected array at path ${event.path.join('.')}`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (event.key === undefined || event.deleteCount === undefined) {
|
|
59
|
-
console.warn('array-splice event missing key or deletecount');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// handle splice with or without items to insert
|
|
63
|
-
if (event.items && event.items.length > 0) {
|
|
64
|
-
targetArray.splice(event.key, event.deleteCount, ...event.items);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
targetArray.splice(event.key, event.deleteCount);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
'array-shift': function (targetArray, _keyIgnored, event) {
|
|
71
|
-
if (!Array.isArray(targetArray)) {
|
|
72
|
-
console.warn(`expected array at path ${event.path.join('.')}`);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// event info not needed to perform shift
|
|
76
|
-
if (targetArray.length > 0) {
|
|
77
|
-
targetArray.shift();
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
'array-unshift': function (targetArray, _keyIgnored, event) {
|
|
81
|
-
if (!Array.isArray(targetArray)) {
|
|
82
|
-
console.warn(`expected array at path ${event.path.join('.')}`);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (!event.items) {
|
|
86
|
-
console.warn('array-unshift event missing items');
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
// event.key (always 0) not needed to perform unshift
|
|
90
|
-
targetArray.unshift(...event.items);
|
|
91
|
-
},
|
|
92
|
-
// note: for map/set operations originating from the proxy wrapper (e.g., map.set(k,v)),
|
|
93
|
-
// the *event path* points to the map itself, and event.key/event.value hold the map's key/value.
|
|
94
|
-
// however, the actionHandlers expect `target` to be the collection and `key` to be the map/set key.
|
|
95
|
-
// the logic in `updateState` below handles finding the correct target collection first.
|
|
96
|
-
'map-set': function (targetMap, _parentKeyIgnored, event) {
|
|
97
|
-
if (!(targetMap instanceof Map)) {
|
|
98
|
-
console.warn(`expected map at path ${event.path.join('.')}`);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
targetMap.set(event.key, event.newValue);
|
|
102
|
-
},
|
|
103
|
-
'map-delete': function (targetMap, _parentKeyIgnored, event) {
|
|
104
|
-
if (!(targetMap instanceof Map)) {
|
|
105
|
-
console.warn(`expected map at path ${event.path.join('.')}`);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
targetMap.delete(event.key);
|
|
109
|
-
},
|
|
110
|
-
'map-clear': function (targetMap, _parentKeyIgnored, event) {
|
|
111
|
-
if (!(targetMap instanceof Map)) {
|
|
112
|
-
console.warn(`expected map at path ${event.path.join('.')}`);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
targetMap.clear();
|
|
116
|
-
},
|
|
117
|
-
'set-add': function (targetSet, _keyIgnored, event) {
|
|
118
|
-
if (!(targetSet instanceof Set)) {
|
|
119
|
-
console.warn(`expected set at path ${event.path.join('.')}`);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
targetSet.add(event.value);
|
|
123
|
-
},
|
|
124
|
-
'set-delete': function (targetSet, _keyIgnored, event) {
|
|
125
|
-
if (!(targetSet instanceof Set)) {
|
|
126
|
-
console.warn(`expected set at path ${event.path.join('.')}`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
targetSet.delete(event.value);
|
|
130
|
-
},
|
|
131
|
-
'set-clear': function (targetSet, _keyIgnored, event) {
|
|
132
|
-
if (!(targetSet instanceof Set)) {
|
|
133
|
-
console.warn(`expected set at path ${event.path.join('.')}`);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
targetSet.clear();
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
// applies a state change event to a plain javascript object/array (the target state)
|
|
140
|
-
export function updateState(root, event) {
|
|
141
|
-
const { action, path } = event;
|
|
142
|
-
if (!path || path.length === 0) {
|
|
143
|
-
console.warn('event path is empty, cannot apply update', event);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const handler = actionHandlers[action];
|
|
147
|
-
if (!handler) {
|
|
148
|
-
// maybe allow custom handlers or ignore unknown actions?
|
|
149
|
-
console.error(`unhandled action type: ${action}`, event);
|
|
150
|
-
return;
|
|
151
|
-
// throw new Error(`Unhandled action: ${action}`);
|
|
152
|
-
}
|
|
153
|
-
// determine the target object/collection and the specific key (if applicable)
|
|
154
|
-
// based on the action type and the event path.
|
|
155
|
-
let targetForHandler;
|
|
156
|
-
let keyForHandler = null; // key passed to handler, relevant for set/delete
|
|
157
|
-
// path resolution logic differs based on action type:
|
|
158
|
-
// - set/delete: path leads to the *value* being set/deleted, so we need the parent object and the final path segment (key).
|
|
159
|
-
// - array-*/map-*/set-*: path leads to the *collection* itself.
|
|
160
|
-
if (action === 'set' || action === 'delete') {
|
|
161
|
-
// need the parent object and the final key
|
|
162
|
-
if (path.length === 1) {
|
|
163
|
-
targetForHandler = root; // parent is the root object
|
|
164
|
-
keyForHandler = path[0];
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
const parentPath = path.slice(0, -1);
|
|
168
|
-
const parentPathKey = parentPath.join('.');
|
|
169
|
-
// try cache first for performance
|
|
170
|
-
let parent = pathCache.get(root)?.get(parentPathKey);
|
|
171
|
-
if (parent === undefined) {
|
|
172
|
-
// traverse path manually if not in cache
|
|
173
|
-
parent = parentPath.reduce((acc, key) => acc ? getValue(acc, key) : undefined, root);
|
|
174
|
-
if (parent !== undefined)
|
|
175
|
-
setInPathCache(root, parentPathKey, parent); // cache if found
|
|
176
|
-
}
|
|
177
|
-
if (parent === undefined) {
|
|
178
|
-
console.warn(`parent path ${parentPathKey} not found for action ${action}`);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
targetForHandler = parent;
|
|
182
|
-
keyForHandler = path[path.length - 1];
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else if (action.startsWith('array-') || action.startsWith('map-') || action.startsWith('set-')) {
|
|
186
|
-
// need the collection object itself (array, map, or set)
|
|
187
|
-
const targetPath = path;
|
|
188
|
-
const targetPathKey = targetPath.join('.');
|
|
189
|
-
// try cache first
|
|
190
|
-
let targetCollection = pathCache.get(root)?.get(targetPathKey);
|
|
191
|
-
if (targetCollection === undefined) {
|
|
192
|
-
// traverse path manually if not in cache
|
|
193
|
-
targetCollection = targetPath.reduce((acc, key) => acc ? getValue(acc, key) : undefined, root);
|
|
194
|
-
if (targetCollection !== undefined)
|
|
195
|
-
setInPathCache(root, targetPathKey, targetCollection); // cache if found
|
|
196
|
-
}
|
|
197
|
-
if (targetCollection === undefined) {
|
|
198
|
-
console.warn(`target collection at path ${targetPathKey} not found for action ${action}`);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
targetForHandler = targetCollection;
|
|
202
|
-
// keyForHandler remains null for these actions as the handler operates directly on the collection
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
// this should not be reachable if handler lookup succeeded
|
|
206
|
-
console.error(`unexpected action type passed checks: ${action}`);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
// call the appropriate handler with the resolved target and key
|
|
210
|
-
handler(targetForHandler, keyForHandler, event);
|
|
211
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|