@ersbeth/picoflow 2.3.1 → 3.0.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 +37 -12
- package/SKILL.md +130 -16
- package/dist/index.d.ts +767 -0
- package/dist/index.js +544 -0
- package/dist/inspect.d.ts +252 -0
- package/dist/inspect.js +300 -0
- package/dist/shared.d.ts +361 -0
- package/dist/shared.js +1496 -0
- package/dist/shared2.d.ts +93 -0
- package/dist/shared2.js +59 -0
- package/dist/shared3.d.ts +54 -0
- package/dist/shared3.js +17 -0
- package/dist/{types/converters/solid.d.ts → solid.d.ts} +10 -8
- package/dist/solid.js +63 -0
- package/package.json +38 -34
- package/dist/picoflow.js +0 -1283
- package/dist/types/api/base/flowConfig.d.ts +0 -17
- package/dist/types/api/base/flowDisposable.d.ts +0 -40
- package/dist/types/api/base/flowErrors.d.ts +0 -1
- package/dist/types/api/base/flowObservable.d.ts +0 -26
- package/dist/types/api/base/flowPrimitiveOptions.d.ts +0 -14
- package/dist/types/api/base/flowSubscribable.d.ts +0 -78
- package/dist/types/api/base/flowTracker.d.ts +0 -7
- package/dist/types/api/base/index.d.ts +0 -7
- package/dist/types/api/index.d.ts +0 -2
- package/dist/types/api/nodes/async/flowConstantAsync.d.ts +0 -32
- package/dist/types/api/nodes/async/flowDerivationAsync.d.ts +0 -37
- package/dist/types/api/nodes/async/flowStateAsync.d.ts +0 -42
- package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts +0 -31
- package/dist/types/api/nodes/async/index.d.ts +0 -4
- package/dist/types/api/nodes/collections/flowArray.d.ts +0 -138
- package/dist/types/api/nodes/collections/flowMap.d.ts +0 -100
- package/dist/types/api/nodes/collections/index.d.ts +0 -2
- package/dist/types/api/nodes/flowEffect.d.ts +0 -28
- package/dist/types/api/nodes/flowSignal.d.ts +0 -26
- package/dist/types/api/nodes/flowValue.d.ts +0 -35
- package/dist/types/api/nodes/index.d.ts +0 -7
- package/dist/types/api/nodes/sync/flowConstant.d.ts +0 -30
- package/dist/types/api/nodes/sync/flowDerivation.d.ts +0 -37
- package/dist/types/api/nodes/sync/flowState.d.ts +0 -40
- package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts +0 -29
- package/dist/types/api/nodes/sync/index.d.ts +0 -4
- package/dist/types/api/nodes/utils.d.ts +0 -22
- package/dist/types/base/dependenciesRegistry.d.ts +0 -1
- package/dist/types/base/dependentsRegistry.d.ts +0 -1
- package/dist/types/base/disposable.d.ts +0 -15
- package/dist/types/base/errors.d.ts +0 -31
- package/dist/types/base/executionStack.d.ts +0 -31
- package/dist/types/base/index.d.ts +0 -8
- package/dist/types/base/node.d.ts +0 -27
- package/dist/types/base/observable.d.ts +0 -34
- package/dist/types/base/observer.d.ts +0 -33
- package/dist/types/converters/index.d.ts +0 -1
- package/dist/types/index.d.ts +0 -2
- package/dist/types/nodes/actionNode.d.ts +0 -1
- package/dist/types/nodes/arrayNode.d.ts +0 -1
- package/dist/types/nodes/effectNode.d.ts +0 -1
- package/dist/types/nodes/index.d.ts +0 -8
- package/dist/types/nodes/mapNode.d.ts +0 -1
- package/dist/types/nodes/signalNode.d.ts +0 -1
- package/dist/types/nodes/valueAsyncNode.d.ts +0 -1
- package/dist/types/nodes/valueNode.d.ts +0 -1
- package/dist/types/nodes/valueSyncNode.d.ts +0 -1
- package/dist/types/schedulers/asyncResolver.d.ts +0 -1
- package/dist/types/schedulers/asyncScheduler.d.ts +0 -1
- package/dist/types/schedulers/index.d.ts +0 -4
- package/dist/types/schedulers/pendingError.d.ts +0 -1
- package/dist/types/schedulers/scheduler.d.ts +0 -1
- package/dist/types/schedulers/syncResolver.d.ts +0 -1
- package/dist/types/schedulers/syncScheduler.d.ts +0 -1
package/dist/picoflow.js
DELETED
|
@@ -1,1283 +0,0 @@
|
|
|
1
|
-
import { createResource, onMount, createEffect, resetErrorBoundaries, onCleanup } from 'solid-js';
|
|
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
|
-
|
|
67
|
-
class Disposable {
|
|
68
|
-
_disposed = false;
|
|
69
|
-
_name;
|
|
70
|
-
constructor(name) {
|
|
71
|
-
this._name = name;
|
|
72
|
-
}
|
|
73
|
-
get disposed() {
|
|
74
|
-
return this._disposed;
|
|
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
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
class ExecutionStack {
|
|
85
|
-
static _MAX_FLUSH_STEPS = 1e4;
|
|
86
|
-
static _pendingQueue = [];
|
|
87
|
-
static _effectQueue = [];
|
|
88
|
-
static _executionScheduled;
|
|
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();
|
|
93
|
-
/** Routes any error escaping the flush loop; defaults to logging rather than crashing (A2-SEM-2, A2-SEC-1). */
|
|
94
|
-
static _onFlushError = (error) => console.error(error);
|
|
95
|
-
/**
|
|
96
|
-
* True once the current synchronous call stack has finished and microtasks have started.
|
|
97
|
-
* EffectNode uses this to treat microtask notifications (e.g. async resolve) differently
|
|
98
|
-
* from coalesced sync-batch notifications.
|
|
99
|
-
*/
|
|
100
|
-
static _pastSyncTurn = false;
|
|
101
|
-
/** @internal Used by EffectNode to distinguish sync-batch from microtask notifications. */
|
|
102
|
-
static get pastSyncTurn() {
|
|
103
|
-
return ExecutionStack._pastSyncTurn;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* @internal Runs after the current sync turn ends (queueMicrotask).
|
|
107
|
-
* Marks pastSyncTurn so the next coalesced effect notify can bump its epoch,
|
|
108
|
-
* and resets per-effect coalesce flags via onReset.
|
|
109
|
-
*/
|
|
110
|
-
static scheduleCoalesceReset(onReset) {
|
|
111
|
-
ExecutionStack._coalesceResets.add(onReset);
|
|
112
|
-
if (ExecutionStack._coalesceResetScheduled) return;
|
|
113
|
-
ExecutionStack._coalesceResetScheduled = true;
|
|
114
|
-
queueMicrotask(() => {
|
|
115
|
-
ExecutionStack._pastSyncTurn = true;
|
|
116
|
-
ExecutionStack._coalesceResetScheduled = false;
|
|
117
|
-
const resets = ExecutionStack._coalesceResets;
|
|
118
|
-
ExecutionStack._coalesceResets = /* @__PURE__ */ new Set();
|
|
119
|
-
resets.forEach((reset) => {
|
|
120
|
-
reset();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/** @internal Wired to the public `onFlushError()` API in `src/api/base/flowConfig.ts`. */
|
|
125
|
-
static setFlushErrorHandler(handler) {
|
|
126
|
-
ExecutionStack._onFlushError = handler;
|
|
127
|
-
}
|
|
128
|
-
static pushPending(node) {
|
|
129
|
-
ExecutionStack._beginSyncTurn();
|
|
130
|
-
ExecutionStack._scheduleExecution();
|
|
131
|
-
ExecutionStack._pendingQueue.push(node);
|
|
132
|
-
}
|
|
133
|
-
static pushEffect(effect) {
|
|
134
|
-
ExecutionStack._beginSyncTurn();
|
|
135
|
-
ExecutionStack._scheduleExecution();
|
|
136
|
-
ExecutionStack._effectQueue.push(effect);
|
|
137
|
-
}
|
|
138
|
-
static _beginSyncTurn() {
|
|
139
|
-
ExecutionStack._pastSyncTurn = false;
|
|
140
|
-
}
|
|
141
|
-
static _scheduleExecution() {
|
|
142
|
-
if (ExecutionStack._executionScheduled) return;
|
|
143
|
-
ExecutionStack._executionScheduled = new Promise((resolve) => {
|
|
144
|
-
setTimeout(() => {
|
|
145
|
-
ExecutionStack._executionScheduled = void 0;
|
|
146
|
-
ExecutionStack._execute();
|
|
147
|
-
resolve();
|
|
148
|
-
}, 0);
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
static _drain(queue) {
|
|
152
|
-
for (let i = 0; i < queue.length; i++) {
|
|
153
|
-
if (i >= ExecutionStack._MAX_FLUSH_STEPS) {
|
|
154
|
-
ExecutionStack._pendingQueue.length = 0;
|
|
155
|
-
ExecutionStack._effectQueue.length = 0;
|
|
156
|
-
ExecutionStack._onFlushError(
|
|
157
|
-
new PicoFlowCycleError("[PicoFlow] Reactive update cycle exceeded maximum depth")
|
|
158
|
-
);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
const node = queue[i];
|
|
162
|
-
try {
|
|
163
|
-
node?.execute();
|
|
164
|
-
} catch (error) {
|
|
165
|
-
ExecutionStack._onFlushError(error);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
queue.length = 0;
|
|
169
|
-
}
|
|
170
|
-
static _execute() {
|
|
171
|
-
ExecutionStack._drain(ExecutionStack._pendingQueue);
|
|
172
|
-
ExecutionStack._drain(ExecutionStack._effectQueue);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
class Node extends Disposable {
|
|
177
|
-
_dependenciesRegistry = new DependenciesRegistry();
|
|
178
|
-
_dependentsRegistry = new DependentsRegistry();
|
|
179
|
-
_status = "resolved";
|
|
180
|
-
get status() {
|
|
181
|
-
if (this.disposed) throw this._disposedError();
|
|
182
|
-
return this._status;
|
|
183
|
-
}
|
|
184
|
-
set status(status) {
|
|
185
|
-
if (this.disposed) throw this._disposedError();
|
|
186
|
-
this._status = status;
|
|
187
|
-
}
|
|
188
|
-
registerDependency(dependency) {
|
|
189
|
-
if (this._disposed) throw this._disposedError();
|
|
190
|
-
this._dependenciesRegistry.register(dependency, this);
|
|
191
|
-
}
|
|
192
|
-
unregisterDependency(dependency) {
|
|
193
|
-
if (this._disposed) throw this._disposedError();
|
|
194
|
-
this._dependenciesRegistry.unregister(dependency, this);
|
|
195
|
-
}
|
|
196
|
-
clearDependencies() {
|
|
197
|
-
if (this.disposed) throw this._disposedError();
|
|
198
|
-
this._dependenciesRegistry.clear(this);
|
|
199
|
-
}
|
|
200
|
-
registerDependent(dependent) {
|
|
201
|
-
if (this.disposed) throw this._disposedError();
|
|
202
|
-
this._dependentsRegistry.register(dependent);
|
|
203
|
-
}
|
|
204
|
-
unregisterDependent(dependent) {
|
|
205
|
-
if (this.disposed) throw this._disposedError();
|
|
206
|
-
this._dependentsRegistry.unregister(dependent);
|
|
207
|
-
}
|
|
208
|
-
markDependencyDisposed() {
|
|
209
|
-
}
|
|
210
|
-
notifyDependents() {
|
|
211
|
-
if (this.disposed) return;
|
|
212
|
-
this._dependentsRegistry.notifyAll();
|
|
213
|
-
}
|
|
214
|
-
watch(tracker) {
|
|
215
|
-
if (this.disposed) throw this._disposedError();
|
|
216
|
-
tracker.registerDependency(this);
|
|
217
|
-
}
|
|
218
|
-
trigger() {
|
|
219
|
-
if (this.disposed) throw this._disposedError();
|
|
220
|
-
this.notifyDependents();
|
|
221
|
-
}
|
|
222
|
-
dispose() {
|
|
223
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
224
|
-
this._dependentsRegistry.forEach((dependant) => {
|
|
225
|
-
dependant.markDependencyDisposed();
|
|
226
|
-
dependant.unregisterDependency(this);
|
|
227
|
-
this.unregisterDependent(dependant);
|
|
228
|
-
});
|
|
229
|
-
this._dependenciesRegistry.forEach((dependency) => {
|
|
230
|
-
this.unregisterDependency(dependency);
|
|
231
|
-
});
|
|
232
|
-
this._disposed = true;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
class Observable extends Disposable {
|
|
237
|
-
_dependentsRegistry = new DependentsRegistry();
|
|
238
|
-
_status = "resolved";
|
|
239
|
-
get status() {
|
|
240
|
-
if (this.disposed) throw this._disposedError();
|
|
241
|
-
return this._status;
|
|
242
|
-
}
|
|
243
|
-
set status(status) {
|
|
244
|
-
if (this.disposed) throw this._disposedError();
|
|
245
|
-
this._status = status;
|
|
246
|
-
}
|
|
247
|
-
registerDependent(dependent) {
|
|
248
|
-
if (this.disposed) throw this._disposedError();
|
|
249
|
-
this._dependentsRegistry.register(dependent);
|
|
250
|
-
}
|
|
251
|
-
unregisterDependent(dependent) {
|
|
252
|
-
if (this.disposed) throw this._disposedError();
|
|
253
|
-
this._dependentsRegistry.unregister(dependent);
|
|
254
|
-
}
|
|
255
|
-
notifyDependents() {
|
|
256
|
-
if (this.disposed) return;
|
|
257
|
-
this._dependentsRegistry.notifyAll();
|
|
258
|
-
}
|
|
259
|
-
watch(tracker) {
|
|
260
|
-
if (this.disposed) throw this._disposedError();
|
|
261
|
-
tracker.registerDependency(this);
|
|
262
|
-
}
|
|
263
|
-
trigger() {
|
|
264
|
-
if (this.disposed) throw this._disposedError();
|
|
265
|
-
this.notifyDependents();
|
|
266
|
-
}
|
|
267
|
-
dispose() {
|
|
268
|
-
if (this._disposed) throw this._disposedError();
|
|
269
|
-
this._dependentsRegistry.forEach((dependant) => {
|
|
270
|
-
dependant.markDependencyDisposed();
|
|
271
|
-
dependant.unregisterDependency(this);
|
|
272
|
-
this.unregisterDependent(dependant);
|
|
273
|
-
});
|
|
274
|
-
this._disposed = true;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
class Observer extends Disposable {
|
|
279
|
-
_dependenciesRegistry = new DependenciesRegistry();
|
|
280
|
-
/** Set when a dependency disposed while this observer was queued; consumed by the next execute(). */
|
|
281
|
-
_dependencyDisposed = false;
|
|
282
|
-
markDependencyDisposed() {
|
|
283
|
-
this._dependencyDisposed = true;
|
|
284
|
-
}
|
|
285
|
-
registerDependency(dependency) {
|
|
286
|
-
if (this._disposed) throw this._disposedError();
|
|
287
|
-
this._dependenciesRegistry.register(dependency, this);
|
|
288
|
-
}
|
|
289
|
-
unregisterDependency(dependency) {
|
|
290
|
-
if (this._disposed) throw this._disposedError();
|
|
291
|
-
this._dependenciesRegistry.unregister(dependency, this);
|
|
292
|
-
}
|
|
293
|
-
clearDependencies() {
|
|
294
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
295
|
-
this._dependenciesRegistry.clear(this);
|
|
296
|
-
}
|
|
297
|
-
dispose() {
|
|
298
|
-
if (this._disposed) throw this._disposedError();
|
|
299
|
-
this._dependenciesRegistry.clear(this);
|
|
300
|
-
this._disposed = true;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function onFlushError(handler) {
|
|
305
|
-
ExecutionStack.setFlushErrorHandler(handler);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function isDisposable(obj) {
|
|
309
|
-
return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
class PendingError extends Error {
|
|
313
|
-
pendingPromise;
|
|
314
|
-
constructor(promise) {
|
|
315
|
-
super("[PicoFlow] Computation is pending");
|
|
316
|
-
this.name = "PendingError";
|
|
317
|
-
this.pendingPromise = promise;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
class AsyncResolver {
|
|
322
|
-
_compute;
|
|
323
|
-
_computed = Promise.withResolvers();
|
|
324
|
-
_iteration = 0;
|
|
325
|
-
_aborted = false;
|
|
326
|
-
_finished = false;
|
|
327
|
-
constructor(compute) {
|
|
328
|
-
this._compute = compute;
|
|
329
|
-
this._computed.promise.catch(() => {
|
|
330
|
-
}).finally(() => {
|
|
331
|
-
this._finished = true;
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
get computed() {
|
|
335
|
-
return this._computed.promise;
|
|
336
|
-
}
|
|
337
|
-
get aborted() {
|
|
338
|
-
return this._aborted;
|
|
339
|
-
}
|
|
340
|
-
get finished() {
|
|
341
|
-
return this._finished;
|
|
342
|
-
}
|
|
343
|
-
compute() {
|
|
344
|
-
if (this._finished) throw new Error("[PicoFlow] AsyncResolver: Can't restart a settled resolver");
|
|
345
|
-
if (this._aborted) throw new Error("[PicoFlow] AsyncResolver: Can't restart an aborted resolver");
|
|
346
|
-
this._iteration++;
|
|
347
|
-
const currentIteration = this._iteration;
|
|
348
|
-
this._compute().then((value) => {
|
|
349
|
-
if (this._iteration === currentIteration && !this._aborted) this._computed.resolve(value);
|
|
350
|
-
}).catch((error) => {
|
|
351
|
-
if (this._iteration === currentIteration && !this._aborted) {
|
|
352
|
-
if (!(error instanceof PendingError)) {
|
|
353
|
-
this._computed.reject(error);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
overwrite(promise) {
|
|
359
|
-
if (this._finished) throw new Error("[PicoFlow] AsyncResolver: Can't overwrite a settled resolver");
|
|
360
|
-
if (this._aborted) throw new Error("[PicoFlow] AsyncResolver: Can't overwrite an aborted resolver");
|
|
361
|
-
this._iteration++;
|
|
362
|
-
const currentIteration = this._iteration;
|
|
363
|
-
promise.then((value) => {
|
|
364
|
-
if (this._iteration === currentIteration && !this._aborted) this._computed.resolve(value);
|
|
365
|
-
}).catch((error) => {
|
|
366
|
-
if (this._iteration === currentIteration && !this._aborted) {
|
|
367
|
-
this._computed.reject(error);
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
abort() {
|
|
372
|
-
this._aborted = true;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
class AsyncScheduler {
|
|
377
|
-
_compute;
|
|
378
|
-
_resolver;
|
|
379
|
-
_onResolve;
|
|
380
|
-
_onReject;
|
|
381
|
-
_settled = Promise.withResolvers();
|
|
382
|
-
_disposed = false;
|
|
383
|
-
constructor(compute, onResolve, onReject) {
|
|
384
|
-
this._compute = compute;
|
|
385
|
-
this._onResolve = onResolve;
|
|
386
|
-
this._onReject = onReject;
|
|
387
|
-
this._resolver = new AsyncResolver(compute);
|
|
388
|
-
this._attachHandlers(this._resolver);
|
|
389
|
-
}
|
|
390
|
-
get settled() {
|
|
391
|
-
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
392
|
-
return this._settled.promise;
|
|
393
|
-
}
|
|
394
|
-
get disposed() {
|
|
395
|
-
return this._disposed;
|
|
396
|
-
}
|
|
397
|
-
overwrite(promise) {
|
|
398
|
-
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
399
|
-
if (this._resolver.finished) {
|
|
400
|
-
this._resolver = new AsyncResolver(this._compute);
|
|
401
|
-
this._settled = Promise.withResolvers();
|
|
402
|
-
this._attachHandlers(this._resolver);
|
|
403
|
-
}
|
|
404
|
-
this._resolver.overwrite(promise);
|
|
405
|
-
}
|
|
406
|
-
compute() {
|
|
407
|
-
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
408
|
-
if (this._resolver.finished) {
|
|
409
|
-
this._resolver = new AsyncResolver(this._compute);
|
|
410
|
-
this._settled = Promise.withResolvers();
|
|
411
|
-
this._attachHandlers(this._resolver);
|
|
412
|
-
}
|
|
413
|
-
this._resolver.compute();
|
|
414
|
-
}
|
|
415
|
-
_attachHandlers(resolver) {
|
|
416
|
-
resolver.computed.then((value) => {
|
|
417
|
-
if (this._disposed) return;
|
|
418
|
-
this._onResolve(value);
|
|
419
|
-
}).catch((error) => {
|
|
420
|
-
if (this._disposed) return;
|
|
421
|
-
this._onReject(error);
|
|
422
|
-
}).finally(() => this._settled.resolve());
|
|
423
|
-
}
|
|
424
|
-
dispose() {
|
|
425
|
-
this._resolver.abort();
|
|
426
|
-
this._disposed = true;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
class SyncResolver {
|
|
431
|
-
_compute;
|
|
432
|
-
_aborted = false;
|
|
433
|
-
_finished = false;
|
|
434
|
-
_onValue;
|
|
435
|
-
_onError;
|
|
436
|
-
constructor(compute, onValue, onError) {
|
|
437
|
-
this._compute = compute;
|
|
438
|
-
this._onValue = onValue;
|
|
439
|
-
this._onError = onError;
|
|
440
|
-
}
|
|
441
|
-
get aborted() {
|
|
442
|
-
return this._aborted;
|
|
443
|
-
}
|
|
444
|
-
get finished() {
|
|
445
|
-
return this._finished;
|
|
446
|
-
}
|
|
447
|
-
compute() {
|
|
448
|
-
if (this._finished) throw new Error("[PicoFlow] SyncResolver: Can't restart a settled resolver");
|
|
449
|
-
if (this._aborted) throw new Error("[PicoFlow] SyncResolver: Can't restart an aborted resolver");
|
|
450
|
-
try {
|
|
451
|
-
const value = this._compute();
|
|
452
|
-
if (!this._finished && !this._aborted) {
|
|
453
|
-
this._finished = true;
|
|
454
|
-
this._onValue(value);
|
|
455
|
-
}
|
|
456
|
-
} catch (error) {
|
|
457
|
-
if (!this._finished && !this._aborted) {
|
|
458
|
-
if (!(error instanceof PendingError)) {
|
|
459
|
-
this._finished = true;
|
|
460
|
-
this._onError(error);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
overwrite(value) {
|
|
466
|
-
if (this._finished) throw new Error("[PicoFlow] SyncResolver: Can't overwrite a settled resolver");
|
|
467
|
-
if (this._aborted) throw new Error("[PicoFlow] SyncResolver: Can't overwrite an aborted resolver");
|
|
468
|
-
this._finished = true;
|
|
469
|
-
this._onValue(value);
|
|
470
|
-
}
|
|
471
|
-
abort() {
|
|
472
|
-
this._aborted = true;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
class SyncScheduler {
|
|
477
|
-
_compute;
|
|
478
|
-
_resolver;
|
|
479
|
-
_onResolve;
|
|
480
|
-
_onReject;
|
|
481
|
-
_settled = Promise.withResolvers();
|
|
482
|
-
_disposed = false;
|
|
483
|
-
constructor(compute, onResolve, onReject) {
|
|
484
|
-
this._compute = compute;
|
|
485
|
-
this._onResolve = (value) => {
|
|
486
|
-
onResolve(value);
|
|
487
|
-
this._settled.resolve();
|
|
488
|
-
};
|
|
489
|
-
this._onReject = (error) => {
|
|
490
|
-
onReject(error);
|
|
491
|
-
this._settled.resolve();
|
|
492
|
-
};
|
|
493
|
-
this._resolver = new SyncResolver(compute, this._onResolve, this._onReject);
|
|
494
|
-
}
|
|
495
|
-
get settled() {
|
|
496
|
-
return this._settled.promise;
|
|
497
|
-
}
|
|
498
|
-
overwrite(value) {
|
|
499
|
-
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
500
|
-
if (this._resolver.finished) {
|
|
501
|
-
this._settled = Promise.withResolvers();
|
|
502
|
-
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
503
|
-
}
|
|
504
|
-
this._resolver.overwrite(value);
|
|
505
|
-
}
|
|
506
|
-
compute() {
|
|
507
|
-
if (this._disposed) throw new PicoFlowDisposedError("[PicoFlow] ComputationScheduler is disposed");
|
|
508
|
-
if (this._resolver.finished) {
|
|
509
|
-
this._settled = Promise.withResolvers();
|
|
510
|
-
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
511
|
-
}
|
|
512
|
-
this._resolver.compute();
|
|
513
|
-
}
|
|
514
|
-
dispose() {
|
|
515
|
-
this._resolver.abort();
|
|
516
|
-
this._disposed = true;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
class EffectNode extends Observer {
|
|
521
|
-
_data;
|
|
522
|
-
_onData;
|
|
523
|
-
_onError;
|
|
524
|
-
_onPending;
|
|
525
|
-
/** True while this effect is already waiting in the effect queue for the next flush. */
|
|
526
|
-
_queued = false;
|
|
527
|
-
/** Guards the single coalesced bump of _notifyEpoch while _queued during one sync turn. */
|
|
528
|
-
_coalescedWhileQueued = false;
|
|
529
|
-
/** Monotonic count of notifications that must eventually be executed. */
|
|
530
|
-
_notifyEpoch = 0;
|
|
531
|
-
/** How many notification epochs have been executed so far. */
|
|
532
|
-
_executedEpoch = 0;
|
|
533
|
-
/** Callbacks registered via {@link onDispose}, run once after dispose() tears down the graph edges. */
|
|
534
|
-
_disposeListeners = [];
|
|
535
|
-
constructor(data, onData, onError, onPending) {
|
|
536
|
-
super();
|
|
537
|
-
this._data = data;
|
|
538
|
-
this._onData = onData;
|
|
539
|
-
this._onError = onError;
|
|
540
|
-
this._onPending = onPending;
|
|
541
|
-
this._notifyEpoch = 1;
|
|
542
|
-
this.execute();
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Registers a callback to run once this effect is disposed, after the graph edges are torn
|
|
546
|
-
* down. Lets a caller that wraps an effect (e.g. {@link ActionNode.subscribe}) attach its own
|
|
547
|
-
* teardown without patching `dispose` on the returned instance (A2-ARCH-8).
|
|
548
|
-
* @internal
|
|
549
|
-
*/
|
|
550
|
-
onDispose(fn) {
|
|
551
|
-
this._disposeListeners.push(fn);
|
|
552
|
-
}
|
|
553
|
-
dispose() {
|
|
554
|
-
super.dispose();
|
|
555
|
-
this._disposeListeners.forEach((fn) => {
|
|
556
|
-
fn();
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
notify() {
|
|
560
|
-
if (this._disposed) throw this._disposedError();
|
|
561
|
-
if (this._queued) {
|
|
562
|
-
if (!this._coalescedWhileQueued) {
|
|
563
|
-
this._coalescedWhileQueued = true;
|
|
564
|
-
if (ExecutionStack.pastSyncTurn) {
|
|
565
|
-
this._notifyEpoch++;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
this._notifyEpoch++;
|
|
571
|
-
this._queued = true;
|
|
572
|
-
ExecutionStack.scheduleCoalesceReset(() => {
|
|
573
|
-
this._coalescedWhileQueued = false;
|
|
574
|
-
});
|
|
575
|
-
ExecutionStack.pushEffect(this);
|
|
576
|
-
}
|
|
577
|
-
execute() {
|
|
578
|
-
this._queued = false;
|
|
579
|
-
if (this._disposed) {
|
|
580
|
-
this._executedEpoch = this._notifyEpoch;
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
if (this._dependencyDisposed) {
|
|
584
|
-
this._dependencyDisposed = false;
|
|
585
|
-
this._executedEpoch = this._notifyEpoch;
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
const targetEpoch = this._notifyEpoch;
|
|
589
|
-
while (this._executedEpoch < targetEpoch) {
|
|
590
|
-
this._executedEpoch++;
|
|
591
|
-
if (this._disposed) {
|
|
592
|
-
this._executedEpoch = this._notifyEpoch;
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
if (this._dependencyDisposed) {
|
|
596
|
-
this._dependencyDisposed = false;
|
|
597
|
-
this._executedEpoch = this._notifyEpoch;
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
try {
|
|
601
|
-
this.clearDependencies();
|
|
602
|
-
const data = this._data(this);
|
|
603
|
-
this._onData(data);
|
|
604
|
-
} catch (error) {
|
|
605
|
-
if (error instanceof PendingError) {
|
|
606
|
-
this._onPending?.();
|
|
607
|
-
} else {
|
|
608
|
-
if (this._onError) {
|
|
609
|
-
this._onError(error instanceof Error ? error : new Error(String(error)));
|
|
610
|
-
} else {
|
|
611
|
-
throw error;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
const PICOFLOW_VALUE_NODE = /* @__PURE__ */ Symbol.for("picoflow.node");
|
|
620
|
-
class ValueNode extends Node {
|
|
621
|
-
[PICOFLOW_VALUE_NODE] = true;
|
|
622
|
-
_value;
|
|
623
|
-
_error;
|
|
624
|
-
/** Builds the "unreachable state" error, naming the primitive when a `name` was given at creation. */
|
|
625
|
-
_internalError() {
|
|
626
|
-
return new PicoFlowInternalError(
|
|
627
|
-
this._name ? `[PicoFlow] Internal error (${this._name})` : "[PicoFlow] Internal error"
|
|
628
|
-
);
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Hook called just before a tracked read throws `PendingError`, i.e. a dependent registered while
|
|
632
|
-
* this node is mid-flight and has no other way to learn the computation later settled (A2-PERF-4).
|
|
633
|
-
* No-op by default; overridden by {@link ValueAsyncNode} to know when its own post-resolve
|
|
634
|
-
* `notifyDependents()` call cannot be skipped even if the resolved value is unchanged.
|
|
635
|
-
*/
|
|
636
|
-
_markPendingRead() {
|
|
637
|
-
}
|
|
638
|
-
watch(tracker) {
|
|
639
|
-
if (this.disposed) throw this._disposedError();
|
|
640
|
-
super.watch(tracker);
|
|
641
|
-
switch (this.status) {
|
|
642
|
-
case "resolved":
|
|
643
|
-
return;
|
|
644
|
-
case "error":
|
|
645
|
-
throw this._error;
|
|
646
|
-
case "pending": {
|
|
647
|
-
this._markPendingRead();
|
|
648
|
-
throw new PendingError(this._scheduler.settled);
|
|
649
|
-
}
|
|
650
|
-
case "dirty": {
|
|
651
|
-
this.execute();
|
|
652
|
-
switch (this.status) {
|
|
653
|
-
case "pending":
|
|
654
|
-
this._markPendingRead();
|
|
655
|
-
throw new PendingError(this._scheduler.settled);
|
|
656
|
-
case "resolved":
|
|
657
|
-
return;
|
|
658
|
-
case "error":
|
|
659
|
-
throw this._error;
|
|
660
|
-
case "dirty": {
|
|
661
|
-
throw this._internalError();
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
notify() {
|
|
668
|
-
if (this.disposed) throw this._disposedError();
|
|
669
|
-
if (this.status === "dirty") return;
|
|
670
|
-
if (this.status === "pending") {
|
|
671
|
-
ExecutionStack.pushPending(this);
|
|
672
|
-
}
|
|
673
|
-
this.status = "dirty";
|
|
674
|
-
this.notifyDependents();
|
|
675
|
-
}
|
|
676
|
-
dispose() {
|
|
677
|
-
super.dispose();
|
|
678
|
-
this._scheduler.dispose();
|
|
679
|
-
}
|
|
680
|
-
get(tracker) {
|
|
681
|
-
if (this.disposed) throw this._disposedError();
|
|
682
|
-
super.watch(tracker);
|
|
683
|
-
switch (this.status) {
|
|
684
|
-
case "resolved":
|
|
685
|
-
return this._value;
|
|
686
|
-
case "error":
|
|
687
|
-
throw this._error;
|
|
688
|
-
case "pending": {
|
|
689
|
-
this._markPendingRead();
|
|
690
|
-
throw new PendingError(this._scheduler.settled);
|
|
691
|
-
}
|
|
692
|
-
case "dirty": {
|
|
693
|
-
this.execute();
|
|
694
|
-
switch (this.status) {
|
|
695
|
-
case "resolved":
|
|
696
|
-
return this._value;
|
|
697
|
-
case "error":
|
|
698
|
-
throw this._error;
|
|
699
|
-
case "pending": {
|
|
700
|
-
this._markPendingRead();
|
|
701
|
-
throw new PendingError(this._scheduler.settled);
|
|
702
|
-
}
|
|
703
|
-
case "dirty": {
|
|
704
|
-
throw this._internalError();
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
async pick() {
|
|
711
|
-
if (this.disposed) throw this._disposedError();
|
|
712
|
-
switch (this.status) {
|
|
713
|
-
case "resolved":
|
|
714
|
-
return this._value;
|
|
715
|
-
case "pending": {
|
|
716
|
-
await this._scheduler.settled;
|
|
717
|
-
if (this.status === "resolved") return this._value;
|
|
718
|
-
if (this.status === "error") throw this._error;
|
|
719
|
-
throw this._internalError();
|
|
720
|
-
}
|
|
721
|
-
case "error":
|
|
722
|
-
throw this._error;
|
|
723
|
-
case "dirty": {
|
|
724
|
-
this.execute();
|
|
725
|
-
switch (this.status) {
|
|
726
|
-
case "resolved":
|
|
727
|
-
return this._value;
|
|
728
|
-
case "pending": {
|
|
729
|
-
await this._scheduler.settled;
|
|
730
|
-
if (this.status === "resolved") return this._value;
|
|
731
|
-
if (this.status === "error") throw this._error;
|
|
732
|
-
throw this._internalError();
|
|
733
|
-
}
|
|
734
|
-
case "error":
|
|
735
|
-
throw this._error;
|
|
736
|
-
case "dirty": {
|
|
737
|
-
throw this._internalError();
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
execute() {
|
|
744
|
-
if (this.disposed) return;
|
|
745
|
-
this.clearDependencies();
|
|
746
|
-
this.status = "pending";
|
|
747
|
-
this._scheduler.compute();
|
|
748
|
-
}
|
|
749
|
-
subscribe(onValue, onError, onPending) {
|
|
750
|
-
if (this.disposed) throw this._disposedError();
|
|
751
|
-
const effect = new EffectNode((t) => this.get(t), onValue, onError, onPending);
|
|
752
|
-
return effect;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
class ValueAsyncNode extends ValueNode {
|
|
757
|
-
_scheduler;
|
|
758
|
-
_compute;
|
|
759
|
-
/** Set by {@link ValueNode._markPendingRead} when a dependent registers while this node is mid-flight. */
|
|
760
|
-
_hasPendingReader = false;
|
|
761
|
-
/**
|
|
762
|
-
* True only while the in-flight computation was started by `set()`, which already ran its own
|
|
763
|
-
* eager `notifyDependents()` cascade before this resolution. `refresh()` and a recompute pulled
|
|
764
|
-
* lazily after an upstream `notify()` have no such pre-cascade — for them, `_onResolve`'s own
|
|
765
|
-
* `notifyDependents()` call is the only notification, so it must never be skipped (A2-PERF-4).
|
|
766
|
-
*/
|
|
767
|
-
_pendingFromSet = false;
|
|
768
|
-
constructor(promiseOrCompute, name) {
|
|
769
|
-
super(name);
|
|
770
|
-
if (typeof promiseOrCompute === "function") {
|
|
771
|
-
this._compute = () => promiseOrCompute(this, this._value);
|
|
772
|
-
} else {
|
|
773
|
-
this._compute = () => promiseOrCompute;
|
|
774
|
-
}
|
|
775
|
-
this.status = "dirty";
|
|
776
|
-
this._scheduler = new AsyncScheduler(
|
|
777
|
-
() => this._compute(),
|
|
778
|
-
(value) => this._onResolve(value),
|
|
779
|
-
(error) => this._onReject(error)
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
_onResolve(value) {
|
|
783
|
-
if (this.disposed) return;
|
|
784
|
-
const changed = value !== this._value;
|
|
785
|
-
const hadPendingReader = this._hasPendingReader;
|
|
786
|
-
const pendingFromSet = this._pendingFromSet;
|
|
787
|
-
this._hasPendingReader = false;
|
|
788
|
-
this._pendingFromSet = false;
|
|
789
|
-
this.status = "resolved";
|
|
790
|
-
this._value = value;
|
|
791
|
-
this._error = void 0;
|
|
792
|
-
if (changed || hadPendingReader || !pendingFromSet) this.notifyDependents();
|
|
793
|
-
}
|
|
794
|
-
_markPendingRead() {
|
|
795
|
-
this._hasPendingReader = true;
|
|
796
|
-
}
|
|
797
|
-
_onReject(error) {
|
|
798
|
-
if (this.disposed) return;
|
|
799
|
-
this.status = "error";
|
|
800
|
-
this._error = error;
|
|
801
|
-
this.notifyDependents();
|
|
802
|
-
}
|
|
803
|
-
set(promiseOrUpdater) {
|
|
804
|
-
if (this.disposed) throw this._disposedError();
|
|
805
|
-
if (typeof promiseOrUpdater === "function") {
|
|
806
|
-
const updater = promiseOrUpdater;
|
|
807
|
-
switch (this.status) {
|
|
808
|
-
case "resolved": {
|
|
809
|
-
this.status = "pending";
|
|
810
|
-
this._pendingFromSet = true;
|
|
811
|
-
this._scheduler.overwrite(updater(this._value));
|
|
812
|
-
this.notifyDependents();
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
case "pending":
|
|
816
|
-
case "error":
|
|
817
|
-
case "dirty": {
|
|
818
|
-
this.status = "pending";
|
|
819
|
-
this._pendingFromSet = true;
|
|
820
|
-
this._scheduler.overwrite(updater(this._value));
|
|
821
|
-
this.notifyDependents();
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
} else {
|
|
826
|
-
this.status = "pending";
|
|
827
|
-
this._pendingFromSet = true;
|
|
828
|
-
const promise = promiseOrUpdater;
|
|
829
|
-
this._scheduler.overwrite(promise);
|
|
830
|
-
this.notifyDependents();
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
refresh() {
|
|
834
|
-
if (this.disposed) throw this._disposedError();
|
|
835
|
-
this.execute();
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
function constantAsync(valueOrInitializer, options) {
|
|
840
|
-
return new ValueAsyncNode(valueOrInitializer, options?.name);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
function derivationAsync(compute, options) {
|
|
844
|
-
return new ValueAsyncNode(compute, options?.name);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
function stateAsync(valueOrInitializer, options) {
|
|
848
|
-
return new ValueAsyncNode(valueOrInitializer, options?.name);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
function writableDerivationAsync(compute, options) {
|
|
852
|
-
return new ValueAsyncNode(compute, options?.name);
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
class ValueSyncNode extends ValueNode {
|
|
856
|
-
_scheduler;
|
|
857
|
-
_compute;
|
|
858
|
-
constructor(valueOrCompute, name) {
|
|
859
|
-
super(name);
|
|
860
|
-
if (typeof valueOrCompute === "function") {
|
|
861
|
-
this._compute = () => valueOrCompute(this, this._value);
|
|
862
|
-
} else {
|
|
863
|
-
this._compute = () => valueOrCompute;
|
|
864
|
-
}
|
|
865
|
-
this.status = "dirty";
|
|
866
|
-
this._scheduler = new SyncScheduler(
|
|
867
|
-
() => this._compute(),
|
|
868
|
-
(value) => this._onResolve(value),
|
|
869
|
-
(error) => this._onReject(error)
|
|
870
|
-
);
|
|
871
|
-
if (typeof valueOrCompute !== "function") {
|
|
872
|
-
this._scheduler.compute();
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
_onResolve(value) {
|
|
876
|
-
this.status = "resolved";
|
|
877
|
-
this._value = value;
|
|
878
|
-
this._error = void 0;
|
|
879
|
-
}
|
|
880
|
-
_onReject(error) {
|
|
881
|
-
this.status = "error";
|
|
882
|
-
this._error = error;
|
|
883
|
-
}
|
|
884
|
-
set(valueOrUpdater) {
|
|
885
|
-
if (this.disposed) throw this._disposedError();
|
|
886
|
-
if (typeof valueOrUpdater === "function") {
|
|
887
|
-
const updater = valueOrUpdater;
|
|
888
|
-
switch (this.status) {
|
|
889
|
-
case "resolved": {
|
|
890
|
-
const nextValue = updater(this._value);
|
|
891
|
-
if (nextValue === this._value) return;
|
|
892
|
-
this.status = "pending";
|
|
893
|
-
this._scheduler.overwrite(nextValue);
|
|
894
|
-
this.notifyDependents();
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
case "pending":
|
|
898
|
-
case "error":
|
|
899
|
-
case "dirty": {
|
|
900
|
-
const nextValue = updater(this._value);
|
|
901
|
-
this.status = "pending";
|
|
902
|
-
this._scheduler.overwrite(nextValue);
|
|
903
|
-
this.notifyDependents();
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
} else {
|
|
908
|
-
const nextValue = valueOrUpdater;
|
|
909
|
-
switch (this.status) {
|
|
910
|
-
case "resolved": {
|
|
911
|
-
if (nextValue === this._value) return;
|
|
912
|
-
this.status = "pending";
|
|
913
|
-
this._scheduler.overwrite(nextValue);
|
|
914
|
-
this.notifyDependents();
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
case "pending":
|
|
918
|
-
case "error":
|
|
919
|
-
case "dirty": {
|
|
920
|
-
this.status = "pending";
|
|
921
|
-
this._scheduler.overwrite(nextValue);
|
|
922
|
-
this.notifyDependents();
|
|
923
|
-
return;
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
refresh() {
|
|
929
|
-
if (this.disposed) throw this._disposedError();
|
|
930
|
-
const currentValue = this._value;
|
|
931
|
-
const currentStatus = this.status;
|
|
932
|
-
this.execute();
|
|
933
|
-
switch (this.status) {
|
|
934
|
-
case "resolved": {
|
|
935
|
-
const nextValue = this._value;
|
|
936
|
-
if (nextValue === currentValue && currentStatus === this.status) return;
|
|
937
|
-
this.notifyDependents();
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
case "error":
|
|
941
|
-
case "pending":
|
|
942
|
-
case "dirty": {
|
|
943
|
-
this.notifyDependents();
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
class ActionNode extends ValueSyncNode {
|
|
951
|
-
_slots = /* @__PURE__ */ new Set();
|
|
952
|
-
_enqueueForSubscribers(action) {
|
|
953
|
-
for (const slot of this._slots) {
|
|
954
|
-
slot.queue.push(action);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
set(valueOrUpdater) {
|
|
958
|
-
if (this.disposed) throw this._disposedError();
|
|
959
|
-
if (typeof valueOrUpdater === "function") {
|
|
960
|
-
const updater = valueOrUpdater;
|
|
961
|
-
const nextValue2 = updater(this._value);
|
|
962
|
-
if (this.status === "resolved" && nextValue2 === this._value) return;
|
|
963
|
-
this._enqueueForSubscribers(nextValue2);
|
|
964
|
-
super.set(updater);
|
|
965
|
-
return;
|
|
966
|
-
}
|
|
967
|
-
const nextValue = valueOrUpdater;
|
|
968
|
-
if (this.status === "resolved" && nextValue === this._value) return;
|
|
969
|
-
this._enqueueForSubscribers(nextValue);
|
|
970
|
-
super.set(nextValue);
|
|
971
|
-
}
|
|
972
|
-
subscribe(onValue, onError, onPending) {
|
|
973
|
-
if (this.disposed) throw this._disposedError();
|
|
974
|
-
const slot = { queue: [] };
|
|
975
|
-
this._slots.add(slot);
|
|
976
|
-
const effect = new EffectNode(
|
|
977
|
-
(tracker) => {
|
|
978
|
-
this.watch(tracker);
|
|
979
|
-
if (slot.queue.length === 0) {
|
|
980
|
-
return [this.get(tracker)];
|
|
981
|
-
}
|
|
982
|
-
return slot.queue.splice(0);
|
|
983
|
-
},
|
|
984
|
-
(actions) => {
|
|
985
|
-
for (const action of actions) {
|
|
986
|
-
onValue(action);
|
|
987
|
-
}
|
|
988
|
-
},
|
|
989
|
-
onError,
|
|
990
|
-
onPending
|
|
991
|
-
);
|
|
992
|
-
effect.onDispose(() => {
|
|
993
|
-
this._slots.delete(slot);
|
|
994
|
-
});
|
|
995
|
-
return effect;
|
|
996
|
-
}
|
|
997
|
-
dispose() {
|
|
998
|
-
this._slots.clear();
|
|
999
|
-
super.dispose();
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
class ArrayNode extends ValueSyncNode {
|
|
1004
|
-
$lastAction;
|
|
1005
|
-
constructor(value = [], name) {
|
|
1006
|
-
super(value, name);
|
|
1007
|
-
this.$lastAction = new ActionNode({
|
|
1008
|
-
type: "set",
|
|
1009
|
-
setItems: value,
|
|
1010
|
-
clearedItems: []
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
get(tracker) {
|
|
1014
|
-
const values = super.get(tracker);
|
|
1015
|
-
return [...values];
|
|
1016
|
-
}
|
|
1017
|
-
async pick() {
|
|
1018
|
-
const values = await super.pick();
|
|
1019
|
-
return [...values];
|
|
1020
|
-
}
|
|
1021
|
-
get length() {
|
|
1022
|
-
if (this.disposed) throw this._disposedError();
|
|
1023
|
-
return this._value.length;
|
|
1024
|
-
}
|
|
1025
|
-
set(itemsOrUpdater) {
|
|
1026
|
-
if (this.disposed) throw this._disposedError();
|
|
1027
|
-
const previousValue = this._value;
|
|
1028
|
-
super.set(itemsOrUpdater);
|
|
1029
|
-
this.$lastAction.set({
|
|
1030
|
-
type: "set",
|
|
1031
|
-
setItems: this._value,
|
|
1032
|
-
clearedItems: previousValue
|
|
1033
|
-
});
|
|
1034
|
-
return previousValue;
|
|
1035
|
-
}
|
|
1036
|
-
update(index, item) {
|
|
1037
|
-
if (this.disposed) throw this._disposedError();
|
|
1038
|
-
if (index < 0 || index >= this._value.length || !Number.isInteger(index)) {
|
|
1039
|
-
throw new Error("[PicoFlow] Index out of bounds");
|
|
1040
|
-
}
|
|
1041
|
-
const previousValue = this._value[index];
|
|
1042
|
-
this._value[index] = item;
|
|
1043
|
-
this.notifyDependents();
|
|
1044
|
-
this.$lastAction.set({
|
|
1045
|
-
type: "update",
|
|
1046
|
-
index,
|
|
1047
|
-
setItem: item,
|
|
1048
|
-
clearedItem: previousValue
|
|
1049
|
-
});
|
|
1050
|
-
return previousValue;
|
|
1051
|
-
}
|
|
1052
|
-
push(item) {
|
|
1053
|
-
if (this.disposed) throw this._disposedError();
|
|
1054
|
-
this._value.push(item);
|
|
1055
|
-
this.notifyDependents();
|
|
1056
|
-
this.$lastAction.set({ type: "push", addedItem: item });
|
|
1057
|
-
}
|
|
1058
|
-
pop() {
|
|
1059
|
-
if (this.disposed) throw this._disposedError();
|
|
1060
|
-
if (this._value.length === 0) return void 0;
|
|
1061
|
-
const item = this._value.pop();
|
|
1062
|
-
this.notifyDependents();
|
|
1063
|
-
this.$lastAction.set({ type: "pop", removedItem: item });
|
|
1064
|
-
return item;
|
|
1065
|
-
}
|
|
1066
|
-
unshift(item) {
|
|
1067
|
-
if (this.disposed) throw this._disposedError();
|
|
1068
|
-
this._value.unshift(item);
|
|
1069
|
-
this.notifyDependents();
|
|
1070
|
-
this.$lastAction.set({ type: "unshift", addedItem: item });
|
|
1071
|
-
}
|
|
1072
|
-
shift() {
|
|
1073
|
-
if (this.disposed) throw this._disposedError();
|
|
1074
|
-
if (this._value.length === 0) return void 0;
|
|
1075
|
-
const item = this._value.shift();
|
|
1076
|
-
this.notifyDependents();
|
|
1077
|
-
this.$lastAction.set({ type: "shift", removedItem: item });
|
|
1078
|
-
return item;
|
|
1079
|
-
}
|
|
1080
|
-
splice(start, deleteCount, ...newItems) {
|
|
1081
|
-
if (this.disposed) throw this._disposedError();
|
|
1082
|
-
const items = this._value.splice(start, deleteCount, ...newItems);
|
|
1083
|
-
this.notifyDependents();
|
|
1084
|
-
this.$lastAction.set({
|
|
1085
|
-
type: "splice",
|
|
1086
|
-
start,
|
|
1087
|
-
deleteCount,
|
|
1088
|
-
addedItems: newItems,
|
|
1089
|
-
removedItems: items
|
|
1090
|
-
});
|
|
1091
|
-
return items;
|
|
1092
|
-
}
|
|
1093
|
-
clear() {
|
|
1094
|
-
if (this.disposed) throw this._disposedError();
|
|
1095
|
-
const previousValue = [...this._value];
|
|
1096
|
-
this._value = [];
|
|
1097
|
-
this.notifyDependents();
|
|
1098
|
-
this.$lastAction.set({ type: "clear", clearedItems: previousValue });
|
|
1099
|
-
return previousValue;
|
|
1100
|
-
}
|
|
1101
|
-
dispose() {
|
|
1102
|
-
super.dispose();
|
|
1103
|
-
if (!this.$lastAction.disposed) {
|
|
1104
|
-
this.$lastAction.dispose();
|
|
1105
|
-
}
|
|
1106
|
-
this._value = [];
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
function array(initial, options) {
|
|
1111
|
-
return new ArrayNode(initial, options?.name);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
class MapNode extends ValueSyncNode {
|
|
1115
|
-
$lastAction;
|
|
1116
|
-
constructor(value = /* @__PURE__ */ new Map(), name) {
|
|
1117
|
-
super(value, name);
|
|
1118
|
-
this.$lastAction = new ActionNode({
|
|
1119
|
-
type: "set",
|
|
1120
|
-
setMap: value,
|
|
1121
|
-
clearedMap: /* @__PURE__ */ new Map()
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
add(key, value) {
|
|
1125
|
-
if (this.disposed) throw this._disposedError();
|
|
1126
|
-
if (this._value.has(key)) {
|
|
1127
|
-
throw new Error("[PicoFlow] Key already exists");
|
|
1128
|
-
}
|
|
1129
|
-
this._value.set(key, value);
|
|
1130
|
-
this.notifyDependents();
|
|
1131
|
-
this.$lastAction.set({ type: "add", key, addedValue: value });
|
|
1132
|
-
}
|
|
1133
|
-
update(key, value) {
|
|
1134
|
-
if (this.disposed) throw this._disposedError();
|
|
1135
|
-
if (!this._value.has(key)) throw new Error("[PicoFlow] Key does not exist");
|
|
1136
|
-
const previousValue = this._value.get(key);
|
|
1137
|
-
this._value.set(key, value);
|
|
1138
|
-
this.notifyDependents();
|
|
1139
|
-
this.$lastAction.set({
|
|
1140
|
-
type: "update",
|
|
1141
|
-
key,
|
|
1142
|
-
setValue: value,
|
|
1143
|
-
clearedValue: previousValue
|
|
1144
|
-
});
|
|
1145
|
-
return previousValue;
|
|
1146
|
-
}
|
|
1147
|
-
delete(key) {
|
|
1148
|
-
if (this.disposed) throw this._disposedError();
|
|
1149
|
-
if (!this._value.has(key)) throw new Error("[PicoFlow] Key does not exist");
|
|
1150
|
-
const value = this._value.get(key);
|
|
1151
|
-
this._value.delete(key);
|
|
1152
|
-
this.notifyDependents();
|
|
1153
|
-
this.$lastAction.set({ type: "delete", key, removedValue: value });
|
|
1154
|
-
return value;
|
|
1155
|
-
}
|
|
1156
|
-
set(mapOrUpdater) {
|
|
1157
|
-
if (this.disposed) throw this._disposedError();
|
|
1158
|
-
const previousValue = this._value;
|
|
1159
|
-
super.set(mapOrUpdater);
|
|
1160
|
-
this.$lastAction.set({
|
|
1161
|
-
type: "set",
|
|
1162
|
-
setMap: this._value,
|
|
1163
|
-
clearedMap: previousValue
|
|
1164
|
-
});
|
|
1165
|
-
return previousValue;
|
|
1166
|
-
}
|
|
1167
|
-
clear() {
|
|
1168
|
-
if (this.disposed) throw this._disposedError();
|
|
1169
|
-
const previousValue = this._value;
|
|
1170
|
-
this._value = /* @__PURE__ */ new Map();
|
|
1171
|
-
this.notifyDependents();
|
|
1172
|
-
this.$lastAction.set({ type: "clear", clearedMap: previousValue });
|
|
1173
|
-
return previousValue;
|
|
1174
|
-
}
|
|
1175
|
-
dispose() {
|
|
1176
|
-
super.dispose();
|
|
1177
|
-
if (!this.$lastAction.disposed) {
|
|
1178
|
-
this.$lastAction.dispose();
|
|
1179
|
-
}
|
|
1180
|
-
this._value.clear();
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function map(initial, options) {
|
|
1185
|
-
if (initial instanceof Map) {
|
|
1186
|
-
return new MapNode(initial, options?.name);
|
|
1187
|
-
}
|
|
1188
|
-
return new MapNode(new Map(initial ? Object.entries(initial) : []), options?.name);
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
function subscribe(data, onData, onError, onPending) {
|
|
1192
|
-
return new EffectNode(data, onData, onError, onPending);
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
class SignalNode extends Observable {
|
|
1196
|
-
subscribe(onTrigger, onError, onPending) {
|
|
1197
|
-
if (this.disposed) throw this._disposedError();
|
|
1198
|
-
const effect = new EffectNode((t) => this.watch(t), onTrigger, onError, onPending);
|
|
1199
|
-
return effect;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
function signal(options) {
|
|
1204
|
-
return new SignalNode(options?.name);
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
function constant(initializer, options) {
|
|
1208
|
-
return new ValueSyncNode(initializer, options?.name);
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
function derivation(compute, options) {
|
|
1212
|
-
return new ValueSyncNode(compute, options?.name);
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
function state(valueOrInitializer, options) {
|
|
1216
|
-
return new ValueSyncNode(valueOrInitializer, options?.name);
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
function writableDerivation(compute, options) {
|
|
1220
|
-
return new ValueSyncNode(compute, options?.name);
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
function describeFlowInput(value) {
|
|
1224
|
-
if (value === null) return "null";
|
|
1225
|
-
if (typeof value === "object") {
|
|
1226
|
-
const name = value.constructor?.name;
|
|
1227
|
-
return name ? `object (${name})` : "object";
|
|
1228
|
-
}
|
|
1229
|
-
return typeof value;
|
|
1230
|
-
}
|
|
1231
|
-
function isValueNode(flow) {
|
|
1232
|
-
return typeof flow === "object" && flow !== null && PICOFLOW_VALUE_NODE in flow;
|
|
1233
|
-
}
|
|
1234
|
-
function fromNode(node, options) {
|
|
1235
|
-
const [resource, { refetch }] = createResource(() => node.pick());
|
|
1236
|
-
let fx;
|
|
1237
|
-
onMount(() => {
|
|
1238
|
-
fx = subscribe(
|
|
1239
|
-
// data
|
|
1240
|
-
(t) => node.get(t),
|
|
1241
|
-
// onData
|
|
1242
|
-
() => {
|
|
1243
|
-
refetch();
|
|
1244
|
-
},
|
|
1245
|
-
// onError
|
|
1246
|
-
() => {
|
|
1247
|
-
refetch();
|
|
1248
|
-
},
|
|
1249
|
-
// onPending
|
|
1250
|
-
() => {
|
|
1251
|
-
refetch();
|
|
1252
|
-
}
|
|
1253
|
-
);
|
|
1254
|
-
});
|
|
1255
|
-
createEffect(() => {
|
|
1256
|
-
resource.error ?? resource();
|
|
1257
|
-
resetErrorBoundaries();
|
|
1258
|
-
});
|
|
1259
|
-
onCleanup(() => {
|
|
1260
|
-
fx.dispose();
|
|
1261
|
-
if (options?.disposeNode && !node.disposed) {
|
|
1262
|
-
node.dispose();
|
|
1263
|
-
}
|
|
1264
|
-
});
|
|
1265
|
-
return resource;
|
|
1266
|
-
}
|
|
1267
|
-
function fromGetter(getter) {
|
|
1268
|
-
const derivation = new ValueSyncNode((t) => {
|
|
1269
|
-
return getter(t);
|
|
1270
|
-
});
|
|
1271
|
-
return fromNode(derivation, { disposeNode: true });
|
|
1272
|
-
}
|
|
1273
|
-
function from(flow) {
|
|
1274
|
-
if (isValueNode(flow)) {
|
|
1275
|
-
return fromNode(flow);
|
|
1276
|
-
}
|
|
1277
|
-
if (typeof flow === "function") {
|
|
1278
|
-
return fromGetter(flow);
|
|
1279
|
-
}
|
|
1280
|
-
throw new Error(`[PicoFlow] from(): expected a FlowValue or getter function, received ${describeFlowInput(flow)}`);
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
export { PicoFlowCycleError, PicoFlowDisposedError, PicoFlowError, PicoFlowInternalError, array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, onFlushError, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|