@bromscandium/core 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/README.md +59 -59
- package/package.json +38 -38
- package/src/dep.ts +148 -148
- package/src/effect.ts +263 -263
- package/src/index.ts +38 -38
- package/src/reactive.ts +162 -162
- package/src/ref.ts +243 -243
package/src/reactive.ts
CHANGED
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Proxy-based reactivity system for deep reactive state management.
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { track, trigger } from './dep.js';
|
|
7
|
-
|
|
8
|
-
const reactiveMap = new WeakMap<object, object>();
|
|
9
|
-
const rawMap = new WeakMap<object, object>();
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Internal flags used to identify reactive objects.
|
|
13
|
-
*/
|
|
14
|
-
export const ReactiveFlags = {
|
|
15
|
-
IS_REACTIVE: '__b_isReactive',
|
|
16
|
-
RAW: '__b_raw',
|
|
17
|
-
} as const;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Creates a deeply reactive proxy of an object.
|
|
21
|
-
* Changes to the object or its nested properties will automatically trigger effects.
|
|
22
|
-
*
|
|
23
|
-
* @param target - The object to make reactive
|
|
24
|
-
* @returns A reactive proxy of the target object
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* const state = reactive({ count: 0, nested: { value: 1 } });
|
|
29
|
-
* state.count++; // Triggers effects tracking 'count'
|
|
30
|
-
* state.nested.value++; // Triggers effects tracking nested 'value'
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export function reactive<T extends object>(target: T): T {
|
|
34
|
-
if (reactiveMap.has(target)) {
|
|
35
|
-
return reactiveMap.get(target) as T;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if ((target as any)[ReactiveFlags.IS_REACTIVE]) {
|
|
39
|
-
return target;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const proxy = new Proxy(target, {
|
|
43
|
-
get(target, key, receiver) {
|
|
44
|
-
if (key === ReactiveFlags.IS_REACTIVE) {
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
if (key === ReactiveFlags.RAW) {
|
|
48
|
-
return target;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const result = Reflect.get(target, key, receiver);
|
|
52
|
-
|
|
53
|
-
track(target, key);
|
|
54
|
-
|
|
55
|
-
if (typeof result === 'object' && result !== null) {
|
|
56
|
-
return reactive(result);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return result;
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
set(target, key, value, receiver) {
|
|
63
|
-
const oldValue = Reflect.get(target, key, receiver);
|
|
64
|
-
|
|
65
|
-
const newValue = (value as any)?.[ReactiveFlags.RAW] || value;
|
|
66
|
-
|
|
67
|
-
const result = Reflect.set(target, key, newValue, receiver);
|
|
68
|
-
|
|
69
|
-
if (!Object.is(oldValue, newValue)) {
|
|
70
|
-
trigger(target, key);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return result;
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
deleteProperty(target, key) {
|
|
77
|
-
const hadKey = Reflect.has(target, key);
|
|
78
|
-
const result = Reflect.deleteProperty(target, key);
|
|
79
|
-
|
|
80
|
-
if (hadKey && result) {
|
|
81
|
-
trigger(target, key);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return result;
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
has(target, key) {
|
|
88
|
-
track(target, key);
|
|
89
|
-
return Reflect.has(target, key);
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
ownKeys(target) {
|
|
93
|
-
track(target, Symbol('iterate'));
|
|
94
|
-
return Reflect.ownKeys(target);
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
reactiveMap.set(target, proxy);
|
|
99
|
-
rawMap.set(proxy, target);
|
|
100
|
-
|
|
101
|
-
return proxy as T;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Checks if a value is a reactive proxy created by `reactive()`.
|
|
106
|
-
*
|
|
107
|
-
* @param value - The value to check
|
|
108
|
-
* @returns True if the value is a reactive proxy
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* ```ts
|
|
112
|
-
* const state = reactive({ count: 0 });
|
|
113
|
-
* isReactive(state); // true
|
|
114
|
-
* isReactive({ count: 0 }); // false
|
|
115
|
-
* ```
|
|
116
|
-
*/
|
|
117
|
-
export function isReactive(value: unknown): boolean {
|
|
118
|
-
return !!(value && (value as any)[ReactiveFlags.IS_REACTIVE]);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Returns the raw, non-reactive version of a reactive object.
|
|
123
|
-
* Useful when you need to read values without triggering dependency tracking.
|
|
124
|
-
*
|
|
125
|
-
* @param observed - The reactive object to unwrap
|
|
126
|
-
* @returns The original non-reactive object
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```ts
|
|
130
|
-
* const state = reactive({ count: 0 });
|
|
131
|
-
* const raw = toRaw(state);
|
|
132
|
-
* raw === state; // false
|
|
133
|
-
* isReactive(raw); // false
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
export function toRaw<T>(observed: T): T {
|
|
137
|
-
const raw = (observed as any)?.[ReactiveFlags.RAW];
|
|
138
|
-
return raw ? toRaw(raw) : observed;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Marks an object so it will never be converted to a reactive proxy.
|
|
143
|
-
* Useful for values that should not be made reactive, like third-party class instances.
|
|
144
|
-
*
|
|
145
|
-
* @param value - The object to mark as non-reactive
|
|
146
|
-
* @returns The same object, marked as raw
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* ```ts
|
|
150
|
-
* const obj = markRaw({ count: 0 });
|
|
151
|
-
* const state = reactive({ data: obj });
|
|
152
|
-
* isReactive(state.data); // false - obj was marked raw
|
|
153
|
-
* ```
|
|
154
|
-
*/
|
|
155
|
-
export function markRaw<T extends object>(value: T): T {
|
|
156
|
-
Object.defineProperty(value, ReactiveFlags.IS_REACTIVE, {
|
|
157
|
-
configurable: true,
|
|
158
|
-
enumerable: false,
|
|
159
|
-
value: false,
|
|
160
|
-
});
|
|
161
|
-
return value;
|
|
162
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Proxy-based reactivity system for deep reactive state management.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { track, trigger } from './dep.js';
|
|
7
|
+
|
|
8
|
+
const reactiveMap = new WeakMap<object, object>();
|
|
9
|
+
const rawMap = new WeakMap<object, object>();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal flags used to identify reactive objects.
|
|
13
|
+
*/
|
|
14
|
+
export const ReactiveFlags = {
|
|
15
|
+
IS_REACTIVE: '__b_isReactive',
|
|
16
|
+
RAW: '__b_raw',
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a deeply reactive proxy of an object.
|
|
21
|
+
* Changes to the object or its nested properties will automatically trigger effects.
|
|
22
|
+
*
|
|
23
|
+
* @param target - The object to make reactive
|
|
24
|
+
* @returns A reactive proxy of the target object
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const state = reactive({ count: 0, nested: { value: 1 } });
|
|
29
|
+
* state.count++; // Triggers effects tracking 'count'
|
|
30
|
+
* state.nested.value++; // Triggers effects tracking nested 'value'
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function reactive<T extends object>(target: T): T {
|
|
34
|
+
if (reactiveMap.has(target)) {
|
|
35
|
+
return reactiveMap.get(target) as T;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if ((target as any)[ReactiveFlags.IS_REACTIVE]) {
|
|
39
|
+
return target;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const proxy = new Proxy(target, {
|
|
43
|
+
get(target, key, receiver) {
|
|
44
|
+
if (key === ReactiveFlags.IS_REACTIVE) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (key === ReactiveFlags.RAW) {
|
|
48
|
+
return target;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = Reflect.get(target, key, receiver);
|
|
52
|
+
|
|
53
|
+
track(target, key);
|
|
54
|
+
|
|
55
|
+
if (typeof result === 'object' && result !== null) {
|
|
56
|
+
return reactive(result);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
set(target, key, value, receiver) {
|
|
63
|
+
const oldValue = Reflect.get(target, key, receiver);
|
|
64
|
+
|
|
65
|
+
const newValue = (value as any)?.[ReactiveFlags.RAW] || value;
|
|
66
|
+
|
|
67
|
+
const result = Reflect.set(target, key, newValue, receiver);
|
|
68
|
+
|
|
69
|
+
if (!Object.is(oldValue, newValue)) {
|
|
70
|
+
trigger(target, key);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return result;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
deleteProperty(target, key) {
|
|
77
|
+
const hadKey = Reflect.has(target, key);
|
|
78
|
+
const result = Reflect.deleteProperty(target, key);
|
|
79
|
+
|
|
80
|
+
if (hadKey && result) {
|
|
81
|
+
trigger(target, key);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
has(target, key) {
|
|
88
|
+
track(target, key);
|
|
89
|
+
return Reflect.has(target, key);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
ownKeys(target) {
|
|
93
|
+
track(target, Symbol('iterate'));
|
|
94
|
+
return Reflect.ownKeys(target);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
reactiveMap.set(target, proxy);
|
|
99
|
+
rawMap.set(proxy, target);
|
|
100
|
+
|
|
101
|
+
return proxy as T;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Checks if a value is a reactive proxy created by `reactive()`.
|
|
106
|
+
*
|
|
107
|
+
* @param value - The value to check
|
|
108
|
+
* @returns True if the value is a reactive proxy
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const state = reactive({ count: 0 });
|
|
113
|
+
* isReactive(state); // true
|
|
114
|
+
* isReactive({ count: 0 }); // false
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function isReactive(value: unknown): boolean {
|
|
118
|
+
return !!(value && (value as any)[ReactiveFlags.IS_REACTIVE]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns the raw, non-reactive version of a reactive object.
|
|
123
|
+
* Useful when you need to read values without triggering dependency tracking.
|
|
124
|
+
*
|
|
125
|
+
* @param observed - The reactive object to unwrap
|
|
126
|
+
* @returns The original non-reactive object
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const state = reactive({ count: 0 });
|
|
131
|
+
* const raw = toRaw(state);
|
|
132
|
+
* raw === state; // false
|
|
133
|
+
* isReactive(raw); // false
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function toRaw<T>(observed: T): T {
|
|
137
|
+
const raw = (observed as any)?.[ReactiveFlags.RAW];
|
|
138
|
+
return raw ? toRaw(raw) : observed;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Marks an object so it will never be converted to a reactive proxy.
|
|
143
|
+
* Useful for values that should not be made reactive, like third-party class instances.
|
|
144
|
+
*
|
|
145
|
+
* @param value - The object to mark as non-reactive
|
|
146
|
+
* @returns The same object, marked as raw
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* const obj = markRaw({ count: 0 });
|
|
151
|
+
* const state = reactive({ data: obj });
|
|
152
|
+
* isReactive(state.data); // false - obj was marked raw
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function markRaw<T extends object>(value: T): T {
|
|
156
|
+
Object.defineProperty(value, ReactiveFlags.IS_REACTIVE, {
|
|
157
|
+
configurable: true,
|
|
158
|
+
enumerable: false,
|
|
159
|
+
value: false,
|
|
160
|
+
});
|
|
161
|
+
return value;
|
|
162
|
+
}
|