@ersbeth/picoflow 2.2.0 → 2.3.0
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 +22 -0
- package/SKILL.md +5 -0
- package/dist/picoflow.js +252 -131
- package/dist/types/api/base/flowConfig.d.ts +0 -1
- package/dist/types/api/base/flowDisposable.d.ts +0 -1
- package/dist/types/api/base/flowErrors.d.ts +1 -0
- package/dist/types/api/base/flowObservable.d.ts +0 -1
- package/dist/types/api/base/flowPrimitiveOptions.d.ts +14 -0
- package/dist/types/api/base/flowSubscribable.d.ts +0 -1
- package/dist/types/api/base/flowTracker.d.ts +0 -1
- package/dist/types/api/base/index.d.ts +2 -1
- package/dist/types/api/index.d.ts +0 -1
- package/dist/types/api/nodes/async/flowConstantAsync.d.ts +4 -3
- package/dist/types/api/nodes/async/flowDerivationAsync.d.ts +3 -3
- package/dist/types/api/nodes/async/flowStateAsync.d.ts +4 -3
- package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts +3 -2
- package/dist/types/api/nodes/async/index.d.ts +0 -1
- package/dist/types/api/nodes/collections/flowArray.d.ts +3 -2
- package/dist/types/api/nodes/collections/flowMap.d.ts +3 -2
- package/dist/types/api/nodes/collections/index.d.ts +0 -1
- package/dist/types/api/nodes/flowEffect.d.ts +0 -1
- package/dist/types/api/nodes/flowSignal.d.ts +3 -2
- package/dist/types/api/nodes/flowValue.d.ts +0 -1
- package/dist/types/api/nodes/index.d.ts +0 -1
- package/dist/types/api/nodes/sync/flowConstant.d.ts +3 -2
- package/dist/types/api/nodes/sync/flowDerivation.d.ts +3 -2
- package/dist/types/api/nodes/sync/flowState.d.ts +4 -3
- package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts +3 -2
- package/dist/types/api/nodes/sync/index.d.ts +0 -1
- package/dist/types/api/nodes/utils.d.ts +0 -1
- package/dist/types/base/dependenciesRegistry.d.ts +1 -0
- package/dist/types/base/dependentsRegistry.d.ts +1 -0
- package/dist/types/base/disposable.d.ts +5 -1
- package/dist/types/base/errors.d.ts +31 -0
- package/dist/types/base/executionStack.d.ts +3 -11
- package/dist/types/base/index.d.ts +3 -1
- package/dist/types/base/node.d.ts +2 -3
- package/dist/types/base/observable.d.ts +1 -2
- package/dist/types/base/observer.d.ts +1 -2
- package/dist/types/converters/index.d.ts +0 -1
- package/dist/types/converters/solid.d.ts +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/nodes/actionNode.d.ts +1 -23
- package/dist/types/nodes/arrayNode.d.ts +1 -26
- package/dist/types/nodes/effectNode.d.ts +1 -32
- package/dist/types/nodes/index.d.ts +0 -1
- package/dist/types/nodes/mapNode.d.ts +1 -20
- package/dist/types/nodes/signalNode.d.ts +1 -10
- package/dist/types/nodes/valueAsyncNode.d.ts +1 -23
- package/dist/types/nodes/valueNode.d.ts +1 -27
- package/dist/types/nodes/valueSyncNode.d.ts +1 -23
- package/dist/types/schedulers/asyncResolver.d.ts +1 -19
- package/dist/types/schedulers/asyncScheduler.d.ts +1 -21
- package/dist/types/schedulers/index.d.ts +0 -1
- package/dist/types/schedulers/pendingError.d.ts +1 -9
- package/dist/types/schedulers/scheduler.d.ts +1 -10
- package/dist/types/schedulers/syncResolver.d.ts +1 -18
- package/dist/types/schedulers/syncScheduler.d.ts +1 -19
- package/package.json +10 -8
- package/dist/types/api/base/flowConfig.d.ts.map +0 -1
- package/dist/types/api/base/flowDisposable.d.ts.map +0 -1
- package/dist/types/api/base/flowObservable.d.ts.map +0 -1
- package/dist/types/api/base/flowSubscribable.d.ts.map +0 -1
- package/dist/types/api/base/flowTracker.d.ts.map +0 -1
- package/dist/types/api/base/index.d.ts.map +0 -1
- package/dist/types/api/index.d.ts.map +0 -1
- package/dist/types/api/nodes/async/flowConstantAsync.d.ts.map +0 -1
- package/dist/types/api/nodes/async/flowDerivationAsync.d.ts.map +0 -1
- package/dist/types/api/nodes/async/flowStateAsync.d.ts.map +0 -1
- package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts.map +0 -1
- package/dist/types/api/nodes/async/index.d.ts.map +0 -1
- package/dist/types/api/nodes/collections/flowArray.d.ts.map +0 -1
- package/dist/types/api/nodes/collections/flowMap.d.ts.map +0 -1
- package/dist/types/api/nodes/collections/index.d.ts.map +0 -1
- package/dist/types/api/nodes/flowEffect.d.ts.map +0 -1
- package/dist/types/api/nodes/flowSignal.d.ts.map +0 -1
- package/dist/types/api/nodes/flowValue.d.ts.map +0 -1
- package/dist/types/api/nodes/index.d.ts.map +0 -1
- package/dist/types/api/nodes/sync/flowConstant.d.ts.map +0 -1
- package/dist/types/api/nodes/sync/flowDerivation.d.ts.map +0 -1
- package/dist/types/api/nodes/sync/flowState.d.ts.map +0 -1
- package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts.map +0 -1
- package/dist/types/api/nodes/sync/index.d.ts.map +0 -1
- package/dist/types/api/nodes/utils.d.ts.map +0 -1
- package/dist/types/base/disposable.d.ts.map +0 -1
- package/dist/types/base/executionStack.d.ts.map +0 -1
- package/dist/types/base/index.d.ts.map +0 -1
- package/dist/types/base/node.d.ts.map +0 -1
- package/dist/types/base/observable.d.ts.map +0 -1
- package/dist/types/base/observer.d.ts.map +0 -1
- package/dist/types/converters/index.d.ts.map +0 -1
- package/dist/types/converters/solid.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/nodes/actionNode.d.ts.map +0 -1
- package/dist/types/nodes/arrayNode.d.ts.map +0 -1
- package/dist/types/nodes/effectNode.d.ts.map +0 -1
- package/dist/types/nodes/index.d.ts.map +0 -1
- package/dist/types/nodes/mapNode.d.ts.map +0 -1
- package/dist/types/nodes/signalNode.d.ts.map +0 -1
- package/dist/types/nodes/valueAsyncNode.d.ts.map +0 -1
- package/dist/types/nodes/valueNode.d.ts.map +0 -1
- package/dist/types/nodes/valueSyncNode.d.ts.map +0 -1
- package/dist/types/schedulers/asyncResolver.d.ts.map +0 -1
- package/dist/types/schedulers/asyncScheduler.d.ts.map +0 -1
- package/dist/types/schedulers/index.d.ts.map +0 -1
- package/dist/types/schedulers/pendingError.d.ts.map +0 -1
- package/dist/types/schedulers/scheduler.d.ts.map +0 -1
- package/dist/types/schedulers/syncResolver.d.ts.map +0 -1
- package/dist/types/schedulers/syncScheduler.d.ts.map +0 -1
package/dist/picoflow.js
CHANGED
|
@@ -1,10 +1,84 @@
|
|
|
1
1
|
import { createResource, onMount, createEffect, resetErrorBoundaries, onCleanup } from 'solid-js';
|
|
2
2
|
|
|
3
|
+
class DependenciesRegistry {
|
|
4
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
5
|
+
register(dependency, dependent) {
|
|
6
|
+
this._dependencies.add(dependency);
|
|
7
|
+
dependency.registerDependent(dependent);
|
|
8
|
+
}
|
|
9
|
+
unregister(dependency, dependent) {
|
|
10
|
+
this._dependencies.delete(dependency);
|
|
11
|
+
dependency.unregisterDependent(dependent);
|
|
12
|
+
}
|
|
13
|
+
clear(dependent) {
|
|
14
|
+
this._dependencies.forEach((dependency) => {
|
|
15
|
+
dependency.unregisterDependent(dependent);
|
|
16
|
+
});
|
|
17
|
+
this._dependencies.clear();
|
|
18
|
+
}
|
|
19
|
+
forEach(fn) {
|
|
20
|
+
Array.from(this._dependencies).forEach(fn);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class DependentsRegistry {
|
|
25
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
26
|
+
register(dependent) {
|
|
27
|
+
this._dependents.add(dependent);
|
|
28
|
+
}
|
|
29
|
+
unregister(dependent) {
|
|
30
|
+
this._dependents.delete(dependent);
|
|
31
|
+
}
|
|
32
|
+
notifyAll() {
|
|
33
|
+
this._dependents.forEach((dependent) => {
|
|
34
|
+
dependent.notify();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
forEach(fn) {
|
|
38
|
+
Array.from(this._dependents).forEach(fn);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class PicoFlowError extends Error {
|
|
43
|
+
constructor(message) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "PicoFlowError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class PicoFlowDisposedError extends PicoFlowError {
|
|
49
|
+
constructor(message) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = "PicoFlowDisposedError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class PicoFlowCycleError extends PicoFlowError {
|
|
55
|
+
constructor(message) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "PicoFlowCycleError";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
class PicoFlowInternalError extends PicoFlowError {
|
|
61
|
+
constructor(message) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.name = "PicoFlowInternalError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
3
67
|
class Disposable {
|
|
4
68
|
_disposed = false;
|
|
69
|
+
_name;
|
|
70
|
+
constructor(name) {
|
|
71
|
+
this._name = name;
|
|
72
|
+
}
|
|
5
73
|
get disposed() {
|
|
6
74
|
return this._disposed;
|
|
7
75
|
}
|
|
76
|
+
/** Builds the disposed-primitive error, naming the primitive when a `name` was given at creation. */
|
|
77
|
+
_disposedError() {
|
|
78
|
+
return new PicoFlowDisposedError(
|
|
79
|
+
this._name ? `[PicoFlow] Primitive "${this._name}" is disposed` : "[PicoFlow] Primitive is disposed"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
8
82
|
}
|
|
9
83
|
|
|
10
84
|
class ExecutionStack {
|
|
@@ -13,6 +87,9 @@ class ExecutionStack {
|
|
|
13
87
|
static _effectQueue = [];
|
|
14
88
|
static _executionScheduled;
|
|
15
89
|
static _coalesceResetScheduled = false;
|
|
90
|
+
/** Per-effect resets accumulated for the microtask scheduled below, so notifying more than one
|
|
91
|
+
* effect in the same sync turn doesn't discard every reset after the first (A2-SEM-5). */
|
|
92
|
+
static _coalesceResets = /* @__PURE__ */ new Set();
|
|
16
93
|
/** Routes any error escaping the flush loop; defaults to logging rather than crashing (A2-SEM-2, A2-SEC-1). */
|
|
17
94
|
static _onFlushError = (error) => console.error(error);
|
|
18
95
|
/**
|
|
@@ -31,12 +108,17 @@ class ExecutionStack {
|
|
|
31
108
|
* and resets per-effect coalesce flags via onReset.
|
|
32
109
|
*/
|
|
33
110
|
static scheduleCoalesceReset(onReset) {
|
|
111
|
+
ExecutionStack._coalesceResets.add(onReset);
|
|
34
112
|
if (ExecutionStack._coalesceResetScheduled) return;
|
|
35
113
|
ExecutionStack._coalesceResetScheduled = true;
|
|
36
114
|
queueMicrotask(() => {
|
|
37
115
|
ExecutionStack._pastSyncTurn = true;
|
|
38
116
|
ExecutionStack._coalesceResetScheduled = false;
|
|
39
|
-
|
|
117
|
+
const resets = ExecutionStack._coalesceResets;
|
|
118
|
+
ExecutionStack._coalesceResets = /* @__PURE__ */ new Set();
|
|
119
|
+
resets.forEach((reset) => {
|
|
120
|
+
reset();
|
|
121
|
+
});
|
|
40
122
|
});
|
|
41
123
|
}
|
|
42
124
|
/** @internal Wired to the public `onFlushError()` API in `src/api/base/flowConfig.ts`. */
|
|
@@ -71,7 +153,9 @@ class ExecutionStack {
|
|
|
71
153
|
if (i >= ExecutionStack._MAX_FLUSH_STEPS) {
|
|
72
154
|
ExecutionStack._pendingQueue.length = 0;
|
|
73
155
|
ExecutionStack._effectQueue.length = 0;
|
|
74
|
-
ExecutionStack._onFlushError(
|
|
156
|
+
ExecutionStack._onFlushError(
|
|
157
|
+
new PicoFlowCycleError("[PicoFlow] Reactive update cycle exceeded maximum depth")
|
|
158
|
+
);
|
|
75
159
|
return;
|
|
76
160
|
}
|
|
77
161
|
const node = queue[i];
|
|
@@ -90,66 +174,59 @@ class ExecutionStack {
|
|
|
90
174
|
}
|
|
91
175
|
|
|
92
176
|
class Node extends Disposable {
|
|
93
|
-
|
|
94
|
-
|
|
177
|
+
_dependenciesRegistry = new DependenciesRegistry();
|
|
178
|
+
_dependentsRegistry = new DependentsRegistry();
|
|
95
179
|
_status = "resolved";
|
|
96
180
|
get status() {
|
|
97
|
-
if (this.disposed) throw
|
|
181
|
+
if (this.disposed) throw this._disposedError();
|
|
98
182
|
return this._status;
|
|
99
183
|
}
|
|
100
184
|
set status(status) {
|
|
101
|
-
if (this.disposed) throw
|
|
185
|
+
if (this.disposed) throw this._disposedError();
|
|
102
186
|
this._status = status;
|
|
103
187
|
}
|
|
104
188
|
registerDependency(dependency) {
|
|
105
|
-
if (this._disposed) throw
|
|
106
|
-
this.
|
|
107
|
-
dependency.registerDependent(this);
|
|
189
|
+
if (this._disposed) throw this._disposedError();
|
|
190
|
+
this._dependenciesRegistry.register(dependency, this);
|
|
108
191
|
}
|
|
109
192
|
unregisterDependency(dependency) {
|
|
110
|
-
if (this._disposed) throw
|
|
111
|
-
this.
|
|
112
|
-
dependency.unregisterDependent(this);
|
|
193
|
+
if (this._disposed) throw this._disposedError();
|
|
194
|
+
this._dependenciesRegistry.unregister(dependency, this);
|
|
113
195
|
}
|
|
114
196
|
clearDependencies() {
|
|
115
|
-
if (this.disposed) throw
|
|
116
|
-
this.
|
|
117
|
-
dependency.unregisterDependent(this);
|
|
118
|
-
});
|
|
119
|
-
this._dependencies.clear();
|
|
197
|
+
if (this.disposed) throw this._disposedError();
|
|
198
|
+
this._dependenciesRegistry.clear(this);
|
|
120
199
|
}
|
|
121
200
|
registerDependent(dependent) {
|
|
122
|
-
if (this.disposed) throw
|
|
123
|
-
this.
|
|
201
|
+
if (this.disposed) throw this._disposedError();
|
|
202
|
+
this._dependentsRegistry.register(dependent);
|
|
124
203
|
}
|
|
125
204
|
unregisterDependent(dependent) {
|
|
126
|
-
if (this.disposed) throw
|
|
127
|
-
this.
|
|
205
|
+
if (this.disposed) throw this._disposedError();
|
|
206
|
+
this._dependentsRegistry.unregister(dependent);
|
|
128
207
|
}
|
|
129
208
|
markDependencyDisposed() {
|
|
130
209
|
}
|
|
131
210
|
notifyDependents() {
|
|
132
211
|
if (this.disposed) return;
|
|
133
|
-
this.
|
|
134
|
-
dependent.notify();
|
|
135
|
-
});
|
|
212
|
+
this._dependentsRegistry.notifyAll();
|
|
136
213
|
}
|
|
137
214
|
watch(tracker) {
|
|
138
|
-
if (this.disposed) throw
|
|
215
|
+
if (this.disposed) throw this._disposedError();
|
|
139
216
|
tracker.registerDependency(this);
|
|
140
217
|
}
|
|
141
218
|
trigger() {
|
|
142
|
-
if (this.disposed) throw
|
|
219
|
+
if (this.disposed) throw this._disposedError();
|
|
143
220
|
this.notifyDependents();
|
|
144
221
|
}
|
|
145
222
|
dispose() {
|
|
146
223
|
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
147
|
-
|
|
224
|
+
this._dependentsRegistry.forEach((dependant) => {
|
|
148
225
|
dependant.markDependencyDisposed();
|
|
149
226
|
dependant.unregisterDependency(this);
|
|
150
227
|
this.unregisterDependent(dependant);
|
|
151
228
|
});
|
|
152
|
-
|
|
229
|
+
this._dependenciesRegistry.forEach((dependency) => {
|
|
153
230
|
this.unregisterDependency(dependency);
|
|
154
231
|
});
|
|
155
232
|
this._disposed = true;
|
|
@@ -157,41 +234,39 @@ class Node extends Disposable {
|
|
|
157
234
|
}
|
|
158
235
|
|
|
159
236
|
class Observable extends Disposable {
|
|
160
|
-
|
|
237
|
+
_dependentsRegistry = new DependentsRegistry();
|
|
161
238
|
_status = "resolved";
|
|
162
239
|
get status() {
|
|
163
|
-
if (this.disposed) throw
|
|
240
|
+
if (this.disposed) throw this._disposedError();
|
|
164
241
|
return this._status;
|
|
165
242
|
}
|
|
166
243
|
set status(status) {
|
|
167
|
-
if (this.disposed) throw
|
|
244
|
+
if (this.disposed) throw this._disposedError();
|
|
168
245
|
this._status = status;
|
|
169
246
|
}
|
|
170
247
|
registerDependent(dependent) {
|
|
171
|
-
if (this.disposed) throw
|
|
172
|
-
this.
|
|
248
|
+
if (this.disposed) throw this._disposedError();
|
|
249
|
+
this._dependentsRegistry.register(dependent);
|
|
173
250
|
}
|
|
174
251
|
unregisterDependent(dependent) {
|
|
175
|
-
if (this.disposed) throw
|
|
176
|
-
this.
|
|
252
|
+
if (this.disposed) throw this._disposedError();
|
|
253
|
+
this._dependentsRegistry.unregister(dependent);
|
|
177
254
|
}
|
|
178
255
|
notifyDependents() {
|
|
179
256
|
if (this.disposed) return;
|
|
180
|
-
this.
|
|
181
|
-
dependent.notify();
|
|
182
|
-
});
|
|
257
|
+
this._dependentsRegistry.notifyAll();
|
|
183
258
|
}
|
|
184
259
|
watch(tracker) {
|
|
185
|
-
if (this.disposed) throw
|
|
260
|
+
if (this.disposed) throw this._disposedError();
|
|
186
261
|
tracker.registerDependency(this);
|
|
187
262
|
}
|
|
188
263
|
trigger() {
|
|
189
|
-
if (this.disposed) throw
|
|
264
|
+
if (this.disposed) throw this._disposedError();
|
|
190
265
|
this.notifyDependents();
|
|
191
266
|
}
|
|
192
267
|
dispose() {
|
|
193
|
-
if (this._disposed) throw
|
|
194
|
-
|
|
268
|
+
if (this._disposed) throw this._disposedError();
|
|
269
|
+
this._dependentsRegistry.forEach((dependant) => {
|
|
195
270
|
dependant.markDependencyDisposed();
|
|
196
271
|
dependant.unregisterDependency(this);
|
|
197
272
|
this.unregisterDependent(dependant);
|
|
@@ -201,34 +276,27 @@ class Observable extends Disposable {
|
|
|
201
276
|
}
|
|
202
277
|
|
|
203
278
|
class Observer extends Disposable {
|
|
204
|
-
|
|
279
|
+
_dependenciesRegistry = new DependenciesRegistry();
|
|
205
280
|
/** Set when a dependency disposed while this observer was queued; consumed by the next execute(). */
|
|
206
281
|
_dependencyDisposed = false;
|
|
207
282
|
markDependencyDisposed() {
|
|
208
283
|
this._dependencyDisposed = true;
|
|
209
284
|
}
|
|
210
285
|
registerDependency(dependency) {
|
|
211
|
-
if (this._disposed) throw
|
|
212
|
-
this.
|
|
213
|
-
dependency.registerDependent(this);
|
|
286
|
+
if (this._disposed) throw this._disposedError();
|
|
287
|
+
this._dependenciesRegistry.register(dependency, this);
|
|
214
288
|
}
|
|
215
289
|
unregisterDependency(dependency) {
|
|
216
|
-
if (this._disposed) throw
|
|
217
|
-
this.
|
|
218
|
-
dependency.unregisterDependent(this);
|
|
290
|
+
if (this._disposed) throw this._disposedError();
|
|
291
|
+
this._dependenciesRegistry.unregister(dependency, this);
|
|
219
292
|
}
|
|
220
293
|
clearDependencies() {
|
|
221
294
|
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
222
|
-
this.
|
|
223
|
-
dependency.unregisterDependent(this);
|
|
224
|
-
});
|
|
225
|
-
this._dependencies.clear();
|
|
295
|
+
this._dependenciesRegistry.clear(this);
|
|
226
296
|
}
|
|
227
297
|
dispose() {
|
|
228
|
-
if (this._disposed) throw
|
|
229
|
-
|
|
230
|
-
this.unregisterDependency(dependency);
|
|
231
|
-
});
|
|
298
|
+
if (this._disposed) throw this._disposedError();
|
|
299
|
+
this._dependenciesRegistry.clear(this);
|
|
232
300
|
this._disposed = true;
|
|
233
301
|
}
|
|
234
302
|
}
|
|
@@ -321,14 +389,14 @@ class AsyncScheduler {
|
|
|
321
389
|
this._attachHandlers(this._resolver);
|
|
322
390
|
}
|
|
323
391
|
get settled() {
|
|
324
|
-
if (this._disposed) throw new
|
|
392
|
+
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
325
393
|
return this._settled.promise;
|
|
326
394
|
}
|
|
327
395
|
get disposed() {
|
|
328
396
|
return this._disposed;
|
|
329
397
|
}
|
|
330
398
|
overwrite(promise) {
|
|
331
|
-
if (this._disposed) throw new
|
|
399
|
+
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
332
400
|
if (this._resolver.finished) {
|
|
333
401
|
this._resolver = new AsyncResolver(this._compute);
|
|
334
402
|
this._settled = Promise.withResolvers();
|
|
@@ -337,7 +405,7 @@ class AsyncScheduler {
|
|
|
337
405
|
this._resolver.overwrite(promise);
|
|
338
406
|
}
|
|
339
407
|
compute() {
|
|
340
|
-
if (this._disposed) throw new
|
|
408
|
+
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
341
409
|
if (this._resolver.finished) {
|
|
342
410
|
this._resolver = new AsyncResolver(this._compute);
|
|
343
411
|
this._settled = Promise.withResolvers();
|
|
@@ -429,7 +497,7 @@ class SyncScheduler {
|
|
|
429
497
|
return this._settled.promise;
|
|
430
498
|
}
|
|
431
499
|
overwrite(value) {
|
|
432
|
-
if (this._disposed) throw new
|
|
500
|
+
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
433
501
|
if (this._resolver.finished) {
|
|
434
502
|
this._settled = Promise.withResolvers();
|
|
435
503
|
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
@@ -437,7 +505,7 @@ class SyncScheduler {
|
|
|
437
505
|
this._resolver.overwrite(value);
|
|
438
506
|
}
|
|
439
507
|
compute() {
|
|
440
|
-
if (this._disposed) throw new
|
|
508
|
+
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
441
509
|
if (this._resolver.finished) {
|
|
442
510
|
this._settled = Promise.withResolvers();
|
|
443
511
|
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
@@ -463,6 +531,8 @@ class EffectNode extends Observer {
|
|
|
463
531
|
_notifyEpoch = 0;
|
|
464
532
|
/** How many notification epochs have been executed so far. */
|
|
465
533
|
_executedEpoch = 0;
|
|
534
|
+
/** Callbacks registered via {@link onDispose}, run once after dispose() tears down the graph edges. */
|
|
535
|
+
_disposeListeners = [];
|
|
466
536
|
constructor(data, onData, onError, onPending) {
|
|
467
537
|
super();
|
|
468
538
|
this._data = data;
|
|
@@ -472,8 +542,23 @@ class EffectNode extends Observer {
|
|
|
472
542
|
this._notifyEpoch = 1;
|
|
473
543
|
this.execute();
|
|
474
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Registers a callback to run once this effect is disposed, after the graph edges are torn
|
|
547
|
+
* down. Lets a caller that wraps an effect (e.g. {@link ActionNode.subscribe}) attach its own
|
|
548
|
+
* teardown without patching `dispose` on the returned instance (A2-ARCH-8).
|
|
549
|
+
* @internal
|
|
550
|
+
*/
|
|
551
|
+
onDispose(fn) {
|
|
552
|
+
this._disposeListeners.push(fn);
|
|
553
|
+
}
|
|
554
|
+
dispose() {
|
|
555
|
+
super.dispose();
|
|
556
|
+
this._disposeListeners.forEach((fn) => {
|
|
557
|
+
fn();
|
|
558
|
+
});
|
|
559
|
+
}
|
|
475
560
|
notify() {
|
|
476
|
-
if (this._disposed) throw
|
|
561
|
+
if (this._disposed) throw this._disposedError();
|
|
477
562
|
if (this._queued) {
|
|
478
563
|
if (!this._coalescedWhileQueued) {
|
|
479
564
|
this._coalescedWhileQueued = true;
|
|
@@ -537,8 +622,22 @@ class ValueNode extends Node {
|
|
|
537
622
|
[PICOFLOW_VALUE_NODE] = true;
|
|
538
623
|
_value;
|
|
539
624
|
_error;
|
|
625
|
+
/** Builds the "unreachable state" error, naming the primitive when a `name` was given at creation. */
|
|
626
|
+
_internalError() {
|
|
627
|
+
return new PicoFlowInternalError(
|
|
628
|
+
this._name ? `[PicoFlow] Internal error (${this._name})` : "[PicoFlow] Internal error"
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Hook called just before a tracked read throws `PendingError`, i.e. a dependent registered while
|
|
633
|
+
* this node is mid-flight and has no other way to learn the computation later settled (A2-PERF-4).
|
|
634
|
+
* No-op by default; overridden by {@link ValueAsyncNode} to know when its own post-resolve
|
|
635
|
+
* `notifyDependents()` call cannot be skipped even if the resolved value is unchanged.
|
|
636
|
+
*/
|
|
637
|
+
_markPendingRead() {
|
|
638
|
+
}
|
|
540
639
|
watch(tracker) {
|
|
541
|
-
if (this.disposed) throw
|
|
640
|
+
if (this.disposed) throw this._disposedError();
|
|
542
641
|
super.watch(tracker);
|
|
543
642
|
switch (this.status) {
|
|
544
643
|
case "resolved":
|
|
@@ -546,26 +645,28 @@ class ValueNode extends Node {
|
|
|
546
645
|
case "error":
|
|
547
646
|
throw this._error;
|
|
548
647
|
case "pending": {
|
|
648
|
+
this._markPendingRead();
|
|
549
649
|
throw new PendingError(this._scheduler.settled);
|
|
550
650
|
}
|
|
551
651
|
case "dirty": {
|
|
552
652
|
this.execute();
|
|
553
653
|
switch (this.status) {
|
|
554
654
|
case "pending":
|
|
655
|
+
this._markPendingRead();
|
|
555
656
|
throw new PendingError(this._scheduler.settled);
|
|
556
657
|
case "resolved":
|
|
557
658
|
return;
|
|
558
659
|
case "error":
|
|
559
660
|
throw this._error;
|
|
560
661
|
case "dirty": {
|
|
561
|
-
throw
|
|
662
|
+
throw this._internalError();
|
|
562
663
|
}
|
|
563
664
|
}
|
|
564
665
|
}
|
|
565
666
|
}
|
|
566
667
|
}
|
|
567
668
|
notify() {
|
|
568
|
-
if (this.disposed) throw
|
|
669
|
+
if (this.disposed) throw this._disposedError();
|
|
569
670
|
if (this.status === "dirty") return;
|
|
570
671
|
if (this.status === "pending") {
|
|
571
672
|
ExecutionStack.pushPending(this);
|
|
@@ -578,7 +679,7 @@ class ValueNode extends Node {
|
|
|
578
679
|
this._scheduler.dispose();
|
|
579
680
|
}
|
|
580
681
|
get(tracker) {
|
|
581
|
-
if (this.disposed) throw
|
|
682
|
+
if (this.disposed) throw this._disposedError();
|
|
582
683
|
super.watch(tracker);
|
|
583
684
|
switch (this.status) {
|
|
584
685
|
case "resolved":
|
|
@@ -586,6 +687,7 @@ class ValueNode extends Node {
|
|
|
586
687
|
case "error":
|
|
587
688
|
throw this._error;
|
|
588
689
|
case "pending": {
|
|
690
|
+
this._markPendingRead();
|
|
589
691
|
throw new PendingError(this._scheduler.settled);
|
|
590
692
|
}
|
|
591
693
|
case "dirty": {
|
|
@@ -596,17 +698,18 @@ class ValueNode extends Node {
|
|
|
596
698
|
case "error":
|
|
597
699
|
throw this._error;
|
|
598
700
|
case "pending": {
|
|
701
|
+
this._markPendingRead();
|
|
599
702
|
throw new PendingError(this._scheduler.settled);
|
|
600
703
|
}
|
|
601
704
|
case "dirty": {
|
|
602
|
-
throw
|
|
705
|
+
throw this._internalError();
|
|
603
706
|
}
|
|
604
707
|
}
|
|
605
708
|
}
|
|
606
709
|
}
|
|
607
710
|
}
|
|
608
711
|
async pick() {
|
|
609
|
-
if (this.disposed) throw
|
|
712
|
+
if (this.disposed) throw this._disposedError();
|
|
610
713
|
switch (this.status) {
|
|
611
714
|
case "resolved":
|
|
612
715
|
return this._value;
|
|
@@ -614,7 +717,7 @@ class ValueNode extends Node {
|
|
|
614
717
|
await this._scheduler.settled;
|
|
615
718
|
if (this.status === "resolved") return this._value;
|
|
616
719
|
if (this.status === "error") throw this._error;
|
|
617
|
-
throw
|
|
720
|
+
throw this._internalError();
|
|
618
721
|
}
|
|
619
722
|
case "error":
|
|
620
723
|
throw this._error;
|
|
@@ -627,12 +730,12 @@ class ValueNode extends Node {
|
|
|
627
730
|
await this._scheduler.settled;
|
|
628
731
|
if (this.status === "resolved") return this._value;
|
|
629
732
|
if (this.status === "error") throw this._error;
|
|
630
|
-
throw
|
|
733
|
+
throw this._internalError();
|
|
631
734
|
}
|
|
632
735
|
case "error":
|
|
633
736
|
throw this._error;
|
|
634
737
|
case "dirty": {
|
|
635
|
-
throw
|
|
738
|
+
throw this._internalError();
|
|
636
739
|
}
|
|
637
740
|
}
|
|
638
741
|
}
|
|
@@ -645,7 +748,7 @@ class ValueNode extends Node {
|
|
|
645
748
|
this._scheduler.compute();
|
|
646
749
|
}
|
|
647
750
|
subscribe(onValue, onError, onPending) {
|
|
648
|
-
if (this.disposed) throw
|
|
751
|
+
if (this.disposed) throw this._disposedError();
|
|
649
752
|
const effect = new EffectNode((t) => this.get(t), onValue, onError, onPending);
|
|
650
753
|
return effect;
|
|
651
754
|
}
|
|
@@ -654,8 +757,17 @@ class ValueNode extends Node {
|
|
|
654
757
|
class ValueAsyncNode extends ValueNode {
|
|
655
758
|
_scheduler;
|
|
656
759
|
_compute;
|
|
657
|
-
|
|
658
|
-
|
|
760
|
+
/** Set by {@link ValueNode._markPendingRead} when a dependent registers while this node is mid-flight. */
|
|
761
|
+
_hasPendingReader = false;
|
|
762
|
+
/**
|
|
763
|
+
* True only while the in-flight computation was started by `set()`, which already ran its own
|
|
764
|
+
* eager `notifyDependents()` cascade before this resolution. `refresh()` and a recompute pulled
|
|
765
|
+
* lazily after an upstream `notify()` have no such pre-cascade — for them, `_onResolve`'s own
|
|
766
|
+
* `notifyDependents()` call is the only notification, so it must never be skipped (A2-PERF-4).
|
|
767
|
+
*/
|
|
768
|
+
_pendingFromSet = false;
|
|
769
|
+
constructor(promiseOrCompute, name) {
|
|
770
|
+
super(name);
|
|
659
771
|
if (typeof promiseOrCompute === "function") {
|
|
660
772
|
this._compute = () => promiseOrCompute(this, this._value);
|
|
661
773
|
} else {
|
|
@@ -670,10 +782,18 @@ class ValueAsyncNode extends ValueNode {
|
|
|
670
782
|
}
|
|
671
783
|
_onResolve(value) {
|
|
672
784
|
if (this.disposed) return;
|
|
785
|
+
const changed = value !== this._value;
|
|
786
|
+
const hadPendingReader = this._hasPendingReader;
|
|
787
|
+
const pendingFromSet = this._pendingFromSet;
|
|
788
|
+
this._hasPendingReader = false;
|
|
789
|
+
this._pendingFromSet = false;
|
|
673
790
|
this.status = "resolved";
|
|
674
791
|
this._value = value;
|
|
675
792
|
this._error = void 0;
|
|
676
|
-
this.notifyDependents();
|
|
793
|
+
if (changed || hadPendingReader || !pendingFromSet) this.notifyDependents();
|
|
794
|
+
}
|
|
795
|
+
_markPendingRead() {
|
|
796
|
+
this._hasPendingReader = true;
|
|
677
797
|
}
|
|
678
798
|
_onReject(error) {
|
|
679
799
|
if (this.disposed) return;
|
|
@@ -682,12 +802,13 @@ class ValueAsyncNode extends ValueNode {
|
|
|
682
802
|
this.notifyDependents();
|
|
683
803
|
}
|
|
684
804
|
set(promiseOrUpdater) {
|
|
685
|
-
if (this.disposed) throw
|
|
805
|
+
if (this.disposed) throw this._disposedError();
|
|
686
806
|
if (typeof promiseOrUpdater === "function") {
|
|
687
807
|
const updater = promiseOrUpdater;
|
|
688
808
|
switch (this.status) {
|
|
689
809
|
case "resolved": {
|
|
690
810
|
this.status = "pending";
|
|
811
|
+
this._pendingFromSet = true;
|
|
691
812
|
this._scheduler.overwrite(updater(this._value));
|
|
692
813
|
this.notifyDependents();
|
|
693
814
|
return;
|
|
@@ -696,6 +817,7 @@ class ValueAsyncNode extends ValueNode {
|
|
|
696
817
|
case "error":
|
|
697
818
|
case "dirty": {
|
|
698
819
|
this.status = "pending";
|
|
820
|
+
this._pendingFromSet = true;
|
|
699
821
|
this._scheduler.overwrite(updater(this._value));
|
|
700
822
|
this.notifyDependents();
|
|
701
823
|
return;
|
|
@@ -703,38 +825,39 @@ class ValueAsyncNode extends ValueNode {
|
|
|
703
825
|
}
|
|
704
826
|
} else {
|
|
705
827
|
this.status = "pending";
|
|
828
|
+
this._pendingFromSet = true;
|
|
706
829
|
const promise = promiseOrUpdater;
|
|
707
830
|
this._scheduler.overwrite(promise);
|
|
708
831
|
this.notifyDependents();
|
|
709
832
|
}
|
|
710
833
|
}
|
|
711
834
|
refresh() {
|
|
712
|
-
if (this.disposed) throw
|
|
835
|
+
if (this.disposed) throw this._disposedError();
|
|
713
836
|
this.execute();
|
|
714
837
|
}
|
|
715
838
|
}
|
|
716
839
|
|
|
717
|
-
function constantAsync(valueOrInitializer) {
|
|
718
|
-
return new ValueAsyncNode(valueOrInitializer);
|
|
840
|
+
function constantAsync(valueOrInitializer, options) {
|
|
841
|
+
return new ValueAsyncNode(valueOrInitializer, options?.name);
|
|
719
842
|
}
|
|
720
843
|
|
|
721
|
-
function derivationAsync(compute) {
|
|
722
|
-
return new ValueAsyncNode(compute);
|
|
844
|
+
function derivationAsync(compute, options) {
|
|
845
|
+
return new ValueAsyncNode(compute, options?.name);
|
|
723
846
|
}
|
|
724
847
|
|
|
725
|
-
function stateAsync(valueOrInitializer) {
|
|
726
|
-
return new ValueAsyncNode(valueOrInitializer);
|
|
848
|
+
function stateAsync(valueOrInitializer, options) {
|
|
849
|
+
return new ValueAsyncNode(valueOrInitializer, options?.name);
|
|
727
850
|
}
|
|
728
851
|
|
|
729
|
-
function writableDerivationAsync(compute) {
|
|
730
|
-
return new ValueAsyncNode(compute);
|
|
852
|
+
function writableDerivationAsync(compute, options) {
|
|
853
|
+
return new ValueAsyncNode(compute, options?.name);
|
|
731
854
|
}
|
|
732
855
|
|
|
733
856
|
class ValueSyncNode extends ValueNode {
|
|
734
857
|
_scheduler;
|
|
735
858
|
_compute;
|
|
736
|
-
constructor(valueOrCompute) {
|
|
737
|
-
super();
|
|
859
|
+
constructor(valueOrCompute, name) {
|
|
860
|
+
super(name);
|
|
738
861
|
if (typeof valueOrCompute === "function") {
|
|
739
862
|
this._compute = () => valueOrCompute(this, this._value);
|
|
740
863
|
} else {
|
|
@@ -760,7 +883,7 @@ class ValueSyncNode extends ValueNode {
|
|
|
760
883
|
this._error = error;
|
|
761
884
|
}
|
|
762
885
|
set(valueOrUpdater) {
|
|
763
|
-
if (this.disposed) throw
|
|
886
|
+
if (this.disposed) throw this._disposedError();
|
|
764
887
|
if (typeof valueOrUpdater === "function") {
|
|
765
888
|
const updater = valueOrUpdater;
|
|
766
889
|
switch (this.status) {
|
|
@@ -804,7 +927,7 @@ class ValueSyncNode extends ValueNode {
|
|
|
804
927
|
}
|
|
805
928
|
}
|
|
806
929
|
refresh() {
|
|
807
|
-
if (this.disposed) throw
|
|
930
|
+
if (this.disposed) throw this._disposedError();
|
|
808
931
|
const currentValue = this._value;
|
|
809
932
|
const currentStatus = this.status;
|
|
810
933
|
this.execute();
|
|
@@ -833,7 +956,7 @@ class ActionNode extends ValueSyncNode {
|
|
|
833
956
|
}
|
|
834
957
|
}
|
|
835
958
|
set(valueOrUpdater) {
|
|
836
|
-
if (this.disposed) throw
|
|
959
|
+
if (this.disposed) throw this._disposedError();
|
|
837
960
|
if (typeof valueOrUpdater === "function") {
|
|
838
961
|
const updater = valueOrUpdater;
|
|
839
962
|
const nextValue2 = updater(this._value);
|
|
@@ -848,7 +971,7 @@ class ActionNode extends ValueSyncNode {
|
|
|
848
971
|
super.set(nextValue);
|
|
849
972
|
}
|
|
850
973
|
subscribe(onValue, onError, onPending) {
|
|
851
|
-
if (this.disposed) throw
|
|
974
|
+
if (this.disposed) throw this._disposedError();
|
|
852
975
|
const slot = { queue: [] };
|
|
853
976
|
this._slots.add(slot);
|
|
854
977
|
const effect = new EffectNode(
|
|
@@ -867,11 +990,9 @@ class ActionNode extends ValueSyncNode {
|
|
|
867
990
|
onError,
|
|
868
991
|
onPending
|
|
869
992
|
);
|
|
870
|
-
|
|
871
|
-
effect.dispose = () => {
|
|
993
|
+
effect.onDispose(() => {
|
|
872
994
|
this._slots.delete(slot);
|
|
873
|
-
|
|
874
|
-
};
|
|
995
|
+
});
|
|
875
996
|
return effect;
|
|
876
997
|
}
|
|
877
998
|
dispose() {
|
|
@@ -882,8 +1003,8 @@ class ActionNode extends ValueSyncNode {
|
|
|
882
1003
|
|
|
883
1004
|
class ArrayNode extends ValueSyncNode {
|
|
884
1005
|
$lastAction;
|
|
885
|
-
constructor(value = []) {
|
|
886
|
-
super(value);
|
|
1006
|
+
constructor(value = [], name) {
|
|
1007
|
+
super(value, name);
|
|
887
1008
|
this.$lastAction = new ActionNode({
|
|
888
1009
|
type: "set",
|
|
889
1010
|
setItems: value,
|
|
@@ -899,11 +1020,11 @@ class ArrayNode extends ValueSyncNode {
|
|
|
899
1020
|
return [...values];
|
|
900
1021
|
}
|
|
901
1022
|
get length() {
|
|
902
|
-
if (this.disposed) throw
|
|
1023
|
+
if (this.disposed) throw this._disposedError();
|
|
903
1024
|
return this._value.length;
|
|
904
1025
|
}
|
|
905
1026
|
set(itemsOrUpdater) {
|
|
906
|
-
if (this.disposed) throw
|
|
1027
|
+
if (this.disposed) throw this._disposedError();
|
|
907
1028
|
const previousValue = this._value;
|
|
908
1029
|
super.set(itemsOrUpdater);
|
|
909
1030
|
this.$lastAction.set({
|
|
@@ -914,7 +1035,7 @@ class ArrayNode extends ValueSyncNode {
|
|
|
914
1035
|
return previousValue;
|
|
915
1036
|
}
|
|
916
1037
|
update(index, item) {
|
|
917
|
-
if (this.disposed) throw
|
|
1038
|
+
if (this.disposed) throw this._disposedError();
|
|
918
1039
|
if (index < 0 || index >= this._value.length || !Number.isInteger(index)) {
|
|
919
1040
|
throw new Error("[PicoFlow] Index out of bounds");
|
|
920
1041
|
}
|
|
@@ -930,13 +1051,13 @@ class ArrayNode extends ValueSyncNode {
|
|
|
930
1051
|
return previousValue;
|
|
931
1052
|
}
|
|
932
1053
|
push(item) {
|
|
933
|
-
if (this.disposed) throw
|
|
1054
|
+
if (this.disposed) throw this._disposedError();
|
|
934
1055
|
this._value.push(item);
|
|
935
1056
|
this.notifyDependents();
|
|
936
1057
|
this.$lastAction.set({ type: "push", addedItem: item });
|
|
937
1058
|
}
|
|
938
1059
|
pop() {
|
|
939
|
-
if (this.disposed) throw
|
|
1060
|
+
if (this.disposed) throw this._disposedError();
|
|
940
1061
|
if (this._value.length === 0) return void 0;
|
|
941
1062
|
const item = this._value.pop();
|
|
942
1063
|
this.notifyDependents();
|
|
@@ -944,13 +1065,13 @@ class ArrayNode extends ValueSyncNode {
|
|
|
944
1065
|
return item;
|
|
945
1066
|
}
|
|
946
1067
|
unshift(item) {
|
|
947
|
-
if (this.disposed) throw
|
|
1068
|
+
if (this.disposed) throw this._disposedError();
|
|
948
1069
|
this._value.unshift(item);
|
|
949
1070
|
this.notifyDependents();
|
|
950
1071
|
this.$lastAction.set({ type: "unshift", addedItem: item });
|
|
951
1072
|
}
|
|
952
1073
|
shift() {
|
|
953
|
-
if (this.disposed) throw
|
|
1074
|
+
if (this.disposed) throw this._disposedError();
|
|
954
1075
|
if (this._value.length === 0) return void 0;
|
|
955
1076
|
const item = this._value.shift();
|
|
956
1077
|
this.notifyDependents();
|
|
@@ -958,7 +1079,7 @@ class ArrayNode extends ValueSyncNode {
|
|
|
958
1079
|
return item;
|
|
959
1080
|
}
|
|
960
1081
|
splice(start, deleteCount, ...newItems) {
|
|
961
|
-
if (this.disposed) throw
|
|
1082
|
+
if (this.disposed) throw this._disposedError();
|
|
962
1083
|
const items = this._value.splice(start, deleteCount, ...newItems);
|
|
963
1084
|
this.notifyDependents();
|
|
964
1085
|
this.$lastAction.set({
|
|
@@ -971,7 +1092,7 @@ class ArrayNode extends ValueSyncNode {
|
|
|
971
1092
|
return items;
|
|
972
1093
|
}
|
|
973
1094
|
clear() {
|
|
974
|
-
if (this.disposed) throw
|
|
1095
|
+
if (this.disposed) throw this._disposedError();
|
|
975
1096
|
const previousValue = [...this._value];
|
|
976
1097
|
this._value = [];
|
|
977
1098
|
this.notifyDependents();
|
|
@@ -987,14 +1108,14 @@ class ArrayNode extends ValueSyncNode {
|
|
|
987
1108
|
}
|
|
988
1109
|
}
|
|
989
1110
|
|
|
990
|
-
function array(initial) {
|
|
991
|
-
return new ArrayNode(initial);
|
|
1111
|
+
function array(initial, options) {
|
|
1112
|
+
return new ArrayNode(initial, options?.name);
|
|
992
1113
|
}
|
|
993
1114
|
|
|
994
1115
|
class MapNode extends ValueSyncNode {
|
|
995
1116
|
$lastAction;
|
|
996
|
-
constructor(value = /* @__PURE__ */ new Map()) {
|
|
997
|
-
super(value);
|
|
1117
|
+
constructor(value = /* @__PURE__ */ new Map(), name) {
|
|
1118
|
+
super(value, name);
|
|
998
1119
|
this.$lastAction = new ActionNode({
|
|
999
1120
|
type: "set",
|
|
1000
1121
|
setMap: value,
|
|
@@ -1002,7 +1123,7 @@ class MapNode extends ValueSyncNode {
|
|
|
1002
1123
|
});
|
|
1003
1124
|
}
|
|
1004
1125
|
add(key, value) {
|
|
1005
|
-
if (this.disposed) throw
|
|
1126
|
+
if (this.disposed) throw this._disposedError();
|
|
1006
1127
|
if (this._value.has(key)) {
|
|
1007
1128
|
throw new Error("[PicoFlow] Key already exists");
|
|
1008
1129
|
}
|
|
@@ -1011,7 +1132,7 @@ class MapNode extends ValueSyncNode {
|
|
|
1011
1132
|
this.$lastAction.set({ type: "add", key, addedValue: value });
|
|
1012
1133
|
}
|
|
1013
1134
|
update(key, value) {
|
|
1014
|
-
if (this.disposed) throw
|
|
1135
|
+
if (this.disposed) throw this._disposedError();
|
|
1015
1136
|
if (!this._value.has(key)) throw new Error("[PicoFlow] Key does not exist");
|
|
1016
1137
|
const previousValue = this._value.get(key);
|
|
1017
1138
|
this._value.set(key, value);
|
|
@@ -1025,7 +1146,7 @@ class MapNode extends ValueSyncNode {
|
|
|
1025
1146
|
return previousValue;
|
|
1026
1147
|
}
|
|
1027
1148
|
delete(key) {
|
|
1028
|
-
if (this.disposed) throw
|
|
1149
|
+
if (this.disposed) throw this._disposedError();
|
|
1029
1150
|
if (!this._value.has(key)) throw new Error("[PicoFlow] Key does not exist");
|
|
1030
1151
|
const value = this._value.get(key);
|
|
1031
1152
|
this._value.delete(key);
|
|
@@ -1034,7 +1155,7 @@ class MapNode extends ValueSyncNode {
|
|
|
1034
1155
|
return value;
|
|
1035
1156
|
}
|
|
1036
1157
|
set(mapOrUpdater) {
|
|
1037
|
-
if (this.disposed) throw
|
|
1158
|
+
if (this.disposed) throw this._disposedError();
|
|
1038
1159
|
const previousValue = this._value;
|
|
1039
1160
|
super.set(mapOrUpdater);
|
|
1040
1161
|
this.$lastAction.set({
|
|
@@ -1045,7 +1166,7 @@ class MapNode extends ValueSyncNode {
|
|
|
1045
1166
|
return previousValue;
|
|
1046
1167
|
}
|
|
1047
1168
|
clear() {
|
|
1048
|
-
if (this.disposed) throw
|
|
1169
|
+
if (this.disposed) throw this._disposedError();
|
|
1049
1170
|
const previousValue = this._value;
|
|
1050
1171
|
this._value = /* @__PURE__ */ new Map();
|
|
1051
1172
|
this.notifyDependents();
|
|
@@ -1061,11 +1182,11 @@ class MapNode extends ValueSyncNode {
|
|
|
1061
1182
|
}
|
|
1062
1183
|
}
|
|
1063
1184
|
|
|
1064
|
-
function map(initial) {
|
|
1185
|
+
function map(initial, options) {
|
|
1065
1186
|
if (initial instanceof Map) {
|
|
1066
|
-
return new MapNode(initial);
|
|
1187
|
+
return new MapNode(initial, options?.name);
|
|
1067
1188
|
}
|
|
1068
|
-
return new MapNode(new Map(initial ? Object.entries(initial) : []));
|
|
1189
|
+
return new MapNode(new Map(initial ? Object.entries(initial) : []), options?.name);
|
|
1069
1190
|
}
|
|
1070
1191
|
|
|
1071
1192
|
function subscribe(data, onData, onError, onPending) {
|
|
@@ -1074,30 +1195,30 @@ function subscribe(data, onData, onError, onPending) {
|
|
|
1074
1195
|
|
|
1075
1196
|
class SignalNode extends Observable {
|
|
1076
1197
|
subscribe(onTrigger, onError, onPending) {
|
|
1077
|
-
if (this.disposed) throw
|
|
1198
|
+
if (this.disposed) throw this._disposedError();
|
|
1078
1199
|
const effect = new EffectNode((t) => this.watch(t), onTrigger, onError, onPending);
|
|
1079
1200
|
return effect;
|
|
1080
1201
|
}
|
|
1081
1202
|
}
|
|
1082
1203
|
|
|
1083
|
-
function signal() {
|
|
1084
|
-
return new SignalNode();
|
|
1204
|
+
function signal(options) {
|
|
1205
|
+
return new SignalNode(options?.name);
|
|
1085
1206
|
}
|
|
1086
1207
|
|
|
1087
|
-
function constant(initializer) {
|
|
1088
|
-
return new ValueSyncNode(initializer);
|
|
1208
|
+
function constant(initializer, options) {
|
|
1209
|
+
return new ValueSyncNode(initializer, options?.name);
|
|
1089
1210
|
}
|
|
1090
1211
|
|
|
1091
|
-
function derivation(compute) {
|
|
1092
|
-
return new ValueSyncNode(compute);
|
|
1212
|
+
function derivation(compute, options) {
|
|
1213
|
+
return new ValueSyncNode(compute, options?.name);
|
|
1093
1214
|
}
|
|
1094
1215
|
|
|
1095
|
-
function state(valueOrInitializer) {
|
|
1096
|
-
return new ValueSyncNode(valueOrInitializer);
|
|
1216
|
+
function state(valueOrInitializer, options) {
|
|
1217
|
+
return new ValueSyncNode(valueOrInitializer, options?.name);
|
|
1097
1218
|
}
|
|
1098
1219
|
|
|
1099
|
-
function writableDerivation(compute) {
|
|
1100
|
-
return new ValueSyncNode(compute);
|
|
1220
|
+
function writableDerivation(compute, options) {
|
|
1221
|
+
return new ValueSyncNode(compute, options?.name);
|
|
1101
1222
|
}
|
|
1102
1223
|
|
|
1103
1224
|
function describeFlowInput(value) {
|
|
@@ -1160,4 +1281,4 @@ function from(flow) {
|
|
|
1160
1281
|
throw new Error(`[PicoFlow] from(): expected a FlowValue or getter function, received ${describeFlowInput(flow)}`);
|
|
1161
1282
|
}
|
|
1162
1283
|
|
|
1163
|
-
export { array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, onFlushError, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|
|
1284
|
+
export { PicoFlowCycleError, PicoFlowDisposedError, PicoFlowError, PicoFlowInternalError, array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, onFlushError, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|