@ersbeth/picoflow 2.1.0 → 2.2.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/SKILL.md +6 -1
- package/dist/picoflow.js +255 -226
- package/dist/types/api/base/flowConfig.d.ts +18 -0
- package/dist/types/api/base/flowConfig.d.ts.map +1 -0
- package/dist/types/api/base/index.d.ts +1 -0
- package/dist/types/api/base/index.d.ts.map +1 -1
- package/dist/types/base/disposable.d.ts +3 -3
- package/dist/types/base/disposable.d.ts.map +1 -1
- package/dist/types/base/executionStack.d.ts +4 -0
- package/dist/types/base/executionStack.d.ts.map +1 -1
- package/dist/types/base/node.d.ts +2 -1
- package/dist/types/base/node.d.ts.map +1 -1
- package/dist/types/base/observable.d.ts.map +1 -1
- package/dist/types/base/observer.d.ts +9 -0
- package/dist/types/base/observer.d.ts.map +1 -1
- package/dist/types/converters/solid.d.ts.map +1 -1
- package/dist/types/nodes/effectNode.d.ts.map +1 -1
- package/dist/types/nodes/valueNode.d.ts +7 -0
- package/dist/types/nodes/valueNode.d.ts.map +1 -1
- package/package.json +5 -4
package/SKILL.md
CHANGED
|
@@ -59,7 +59,12 @@ callback) rather than needing manual `await`/try-catch inside a derivation.
|
|
|
59
59
|
- **Creating a subscription inside another subscription's callback** — it re-creates one on every run
|
|
60
60
|
and leaks. Combine into a single `subscribe()` whose data function reads all the needed primitives.
|
|
61
61
|
- **A `subscribe()`/`.subscribe()` callback that calls `.set()` on a primitive it also reads** — infinite
|
|
62
|
-
loop.
|
|
62
|
+
loop. PicoFlow's own cycle guard stops it after 10 000 steps and reports it through `onFlushError()`
|
|
63
|
+
(see below) rather than crashing the process, but the loop is still a bug to fix, not something to rely
|
|
64
|
+
on the guard for.
|
|
65
|
+
- **`subscribe(fn)` with no `onError`, and no global `onFlushError()` handler installed** — any exception
|
|
66
|
+
thrown inside `fn` is only logged via `console.error` by default; install `onFlushError((error) => ...)`
|
|
67
|
+
once at startup if the application needs to observe or report these instead of the default logging.
|
|
63
68
|
- **Using `add()`/`update()` on `array`/`map` without checking preconditions** — `array.update(index, x)`
|
|
64
69
|
throws if the index is out of bounds; `map.add(key, x)` throws if the key exists; `map.update(key, x)`
|
|
65
70
|
throws if it doesn't. Check first (`.pick()`) or branch on it, don't assume.
|
package/dist/picoflow.js
CHANGED
|
@@ -1,5 +1,242 @@
|
|
|
1
1
|
import { createResource, onMount, createEffect, resetErrorBoundaries, onCleanup } from 'solid-js';
|
|
2
2
|
|
|
3
|
+
class Disposable {
|
|
4
|
+
_disposed = false;
|
|
5
|
+
get disposed() {
|
|
6
|
+
return this._disposed;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class ExecutionStack {
|
|
11
|
+
static _MAX_FLUSH_STEPS = 1e4;
|
|
12
|
+
static _pendingQueue = [];
|
|
13
|
+
static _effectQueue = [];
|
|
14
|
+
static _executionScheduled;
|
|
15
|
+
static _coalesceResetScheduled = false;
|
|
16
|
+
/** Routes any error escaping the flush loop; defaults to logging rather than crashing (A2-SEM-2, A2-SEC-1). */
|
|
17
|
+
static _onFlushError = (error) => console.error(error);
|
|
18
|
+
/**
|
|
19
|
+
* True once the current synchronous call stack has finished and microtasks have started.
|
|
20
|
+
* EffectNode uses this to treat microtask notifications (e.g. async resolve) differently
|
|
21
|
+
* from coalesced sync-batch notifications.
|
|
22
|
+
*/
|
|
23
|
+
static _pastSyncTurn = false;
|
|
24
|
+
/** @internal Used by EffectNode to distinguish sync-batch from microtask notifications. */
|
|
25
|
+
static get pastSyncTurn() {
|
|
26
|
+
return ExecutionStack._pastSyncTurn;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @internal Runs after the current sync turn ends (queueMicrotask).
|
|
30
|
+
* Marks pastSyncTurn so the next coalesced effect notify can bump its epoch,
|
|
31
|
+
* and resets per-effect coalesce flags via onReset.
|
|
32
|
+
*/
|
|
33
|
+
static scheduleCoalesceReset(onReset) {
|
|
34
|
+
if (ExecutionStack._coalesceResetScheduled) return;
|
|
35
|
+
ExecutionStack._coalesceResetScheduled = true;
|
|
36
|
+
queueMicrotask(() => {
|
|
37
|
+
ExecutionStack._pastSyncTurn = true;
|
|
38
|
+
ExecutionStack._coalesceResetScheduled = false;
|
|
39
|
+
onReset();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** @internal Wired to the public `onFlushError()` API in `src/api/base/flowConfig.ts`. */
|
|
43
|
+
static setFlushErrorHandler(handler) {
|
|
44
|
+
ExecutionStack._onFlushError = handler;
|
|
45
|
+
}
|
|
46
|
+
static pushPending(node) {
|
|
47
|
+
ExecutionStack._beginSyncTurn();
|
|
48
|
+
ExecutionStack._scheduleExecution();
|
|
49
|
+
ExecutionStack._pendingQueue.push(node);
|
|
50
|
+
}
|
|
51
|
+
static pushEffect(effect) {
|
|
52
|
+
ExecutionStack._beginSyncTurn();
|
|
53
|
+
ExecutionStack._scheduleExecution();
|
|
54
|
+
ExecutionStack._effectQueue.push(effect);
|
|
55
|
+
}
|
|
56
|
+
static _beginSyncTurn() {
|
|
57
|
+
ExecutionStack._pastSyncTurn = false;
|
|
58
|
+
}
|
|
59
|
+
static _scheduleExecution() {
|
|
60
|
+
if (ExecutionStack._executionScheduled) return;
|
|
61
|
+
ExecutionStack._executionScheduled = new Promise((resolve) => {
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
ExecutionStack._executionScheduled = void 0;
|
|
64
|
+
ExecutionStack._execute();
|
|
65
|
+
resolve();
|
|
66
|
+
}, 0);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
static _drain(queue) {
|
|
70
|
+
for (let i = 0; i < queue.length; i++) {
|
|
71
|
+
if (i >= ExecutionStack._MAX_FLUSH_STEPS) {
|
|
72
|
+
ExecutionStack._pendingQueue.length = 0;
|
|
73
|
+
ExecutionStack._effectQueue.length = 0;
|
|
74
|
+
ExecutionStack._onFlushError(new Error("[PicoFlow] Reactive update cycle exceeded maximum depth"));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const node = queue[i];
|
|
78
|
+
try {
|
|
79
|
+
node?.execute();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
ExecutionStack._onFlushError(error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
queue.length = 0;
|
|
85
|
+
}
|
|
86
|
+
static _execute() {
|
|
87
|
+
ExecutionStack._drain(ExecutionStack._pendingQueue);
|
|
88
|
+
ExecutionStack._drain(ExecutionStack._effectQueue);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class Node extends Disposable {
|
|
93
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
94
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
95
|
+
_status = "resolved";
|
|
96
|
+
get status() {
|
|
97
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
98
|
+
return this._status;
|
|
99
|
+
}
|
|
100
|
+
set status(status) {
|
|
101
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
102
|
+
this._status = status;
|
|
103
|
+
}
|
|
104
|
+
registerDependency(dependency) {
|
|
105
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
106
|
+
this._dependencies.add(dependency);
|
|
107
|
+
dependency.registerDependent(this);
|
|
108
|
+
}
|
|
109
|
+
unregisterDependency(dependency) {
|
|
110
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
111
|
+
this._dependencies.delete(dependency);
|
|
112
|
+
dependency.unregisterDependent(this);
|
|
113
|
+
}
|
|
114
|
+
clearDependencies() {
|
|
115
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
116
|
+
this._dependencies.forEach((dependency) => {
|
|
117
|
+
dependency.unregisterDependent(this);
|
|
118
|
+
});
|
|
119
|
+
this._dependencies.clear();
|
|
120
|
+
}
|
|
121
|
+
registerDependent(dependent) {
|
|
122
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
123
|
+
this._dependents.add(dependent);
|
|
124
|
+
}
|
|
125
|
+
unregisterDependent(dependent) {
|
|
126
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
127
|
+
this._dependents.delete(dependent);
|
|
128
|
+
}
|
|
129
|
+
markDependencyDisposed() {
|
|
130
|
+
}
|
|
131
|
+
notifyDependents() {
|
|
132
|
+
if (this.disposed) return;
|
|
133
|
+
this._dependents.forEach((dependent) => {
|
|
134
|
+
dependent.notify();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
watch(tracker) {
|
|
138
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
139
|
+
tracker.registerDependency(this);
|
|
140
|
+
}
|
|
141
|
+
trigger() {
|
|
142
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
143
|
+
this.notifyDependents();
|
|
144
|
+
}
|
|
145
|
+
dispose() {
|
|
146
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
147
|
+
Array.from(this._dependents).forEach((dependant) => {
|
|
148
|
+
dependant.markDependencyDisposed();
|
|
149
|
+
dependant.unregisterDependency(this);
|
|
150
|
+
this.unregisterDependent(dependant);
|
|
151
|
+
});
|
|
152
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
153
|
+
this.unregisterDependency(dependency);
|
|
154
|
+
});
|
|
155
|
+
this._disposed = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class Observable extends Disposable {
|
|
160
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
161
|
+
_status = "resolved";
|
|
162
|
+
get status() {
|
|
163
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
164
|
+
return this._status;
|
|
165
|
+
}
|
|
166
|
+
set status(status) {
|
|
167
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
168
|
+
this._status = status;
|
|
169
|
+
}
|
|
170
|
+
registerDependent(dependent) {
|
|
171
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
172
|
+
this._dependents.add(dependent);
|
|
173
|
+
}
|
|
174
|
+
unregisterDependent(dependent) {
|
|
175
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
176
|
+
this._dependents.delete(dependent);
|
|
177
|
+
}
|
|
178
|
+
notifyDependents() {
|
|
179
|
+
if (this.disposed) return;
|
|
180
|
+
this._dependents.forEach((dependent) => {
|
|
181
|
+
dependent.notify();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
watch(tracker) {
|
|
185
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
186
|
+
tracker.registerDependency(this);
|
|
187
|
+
}
|
|
188
|
+
trigger() {
|
|
189
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
190
|
+
this.notifyDependents();
|
|
191
|
+
}
|
|
192
|
+
dispose() {
|
|
193
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
194
|
+
Array.from(this._dependents).forEach((dependant) => {
|
|
195
|
+
dependant.markDependencyDisposed();
|
|
196
|
+
dependant.unregisterDependency(this);
|
|
197
|
+
this.unregisterDependent(dependant);
|
|
198
|
+
});
|
|
199
|
+
this._disposed = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
class Observer extends Disposable {
|
|
204
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
205
|
+
/** Set when a dependency disposed while this observer was queued; consumed by the next execute(). */
|
|
206
|
+
_dependencyDisposed = false;
|
|
207
|
+
markDependencyDisposed() {
|
|
208
|
+
this._dependencyDisposed = true;
|
|
209
|
+
}
|
|
210
|
+
registerDependency(dependency) {
|
|
211
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
212
|
+
this._dependencies.add(dependency);
|
|
213
|
+
dependency.registerDependent(this);
|
|
214
|
+
}
|
|
215
|
+
unregisterDependency(dependency) {
|
|
216
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
217
|
+
this._dependencies.delete(dependency);
|
|
218
|
+
dependency.unregisterDependent(this);
|
|
219
|
+
}
|
|
220
|
+
clearDependencies() {
|
|
221
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
222
|
+
this._dependencies.forEach((dependency) => {
|
|
223
|
+
dependency.unregisterDependent(this);
|
|
224
|
+
});
|
|
225
|
+
this._dependencies.clear();
|
|
226
|
+
}
|
|
227
|
+
dispose() {
|
|
228
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
229
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
230
|
+
this.unregisterDependency(dependency);
|
|
231
|
+
});
|
|
232
|
+
this._disposed = true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function onFlushError(handler) {
|
|
237
|
+
ExecutionStack.setFlushErrorHandler(handler);
|
|
238
|
+
}
|
|
239
|
+
|
|
3
240
|
function isDisposable(obj) {
|
|
4
241
|
return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
|
|
5
242
|
}
|
|
@@ -213,229 +450,6 @@ class SyncScheduler {
|
|
|
213
450
|
}
|
|
214
451
|
}
|
|
215
452
|
|
|
216
|
-
class Disposable {
|
|
217
|
-
_disposed = false;
|
|
218
|
-
get disposed() {
|
|
219
|
-
return this._disposed;
|
|
220
|
-
}
|
|
221
|
-
dispose() {
|
|
222
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
223
|
-
this._disposed = true;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
class ExecutionStack {
|
|
228
|
-
static _MAX_FLUSH_STEPS = 1e4;
|
|
229
|
-
static _pendingQueue = [];
|
|
230
|
-
static _effectQueue = [];
|
|
231
|
-
static _executionScheduled;
|
|
232
|
-
static _coalesceResetScheduled = false;
|
|
233
|
-
/**
|
|
234
|
-
* True once the current synchronous call stack has finished and microtasks have started.
|
|
235
|
-
* EffectNode uses this to treat microtask notifications (e.g. async resolve) differently
|
|
236
|
-
* from coalesced sync-batch notifications.
|
|
237
|
-
*/
|
|
238
|
-
static _pastSyncTurn = false;
|
|
239
|
-
/** @internal Used by EffectNode to distinguish sync-batch from microtask notifications. */
|
|
240
|
-
static get pastSyncTurn() {
|
|
241
|
-
return ExecutionStack._pastSyncTurn;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* @internal Runs after the current sync turn ends (queueMicrotask).
|
|
245
|
-
* Marks pastSyncTurn so the next coalesced effect notify can bump its epoch,
|
|
246
|
-
* and resets per-effect coalesce flags via onReset.
|
|
247
|
-
*/
|
|
248
|
-
static scheduleCoalesceReset(onReset) {
|
|
249
|
-
if (ExecutionStack._coalesceResetScheduled) return;
|
|
250
|
-
ExecutionStack._coalesceResetScheduled = true;
|
|
251
|
-
queueMicrotask(() => {
|
|
252
|
-
ExecutionStack._pastSyncTurn = true;
|
|
253
|
-
ExecutionStack._coalesceResetScheduled = false;
|
|
254
|
-
onReset();
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
static pushPending(node) {
|
|
258
|
-
ExecutionStack._beginSyncTurn();
|
|
259
|
-
ExecutionStack._scheduleExecution();
|
|
260
|
-
ExecutionStack._pendingQueue.push(node);
|
|
261
|
-
}
|
|
262
|
-
static pushEffect(effect) {
|
|
263
|
-
ExecutionStack._beginSyncTurn();
|
|
264
|
-
ExecutionStack._scheduleExecution();
|
|
265
|
-
ExecutionStack._effectQueue.push(effect);
|
|
266
|
-
}
|
|
267
|
-
static _beginSyncTurn() {
|
|
268
|
-
ExecutionStack._pastSyncTurn = false;
|
|
269
|
-
}
|
|
270
|
-
static _scheduleExecution() {
|
|
271
|
-
if (ExecutionStack._executionScheduled) return;
|
|
272
|
-
ExecutionStack._executionScheduled = new Promise((resolve) => {
|
|
273
|
-
setTimeout(() => {
|
|
274
|
-
ExecutionStack._executionScheduled = void 0;
|
|
275
|
-
ExecutionStack._execute();
|
|
276
|
-
resolve();
|
|
277
|
-
}, 0);
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
static _drain(queue) {
|
|
281
|
-
for (let i = 0; i < queue.length; i++) {
|
|
282
|
-
if (i >= ExecutionStack._MAX_FLUSH_STEPS) {
|
|
283
|
-
ExecutionStack._pendingQueue.length = 0;
|
|
284
|
-
ExecutionStack._effectQueue.length = 0;
|
|
285
|
-
throw new Error("[PicoFlow] Reactive update cycle exceeded maximum depth");
|
|
286
|
-
}
|
|
287
|
-
const node = queue[i];
|
|
288
|
-
node?.execute();
|
|
289
|
-
}
|
|
290
|
-
queue.length = 0;
|
|
291
|
-
}
|
|
292
|
-
static _execute() {
|
|
293
|
-
ExecutionStack._drain(ExecutionStack._pendingQueue);
|
|
294
|
-
ExecutionStack._drain(ExecutionStack._effectQueue);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
class Node extends Disposable {
|
|
299
|
-
_dependencies = /* @__PURE__ */ new Set();
|
|
300
|
-
_dependents = /* @__PURE__ */ new Set();
|
|
301
|
-
_status = "resolved";
|
|
302
|
-
get status() {
|
|
303
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
304
|
-
return this._status;
|
|
305
|
-
}
|
|
306
|
-
set status(status) {
|
|
307
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
308
|
-
this._status = status;
|
|
309
|
-
}
|
|
310
|
-
registerDependency(dependency) {
|
|
311
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
312
|
-
this._dependencies.add(dependency);
|
|
313
|
-
dependency.registerDependent(this);
|
|
314
|
-
}
|
|
315
|
-
unregisterDependency(dependency) {
|
|
316
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
317
|
-
this._dependencies.delete(dependency);
|
|
318
|
-
dependency.unregisterDependent(this);
|
|
319
|
-
}
|
|
320
|
-
clearDependencies() {
|
|
321
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
322
|
-
this._dependencies.forEach((dependency) => {
|
|
323
|
-
dependency.unregisterDependent(this);
|
|
324
|
-
});
|
|
325
|
-
this._dependencies.clear();
|
|
326
|
-
}
|
|
327
|
-
registerDependent(dependent) {
|
|
328
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
329
|
-
this._dependents.add(dependent);
|
|
330
|
-
}
|
|
331
|
-
unregisterDependent(dependent) {
|
|
332
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
333
|
-
this._dependents.delete(dependent);
|
|
334
|
-
}
|
|
335
|
-
notifyDependents() {
|
|
336
|
-
if (this.disposed) return;
|
|
337
|
-
this._dependents.forEach((dependent) => {
|
|
338
|
-
dependent.notify();
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
watch(tracker) {
|
|
342
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
343
|
-
tracker.registerDependency(this);
|
|
344
|
-
}
|
|
345
|
-
trigger() {
|
|
346
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
347
|
-
this.notifyDependents();
|
|
348
|
-
}
|
|
349
|
-
notify() {
|
|
350
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
351
|
-
if (this.status === "dirty") return;
|
|
352
|
-
this.status = "dirty";
|
|
353
|
-
this.notifyDependents();
|
|
354
|
-
}
|
|
355
|
-
dispose() {
|
|
356
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
357
|
-
Array.from(this._dependents).forEach((dependant) => {
|
|
358
|
-
dependant.unregisterDependency(this);
|
|
359
|
-
this.unregisterDependent(dependant);
|
|
360
|
-
});
|
|
361
|
-
Array.from(this._dependencies).forEach((dependency) => {
|
|
362
|
-
this.unregisterDependency(dependency);
|
|
363
|
-
});
|
|
364
|
-
this._disposed = true;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
class Observable extends Disposable {
|
|
369
|
-
_dependents = /* @__PURE__ */ new Set();
|
|
370
|
-
_status = "resolved";
|
|
371
|
-
get status() {
|
|
372
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
373
|
-
return this._status;
|
|
374
|
-
}
|
|
375
|
-
set status(status) {
|
|
376
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
377
|
-
this._status = status;
|
|
378
|
-
}
|
|
379
|
-
registerDependent(dependent) {
|
|
380
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
381
|
-
this._dependents.add(dependent);
|
|
382
|
-
}
|
|
383
|
-
unregisterDependent(dependent) {
|
|
384
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
385
|
-
this._dependents.delete(dependent);
|
|
386
|
-
}
|
|
387
|
-
notifyDependents() {
|
|
388
|
-
if (this.disposed) return;
|
|
389
|
-
this._dependents.forEach((dependent) => {
|
|
390
|
-
dependent.notify();
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
watch(tracker) {
|
|
394
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
395
|
-
tracker.registerDependency(this);
|
|
396
|
-
}
|
|
397
|
-
trigger() {
|
|
398
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
399
|
-
this.notifyDependents();
|
|
400
|
-
}
|
|
401
|
-
dispose() {
|
|
402
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
403
|
-
Array.from(this._dependents).forEach((dependant) => {
|
|
404
|
-
dependant.unregisterDependency(this);
|
|
405
|
-
this.unregisterDependent(dependant);
|
|
406
|
-
});
|
|
407
|
-
this._disposed = true;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
class Observer extends Disposable {
|
|
412
|
-
_dependencies = /* @__PURE__ */ new Set();
|
|
413
|
-
registerDependency(dependency) {
|
|
414
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
415
|
-
this._dependencies.add(dependency);
|
|
416
|
-
dependency.registerDependent(this);
|
|
417
|
-
}
|
|
418
|
-
unregisterDependency(dependency) {
|
|
419
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
420
|
-
this._dependencies.delete(dependency);
|
|
421
|
-
dependency.unregisterDependent(this);
|
|
422
|
-
}
|
|
423
|
-
clearDependencies() {
|
|
424
|
-
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
425
|
-
this._dependencies.forEach((dependency) => {
|
|
426
|
-
dependency.unregisterDependent(this);
|
|
427
|
-
});
|
|
428
|
-
this._dependencies.clear();
|
|
429
|
-
}
|
|
430
|
-
dispose() {
|
|
431
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
432
|
-
Array.from(this._dependencies).forEach((dependency) => {
|
|
433
|
-
this.unregisterDependency(dependency);
|
|
434
|
-
});
|
|
435
|
-
this._disposed = true;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
453
|
class EffectNode extends Observer {
|
|
440
454
|
_data;
|
|
441
455
|
_onData;
|
|
@@ -482,6 +496,11 @@ class EffectNode extends Observer {
|
|
|
482
496
|
this._executedEpoch = this._notifyEpoch;
|
|
483
497
|
return;
|
|
484
498
|
}
|
|
499
|
+
if (this._dependencyDisposed) {
|
|
500
|
+
this._dependencyDisposed = false;
|
|
501
|
+
this._executedEpoch = this._notifyEpoch;
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
485
504
|
const targetEpoch = this._notifyEpoch;
|
|
486
505
|
while (this._executedEpoch < targetEpoch) {
|
|
487
506
|
this._executedEpoch++;
|
|
@@ -489,6 +508,11 @@ class EffectNode extends Observer {
|
|
|
489
508
|
this._executedEpoch = this._notifyEpoch;
|
|
490
509
|
return;
|
|
491
510
|
}
|
|
511
|
+
if (this._dependencyDisposed) {
|
|
512
|
+
this._dependencyDisposed = false;
|
|
513
|
+
this._executedEpoch = this._notifyEpoch;
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
492
516
|
try {
|
|
493
517
|
this.clearDependencies();
|
|
494
518
|
const data = this._data(this);
|
|
@@ -508,7 +532,9 @@ class EffectNode extends Observer {
|
|
|
508
532
|
}
|
|
509
533
|
}
|
|
510
534
|
|
|
535
|
+
const PICOFLOW_VALUE_NODE = /* @__PURE__ */ Symbol.for("picoflow.node");
|
|
511
536
|
class ValueNode extends Node {
|
|
537
|
+
[PICOFLOW_VALUE_NODE] = true;
|
|
512
538
|
_value;
|
|
513
539
|
_error;
|
|
514
540
|
watch(tracker) {
|
|
@@ -889,7 +915,7 @@ class ArrayNode extends ValueSyncNode {
|
|
|
889
915
|
}
|
|
890
916
|
update(index, item) {
|
|
891
917
|
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
892
|
-
if (index < 0 || index >= this._value.length) {
|
|
918
|
+
if (index < 0 || index >= this._value.length || !Number.isInteger(index)) {
|
|
893
919
|
throw new Error("[PicoFlow] Index out of bounds");
|
|
894
920
|
}
|
|
895
921
|
const previousValue = this._value[index];
|
|
@@ -1082,6 +1108,9 @@ function describeFlowInput(value) {
|
|
|
1082
1108
|
}
|
|
1083
1109
|
return typeof value;
|
|
1084
1110
|
}
|
|
1111
|
+
function isValueNode(flow) {
|
|
1112
|
+
return typeof flow === "object" && flow !== null && PICOFLOW_VALUE_NODE in flow;
|
|
1113
|
+
}
|
|
1085
1114
|
function fromNode(node, options) {
|
|
1086
1115
|
const [resource, { refetch }] = createResource(() => node.pick());
|
|
1087
1116
|
let fx;
|
|
@@ -1122,7 +1151,7 @@ function fromGetter(getter) {
|
|
|
1122
1151
|
return fromNode(derivation, { disposeNode: true });
|
|
1123
1152
|
}
|
|
1124
1153
|
function from(flow) {
|
|
1125
|
-
if (flow
|
|
1154
|
+
if (isValueNode(flow)) {
|
|
1126
1155
|
return fromNode(flow);
|
|
1127
1156
|
}
|
|
1128
1157
|
if (typeof flow === "function") {
|
|
@@ -1131,4 +1160,4 @@ function from(flow) {
|
|
|
1131
1160
|
throw new Error(`[PicoFlow] from(): expected a FlowValue or getter function, received ${describeFlowInput(flow)}`);
|
|
1132
1161
|
}
|
|
1133
1162
|
|
|
1134
|
-
export { array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|
|
1163
|
+
export { array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, onFlushError, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs a global handler for errors that escape the reactive flush.
|
|
3
|
+
*
|
|
4
|
+
* All reactive work — recomputing derivations, running effects — happens inside an
|
|
5
|
+
* asynchronous flush. Without this hook, an error escaping the flush (an effect callback
|
|
6
|
+
* that throws with no `onError`, or a feedback loop between two effects) becomes an
|
|
7
|
+
* uncaught exception, which terminates a Node process. Installing a handler here makes
|
|
8
|
+
* that error catchable instead.
|
|
9
|
+
*
|
|
10
|
+
* Defaults to `console.error`. The handler replaces any previously installed handler —
|
|
11
|
+
* it is not additive.
|
|
12
|
+
*
|
|
13
|
+
* @param handler - Called with the error that escaped the flush.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export declare function onFlushError(handler: (error: unknown) => void): void;
|
|
18
|
+
//# sourceMappingURL=flowConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flowConfig.d.ts","sourceRoot":"","sources":["../../../../src/api/base/flowConfig.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAEpE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/base/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/base/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { FlowDisposable } from '../api/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Base implementation of the disposable pattern for reactive primitives.
|
|
4
|
-
*
|
|
4
|
+
* Concrete subclasses implement `dispose()` and are expected to throw if disposed multiple times.
|
|
5
5
|
*/
|
|
6
|
-
export declare class Disposable implements FlowDisposable {
|
|
6
|
+
export declare abstract class Disposable implements FlowDisposable {
|
|
7
7
|
protected _disposed: boolean;
|
|
8
8
|
get disposed(): boolean;
|
|
9
|
-
dispose(): void;
|
|
9
|
+
abstract dispose(): void;
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=disposable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../../../src/base/disposable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;GAGG;AACH,
|
|
1
|
+
{"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../../../src/base/disposable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;GAGG;AACH,8BAAsB,UAAW,YAAW,cAAc;IACtD,SAAS,CAAC,SAAS,UAAS;IAE5B,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,QAAQ,CAAC,OAAO,IAAI,IAAI;CAC3B"}
|
|
@@ -11,6 +11,8 @@ export declare class ExecutionStack {
|
|
|
11
11
|
private static readonly _effectQueue;
|
|
12
12
|
private static _executionScheduled?;
|
|
13
13
|
private static _coalesceResetScheduled;
|
|
14
|
+
/** Routes any error escaping the flush loop; defaults to logging rather than crashing (A2-SEM-2, A2-SEC-1). */
|
|
15
|
+
private static _onFlushError;
|
|
14
16
|
/**
|
|
15
17
|
* True once the current synchronous call stack has finished and microtasks have started.
|
|
16
18
|
* EffectNode uses this to treat microtask notifications (e.g. async resolve) differently
|
|
@@ -25,6 +27,8 @@ export declare class ExecutionStack {
|
|
|
25
27
|
* and resets per-effect coalesce flags via onReset.
|
|
26
28
|
*/
|
|
27
29
|
static scheduleCoalesceReset(onReset: () => void): void;
|
|
30
|
+
/** @internal Wired to the public `onFlushError()` API in `src/api/base/flowConfig.ts`. */
|
|
31
|
+
static setFlushErrorHandler(handler: (error: unknown) => void): void;
|
|
28
32
|
static pushPending(node: IObserver): void;
|
|
29
33
|
static pushEffect(effect: IObserver): void;
|
|
30
34
|
private static _beginSyncTurn;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executionStack.d.ts","sourceRoot":"","sources":["../../../src/base/executionStack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;GAKG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAmB;IACxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAmB;IACvD,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAgB;IACnD,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAS;IAC/C;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa,CAAS;IAErC,2FAA2F;IAC3F,MAAM,KAAK,YAAY,IAAI,OAAO,CAEjC;IAED;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAUvD,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAMzC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAM1C,OAAO,CAAC,MAAM,CAAC,cAAc;IAK7B,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAWjC,OAAO,CAAC,MAAM,CAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"executionStack.d.ts","sourceRoot":"","sources":["../../../src/base/executionStack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;GAKG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAmB;IACxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAmB;IACvD,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAgB;IACnD,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAS;IAC/C,+GAA+G;IAC/G,OAAO,CAAC,MAAM,CAAC,aAAa,CAA6D;IACzF;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa,CAAS;IAErC,2FAA2F;IAC3F,MAAM,KAAK,YAAY,IAAI,OAAO,CAEjC;IAED;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAUvD,0FAA0F;IAC1F,MAAM,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAIpE,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAMzC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAM1C,OAAO,CAAC,MAAM,CAAC,cAAc;IAK7B,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAWjC,OAAO,CAAC,MAAM,CAAC,MAAM;IAsBrB,OAAO,CAAC,MAAM,CAAC,QAAQ;CAI1B"}
|
|
@@ -16,11 +16,12 @@ export declare abstract class Node<T> extends Disposable implements IObservable<
|
|
|
16
16
|
clearDependencies(): void;
|
|
17
17
|
registerDependent(dependent: IObserver): void;
|
|
18
18
|
unregisterDependent(dependent: IObserver): void;
|
|
19
|
+
markDependencyDisposed(): void;
|
|
19
20
|
notifyDependents(): void;
|
|
20
21
|
watch(tracker: FlowTracker): void;
|
|
21
22
|
trigger(): void;
|
|
22
|
-
notify(): void;
|
|
23
23
|
dispose(): void;
|
|
24
|
+
abstract notify(): void;
|
|
24
25
|
abstract execute(): void;
|
|
25
26
|
abstract subscribe(onValue: FlowOnDataListener<T>, onError?: FlowOnErrorListener, onPending?: FlowOnPendingListener): FlowEffect;
|
|
26
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/base/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;GAEG;AACH,8BAAsB,IAAI,CAAC,CAAC,CAAE,SAAQ,UAAW,YAAW,WAAW,CAAC,CAAC,CAAC,EAAE,SAAS;IACjF,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,WAAW,CAAwB;IAC3C,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAc;IAEjD,IAAI,MAAM,IAAI,gBAAgB,CAG7B;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAGlC;IAED,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAM1D,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAM5D,iBAAiB,IAAI,IAAI;IAQzB,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK7C,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK/C,gBAAgB,IAAI,IAAI;IAOxB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjC,OAAO,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/base/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;GAEG;AACH,8BAAsB,IAAI,CAAC,CAAC,CAAE,SAAQ,UAAW,YAAW,WAAW,CAAC,CAAC,CAAC,EAAE,SAAS;IACjF,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,WAAW,CAAwB;IAC3C,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAc;IAEjD,IAAI,MAAM,IAAI,gBAAgB,CAG7B;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAGlC;IAED,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAM1D,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAM5D,iBAAiB,IAAI,IAAI;IAQzB,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK7C,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK/C,sBAAsB,IAAI,IAAI;IAM9B,gBAAgB,IAAI,IAAI;IAOxB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjC,OAAO,IAAI,IAAI;IAKN,OAAO,IAAI,IAAI;IAcxB,QAAQ,CAAC,MAAM,IAAI,IAAI;IACvB,QAAQ,CAAC,OAAO,IAAI,IAAI;IAExB,QAAQ,CAAC,SAAS,CACd,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB,GAClC,UAAU;CAChB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observable.d.ts","sourceRoot":"","sources":["../../../src/base/observable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACrD,IAAI,MAAM,IAAI,gBAAgB,CAAC;IAC/B,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE;IACrC,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9C,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,gBAAgB,IAAI,IAAI,CAAC;IACzB,SAAS,CACL,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB,GAClC,UAAU,CAAC;CACjB;AAED;;GAEG;AACH,8BAAsB,UAAU,CAAC,CAAC,CAAE,SAAQ,UAAW,YAAW,WAAW,CAAC,CAAC,CAAC;IAC5E,OAAO,CAAC,WAAW,CAAwB;IAC3C,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAc;IAEjD,IAAI,MAAM,IAAI,gBAAgB,CAG7B;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAGlC;IAED,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK7C,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK/C,gBAAgB,IAAI,IAAI;IAOxB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjC,OAAO,IAAI,IAAI;IAKN,OAAO,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"observable.d.ts","sourceRoot":"","sources":["../../../src/base/observable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACrD,IAAI,MAAM,IAAI,gBAAgB,CAAC;IAC/B,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE;IACrC,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9C,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,gBAAgB,IAAI,IAAI,CAAC;IACzB,SAAS,CACL,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB,GAClC,UAAU,CAAC;CACjB;AAED;;GAEG;AACH,8BAAsB,UAAU,CAAC,CAAC,CAAE,SAAQ,UAAW,YAAW,WAAW,CAAC,CAAC,CAAC;IAC5E,OAAO,CAAC,WAAW,CAAwB;IAC3C,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAc;IAEjD,IAAI,MAAM,IAAI,gBAAgB,CAG7B;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAGlC;IAED,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK7C,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAK/C,gBAAgB,IAAI,IAAI;IAOxB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjC,OAAO,IAAI,IAAI;IAKN,OAAO,IAAI,IAAI;IAWxB,QAAQ,CAAC,SAAS,CACd,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB,GAClC,UAAU;CAChB"}
|
|
@@ -9,12 +9,21 @@ export interface IObserver {
|
|
|
9
9
|
clearDependencies(): void;
|
|
10
10
|
notify(): void;
|
|
11
11
|
execute(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Called by a dependency's `dispose()` on each of its remaining dependents, so an
|
|
14
|
+
* observer already queued for the next flush can skip that run instead of reading
|
|
15
|
+
* through the now-disposed dependency and throwing (A2-SEM-1).
|
|
16
|
+
*/
|
|
17
|
+
markDependencyDisposed(): void;
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
20
|
* Base implementation managing dependency tracking for reactive observers.
|
|
15
21
|
*/
|
|
16
22
|
export declare abstract class Observer extends Disposable implements IObserver {
|
|
17
23
|
private _dependencies;
|
|
24
|
+
/** Set when a dependency disposed while this observer was queued; consumed by the next execute(). */
|
|
25
|
+
protected _dependencyDisposed: boolean;
|
|
26
|
+
markDependencyDisposed(): void;
|
|
18
27
|
registerDependency(dependency: IObservable<unknown>): void;
|
|
19
28
|
unregisterDependency(dependency: IObservable<unknown>): void;
|
|
20
29
|
clearDependencies(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../../../src/base/observer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,iBAAiB,IAAI,IAAI,CAAC;IAC1B,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../../../src/base/observer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,iBAAiB,IAAI,IAAI,CAAC;IAC1B,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB;;;;OAIG;IACH,sBAAsB,IAAI,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,8BAAsB,QAAS,SAAQ,UAAW,YAAW,SAAS;IAClE,OAAO,CAAC,aAAa,CAAmC;IACxD,qGAAqG;IACrG,SAAS,CAAC,mBAAmB,UAAS;IAEtC,sBAAsB,IAAI,IAAI;IAI9B,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAM1D,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAM5D,iBAAiB,IAAI,IAAI;IAQhB,OAAO,IAAI,IAAI;IAQxB,QAAQ,CAAC,MAAM,IAAI,IAAI;IACvB,QAAQ,CAAC,OAAO,IAAI,IAAI;CAC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid.d.ts","sourceRoot":"","sources":["../../../src/converters/solid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,QAAQ,EAAwB,MAAM,UAAU,CAAC;AACjH,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAa,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"solid.d.ts","sourceRoot":"","sources":["../../../src/converters/solid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,QAAQ,EAAwB,MAAM,UAAU,CAAC;AACjH,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAa,MAAM,iBAAiB,CAAC;AA8EhH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AACzD,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effectNode.d.ts","sourceRoot":"","sources":["../../../src/nodes/effectNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAkB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5D;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,SAAQ,QAAS,YAAW,UAAU;IAC7D,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAC,CAAsB;IACvC,OAAO,CAAC,UAAU,CAAC,CAAwB;IAC3C,wFAAwF;IACxF,OAAO,CAAC,OAAO,CAAS;IACxB,2FAA2F;IAC3F,OAAO,CAAC,qBAAqB,CAAS;IACtC,yEAAyE;IACzE,OAAO,CAAC,YAAY,CAAK;IACzB,8DAA8D;IAC9D,OAAO,CAAC,cAAc,CAAK;gBAGvB,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EACxB,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC7B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB;IAYrC,MAAM,IAAI,IAAI;IAsBd,OAAO,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"effectNode.d.ts","sourceRoot":"","sources":["../../../src/nodes/effectNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAkB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5D;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,SAAQ,QAAS,YAAW,UAAU;IAC7D,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAC,CAAsB;IACvC,OAAO,CAAC,UAAU,CAAC,CAAwB;IAC3C,wFAAwF;IACxF,OAAO,CAAC,OAAO,CAAS;IACxB,2FAA2F;IAC3F,OAAO,CAAC,qBAAqB,CAAS;IACtC,yEAAyE;IACzE,OAAO,CAAC,YAAY,CAAK;IACzB,8DAA8D;IAC9D,OAAO,CAAC,cAAc,CAAK;gBAGvB,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EACxB,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC7B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB;IAYrC,MAAM,IAAI,IAAI;IAsBd,OAAO,IAAI,IAAI;CA6ClB"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { FlowEffect, FlowOnDataListener, FlowOnErrorListener, FlowOnPendingListener, FlowTracker, NotPromise } from '../api/index.js';
|
|
2
2
|
import { Node } from '../base/index.js';
|
|
3
3
|
import { Scheduler } from '../schedulers/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Global-registry symbol branding a {@link ValueNode} instance, so `from()` can recognize a node
|
|
6
|
+
* built by a different copy of the library — where its class objects, and thus `instanceof`, differ.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare const PICOFLOW_VALUE_NODE: unique symbol;
|
|
4
10
|
/**
|
|
5
11
|
* Base class for reactive values that compute, cache, and propagate changes through the dependency graph.
|
|
6
12
|
* @internal
|
|
7
13
|
*/
|
|
8
14
|
export declare abstract class ValueNode<T> extends Node<T> {
|
|
15
|
+
readonly [PICOFLOW_VALUE_NODE] = true;
|
|
9
16
|
protected abstract _scheduler: Scheduler;
|
|
10
17
|
protected _value?: NotPromise<T>;
|
|
11
18
|
protected _error?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valueNode.d.ts","sourceRoot":"","sources":["../../../src/nodes/valueNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,UAAU,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAkB,IAAI,EAAwC,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGtE;;;GAGG;AACH,8BAAsB,SAAS,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,CAAC,CAAC;IAC9C,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IACzC,SAAS,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACjC,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAElB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IA6BjC,MAAM,IAAI,IAAI;IAUd,OAAO,IAAI,IAAI;IAKxB,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,CAAC;IA8BtB,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC;IAkCxB,OAAO,IAAI,IAAI;IAUf,SAAS,CACL,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB,GAClC,UAAU;CAKhB"}
|
|
1
|
+
{"version":3,"file":"valueNode.d.ts","sourceRoot":"","sources":["../../../src/nodes/valueNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,UAAU,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAkB,IAAI,EAAwC,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGtE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,eAA8B,CAAC;AAE/D;;;GAGG;AACH,8BAAsB,SAAS,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,CAAC,CAAC;IAC9C,QAAQ,CAAC,CAAC,mBAAmB,CAAC,QAAQ;IACtC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IACzC,SAAS,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACjC,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAElB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IA6BjC,MAAM,IAAI,IAAI;IAUd,OAAO,IAAI,IAAI;IAKxB,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,CAAC;IA8BtB,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC;IAkCxB,OAAO,IAAI,IAAI;IAUf,SAAS,CACL,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB,EAC7B,SAAS,CAAC,EAAE,qBAAqB,GAClC,UAAU;CAKhB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ersbeth/picoflow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Minimal Dataflow library for TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -23,10 +23,11 @@
|
|
|
23
23
|
"test:coverage": "pnpm exec vitest run --coverage",
|
|
24
24
|
"test:browser": "pnpm exec vitest --config=vitest.browser.config.ts",
|
|
25
25
|
"test:browser:coverage": "pnpm exec vitest run --config=vitest.browser.config.ts --coverage",
|
|
26
|
+
"test:coverage:merge": "rm -rf .vitest-reports && pnpm exec vitest run --coverage --reporter=blob --outputFile=.vitest-reports/node.json && pnpm exec vitest run --config=vitest.browser.config.ts --coverage --reporter=blob --outputFile=.vitest-reports/browser.json && pnpm exec vitest --mergeReports=.vitest-reports --coverage",
|
|
26
27
|
"ci:check": "pnpm exec tsc --noEmit",
|
|
27
28
|
"ci:lint": "pnpm exec biome check .",
|
|
28
|
-
"ci:test": "pnpm exec vitest run",
|
|
29
|
-
"ci:test:browser": "pnpm exec vitest run --config=vitest.browser.config.ts",
|
|
29
|
+
"ci:test": "pnpm exec vitest run --coverage",
|
|
30
|
+
"ci:test:browser": "pnpm exec vitest run --config=vitest.browser.config.ts --coverage",
|
|
30
31
|
"ci:pack:smoke": "node tools/release/smoke-tarball.mjs",
|
|
31
32
|
"ci:release:check": "node tools/release/check-release.mjs",
|
|
32
33
|
"playwright:install": "playwright install --with-deps chromium",
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
},
|
|
70
71
|
"repository": {
|
|
71
72
|
"type": "git",
|
|
72
|
-
"url": "git+
|
|
73
|
+
"url": "git+https://gitlab.com/ersbeth-web/picoflow.git"
|
|
73
74
|
},
|
|
74
75
|
"author": "Elisabeth Rousset",
|
|
75
76
|
"license": "MIT",
|