@nerdalytics/beacon 1000.2.0 → 1000.2.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/dist/src/index.d.ts +1 -25
- package/dist/src/index.js +90 -206
- package/dist/src/index.min.js +1 -0
- package/package.json +78 -55
- package/src/index.ts +0 -639
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type Unsubscribe = () => void;
|
|
1
|
+
export type Unsubscribe = () => void;
|
|
2
2
|
export type ReadOnlyState<T> = () => T;
|
|
3
3
|
export interface WriteableState<T> {
|
|
4
4
|
set(value: T): void;
|
|
@@ -8,36 +8,12 @@ declare const STATE_ID: unique symbol;
|
|
|
8
8
|
export type State<T> = ReadOnlyState<T> & WriteableState<T> & {
|
|
9
9
|
[STATE_ID]?: symbol;
|
|
10
10
|
};
|
|
11
|
-
/**
|
|
12
|
-
* Creates a reactive state container with the provided initial value.
|
|
13
|
-
*/
|
|
14
11
|
export declare const state: <T>(initialValue: T, equalityFn?: (a: T, b: T) => boolean) => State<T>;
|
|
15
|
-
/**
|
|
16
|
-
* Registers a function to run whenever its reactive dependencies change.
|
|
17
|
-
*/
|
|
18
12
|
export declare const effect: (fn: () => void) => Unsubscribe;
|
|
19
|
-
/**
|
|
20
|
-
* Groups multiple state updates to trigger effects only once at the end.
|
|
21
|
-
*/
|
|
22
13
|
export declare const batch: <T>(fn: () => T) => T;
|
|
23
|
-
/**
|
|
24
|
-
* Creates a read-only computed value that updates when its dependencies change.
|
|
25
|
-
*/
|
|
26
14
|
export declare const derive: <T>(computeFn: () => T) => ReadOnlyState<T>;
|
|
27
|
-
/**
|
|
28
|
-
* Creates an efficient subscription to a subset of a state value.
|
|
29
|
-
*/
|
|
30
15
|
export declare const select: <T, R>(source: ReadOnlyState<T>, selectorFn: (state: T) => R, equalityFn?: (a: R, b: R) => boolean) => ReadOnlyState<R>;
|
|
31
|
-
/**
|
|
32
|
-
* Creates a read-only view of a state, hiding mutation methods.
|
|
33
|
-
*/
|
|
34
16
|
export declare const readonlyState: <T>(state: State<T>) => ReadOnlyState<T>;
|
|
35
|
-
/**
|
|
36
|
-
* Creates a state with access control, returning a tuple of reader and writer.
|
|
37
|
-
*/
|
|
38
17
|
export declare const protectedState: <T>(initialValue: T, equalityFn?: (a: T, b: T) => boolean) => [ReadOnlyState<T>, WriteableState<T>];
|
|
39
|
-
/**
|
|
40
|
-
* Creates a lens for direct updates to nested properties of a state.
|
|
41
|
-
*/
|
|
42
18
|
export declare const lens: <T, K>(source: State<T>, accessor: (state: T) => K) => State<K>;
|
|
43
19
|
export {};
|
package/dist/src/index.js
CHANGED
|
@@ -1,32 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
const STATE_ID = Symbol();
|
|
3
|
-
/**
|
|
4
|
-
* Creates a reactive state container with the provided initial value.
|
|
5
|
-
*/
|
|
1
|
+
const STATE_ID = Symbol('STATE_ID');
|
|
6
2
|
export const state = (initialValue, equalityFn = Object.is) => StateImpl.createState(initialValue, equalityFn);
|
|
7
|
-
/**
|
|
8
|
-
* Registers a function to run whenever its reactive dependencies change.
|
|
9
|
-
*/
|
|
10
3
|
export const effect = (fn) => StateImpl.createEffect(fn);
|
|
11
|
-
/**
|
|
12
|
-
* Groups multiple state updates to trigger effects only once at the end.
|
|
13
|
-
*/
|
|
14
4
|
export const batch = (fn) => StateImpl.executeBatch(fn);
|
|
15
|
-
/**
|
|
16
|
-
* Creates a read-only computed value that updates when its dependencies change.
|
|
17
|
-
*/
|
|
18
5
|
export const derive = (computeFn) => StateImpl.createDerive(computeFn);
|
|
19
|
-
/**
|
|
20
|
-
* Creates an efficient subscription to a subset of a state value.
|
|
21
|
-
*/
|
|
22
6
|
export const select = (source, selectorFn, equalityFn = Object.is) => StateImpl.createSelect(source, selectorFn, equalityFn);
|
|
23
|
-
/**
|
|
24
|
-
* Creates a read-only view of a state, hiding mutation methods.
|
|
25
|
-
*/
|
|
26
7
|
export const readonlyState = (state) => () => state();
|
|
27
|
-
/**
|
|
28
|
-
* Creates a state with access control, returning a tuple of reader and writer.
|
|
29
|
-
*/
|
|
30
8
|
export const protectedState = (initialValue, equalityFn = Object.is) => {
|
|
31
9
|
const fullState = state(initialValue, equalityFn);
|
|
32
10
|
return [
|
|
@@ -37,26 +15,18 @@ export const protectedState = (initialValue, equalityFn = Object.is) => {
|
|
|
37
15
|
},
|
|
38
16
|
];
|
|
39
17
|
};
|
|
40
|
-
/**
|
|
41
|
-
* Creates a lens for direct updates to nested properties of a state.
|
|
42
|
-
*/
|
|
43
18
|
export const lens = (source, accessor) => StateImpl.createLens(source, accessor);
|
|
44
19
|
class StateImpl {
|
|
45
|
-
// Static fields track global reactivity state - this centralized approach allows
|
|
46
|
-
// for coordinated updates while maintaining individual state isolation
|
|
47
20
|
static currentSubscriber = null;
|
|
48
21
|
static pendingSubscribers = new Set();
|
|
49
22
|
static isNotifying = false;
|
|
50
23
|
static batchDepth = 0;
|
|
51
24
|
static deferredEffectCreations = [];
|
|
52
25
|
static activeSubscribers = new Set();
|
|
53
|
-
// WeakMaps enable automatic garbage collection when subscribers are no
|
|
54
|
-
// longer referenced, preventing memory leaks in long-running applications
|
|
55
26
|
static stateTracking = new WeakMap();
|
|
56
27
|
static subscriberDependencies = new WeakMap();
|
|
57
28
|
static parentSubscriber = new WeakMap();
|
|
58
29
|
static childSubscribers = new WeakMap();
|
|
59
|
-
// Instance state - each state has unique subscribers and ID
|
|
60
30
|
value;
|
|
61
31
|
subscribers = new Set();
|
|
62
32
|
stateId = Symbol();
|
|
@@ -65,10 +35,6 @@ class StateImpl {
|
|
|
65
35
|
this.value = initialValue;
|
|
66
36
|
this.equalityFn = equalityFn;
|
|
67
37
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Creates a reactive state container with the provided initial value.
|
|
70
|
-
* Implementation of the public 'state' function.
|
|
71
|
-
*/
|
|
72
38
|
static createState = (initialValue, equalityFn = Object.is) => {
|
|
73
39
|
const instance = new StateImpl(initialValue, equalityFn);
|
|
74
40
|
const get = () => instance.get();
|
|
@@ -77,23 +43,16 @@ class StateImpl {
|
|
|
77
43
|
get[STATE_ID] = instance.stateId;
|
|
78
44
|
return get;
|
|
79
45
|
};
|
|
80
|
-
// Auto-tracks dependencies when called within effects, creating a fine-grained
|
|
81
|
-
// reactivity graph that only updates affected components
|
|
82
46
|
get = () => {
|
|
83
47
|
const currentEffect = StateImpl.currentSubscriber;
|
|
84
48
|
if (currentEffect) {
|
|
85
|
-
// Add this effect to subscribers for future notification
|
|
86
49
|
this.subscribers.add(currentEffect);
|
|
87
|
-
// Maintain bidirectional dependency tracking to enable precise cleanup
|
|
88
|
-
// when effects are unsubscribed, preventing memory leaks
|
|
89
50
|
let dependencies = StateImpl.subscriberDependencies.get(currentEffect);
|
|
90
51
|
if (!dependencies) {
|
|
91
52
|
dependencies = new Set();
|
|
92
53
|
StateImpl.subscriberDependencies.set(currentEffect, dependencies);
|
|
93
54
|
}
|
|
94
55
|
dependencies.add(this.subscribers);
|
|
95
|
-
// Track read states to detect direct cyclical dependencies that
|
|
96
|
-
// could cause infinite loops
|
|
97
56
|
let readStates = StateImpl.stateTracking.get(currentEffect);
|
|
98
57
|
if (!readStates) {
|
|
99
58
|
readStates = new Set();
|
|
@@ -103,14 +62,10 @@ class StateImpl {
|
|
|
103
62
|
}
|
|
104
63
|
return this.value;
|
|
105
64
|
};
|
|
106
|
-
// Handles value updates with built-in optimizations and safeguards
|
|
107
65
|
set = (newValue) => {
|
|
108
|
-
// Skip updates for unchanged values to prevent redundant effect executions
|
|
109
66
|
if (this.equalityFn(this.value, newValue)) {
|
|
110
67
|
return;
|
|
111
68
|
}
|
|
112
|
-
// Infinite loop detection prevents direct self-mutation within effects,
|
|
113
|
-
// while allowing nested effect patterns that would otherwise appear cyclical
|
|
114
69
|
const effect = StateImpl.currentSubscriber;
|
|
115
70
|
if (effect) {
|
|
116
71
|
const states = StateImpl.stateTracking.get(effect);
|
|
@@ -119,16 +74,12 @@ class StateImpl {
|
|
|
119
74
|
}
|
|
120
75
|
}
|
|
121
76
|
this.value = newValue;
|
|
122
|
-
// Skip updates when there are no subscribers, avoiding unnecessary processing
|
|
123
77
|
if (this.subscribers.size === 0) {
|
|
124
78
|
return;
|
|
125
79
|
}
|
|
126
|
-
// Queue notifications instead of executing immediately to support batch operations
|
|
127
|
-
// and prevent redundant effect runs
|
|
128
80
|
for (const sub of this.subscribers) {
|
|
129
81
|
StateImpl.pendingSubscribers.add(sub);
|
|
130
82
|
}
|
|
131
|
-
// Immediate execution outside of batches, deferred execution inside batches
|
|
132
83
|
if (StateImpl.batchDepth === 0 && !StateImpl.isNotifying) {
|
|
133
84
|
StateImpl.notifySubscribers();
|
|
134
85
|
}
|
|
@@ -136,27 +87,17 @@ class StateImpl {
|
|
|
136
87
|
update = (fn) => {
|
|
137
88
|
this.set(fn(this.value));
|
|
138
89
|
};
|
|
139
|
-
/**
|
|
140
|
-
* Registers a function to run whenever its reactive dependencies change.
|
|
141
|
-
* Implementation of the public 'effect' function.
|
|
142
|
-
*/
|
|
143
90
|
static createEffect = (fn) => {
|
|
144
91
|
const runEffect = () => {
|
|
145
|
-
// Prevent re-entrance to avoid cascade updates during effect execution
|
|
146
92
|
if (StateImpl.activeSubscribers.has(runEffect)) {
|
|
147
93
|
return;
|
|
148
94
|
}
|
|
149
95
|
StateImpl.activeSubscribers.add(runEffect);
|
|
150
96
|
const parentEffect = StateImpl.currentSubscriber;
|
|
151
97
|
try {
|
|
152
|
-
// Clean existing subscriptions before running to ensure only
|
|
153
|
-
// currently accessed states are tracked as dependencies
|
|
154
98
|
StateImpl.cleanupEffect(runEffect);
|
|
155
|
-
// Set current context for automatic dependency tracking
|
|
156
99
|
StateImpl.currentSubscriber = runEffect;
|
|
157
100
|
StateImpl.stateTracking.set(runEffect, new Set());
|
|
158
|
-
// Track parent-child relationships to handle nested effects correctly
|
|
159
|
-
// and enable hierarchical cleanup later
|
|
160
101
|
if (parentEffect) {
|
|
161
102
|
StateImpl.parentSubscriber.set(runEffect, parentEffect);
|
|
162
103
|
let children = StateImpl.childSubscribers.get(parentEffect);
|
|
@@ -166,22 +107,17 @@ class StateImpl {
|
|
|
166
107
|
}
|
|
167
108
|
children.add(runEffect);
|
|
168
109
|
}
|
|
169
|
-
// Execute the effect function, which will auto-track dependencies
|
|
170
110
|
fn();
|
|
171
111
|
}
|
|
172
112
|
finally {
|
|
173
|
-
// Restore previous context when done
|
|
174
113
|
StateImpl.currentSubscriber = parentEffect;
|
|
175
114
|
StateImpl.activeSubscribers.delete(runEffect);
|
|
176
115
|
}
|
|
177
116
|
};
|
|
178
|
-
// Run immediately unless we're in a batch operation
|
|
179
117
|
if (StateImpl.batchDepth === 0) {
|
|
180
118
|
runEffect();
|
|
181
119
|
}
|
|
182
120
|
else {
|
|
183
|
-
// Still track parent-child relationship even when deferred,
|
|
184
|
-
// ensuring proper hierarchical cleanup later
|
|
185
121
|
if (StateImpl.currentSubscriber) {
|
|
186
122
|
const parent = StateImpl.currentSubscriber;
|
|
187
123
|
StateImpl.parentSubscriber.set(runEffect, parent);
|
|
@@ -192,17 +128,13 @@ class StateImpl {
|
|
|
192
128
|
}
|
|
193
129
|
children.add(runEffect);
|
|
194
130
|
}
|
|
195
|
-
// Queue for execution when batch completes
|
|
196
131
|
StateImpl.deferredEffectCreations.push(runEffect);
|
|
197
132
|
}
|
|
198
|
-
// Return cleanup function to properly disconnect from reactivity graph
|
|
199
133
|
return () => {
|
|
200
|
-
// Remove from dependency tracking to stop future notifications
|
|
201
134
|
StateImpl.cleanupEffect(runEffect);
|
|
202
135
|
StateImpl.pendingSubscribers.delete(runEffect);
|
|
203
136
|
StateImpl.activeSubscribers.delete(runEffect);
|
|
204
137
|
StateImpl.stateTracking.delete(runEffect);
|
|
205
|
-
// Clean up parent-child relationship bidirectionally
|
|
206
138
|
const parent = StateImpl.parentSubscriber.get(runEffect);
|
|
207
139
|
if (parent) {
|
|
208
140
|
const siblings = StateImpl.childSubscribers.get(parent);
|
|
@@ -211,8 +143,6 @@ class StateImpl {
|
|
|
211
143
|
}
|
|
212
144
|
}
|
|
213
145
|
StateImpl.parentSubscriber.delete(runEffect);
|
|
214
|
-
// Recursively clean up child effects to prevent memory leaks in
|
|
215
|
-
// nested effect scenarios
|
|
216
146
|
const children = StateImpl.childSubscribers.get(runEffect);
|
|
217
147
|
if (children) {
|
|
218
148
|
for (const child of children) {
|
|
@@ -223,19 +153,12 @@ class StateImpl {
|
|
|
223
153
|
}
|
|
224
154
|
};
|
|
225
155
|
};
|
|
226
|
-
/**
|
|
227
|
-
* Groups multiple state updates to trigger effects only once at the end.
|
|
228
|
-
* Implementation of the public 'batch' function.
|
|
229
|
-
*/
|
|
230
156
|
static executeBatch = (fn) => {
|
|
231
|
-
// Increment depth counter to handle nested batches correctly
|
|
232
157
|
StateImpl.batchDepth++;
|
|
233
158
|
try {
|
|
234
159
|
return fn();
|
|
235
160
|
}
|
|
236
161
|
catch (error) {
|
|
237
|
-
// Clean up on error to prevent stale subscribers from executing
|
|
238
|
-
// and potentially causing cascading errors
|
|
239
162
|
if (StateImpl.batchDepth === 1) {
|
|
240
163
|
StateImpl.pendingSubscribers.clear();
|
|
241
164
|
StateImpl.deferredEffectCreations.length = 0;
|
|
@@ -244,173 +167,148 @@ class StateImpl {
|
|
|
244
167
|
}
|
|
245
168
|
finally {
|
|
246
169
|
StateImpl.batchDepth--;
|
|
247
|
-
// Only process effects when exiting the outermost batch,
|
|
248
|
-
// maintaining proper execution order while avoiding redundant runs
|
|
249
170
|
if (StateImpl.batchDepth === 0) {
|
|
250
|
-
// Process effects created during the batch
|
|
251
171
|
if (StateImpl.deferredEffectCreations.length > 0) {
|
|
252
|
-
const effectsToRun = [
|
|
172
|
+
const effectsToRun = [
|
|
173
|
+
...StateImpl.deferredEffectCreations,
|
|
174
|
+
];
|
|
253
175
|
StateImpl.deferredEffectCreations.length = 0;
|
|
254
176
|
for (const effect of effectsToRun) {
|
|
255
177
|
effect();
|
|
256
178
|
}
|
|
257
179
|
}
|
|
258
|
-
// Process state updates that occurred during the batch
|
|
259
180
|
if (StateImpl.pendingSubscribers.size > 0 && !StateImpl.isNotifying) {
|
|
260
181
|
StateImpl.notifySubscribers();
|
|
261
182
|
}
|
|
262
183
|
}
|
|
263
184
|
}
|
|
264
185
|
};
|
|
265
|
-
/**
|
|
266
|
-
* Creates a read-only computed value that updates when its dependencies change.
|
|
267
|
-
* Implementation of the public 'derive' function.
|
|
268
|
-
*/
|
|
269
186
|
static createDerive = (computeFn) => {
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if (!(initialized && Object.is(cachedValue, newValue))) {
|
|
279
|
-
cachedValue = newValue;
|
|
280
|
-
valueState.set(newValue);
|
|
187
|
+
const container = {
|
|
188
|
+
cachedValue: undefined,
|
|
189
|
+
computeFn,
|
|
190
|
+
initialized: false,
|
|
191
|
+
valueState: StateImpl.createState(undefined),
|
|
192
|
+
};
|
|
193
|
+
StateImpl.createEffect(function deriveEffect() {
|
|
194
|
+
const newValue = container.computeFn();
|
|
195
|
+
if (!(container.initialized && Object.is(container.cachedValue, newValue))) {
|
|
196
|
+
container.cachedValue = newValue;
|
|
197
|
+
container.valueState.set(newValue);
|
|
281
198
|
}
|
|
282
|
-
initialized = true;
|
|
199
|
+
container.initialized = true;
|
|
283
200
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
cachedValue
|
|
289
|
-
initialized = true;
|
|
290
|
-
valueState.set(cachedValue);
|
|
201
|
+
return function deriveGetter() {
|
|
202
|
+
if (!container.initialized) {
|
|
203
|
+
container.cachedValue = container.computeFn();
|
|
204
|
+
container.initialized = true;
|
|
205
|
+
container.valueState.set(container.cachedValue);
|
|
291
206
|
}
|
|
292
|
-
return valueState();
|
|
207
|
+
return container.valueState();
|
|
293
208
|
};
|
|
294
209
|
};
|
|
295
|
-
/**
|
|
296
|
-
* Creates an efficient subscription to a subset of a state value.
|
|
297
|
-
* Implementation of the public 'select' function.
|
|
298
|
-
*/
|
|
299
210
|
static createSelect = (source, selectorFn, equalityFn = Object.is) => {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
211
|
+
const container = {
|
|
212
|
+
equalityFn,
|
|
213
|
+
initialized: false,
|
|
214
|
+
lastSelectedValue: undefined,
|
|
215
|
+
lastSourceValue: undefined,
|
|
216
|
+
selectorFn,
|
|
217
|
+
source,
|
|
218
|
+
valueState: StateImpl.createState(undefined),
|
|
219
|
+
};
|
|
220
|
+
StateImpl.createEffect(function selectEffect() {
|
|
221
|
+
const sourceValue = container.source();
|
|
222
|
+
if (container.initialized && Object.is(container.lastSourceValue, sourceValue)) {
|
|
309
223
|
return;
|
|
310
224
|
}
|
|
311
|
-
lastSourceValue = sourceValue;
|
|
312
|
-
const newSelectedValue = selectorFn(sourceValue);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
225
|
+
container.lastSourceValue = sourceValue;
|
|
226
|
+
const newSelectedValue = container.selectorFn(sourceValue);
|
|
227
|
+
if (container.initialized &&
|
|
228
|
+
container.lastSelectedValue !== undefined &&
|
|
229
|
+
container.equalityFn(container.lastSelectedValue, newSelectedValue)) {
|
|
316
230
|
return;
|
|
317
231
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
initialized = true;
|
|
232
|
+
container.lastSelectedValue = newSelectedValue;
|
|
233
|
+
container.valueState.set(newSelectedValue);
|
|
234
|
+
container.initialized = true;
|
|
322
235
|
});
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
lastSelectedValue
|
|
328
|
-
|
|
329
|
-
initialized = true;
|
|
236
|
+
return function selectGetter() {
|
|
237
|
+
if (!container.initialized) {
|
|
238
|
+
container.lastSourceValue = container.source();
|
|
239
|
+
container.lastSelectedValue = container.selectorFn(container.lastSourceValue);
|
|
240
|
+
container.valueState.set(container.lastSelectedValue);
|
|
241
|
+
container.initialized = true;
|
|
330
242
|
}
|
|
331
|
-
return valueState();
|
|
243
|
+
return container.valueState();
|
|
332
244
|
};
|
|
333
245
|
};
|
|
334
|
-
/**
|
|
335
|
-
* Creates a lens for direct updates to nested properties of a state.
|
|
336
|
-
* Implementation of the public 'lens' function.
|
|
337
|
-
*/
|
|
338
246
|
static createLens = (source, accessor) => {
|
|
339
|
-
|
|
247
|
+
const container = {
|
|
248
|
+
accessor,
|
|
249
|
+
isUpdating: false,
|
|
250
|
+
lensState: null,
|
|
251
|
+
originalSet: null,
|
|
252
|
+
path: [],
|
|
253
|
+
source,
|
|
254
|
+
};
|
|
340
255
|
const extractPath = () => {
|
|
341
|
-
const
|
|
256
|
+
const pathCollector = [];
|
|
342
257
|
const proxy = new Proxy({}, {
|
|
343
258
|
get: (_, prop) => {
|
|
344
259
|
if (typeof prop === 'string' || typeof prop === 'number') {
|
|
345
|
-
|
|
260
|
+
pathCollector.push(prop);
|
|
346
261
|
}
|
|
347
262
|
return proxy;
|
|
348
263
|
},
|
|
349
264
|
});
|
|
350
265
|
try {
|
|
351
|
-
accessor(proxy);
|
|
266
|
+
container.accessor(proxy);
|
|
352
267
|
}
|
|
353
268
|
catch {
|
|
354
|
-
// Ignore errors, we're just collecting the path
|
|
355
269
|
}
|
|
356
|
-
return
|
|
270
|
+
return pathCollector;
|
|
357
271
|
};
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
let isUpdating = false;
|
|
364
|
-
// Set up an effect to sync from source to lens
|
|
365
|
-
StateImpl.createEffect(() => {
|
|
366
|
-
if (isUpdating) {
|
|
272
|
+
container.path = extractPath();
|
|
273
|
+
container.lensState = StateImpl.createState(container.accessor(container.source()));
|
|
274
|
+
container.originalSet = container.lensState.set;
|
|
275
|
+
StateImpl.createEffect(function lensEffect() {
|
|
276
|
+
if (container.isUpdating) {
|
|
367
277
|
return;
|
|
368
278
|
}
|
|
369
|
-
isUpdating = true;
|
|
279
|
+
container.isUpdating = true;
|
|
370
280
|
try {
|
|
371
|
-
lensState.set(accessor(source()));
|
|
281
|
+
container.lensState.set(container.accessor(container.source()));
|
|
372
282
|
}
|
|
373
283
|
finally {
|
|
374
|
-
isUpdating = false;
|
|
284
|
+
container.isUpdating = false;
|
|
375
285
|
}
|
|
376
286
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
lensState.set = (value) => {
|
|
380
|
-
if (isUpdating) {
|
|
287
|
+
container.lensState.set = function lensSet(value) {
|
|
288
|
+
if (container.isUpdating) {
|
|
381
289
|
return;
|
|
382
290
|
}
|
|
383
|
-
isUpdating = true;
|
|
291
|
+
container.isUpdating = true;
|
|
384
292
|
try {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
// Update source by modifying the value at path
|
|
388
|
-
source.update((current) => setValueAtPath(current, path, value));
|
|
293
|
+
container.originalSet(value);
|
|
294
|
+
container.source.update((current) => setValueAtPath(current, container.path, value));
|
|
389
295
|
}
|
|
390
296
|
finally {
|
|
391
|
-
isUpdating = false;
|
|
297
|
+
container.isUpdating = false;
|
|
392
298
|
}
|
|
393
299
|
};
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
lensState.set(fn(lensState()));
|
|
300
|
+
container.lensState.update = function lensUpdate(fn) {
|
|
301
|
+
container.lensState.set(fn(container.lensState()));
|
|
397
302
|
};
|
|
398
|
-
return lensState;
|
|
303
|
+
return container.lensState;
|
|
399
304
|
};
|
|
400
|
-
// Processes queued subscriber notifications in a controlled, non-reentrant way
|
|
401
305
|
static notifySubscribers = () => {
|
|
402
|
-
// Prevent reentrance to avoid cascading notification loops when
|
|
403
|
-
// effects trigger further state changes
|
|
404
306
|
if (StateImpl.isNotifying) {
|
|
405
307
|
return;
|
|
406
308
|
}
|
|
407
309
|
StateImpl.isNotifying = true;
|
|
408
310
|
try {
|
|
409
|
-
// Process all pending effects in batches for better perf,
|
|
410
|
-
// ensuring topological execution order is maintained
|
|
411
311
|
while (StateImpl.pendingSubscribers.size > 0) {
|
|
412
|
-
// Process in snapshot batches to prevent infinite loops
|
|
413
|
-
// when effects trigger further state changes
|
|
414
312
|
const subscribers = Array.from(StateImpl.pendingSubscribers);
|
|
415
313
|
StateImpl.pendingSubscribers.clear();
|
|
416
314
|
for (const effect of subscribers) {
|
|
@@ -422,11 +320,8 @@ class StateImpl {
|
|
|
422
320
|
StateImpl.isNotifying = false;
|
|
423
321
|
}
|
|
424
322
|
};
|
|
425
|
-
// Removes effect from dependency tracking to prevent memory leaks
|
|
426
323
|
static cleanupEffect = (effect) => {
|
|
427
|
-
// Remove from execution queue to prevent stale updates
|
|
428
324
|
StateImpl.pendingSubscribers.delete(effect);
|
|
429
|
-
// Remove bidirectional dependency references to prevent memory leaks
|
|
430
325
|
const deps = StateImpl.subscriberDependencies.get(effect);
|
|
431
326
|
if (deps) {
|
|
432
327
|
for (const subscribers of deps) {
|
|
@@ -437,72 +332,62 @@ class StateImpl {
|
|
|
437
332
|
}
|
|
438
333
|
};
|
|
439
334
|
}
|
|
440
|
-
// Helper for array updates
|
|
441
335
|
const updateArrayItem = (arr, index, value) => {
|
|
442
|
-
const copy = [
|
|
336
|
+
const copy = [
|
|
337
|
+
...arr,
|
|
338
|
+
];
|
|
443
339
|
copy[index] = value;
|
|
444
340
|
return copy;
|
|
445
341
|
};
|
|
446
|
-
// Helper for single-level updates (optimization)
|
|
447
342
|
const updateShallowProperty = (obj, key, value) => {
|
|
448
|
-
const result = {
|
|
343
|
+
const result = {
|
|
344
|
+
...obj,
|
|
345
|
+
};
|
|
449
346
|
result[key] = value;
|
|
450
347
|
return result;
|
|
451
348
|
};
|
|
452
|
-
// Helper to create the appropriate container type
|
|
453
349
|
const createContainer = (key) => {
|
|
454
350
|
const isArrayKey = typeof key === 'number' || !Number.isNaN(Number(key));
|
|
455
351
|
return isArrayKey ? [] : {};
|
|
456
352
|
};
|
|
457
|
-
// Helper for handling array path updates
|
|
458
353
|
const updateArrayPath = (array, pathSegments, value) => {
|
|
459
354
|
const index = Number(pathSegments[0]);
|
|
460
355
|
if (pathSegments.length === 1) {
|
|
461
|
-
// Simple array item update
|
|
462
356
|
return updateArrayItem(array, index, value);
|
|
463
357
|
}
|
|
464
|
-
|
|
465
|
-
|
|
358
|
+
const copy = [
|
|
359
|
+
...array,
|
|
360
|
+
];
|
|
466
361
|
const nextPathSegments = pathSegments.slice(1);
|
|
467
362
|
const nextKey = nextPathSegments[0];
|
|
468
|
-
// For null/undefined values in arrays, create appropriate containers
|
|
469
363
|
let nextValue = array[index];
|
|
470
364
|
if (nextValue === undefined || nextValue === null) {
|
|
471
|
-
// Use empty object as default if nextKey is undefined
|
|
472
365
|
nextValue = nextKey !== undefined ? createContainer(nextKey) : {};
|
|
473
366
|
}
|
|
474
367
|
copy[index] = setValueAtPath(nextValue, nextPathSegments, value);
|
|
475
368
|
return copy;
|
|
476
369
|
};
|
|
477
|
-
// Helper for handling object path updates
|
|
478
370
|
const updateObjectPath = (obj, pathSegments, value) => {
|
|
479
|
-
// Ensure we have a valid key
|
|
480
371
|
const currentKey = pathSegments[0];
|
|
481
372
|
if (currentKey === undefined) {
|
|
482
|
-
// This shouldn't happen given our checks in the main function
|
|
483
373
|
return obj;
|
|
484
374
|
}
|
|
485
375
|
if (pathSegments.length === 1) {
|
|
486
|
-
// Simple object property update
|
|
487
376
|
return updateShallowProperty(obj, currentKey, value);
|
|
488
377
|
}
|
|
489
|
-
// Nested path in object
|
|
490
378
|
const nextPathSegments = pathSegments.slice(1);
|
|
491
379
|
const nextKey = nextPathSegments[0];
|
|
492
|
-
// For null/undefined values, create appropriate containers
|
|
493
380
|
let currentValue = obj[currentKey];
|
|
494
381
|
if (currentValue === undefined || currentValue === null) {
|
|
495
|
-
// Use empty object as default if nextKey is undefined
|
|
496
382
|
currentValue = nextKey !== undefined ? createContainer(nextKey) : {};
|
|
497
383
|
}
|
|
498
|
-
|
|
499
|
-
|
|
384
|
+
const result = {
|
|
385
|
+
...obj,
|
|
386
|
+
};
|
|
500
387
|
result[currentKey] = setValueAtPath(currentValue, nextPathSegments, value);
|
|
501
388
|
return result;
|
|
502
389
|
};
|
|
503
|
-
// Simplified function to update a nested value at a path
|
|
504
390
|
const setValueAtPath = (obj, pathSegments, value) => {
|
|
505
|
-
// Handle base cases
|
|
506
391
|
if (pathSegments.length === 0) {
|
|
507
392
|
return value;
|
|
508
393
|
}
|
|
@@ -513,7 +398,6 @@ const setValueAtPath = (obj, pathSegments, value) => {
|
|
|
513
398
|
if (currentKey === undefined) {
|
|
514
399
|
return obj;
|
|
515
400
|
}
|
|
516
|
-
// Delegate to specialized handlers based on data type
|
|
517
401
|
if (Array.isArray(obj)) {
|
|
518
402
|
return updateArrayPath(obj, pathSegments, value);
|
|
519
403
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let i=Symbol("STATE_ID"),a=(e,t=Object.is)=>l.createState(e,t);var e=e=>l.createEffect(e),t=e=>l.executeBatch(e),r=e=>l.createDerive(e),s=(e,t,r=Object.is)=>l.createSelect(e,t,r);let c=e=>()=>e();var n=(e,t=Object.is)=>{let r=a(e,t);return[()=>c(r)(),{set:e=>r.set(e),update:e=>r.update(e)}]},u=(e,t)=>l.createLens(e,t);class l{static currentSubscriber=null;static pendingSubscribers=new Set;static isNotifying=!1;static batchDepth=0;static deferredEffectCreations=[];static activeSubscribers=new Set;static stateTracking=new WeakMap;static subscriberDependencies=new WeakMap;static parentSubscriber=new WeakMap;static childSubscribers=new WeakMap;value;subscribers=new Set;stateId=Symbol();equalityFn;constructor(e,t=Object.is){this.value=e,this.equalityFn=t}static createState=(e,t=Object.is)=>{let r=new l(e,t);e=()=>r.get();return e.set=e=>r.set(e),e.update=e=>r.update(e),e[i]=r.stateId,e};get=()=>{var r=l.currentSubscriber;if(r){this.subscribers.add(r);let e=l.subscriberDependencies.get(r),t=(e||(e=new Set,l.subscriberDependencies.set(r,e)),e.add(this.subscribers),l.stateTracking.get(r));t||(t=new Set,l.stateTracking.set(r,t)),t.add(this.stateId)}return this.value};set=e=>{if(!this.equalityFn(this.value,e)){var t=l.currentSubscriber;if(t)if(l.stateTracking.get(t)?.has(this.stateId)&&!l.parentSubscriber.get(t))throw new Error("Infinite loop detected: effect() cannot update a state() it depends on!");if(this.value=e,0!==this.subscribers.size){for(var r of this.subscribers)l.pendingSubscribers.add(r);0!==l.batchDepth||l.isNotifying||l.notifySubscribers()}}};update=e=>{this.set(e(this.value))};static createEffect=e=>{let r=()=>{if(!l.activeSubscribers.has(r)){l.activeSubscribers.add(r);var t=l.currentSubscriber;try{if(l.cleanupEffect(r),l.currentSubscriber=r,l.stateTracking.set(r,new Set),t){l.parentSubscriber.set(r,t);let e=l.childSubscribers.get(t);e||(e=new Set,l.childSubscribers.set(t,e)),e.add(r)}e()}finally{l.currentSubscriber=t,l.activeSubscribers.delete(r)}}};if(0===l.batchDepth)r();else{if(l.currentSubscriber){var t=l.currentSubscriber;l.parentSubscriber.set(r,t);let e=l.childSubscribers.get(t);e||(e=new Set,l.childSubscribers.set(t,e)),e.add(r)}l.deferredEffectCreations.push(r)}return()=>{l.cleanupEffect(r),l.pendingSubscribers.delete(r),l.activeSubscribers.delete(r),l.stateTracking.delete(r);var e=l.parentSubscriber.get(r),e=(e&&(e=l.childSubscribers.get(e))&&e.delete(r),l.parentSubscriber.delete(r),l.childSubscribers.get(r));if(e){for(var t of e)l.cleanupEffect(t);e.clear(),l.childSubscribers.delete(r)}}};static executeBatch=e=>{l.batchDepth++;try{return e()}catch(e){throw 1===l.batchDepth&&(l.pendingSubscribers.clear(),l.deferredEffectCreations.length=0),e}finally{if(l.batchDepth--,0===l.batchDepth){if(0<l.deferredEffectCreations.length){var t,e=[...l.deferredEffectCreations];l.deferredEffectCreations.length=0;for(t of e)t()}0<l.pendingSubscribers.size&&!l.isNotifying&&l.notifySubscribers()}}};static createDerive=e=>{let t={cachedValue:void 0,computeFn:e,initialized:!1,valueState:l.createState(void 0)};return l.createEffect(function(){var e=t.computeFn();t.initialized&&Object.is(t.cachedValue,e)||(t.cachedValue=e,t.valueState.set(e)),t.initialized=!0}),function(){return t.initialized||(t.cachedValue=t.computeFn(),t.initialized=!0,t.valueState.set(t.cachedValue)),t.valueState()}};static createSelect=(e,t,r=Object.is)=>{let i={equalityFn:r,initialized:!1,lastSelectedValue:void 0,lastSourceValue:void 0,selectorFn:t,source:e,valueState:l.createState(void 0)};return l.createEffect(function(){var e=i.source();i.initialized&&Object.is(i.lastSourceValue,e)||(i.lastSourceValue=e,e=i.selectorFn(e),i.initialized&&void 0!==i.lastSelectedValue&&i.equalityFn(i.lastSelectedValue,e))||(i.lastSelectedValue=e,i.valueState.set(e),i.initialized=!0)}),function(){return i.initialized||(i.lastSourceValue=i.source(),i.lastSelectedValue=i.selectorFn(i.lastSourceValue),i.valueState.set(i.lastSelectedValue),i.initialized=!0),i.valueState()}};static createLens=(e,t)=>{let a={accessor:t,isUpdating:!1,lensState:null,originalSet:null,path:[],source:e};return a.path=(()=>{let r=[],i=new Proxy({},{get:(e,t)=>("string"!=typeof t&&"number"!=typeof t||r.push(t),i)});try{a.accessor(i)}catch{}return r})(),a.lensState=l.createState(a.accessor(a.source())),a.originalSet=a.lensState.set,l.createEffect(function(){if(!a.isUpdating){a.isUpdating=!0;try{a.lensState.set(a.accessor(a.source()))}finally{a.isUpdating=!1}}}),a.lensState.set=function(t){if(!a.isUpdating){a.isUpdating=!0;try{a.originalSet(t),a.source.update(e=>p(e,a.path,t))}finally{a.isUpdating=!1}}},a.lensState.update=function(e){a.lensState.set(e(a.lensState()))},a.lensState};static notifySubscribers=()=>{if(!l.isNotifying){l.isNotifying=!0;try{for(;0<l.pendingSubscribers.size;){var e,t=Array.from(l.pendingSubscribers);l.pendingSubscribers.clear();for(e of t)e()}}finally{l.isNotifying=!1}}};static cleanupEffect=e=>{l.pendingSubscribers.delete(e);var t=l.subscriberDependencies.get(e);if(t){for(var r of t)r.delete(e);t.clear(),l.subscriberDependencies.delete(e)}}}let b=(e,t,r)=>{e=[...e];return e[t]=r,e},d=(e,t,r)=>{e={...e};return e[t]=r,e},f=e=>"number"==typeof e||!Number.isNaN(Number(e))?[]:{},S=(e,t,r)=>{var i=Number(t[0]);if(1===t.length)return b(e,i,r);var a=[...e],t=t.slice(1),s=t[0];let c=e[i];return null==c&&(c=void 0!==s?f(s):{}),a[i]=p(c,t,r),a},o=(e,t,r)=>{var i=t[0];if(void 0===i)return e;if(1===t.length)return d(e,i,r);var t=t.slice(1),a=t[0];let s=e[i];null==s&&(s=void 0!==a?f(a):{});a={...e};return a[i]=p(s,t,r),a},p=(e,t,r)=>0===t.length?r:null==e?p({},t,r):void 0===t[0]?e:(Array.isArray(e)?S:o)(e,t,r);export{a as state,e as effect,t as batch,r as derive,s as select,c as readonlyState,n as protectedState,u as lens};
|