@nerdalytics/beacon 1000.3.0 → 1000.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.min.js +1 -1
- package/package.json +7 -7
- package/src/index.ts +132 -197
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ count.set(5);
|
|
|
37
37
|
## Documentation
|
|
38
38
|
|
|
39
39
|
Full documentation, API reference, and examples available at:
|
|
40
|
-
**[github.
|
|
40
|
+
**[nerdalytics.github.io/beacon](https://nerdalytics.github.io/beacon/)**
|
|
41
41
|
|
|
42
42
|
## License
|
|
43
43
|
|
package/dist/src/index.d.ts
CHANGED
|
@@ -16,5 +16,5 @@ declare const createSelect: <T, R>(source: ReadOnlyState<T>, selectorFn: (state:
|
|
|
16
16
|
declare const createLens: <T, K>(source: State<T>, accessor: (state: T) => K) => State<K>;
|
|
17
17
|
declare const createReadonlyState: <T>(source: State<T>) => ReadOnlyState<T>;
|
|
18
18
|
declare const createProtectedState: <T>(initialValue: T, equalityFn?: (a: T, b: T) => boolean) => [ReadOnlyState<T>, WriteableState<T>];
|
|
19
|
-
export { createDerive as derive, createEffect as effect, createLens as lens, createProtectedState as protectedState, createReadonlyState as readonlyState, createSelect as select, createState as state, executeBatch as batch, };
|
|
20
19
|
export type { ReadOnlyState, State, Unsubscribe, WriteableState };
|
|
20
|
+
export { createDerive as derive, createEffect as effect, createLens as lens, createProtectedState as protectedState, createReadonlyState as readonlyState, createSelect as select, createState as state, executeBatch as batch, };
|
package/dist/src/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let
|
|
1
|
+
let r=Symbol("STATE_ID"),f=null,u=new Set,s=!1,d=0,a=[],l=new Set,o=new WeakMap,v=new WeakMap,c=new WeakMap,i=new WeakMap,y=(e,t,r)=>{let n=e.get(t);return n||(n=r(),e.set(t,n)),n},p=()=>{if(!s){s=!0;try{for(;0<u.size;){var e,t=u;u=new Set;for(e of t)e()}}finally{s=!1}}},w=e=>{u.delete(e);var t=v.get(e);if(t){for(var r of t)r.delete(e);t.clear(),v.delete(e)}},g=e=>{w(e),l.delete(e),o.delete(e);var t=c.get(e),t=(t&&(t=i.get(t))&&t.delete(e),c.delete(e),i.get(e));if(t){for(var r of t)g(r);t.clear(),i.delete(e)}},h=(e,n=Object.is)=>{let a=e,l=new Set,i=Symbol(),t=()=>{var e=f;return e&&(l.add(e),y(v,e,()=>new Set).add(l),y(o,e,()=>new Set).add(i)),a};return t.set=e=>{if(!n(a,e)){var t=f;if(t)if(o.get(t)?.has(i)&&!c.get(t))throw new Error("Infinite loop detected: effect() cannot update a state() it depends on!");if(a=e,0!==l.size){for(var r of l)u.add(r);0!==d||s||p()}}},t.update=e=>{t.set(e(a))},t[r]=i,t},S=r=>{let n=()=>{if(!l.has(n)){l.add(n);var e=f;try{w(n),f=n;var t=o.get(n);t?t.clear():o.set(n,new Set),e&&(c.set(n,e),y(i,e,()=>new Set).add(n)),r()}finally{f=e,l.delete(n)}}};var e;return 0===d?n():(f&&(e=f,c.set(n,e),y(i,e,()=>new Set).add(n)),a.push(n)),()=>{g(n)}};var e=e=>{d++;try{return e()}catch(e){throw 1===d&&(u.clear(),a.length=0),e}finally{if(0===--d){if(0<a.length){var t,e=a;a=[];for(t of e)t()}0<u.size&&!s&&p()}}},t=t=>{let r=void 0,n=!1,a=h(void 0);return S(function(){var e=t();n&&Object.is(r,e)||(r=e,a.set(e)),n=!0}),function(){return n||(r=t(),n=!0,a.set(r)),a()}},n=(t,r,n=Object.is)=>{let a=!1,l,i,f=h(void 0);return S(function(){var e=t();a&&Object.is(i,e)||(i=e,e=r(e),a&&void 0!==l&&n(l,e))||(l=e,f.set(e),a=!0)}),function(){return a||(i=t(),l=r(i),f.set(l),a=!0),f()}};let b=(e,t,r)=>{e=[...e];return e[t]=r,e},m=(e,t,r)=>{e={...e};return e[t]=r,e},j=e=>"number"==typeof e||!Number.isNaN(Number(e))?[]:{},N=(n,a,l,i)=>{if(l>=a.length)return i;if(null==n)return N({},a,l,i);var f=a[l];if(void 0===f)return n;if(Array.isArray(n)){var u=Number(f);if(l===a.length-1)return b(n,u,i);var s=[...n];let e=l+1,t=a[e],r=n[u];return null==r&&(r=void 0===t?{}:j(t)),s[u]=N(r,a,e,i),s}u=n;if(l===a.length-1)return m(u,f,i);let e=l+1,t=a[e],r=u[f];null==r&&(r=void 0===t?{}:j(t));s={...u};return s[f]=N(r,a,e,i),s};var O=(e,t)=>{let r=!1;let n=(()=>{let r=[],n=new Proxy({},{get:(e,t)=>("string"!=typeof t&&"number"!=typeof t||r.push(t),n)});try{t(n)}catch{}return r})(),a=h(t(e())),l=a.set;return S(function(){if(!r){r=!0;try{a.set(t(e()))}finally{r=!1}}}),a.set=function(t){if(!r){r=!0;try{l(t),e.update(e=>N(e,n,0,t))}finally{r=!1}}},a.update=function(e){a.set(e(a()))},a};let k=e=>()=>e();var M=(e,t=Object.is)=>{let r=h(e,t);return[k(r),{set:e=>r.set(e),update:e=>r.update(e)}]};export{t as derive,S as effect,O as lens,M as protectedState,k as readonlyState,n as select,h as state,e as batch};
|
package/package.json
CHANGED
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
},
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
|
+
"types": "./dist/src/index.d.ts",
|
|
17
|
+
"typescript": "./src/index.ts",
|
|
16
18
|
"import": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
+
"types": "./dist/src/index.d.ts",
|
|
20
|
+
"default": "./dist/src/index.min.js"
|
|
19
21
|
},
|
|
20
22
|
"require": {
|
|
21
23
|
"default": "./dist/src/index.min.js"
|
|
22
|
-
}
|
|
23
|
-
"types": "./dist/src/index.d.ts",
|
|
24
|
-
"typescript": "./src/index.ts"
|
|
24
|
+
}
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"url": "git+https://github.com/nerdalytics/beacon.git"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
|
-
"benchmark": "node --expose-gc scripts/benchmark.ts",
|
|
59
|
+
"benchmark": "node --expose-gc scripts/benchmark.ts -R 3",
|
|
60
60
|
"benchmark:naiv": "node scripts/naiv-benchmark.ts",
|
|
61
61
|
"build": "npm run build:lts",
|
|
62
62
|
"build:lts": "tsc -p tsconfig.lts.json",
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"sideEffects": false,
|
|
95
95
|
"type": "module",
|
|
96
96
|
"types": "dist/src/index.d.ts",
|
|
97
|
-
"version": "1000.3.
|
|
97
|
+
"version": "1000.3.1"
|
|
98
98
|
}
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,15 @@ const subscriberDependencies: WeakMap<Subscriber, Set<Set<Subscriber>>> = new We
|
|
|
30
30
|
const parentSubscriber: WeakMap<Subscriber, Subscriber> = new WeakMap<Subscriber, Subscriber>()
|
|
31
31
|
const childSubscribers: WeakMap<Subscriber, Set<Subscriber>> = new WeakMap<Subscriber, Set<Subscriber>>()
|
|
32
32
|
|
|
33
|
+
const getOrCreate = <K extends object, V>(map: WeakMap<K, V>, key: K, factory: () => V): V => {
|
|
34
|
+
let value = map.get(key)
|
|
35
|
+
if (!value) {
|
|
36
|
+
value = factory()
|
|
37
|
+
map.set(key, value)
|
|
38
|
+
}
|
|
39
|
+
return value
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
const notifySubscribers = (): void => {
|
|
34
43
|
if (isNotifying) {
|
|
35
44
|
return
|
|
@@ -64,9 +73,30 @@ const cleanupEffect = (effect: Subscriber): void => {
|
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
const disposeEffect = (effect: Subscriber): void => {
|
|
77
|
+
cleanupEffect(effect)
|
|
78
|
+
activeSubscribers.delete(effect)
|
|
79
|
+
stateTracking.delete(effect)
|
|
80
|
+
|
|
81
|
+
const parent = parentSubscriber.get(effect)
|
|
82
|
+
if (parent) {
|
|
83
|
+
const siblings = childSubscribers.get(parent)
|
|
84
|
+
if (siblings) {
|
|
85
|
+
siblings.delete(effect)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
parentSubscriber.delete(effect)
|
|
89
|
+
|
|
90
|
+
const children = childSubscribers.get(effect)
|
|
91
|
+
if (children) {
|
|
92
|
+
for (const child of children) {
|
|
93
|
+
disposeEffect(child)
|
|
94
|
+
}
|
|
95
|
+
children.clear()
|
|
96
|
+
childSubscribers.delete(effect)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
70
100
|
const createState = <T>(initialValue: T, equalityFn: (a: T, b: T) => boolean = Object.is): State<T> => {
|
|
71
101
|
let value = initialValue
|
|
72
102
|
const subscribers = new Set<Subscriber>()
|
|
@@ -77,19 +107,9 @@ const createState = <T>(initialValue: T, equalityFn: (a: T, b: T) => boolean = O
|
|
|
77
107
|
if (currentEffect) {
|
|
78
108
|
subscribers.add(currentEffect)
|
|
79
109
|
|
|
80
|
-
|
|
81
|
-
if (!dependencies) {
|
|
82
|
-
dependencies = new Set()
|
|
83
|
-
subscriberDependencies.set(currentEffect, dependencies)
|
|
84
|
-
}
|
|
85
|
-
dependencies.add(subscribers)
|
|
110
|
+
getOrCreate(subscriberDependencies, currentEffect, () => new Set()).add(subscribers)
|
|
86
111
|
|
|
87
|
-
|
|
88
|
-
if (!readStates) {
|
|
89
|
-
readStates = new Set()
|
|
90
|
-
stateTracking.set(currentEffect, readStates)
|
|
91
|
-
}
|
|
92
|
-
readStates.add(stateId)
|
|
112
|
+
getOrCreate(stateTracking, currentEffect, () => new Set()).add(stateId)
|
|
93
113
|
}
|
|
94
114
|
return value
|
|
95
115
|
}
|
|
@@ -130,9 +150,6 @@ const createState = <T>(initialValue: T, equalityFn: (a: T, b: T) => boolean = O
|
|
|
130
150
|
return get as State<T>
|
|
131
151
|
}
|
|
132
152
|
|
|
133
|
-
/**
|
|
134
|
-
* Registers a function to run whenever its reactive dependencies change.
|
|
135
|
-
*/
|
|
136
153
|
const createEffect = (fn: () => void): Unsubscribe => {
|
|
137
154
|
const runEffect = (): void => {
|
|
138
155
|
if (activeSubscribers.has(runEffect)) {
|
|
@@ -146,16 +163,16 @@ const createEffect = (fn: () => void): Unsubscribe => {
|
|
|
146
163
|
cleanupEffect(runEffect)
|
|
147
164
|
|
|
148
165
|
currentSubscriber = runEffect
|
|
149
|
-
stateTracking.
|
|
166
|
+
const existingStates = stateTracking.get(runEffect)
|
|
167
|
+
if (existingStates) {
|
|
168
|
+
existingStates.clear()
|
|
169
|
+
} else {
|
|
170
|
+
stateTracking.set(runEffect, new Set())
|
|
171
|
+
}
|
|
150
172
|
|
|
151
173
|
if (parentEffect) {
|
|
152
174
|
parentSubscriber.set(runEffect, parentEffect)
|
|
153
|
-
|
|
154
|
-
if (!children) {
|
|
155
|
-
children = new Set()
|
|
156
|
-
childSubscribers.set(parentEffect, children)
|
|
157
|
-
}
|
|
158
|
-
children.add(runEffect)
|
|
175
|
+
getOrCreate(childSubscribers, parentEffect, () => new Set()).add(runEffect)
|
|
159
176
|
}
|
|
160
177
|
|
|
161
178
|
fn()
|
|
@@ -171,46 +188,17 @@ const createEffect = (fn: () => void): Unsubscribe => {
|
|
|
171
188
|
if (currentSubscriber) {
|
|
172
189
|
const parent = currentSubscriber
|
|
173
190
|
parentSubscriber.set(runEffect, parent)
|
|
174
|
-
|
|
175
|
-
if (!children) {
|
|
176
|
-
children = new Set()
|
|
177
|
-
childSubscribers.set(parent, children)
|
|
178
|
-
}
|
|
179
|
-
children.add(runEffect)
|
|
191
|
+
getOrCreate(childSubscribers, parent, () => new Set()).add(runEffect)
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
deferredEffectCreations.push(runEffect)
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
return (): void => {
|
|
186
|
-
|
|
187
|
-
pendingSubscribers.delete(runEffect)
|
|
188
|
-
activeSubscribers.delete(runEffect)
|
|
189
|
-
stateTracking.delete(runEffect)
|
|
190
|
-
|
|
191
|
-
const parent = parentSubscriber.get(runEffect)
|
|
192
|
-
if (parent) {
|
|
193
|
-
const siblings = childSubscribers.get(parent)
|
|
194
|
-
if (siblings) {
|
|
195
|
-
siblings.delete(runEffect)
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
parentSubscriber.delete(runEffect)
|
|
199
|
-
|
|
200
|
-
const children = childSubscribers.get(runEffect)
|
|
201
|
-
if (children) {
|
|
202
|
-
for (const child of children) {
|
|
203
|
-
cleanupEffect(child)
|
|
204
|
-
}
|
|
205
|
-
children.clear()
|
|
206
|
-
childSubscribers.delete(runEffect)
|
|
207
|
-
}
|
|
198
|
+
disposeEffect(runEffect)
|
|
208
199
|
}
|
|
209
200
|
}
|
|
210
201
|
|
|
211
|
-
/**
|
|
212
|
-
* Groups multiple state updates to trigger effects only once at the end.
|
|
213
|
-
*/
|
|
214
202
|
const executeBatch = <T>(fn: () => T): T => {
|
|
215
203
|
batchDepth++
|
|
216
204
|
try {
|
|
@@ -240,87 +228,69 @@ const executeBatch = <T>(fn: () => T): T => {
|
|
|
240
228
|
}
|
|
241
229
|
}
|
|
242
230
|
|
|
243
|
-
/**
|
|
244
|
-
* Creates a read-only computed value that updates when its dependencies change.
|
|
245
|
-
*/
|
|
246
231
|
const createDerive = <T>(computeFn: () => T): ReadOnlyState<T> => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
initialized: false,
|
|
251
|
-
valueState: createState<T | undefined>(undefined),
|
|
252
|
-
}
|
|
232
|
+
let cachedValue: T = undefined as unknown as T
|
|
233
|
+
let initialized = false
|
|
234
|
+
const valueState = createState<T | undefined>(undefined)
|
|
253
235
|
|
|
254
236
|
createEffect(function deriveEffect(): void {
|
|
255
|
-
const newValue =
|
|
237
|
+
const newValue = computeFn()
|
|
256
238
|
|
|
257
|
-
if (!(
|
|
258
|
-
|
|
259
|
-
|
|
239
|
+
if (!(initialized && Object.is(cachedValue, newValue))) {
|
|
240
|
+
cachedValue = newValue
|
|
241
|
+
valueState.set(newValue)
|
|
260
242
|
}
|
|
261
243
|
|
|
262
|
-
|
|
244
|
+
initialized = true
|
|
263
245
|
})
|
|
264
246
|
|
|
265
247
|
return function deriveGetter(): T {
|
|
266
|
-
if (!
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
248
|
+
if (!initialized) {
|
|
249
|
+
cachedValue = computeFn()
|
|
250
|
+
initialized = true
|
|
251
|
+
valueState.set(cachedValue)
|
|
270
252
|
}
|
|
271
|
-
return
|
|
253
|
+
return valueState() as T
|
|
272
254
|
}
|
|
273
255
|
}
|
|
274
256
|
|
|
275
|
-
/**
|
|
276
|
-
* Creates an efficient subscription to a subset of a state value.
|
|
277
|
-
*/
|
|
278
257
|
const createSelect = <T, R>(
|
|
279
258
|
source: ReadOnlyState<T>,
|
|
280
259
|
selectorFn: (state: T) => R,
|
|
281
260
|
equalityFn: (a: R, b: R) => boolean = Object.is
|
|
282
261
|
): ReadOnlyState<R> => {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
lastSourceValue: undefined as T | undefined,
|
|
288
|
-
selectorFn,
|
|
289
|
-
source,
|
|
290
|
-
valueState: createState<R | undefined>(undefined),
|
|
291
|
-
}
|
|
262
|
+
let initialized = false
|
|
263
|
+
let lastSelectedValue: R | undefined
|
|
264
|
+
let lastSourceValue: T | undefined
|
|
265
|
+
const valueState = createState<R | undefined>(undefined)
|
|
292
266
|
|
|
293
267
|
createEffect(function selectEffect(): void {
|
|
294
|
-
const sourceValue =
|
|
268
|
+
const sourceValue = source()
|
|
295
269
|
|
|
296
|
-
if (
|
|
270
|
+
if (initialized && Object.is(lastSourceValue, sourceValue)) {
|
|
297
271
|
return
|
|
298
272
|
}
|
|
299
273
|
|
|
300
|
-
|
|
301
|
-
const newSelectedValue =
|
|
274
|
+
lastSourceValue = sourceValue
|
|
275
|
+
const newSelectedValue = selectorFn(sourceValue)
|
|
302
276
|
|
|
303
|
-
if (
|
|
304
|
-
container.initialized &&
|
|
305
|
-
container.lastSelectedValue !== undefined &&
|
|
306
|
-
container.equalityFn(container.lastSelectedValue, newSelectedValue)
|
|
307
|
-
) {
|
|
277
|
+
if (initialized && lastSelectedValue !== undefined && equalityFn(lastSelectedValue, newSelectedValue)) {
|
|
308
278
|
return
|
|
309
279
|
}
|
|
310
280
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
281
|
+
lastSelectedValue = newSelectedValue
|
|
282
|
+
valueState.set(newSelectedValue)
|
|
283
|
+
initialized = true
|
|
314
284
|
})
|
|
315
285
|
|
|
316
286
|
return function selectGetter(): R {
|
|
317
|
-
if (!
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
287
|
+
if (!initialized) {
|
|
288
|
+
lastSourceValue = source()
|
|
289
|
+
lastSelectedValue = selectorFn(lastSourceValue)
|
|
290
|
+
valueState.set(lastSelectedValue)
|
|
291
|
+
initialized = true
|
|
322
292
|
}
|
|
323
|
-
return
|
|
293
|
+
return valueState() as R
|
|
324
294
|
}
|
|
325
295
|
}
|
|
326
296
|
|
|
@@ -352,92 +322,65 @@ const createContainer = (key: string | number): Record<string | number, unknown>
|
|
|
352
322
|
return isArrayKey ? [] : {}
|
|
353
323
|
}
|
|
354
324
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (pathSegments.length === 1) {
|
|
360
|
-
return updateArrayItem(array, index, value)
|
|
325
|
+
const setValueAtPath = <V, O>(obj: O, pathSegments: (string | number)[], depth: number, value: V): O => {
|
|
326
|
+
if (depth >= pathSegments.length) {
|
|
327
|
+
return value as unknown as O
|
|
361
328
|
}
|
|
362
329
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
]
|
|
366
|
-
const nextPathSegments = pathSegments.slice(1)
|
|
367
|
-
const nextKey = nextPathSegments[0]
|
|
368
|
-
|
|
369
|
-
let nextValue = array[index]
|
|
370
|
-
if (nextValue === undefined || nextValue === null) {
|
|
371
|
-
nextValue = nextKey !== undefined ? createContainer(nextKey) : {}
|
|
330
|
+
if (obj === undefined || obj === null) {
|
|
331
|
+
return setValueAtPath({} as O, pathSegments, depth, value)
|
|
372
332
|
}
|
|
373
333
|
|
|
374
|
-
|
|
375
|
-
return copy
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Helper for handling object path updates
|
|
379
|
-
const updateObjectPath = <V>(
|
|
380
|
-
obj: Record<string | number, unknown>,
|
|
381
|
-
pathSegments: (string | number)[],
|
|
382
|
-
value: V
|
|
383
|
-
): Record<string | number, unknown> => {
|
|
384
|
-
const currentKey = pathSegments[0]
|
|
334
|
+
const currentKey = pathSegments[depth]
|
|
385
335
|
if (currentKey === undefined) {
|
|
386
336
|
return obj
|
|
387
337
|
}
|
|
388
338
|
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
}
|
|
339
|
+
if (Array.isArray(obj)) {
|
|
340
|
+
const index = Number(currentKey)
|
|
392
341
|
|
|
393
|
-
|
|
394
|
-
|
|
342
|
+
if (depth === pathSegments.length - 1) {
|
|
343
|
+
return updateArrayItem(obj, index, value) as unknown as O
|
|
344
|
+
}
|
|
395
345
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
346
|
+
const copy = [
|
|
347
|
+
...obj,
|
|
348
|
+
]
|
|
349
|
+
const nextDepth = depth + 1
|
|
350
|
+
const nextKey = pathSegments[nextDepth]
|
|
400
351
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
return result
|
|
406
|
-
}
|
|
352
|
+
let nextValue = obj[index]
|
|
353
|
+
if (nextValue === undefined || nextValue === null) {
|
|
354
|
+
nextValue = nextKey === undefined ? {} : createContainer(nextKey)
|
|
355
|
+
}
|
|
407
356
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
return value as unknown as O
|
|
357
|
+
copy[index] = setValueAtPath(nextValue, pathSegments, nextDepth, value)
|
|
358
|
+
return copy as unknown as O
|
|
411
359
|
}
|
|
412
360
|
|
|
413
|
-
|
|
414
|
-
return setValueAtPath({} as O, pathSegments, value)
|
|
415
|
-
}
|
|
361
|
+
const record = obj as Record<string | number, unknown>
|
|
416
362
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
return obj
|
|
363
|
+
if (depth === pathSegments.length - 1) {
|
|
364
|
+
return updateShallowProperty(record, currentKey, value) as unknown as O
|
|
420
365
|
}
|
|
421
366
|
|
|
422
|
-
|
|
423
|
-
|
|
367
|
+
const nextDepth = depth + 1
|
|
368
|
+
const nextKey = pathSegments[nextDepth]
|
|
369
|
+
|
|
370
|
+
let currentValue = record[currentKey]
|
|
371
|
+
if (currentValue === undefined || currentValue === null) {
|
|
372
|
+
currentValue = nextKey === undefined ? {} : createContainer(nextKey)
|
|
424
373
|
}
|
|
425
374
|
|
|
426
|
-
|
|
375
|
+
const result = {
|
|
376
|
+
...record,
|
|
377
|
+
}
|
|
378
|
+
result[currentKey] = setValueAtPath(currentValue, pathSegments, nextDepth, value)
|
|
379
|
+
return result as unknown as O
|
|
427
380
|
}
|
|
428
381
|
|
|
429
|
-
/**
|
|
430
|
-
* Creates a lens for direct updates to nested properties of a state.
|
|
431
|
-
*/
|
|
432
382
|
const createLens = <T, K>(source: State<T>, accessor: (state: T) => K): State<K> => {
|
|
433
|
-
|
|
434
|
-
accessor,
|
|
435
|
-
isUpdating: false,
|
|
436
|
-
lensState: null as unknown as State<K>,
|
|
437
|
-
originalSet: null as unknown as (value: K) => void,
|
|
438
|
-
path: [] as (string | number)[],
|
|
439
|
-
source,
|
|
440
|
-
}
|
|
383
|
+
let isUpdating = false
|
|
441
384
|
|
|
442
385
|
const extractPath = (): (string | number)[] => {
|
|
443
386
|
const pathCollector: (string | number)[] = []
|
|
@@ -454,7 +397,7 @@ const createLens = <T, K>(source: State<T>, accessor: (state: T) => K): State<K>
|
|
|
454
397
|
)
|
|
455
398
|
|
|
456
399
|
try {
|
|
457
|
-
|
|
400
|
+
accessor(proxy as unknown as T)
|
|
458
401
|
} catch {
|
|
459
402
|
// Ignore errors, we're just collecting the path
|
|
460
403
|
}
|
|
@@ -462,57 +405,49 @@ const createLens = <T, K>(source: State<T>, accessor: (state: T) => K): State<K>
|
|
|
462
405
|
return pathCollector
|
|
463
406
|
}
|
|
464
407
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
container.originalSet = container.lensState.set
|
|
408
|
+
const path = extractPath()
|
|
409
|
+
const lensState = createState<K>(accessor(source()))
|
|
410
|
+
const originalSet = lensState.set
|
|
469
411
|
|
|
470
412
|
createEffect(function lensEffect(): void {
|
|
471
|
-
if (
|
|
413
|
+
if (isUpdating) {
|
|
472
414
|
return
|
|
473
415
|
}
|
|
474
416
|
|
|
475
|
-
|
|
417
|
+
isUpdating = true
|
|
476
418
|
try {
|
|
477
|
-
|
|
419
|
+
lensState.set(accessor(source()))
|
|
478
420
|
} finally {
|
|
479
|
-
|
|
421
|
+
isUpdating = false
|
|
480
422
|
}
|
|
481
423
|
})
|
|
482
424
|
|
|
483
|
-
|
|
484
|
-
if (
|
|
425
|
+
lensState.set = function lensSet(value: K): void {
|
|
426
|
+
if (isUpdating) {
|
|
485
427
|
return
|
|
486
428
|
}
|
|
487
429
|
|
|
488
|
-
|
|
430
|
+
isUpdating = true
|
|
489
431
|
try {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
container.source.update((current: T): T => setValueAtPath(current, container.path, value))
|
|
432
|
+
originalSet(value)
|
|
433
|
+
source.update((current: T): T => setValueAtPath(current, path, 0, value))
|
|
493
434
|
} finally {
|
|
494
|
-
|
|
435
|
+
isUpdating = false
|
|
495
436
|
}
|
|
496
437
|
}
|
|
497
438
|
|
|
498
|
-
|
|
499
|
-
|
|
439
|
+
lensState.update = function lensUpdate(fn: (value: K) => K): void {
|
|
440
|
+
lensState.set(fn(lensState()))
|
|
500
441
|
}
|
|
501
442
|
|
|
502
|
-
return
|
|
443
|
+
return lensState
|
|
503
444
|
}
|
|
504
445
|
|
|
505
|
-
/**
|
|
506
|
-
* Creates a read-only view of a state, hiding mutation methods.
|
|
507
|
-
*/
|
|
508
446
|
const createReadonlyState =
|
|
509
447
|
<T>(source: State<T>): ReadOnlyState<T> =>
|
|
510
448
|
(): T =>
|
|
511
449
|
source()
|
|
512
450
|
|
|
513
|
-
/**
|
|
514
|
-
* Creates a state with access control, returning a tuple of reader and writer.
|
|
515
|
-
*/
|
|
516
451
|
const createProtectedState = <T>(
|
|
517
452
|
initialValue: T,
|
|
518
453
|
equalityFn: (a: T, b: T) => boolean = Object.is
|
|
@@ -521,8 +456,9 @@ const createProtectedState = <T>(
|
|
|
521
456
|
WriteableState<T>,
|
|
522
457
|
] => {
|
|
523
458
|
const fullState = createState(initialValue, equalityFn)
|
|
459
|
+
const reader = createReadonlyState(fullState)
|
|
524
460
|
return [
|
|
525
|
-
|
|
461
|
+
reader,
|
|
526
462
|
{
|
|
527
463
|
set: (value: T): void => fullState.set(value),
|
|
528
464
|
update: (fn: (value: T) => T): void => fullState.update(fn),
|
|
@@ -530,6 +466,7 @@ const createProtectedState = <T>(
|
|
|
530
466
|
]
|
|
531
467
|
}
|
|
532
468
|
|
|
469
|
+
export type { ReadOnlyState, State, Unsubscribe, WriteableState }
|
|
533
470
|
export {
|
|
534
471
|
createDerive as derive,
|
|
535
472
|
createEffect as effect,
|
|
@@ -540,5 +477,3 @@ export {
|
|
|
540
477
|
createState as state,
|
|
541
478
|
executeBatch as batch,
|
|
542
479
|
}
|
|
543
|
-
|
|
544
|
-
export type { ReadOnlyState, State, Unsubscribe, WriteableState }
|