@ersbeth/picoflow 2.3.2 → 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 +34 -5
- 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 +37 -25
- package/dist/picoflow.js +0 -1283
- package/dist/types/converters/index.d.ts +0 -1
- package/dist/types/core/api/base/flowConfig.d.ts +0 -17
- package/dist/types/core/api/base/flowDisposable.d.ts +0 -40
- package/dist/types/core/api/base/flowErrors.d.ts +0 -1
- package/dist/types/core/api/base/flowObservable.d.ts +0 -26
- package/dist/types/core/api/base/flowPrimitiveOptions.d.ts +0 -14
- package/dist/types/core/api/base/flowSubscribable.d.ts +0 -78
- package/dist/types/core/api/base/flowTracker.d.ts +0 -7
- package/dist/types/core/api/base/index.d.ts +0 -7
- package/dist/types/core/api/index.d.ts +0 -2
- package/dist/types/core/api/nodes/async/flowConstantAsync.d.ts +0 -32
- package/dist/types/core/api/nodes/async/flowDerivationAsync.d.ts +0 -37
- package/dist/types/core/api/nodes/async/flowStateAsync.d.ts +0 -42
- package/dist/types/core/api/nodes/async/flowWritableDerivationAsync.d.ts +0 -31
- package/dist/types/core/api/nodes/async/index.d.ts +0 -4
- package/dist/types/core/api/nodes/collections/flowArray.d.ts +0 -138
- package/dist/types/core/api/nodes/collections/flowMap.d.ts +0 -100
- package/dist/types/core/api/nodes/collections/index.d.ts +0 -2
- package/dist/types/core/api/nodes/flowEffect.d.ts +0 -28
- package/dist/types/core/api/nodes/flowSignal.d.ts +0 -26
- package/dist/types/core/api/nodes/flowValue.d.ts +0 -35
- package/dist/types/core/api/nodes/index.d.ts +0 -7
- package/dist/types/core/api/nodes/sync/flowConstant.d.ts +0 -30
- package/dist/types/core/api/nodes/sync/flowDerivation.d.ts +0 -37
- package/dist/types/core/api/nodes/sync/flowState.d.ts +0 -40
- package/dist/types/core/api/nodes/sync/flowWritableDerivation.d.ts +0 -29
- package/dist/types/core/api/nodes/sync/index.d.ts +0 -4
- package/dist/types/core/api/nodes/utils.d.ts +0 -22
- package/dist/types/core/base/dependenciesRegistry.d.ts +0 -1
- package/dist/types/core/base/dependentsRegistry.d.ts +0 -1
- package/dist/types/core/base/disposable.d.ts +0 -15
- package/dist/types/core/base/errors.d.ts +0 -31
- package/dist/types/core/base/executionStack.d.ts +0 -31
- package/dist/types/core/base/index.d.ts +0 -8
- package/dist/types/core/base/node.d.ts +0 -27
- package/dist/types/core/base/observable.d.ts +0 -34
- package/dist/types/core/base/observer.d.ts +0 -33
- package/dist/types/core/nodes/actionNode.d.ts +0 -1
- package/dist/types/core/nodes/arrayNode.d.ts +0 -1
- package/dist/types/core/nodes/effectNode.d.ts +0 -1
- package/dist/types/core/nodes/index.d.ts +0 -8
- package/dist/types/core/nodes/mapNode.d.ts +0 -1
- package/dist/types/core/nodes/signalNode.d.ts +0 -1
- package/dist/types/core/nodes/valueAsyncNode.d.ts +0 -1
- package/dist/types/core/nodes/valueNode.d.ts +0 -1
- package/dist/types/core/nodes/valueSyncNode.d.ts +0 -1
- package/dist/types/core/schedulers/asyncResolver.d.ts +0 -1
- package/dist/types/core/schedulers/asyncScheduler.d.ts +0 -1
- package/dist/types/core/schedulers/index.d.ts +0 -4
- package/dist/types/core/schedulers/pendingError.d.ts +0 -1
- package/dist/types/core/schedulers/scheduler.d.ts +0 -1
- package/dist/types/core/schedulers/syncResolver.d.ts +0 -1
- package/dist/types/core/schedulers/syncScheduler.d.ts +0 -1
- package/dist/types/index.d.ts +0 -2
package/dist/index.js
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import { _ as PicoFlowPreconditionError, a as ReadableNode, g as PicoFlowInternalError, h as PicoFlowError, m as PicoFlowDisposedError, n as asyncCore, o as EffectNode, p as PicoFlowCycleError, r as syncCore, u as ExecutionStack } from "./shared.js";
|
|
2
|
+
import { n as subscribe, r as DerivationNode, t as derivation } from "./shared2.js";
|
|
3
|
+
import { t as SignalNode } from "./shared3.js";
|
|
4
|
+
|
|
5
|
+
//#region src/core/api/base/flowConfig.ts
|
|
6
|
+
/**
|
|
7
|
+
* Installs a global handler for errors that escape the reactive flush.
|
|
8
|
+
*
|
|
9
|
+
* All reactive work — recomputing derivations, running effects — happens inside an
|
|
10
|
+
* asynchronous flush. Without this hook, an error escaping the flush (an effect callback
|
|
11
|
+
* that throws with no `onError`, or a feedback loop between two effects) becomes an
|
|
12
|
+
* uncaught exception, which terminates a Node process. Installing a handler here makes
|
|
13
|
+
* that error catchable instead.
|
|
14
|
+
*
|
|
15
|
+
* Defaults to `console.error`. The handler replaces any previously installed handler —
|
|
16
|
+
* it is not additive.
|
|
17
|
+
*
|
|
18
|
+
* @param handler - Called with the error that escaped the flush.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
function onFlushError(handler) {
|
|
23
|
+
ExecutionStack.setFlushErrorHandler(handler);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns the scheduler to its just-started state: both queues emptied, any scheduled flush
|
|
27
|
+
* cancelled, and the flush error handler back to its `console.error` default.
|
|
28
|
+
*
|
|
29
|
+
* Scheduling is process-global, so reactive work left over from one test can still be waiting when
|
|
30
|
+
* the next one starts, and a handler installed with {@link onFlushError} stays installed. Calling
|
|
31
|
+
* this from an `afterEach` gives each test a clean scheduler. It is the supported alternative to
|
|
32
|
+
* reaching into internals, and applications testing their own code need it for the same reason
|
|
33
|
+
* PicoFlow's own suite does.
|
|
34
|
+
*
|
|
35
|
+
* Queued work is **discarded, not run** — this cancels pending reactivity rather than flushing it.
|
|
36
|
+
* Primitives created before the call stay usable, but any recomputation that was waiting is lost:
|
|
37
|
+
* those nodes only recompute once something notifies them again. To let pending work complete
|
|
38
|
+
* instead, `await` a value with `pick()`.
|
|
39
|
+
*
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
function reset() {
|
|
43
|
+
ExecutionStack.reset();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/core/api/base/flowDisposable.ts
|
|
48
|
+
/**
|
|
49
|
+
* Type guard to check if an object implements the FlowDisposable interface.
|
|
50
|
+
*
|
|
51
|
+
* Useful for conditionally disposing objects that may or may not be disposable,
|
|
52
|
+
* such as when cleaning up mixed collections of values. Returns true if the object
|
|
53
|
+
* has a `dispose` method, providing type-safe access to disposal functionality.
|
|
54
|
+
*
|
|
55
|
+
* @param obj - The object to check
|
|
56
|
+
* @returns True if the object implements FlowDisposable, false otherwise
|
|
57
|
+
*
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
function isDisposable(obj) {
|
|
61
|
+
return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/core/nodes/constantNode.ts
|
|
66
|
+
/**
|
|
67
|
+
* The primitive behind `constant()` and `constantAsync()`: a {@link ReadableNode} and nothing more.
|
|
68
|
+
* It computes once, lazily, and carries no mutator on its prototype chain (#26, A2-SEM-3).
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
var ConstantNode = class extends ReadableNode {};
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/core/api/nodes/async/flowConstantAsync.ts
|
|
75
|
+
function constantAsync(valueOrInitializer, options) {
|
|
76
|
+
return new ConstantNode(asyncCore(valueOrInitializer, options), "constantAsync");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/core/api/nodes/async/flowDerivationAsync.ts
|
|
81
|
+
/**
|
|
82
|
+
* Creates an async derived value that automatically recomputes when dependencies change.
|
|
83
|
+
*
|
|
84
|
+
* The async compute function tracks dependencies and returns a promise. When any tracked dependency changes,
|
|
85
|
+
* the function runs again and returns a new promise. The resolved value is cached until the next change.
|
|
86
|
+
* While the promise is pending, reactive computations that access this value receive PendingError and automatically
|
|
87
|
+
* retry once resolved. Use for derived data from async sources like filtered API results or computed database queries.
|
|
88
|
+
*
|
|
89
|
+
* @param compute - Async function that accesses dependencies and returns a promise of the derived value
|
|
90
|
+
* @param options - Optional settings; `name` identifies this primitive in error messages, `equals` overrides the
|
|
91
|
+
* default `===` change detection, `eager` recomputes on dirty instead of on read (#118)
|
|
92
|
+
* @returns A FlowDerivationAsync that provides read-only access to the resolved computed value
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
function derivationAsync(compute, options) {
|
|
97
|
+
return new DerivationNode(asyncCore(compute, options, true), "derivationAsync");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/core/nodes/stateNode.ts
|
|
102
|
+
/**
|
|
103
|
+
* The primitive behind `state()` and `stateAsync()`: readable and writable. `set` replaces, `update`
|
|
104
|
+
* derives the next value from the stabilised current one (A2-TYPE-1, #43). `I` is the core's input
|
|
105
|
+
* type — the value for a sync state, a promise of it for an async one. No `refresh`: a state has
|
|
106
|
+
* nothing to recompute, and re-running its initializer would undo every write since.
|
|
107
|
+
* @internal
|
|
108
|
+
*/
|
|
109
|
+
var StateNode = class extends ReadableNode {
|
|
110
|
+
get _core() {
|
|
111
|
+
return super._core;
|
|
112
|
+
}
|
|
113
|
+
set(input) {
|
|
114
|
+
this._core.overwrite(input);
|
|
115
|
+
}
|
|
116
|
+
update(updater) {
|
|
117
|
+
this._core.update(updater);
|
|
118
|
+
}
|
|
119
|
+
/** Notifies dependents without changing the value — a write, hence its place here (#132). */
|
|
120
|
+
trigger() {
|
|
121
|
+
this._core.trigger();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/core/api/nodes/async/flowStateAsync.ts
|
|
127
|
+
function stateAsync(valueOrInitializer, options) {
|
|
128
|
+
return new StateNode(asyncCore(valueOrInitializer, options), "stateAsync");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/core/nodes/writableDerivationNode.ts
|
|
133
|
+
/**
|
|
134
|
+
* The primitive behind `writableDerivation()` and `writableDerivationAsync()`: a {@link StateNode}
|
|
135
|
+
* that also recomputes from its dependencies, hence `refresh()` on top of `set` / `update`. Each
|
|
136
|
+
* mutator is a one-line delegation to the core, so declaring all three here duplicates nothing —
|
|
137
|
+
* which is what lets the sync/async × state/derivation diamond go (#114, step 4 of #110).
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
140
|
+
var WritableDerivationNode = class extends StateNode {
|
|
141
|
+
refresh() {
|
|
142
|
+
this._core.refresh();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/core/api/nodes/async/flowWritableDerivationAsync.ts
|
|
148
|
+
/**
|
|
149
|
+
* Creates an async derived value that recomputes automatically but can also be manually overridden.
|
|
150
|
+
*
|
|
151
|
+
* The async compute function tracks dependencies and returns a promise. When any tracked dependency changes,
|
|
152
|
+
* the function runs again and returns a new promise. However, you can also call `set()` to override the computed
|
|
153
|
+
* value with a new promise. Use for async values that are normally derived but need occasional manual adjustments,
|
|
154
|
+
* such as editable async calculated fields or user-correctable async totals.
|
|
155
|
+
*
|
|
156
|
+
* @param compute - Async function that accesses dependencies and returns a promise of the derived value
|
|
157
|
+
* @param options - Optional settings; `name` identifies this primitive in error messages, `equals` overrides the
|
|
158
|
+
* default `===` change detection, `eager` recomputes on dirty instead of on read (#118)
|
|
159
|
+
* @returns A FlowWritableDerivationAsync that provides both reactive async computation and manual control
|
|
160
|
+
*
|
|
161
|
+
* @public
|
|
162
|
+
*/
|
|
163
|
+
function writableDerivationAsync(compute, options) {
|
|
164
|
+
return new WritableDerivationNode(asyncCore(compute, options, true), "writableDerivationAsync");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/core/nodes/actionNode.ts
|
|
169
|
+
/**
|
|
170
|
+
* Reactive state that tracks the latest action while delivering every emitted action
|
|
171
|
+
* to `.subscribe()` callbacks, including multiple mutations within the same batch.
|
|
172
|
+
*
|
|
173
|
+
* Two read models:
|
|
174
|
+
* - get() / pick(): last-write-wins snapshot (current action value).
|
|
175
|
+
* - subscribe(): event stream — one onData call per emitted action, even when several
|
|
176
|
+
* mutations happen before the next flush. Each subscriber owns its own queue so
|
|
177
|
+
* delivery is not shared or stolen between subscribers.
|
|
178
|
+
*
|
|
179
|
+
* @internal
|
|
180
|
+
*/
|
|
181
|
+
var ActionNode = class extends StateNode {
|
|
182
|
+
#slots = /* @__PURE__ */ new Set();
|
|
183
|
+
constructor(value, name) {
|
|
184
|
+
super(syncCore(value, { name }), "action");
|
|
185
|
+
}
|
|
186
|
+
/** The engine's cached value — an action node is built with a value and is always resolved. */
|
|
187
|
+
get _value() {
|
|
188
|
+
return this._core.current;
|
|
189
|
+
}
|
|
190
|
+
_enqueueForSubscribers(action) {
|
|
191
|
+
for (const slot of this.#slots) slot.queue.push(action);
|
|
192
|
+
}
|
|
193
|
+
set(value) {
|
|
194
|
+
this._core.assertAlive();
|
|
195
|
+
if (this.status === "resolved" && value === this._value) return;
|
|
196
|
+
this._enqueueForSubscribers(value);
|
|
197
|
+
super.set(value);
|
|
198
|
+
}
|
|
199
|
+
update(updater) {
|
|
200
|
+
this._core.assertAlive();
|
|
201
|
+
let action;
|
|
202
|
+
try {
|
|
203
|
+
action = updater(this._value);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
this._core.fail(error);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.set(action);
|
|
209
|
+
}
|
|
210
|
+
subscribe(onValue, onError, onPending, options) {
|
|
211
|
+
this._core.assertAlive();
|
|
212
|
+
const slot = { queue: [] };
|
|
213
|
+
this.#slots.add(slot);
|
|
214
|
+
const effect = new EffectNode((tracker) => {
|
|
215
|
+
this.watch(tracker);
|
|
216
|
+
if (slot.queue.length === 0) return [this.get(tracker)];
|
|
217
|
+
return slot.queue.splice(0);
|
|
218
|
+
}, (actions) => {
|
|
219
|
+
for (const action of actions) onValue(action);
|
|
220
|
+
}, onError, onPending, options?.name);
|
|
221
|
+
effect.onDispose(() => {
|
|
222
|
+
this.#slots.delete(slot);
|
|
223
|
+
});
|
|
224
|
+
return effect;
|
|
225
|
+
}
|
|
226
|
+
dispose() {
|
|
227
|
+
super.dispose();
|
|
228
|
+
this.#slots.clear();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region src/core/nodes/arrayNode.ts
|
|
234
|
+
/**
|
|
235
|
+
* Internal implementation of reactive array with mutation tracking.
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
var ArrayNode = class extends ReadableNode {
|
|
239
|
+
$lastAction;
|
|
240
|
+
get _core() {
|
|
241
|
+
return super._core;
|
|
242
|
+
}
|
|
243
|
+
/** The live backing store, owned by the engine and mutated in place here. */
|
|
244
|
+
get _value() {
|
|
245
|
+
return this._core.current;
|
|
246
|
+
}
|
|
247
|
+
set _value(value) {
|
|
248
|
+
this._core.current = value;
|
|
249
|
+
}
|
|
250
|
+
constructor(value = [], name) {
|
|
251
|
+
super(syncCore(value, { name }), "array");
|
|
252
|
+
this.$lastAction = new ActionNode({
|
|
253
|
+
type: "set",
|
|
254
|
+
setItems: value,
|
|
255
|
+
clearedItems: []
|
|
256
|
+
}, name && `${name}.$lastAction`);
|
|
257
|
+
}
|
|
258
|
+
/** Notifies dependents without changing the contents. */
|
|
259
|
+
trigger() {
|
|
260
|
+
this._core.trigger();
|
|
261
|
+
}
|
|
262
|
+
set(items) {
|
|
263
|
+
this._core.assertAlive();
|
|
264
|
+
const previousValue = this._value;
|
|
265
|
+
if (!this._core.overwrite(items)) return;
|
|
266
|
+
this.$lastAction.set({
|
|
267
|
+
type: "set",
|
|
268
|
+
setItems: this._value,
|
|
269
|
+
clearedItems: previousValue
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
setAt(index, item) {
|
|
273
|
+
this._core.assertAlive();
|
|
274
|
+
if (index < 0 || index >= this._value.length || !Number.isInteger(index)) throw new PicoFlowPreconditionError("[PicoFlow] Index out of bounds");
|
|
275
|
+
const previousValue = this._value[index];
|
|
276
|
+
this._value[index] = item;
|
|
277
|
+
this._core.notifyDependents();
|
|
278
|
+
this.$lastAction.set({
|
|
279
|
+
type: "setAt",
|
|
280
|
+
index,
|
|
281
|
+
setItem: item,
|
|
282
|
+
clearedItem: previousValue
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
push(item) {
|
|
286
|
+
this._core.assertAlive();
|
|
287
|
+
this._value.push(item);
|
|
288
|
+
this._core.notifyDependents();
|
|
289
|
+
this.$lastAction.set({
|
|
290
|
+
type: "push",
|
|
291
|
+
addedItem: item
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
pop() {
|
|
295
|
+
this._core.assertAlive();
|
|
296
|
+
if (this._value.length === 0) return void 0;
|
|
297
|
+
const item = this._value.pop();
|
|
298
|
+
this._core.notifyDependents();
|
|
299
|
+
this.$lastAction.set({
|
|
300
|
+
type: "pop",
|
|
301
|
+
removedItem: item
|
|
302
|
+
});
|
|
303
|
+
return item;
|
|
304
|
+
}
|
|
305
|
+
unshift(item) {
|
|
306
|
+
this._core.assertAlive();
|
|
307
|
+
this._value.unshift(item);
|
|
308
|
+
this._core.notifyDependents();
|
|
309
|
+
this.$lastAction.set({
|
|
310
|
+
type: "unshift",
|
|
311
|
+
addedItem: item
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
shift() {
|
|
315
|
+
this._core.assertAlive();
|
|
316
|
+
if (this._value.length === 0) return void 0;
|
|
317
|
+
const item = this._value.shift();
|
|
318
|
+
this._core.notifyDependents();
|
|
319
|
+
this.$lastAction.set({
|
|
320
|
+
type: "shift",
|
|
321
|
+
removedItem: item
|
|
322
|
+
});
|
|
323
|
+
return item;
|
|
324
|
+
}
|
|
325
|
+
splice(start, deleteCount, ...newItems) {
|
|
326
|
+
this._core.assertAlive();
|
|
327
|
+
const length = this._value.length;
|
|
328
|
+
if (!Number.isInteger(start) || start < 0 || start > length) throw new PicoFlowPreconditionError("[PicoFlow] Index out of bounds");
|
|
329
|
+
if (!Number.isInteger(deleteCount) || deleteCount < 0 || deleteCount > length - start) throw new PicoFlowPreconditionError("[PicoFlow] Delete count out of bounds");
|
|
330
|
+
const items = this._value.splice(start, deleteCount, ...newItems);
|
|
331
|
+
this._core.notifyDependents();
|
|
332
|
+
this.$lastAction.set({
|
|
333
|
+
type: "splice",
|
|
334
|
+
start,
|
|
335
|
+
deleteCount,
|
|
336
|
+
addedItems: newItems,
|
|
337
|
+
removedItems: items
|
|
338
|
+
});
|
|
339
|
+
return items;
|
|
340
|
+
}
|
|
341
|
+
clear() {
|
|
342
|
+
this._core.assertAlive();
|
|
343
|
+
const previousValue = [...this._value];
|
|
344
|
+
this._value = [];
|
|
345
|
+
this._core.notifyDependents();
|
|
346
|
+
this.$lastAction.set({
|
|
347
|
+
type: "clear",
|
|
348
|
+
clearedItems: previousValue
|
|
349
|
+
});
|
|
350
|
+
return previousValue;
|
|
351
|
+
}
|
|
352
|
+
dispose() {
|
|
353
|
+
super.dispose();
|
|
354
|
+
this.$lastAction.dispose();
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/core/api/nodes/collections/flowArray.ts
|
|
360
|
+
/**
|
|
361
|
+
* Creates a reactive array with mutation methods and fine-grained action tracking.
|
|
362
|
+
*
|
|
363
|
+
* The array starts with the provided initial items (or empty if none provided). All mutation methods
|
|
364
|
+
* (push, pop, splice, etc.) notify dependents and emit detailed action information to `$lastAction`.
|
|
365
|
+
* Use the array itself for coarse-grained reactivity, or subscribe to `$lastAction` for fine-grained
|
|
366
|
+
* reactivity to specific mutation types. Useful for reactive lists, collections, or any data that
|
|
367
|
+
* needs array-like operations with automatic change propagation.
|
|
368
|
+
*
|
|
369
|
+
* @param initial - Optional initial array of items
|
|
370
|
+
* @param options - Optional settings; `name` identifies this primitive in error messages
|
|
371
|
+
* @returns A FlowArray with reactive mutation methods
|
|
372
|
+
*
|
|
373
|
+
* @public
|
|
374
|
+
*/
|
|
375
|
+
function array(initial, options) {
|
|
376
|
+
return new ArrayNode(initial, options?.name);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/core/nodes/mapNode.ts
|
|
381
|
+
/**
|
|
382
|
+
* Internal implementation of reactive map with mutation tracking.
|
|
383
|
+
* @internal
|
|
384
|
+
*/
|
|
385
|
+
var MapNode = class extends ReadableNode {
|
|
386
|
+
$lastAction;
|
|
387
|
+
get _core() {
|
|
388
|
+
return super._core;
|
|
389
|
+
}
|
|
390
|
+
/** The live backing store, owned by the engine and mutated in place here. */
|
|
391
|
+
get _value() {
|
|
392
|
+
return this._core.current;
|
|
393
|
+
}
|
|
394
|
+
set _value(value) {
|
|
395
|
+
this._core.current = value;
|
|
396
|
+
}
|
|
397
|
+
constructor(value = /* @__PURE__ */ new Map(), name) {
|
|
398
|
+
super(syncCore(value, { name }), "map");
|
|
399
|
+
this.$lastAction = new ActionNode({
|
|
400
|
+
type: "set",
|
|
401
|
+
setMap: value,
|
|
402
|
+
clearedMap: /* @__PURE__ */ new Map()
|
|
403
|
+
}, name && `${name}.$lastAction`);
|
|
404
|
+
}
|
|
405
|
+
add(key, value) {
|
|
406
|
+
this._core.assertAlive();
|
|
407
|
+
if (this._value.has(key)) throw new PicoFlowPreconditionError("[PicoFlow] Key already exists");
|
|
408
|
+
this._value.set(key, value);
|
|
409
|
+
this._core.notifyDependents();
|
|
410
|
+
this.$lastAction.set({
|
|
411
|
+
type: "add",
|
|
412
|
+
key,
|
|
413
|
+
addedValue: value
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
setAt(key, value) {
|
|
417
|
+
this._core.assertAlive();
|
|
418
|
+
if (!this._value.has(key)) throw new PicoFlowPreconditionError("[PicoFlow] Key does not exist");
|
|
419
|
+
const previousValue = this._value.get(key);
|
|
420
|
+
this._value.set(key, value);
|
|
421
|
+
this._core.notifyDependents();
|
|
422
|
+
this.$lastAction.set({
|
|
423
|
+
type: "setAt",
|
|
424
|
+
key,
|
|
425
|
+
setValue: value,
|
|
426
|
+
clearedValue: previousValue
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
delete(key) {
|
|
430
|
+
this._core.assertAlive();
|
|
431
|
+
if (!this._value.has(key)) throw new PicoFlowPreconditionError("[PicoFlow] Key does not exist");
|
|
432
|
+
const value = this._value.get(key);
|
|
433
|
+
this._value.delete(key);
|
|
434
|
+
this._core.notifyDependents();
|
|
435
|
+
this.$lastAction.set({
|
|
436
|
+
type: "delete",
|
|
437
|
+
key,
|
|
438
|
+
removedValue: value
|
|
439
|
+
});
|
|
440
|
+
return value;
|
|
441
|
+
}
|
|
442
|
+
/** Notifies dependents without changing the contents. */
|
|
443
|
+
trigger() {
|
|
444
|
+
this._core.trigger();
|
|
445
|
+
}
|
|
446
|
+
set(map) {
|
|
447
|
+
this._core.assertAlive();
|
|
448
|
+
const previousValue = this._value;
|
|
449
|
+
if (!this._core.overwrite(map)) return;
|
|
450
|
+
this.$lastAction.set({
|
|
451
|
+
type: "set",
|
|
452
|
+
setMap: this._value,
|
|
453
|
+
clearedMap: previousValue
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
clear() {
|
|
457
|
+
this._core.assertAlive();
|
|
458
|
+
const previousValue = this._value;
|
|
459
|
+
this._value = /* @__PURE__ */ new Map();
|
|
460
|
+
this._core.notifyDependents();
|
|
461
|
+
this.$lastAction.set({
|
|
462
|
+
type: "clear",
|
|
463
|
+
clearedMap: previousValue
|
|
464
|
+
});
|
|
465
|
+
return previousValue;
|
|
466
|
+
}
|
|
467
|
+
dispose() {
|
|
468
|
+
super.dispose();
|
|
469
|
+
this.$lastAction.dispose();
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/core/api/nodes/collections/flowMap.ts
|
|
475
|
+
function map(initial, options) {
|
|
476
|
+
if (initial instanceof Map) return new MapNode(initial, options?.name);
|
|
477
|
+
return new MapNode(new Map(initial ? Object.entries(initial) : []), options?.name);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/core/api/nodes/flowSignal.ts
|
|
482
|
+
/**
|
|
483
|
+
* Creates a signal that can be manually triggered to notify dependents.
|
|
484
|
+
*
|
|
485
|
+
* Signals act as event emitters in the reactive graph. Call `trigger()` to notify all subscribers
|
|
486
|
+
* and reactive computations that depend on the signal. Unlike state or derivations, signals don't
|
|
487
|
+
* carry data values - they simply represent that an event occurred.
|
|
488
|
+
*
|
|
489
|
+
* @param options - Optional settings; `name` identifies this primitive in error messages
|
|
490
|
+
* @returns A FlowSignal that can be triggered manually and subscribed to
|
|
491
|
+
*
|
|
492
|
+
* @public
|
|
493
|
+
*/
|
|
494
|
+
function signal(options) {
|
|
495
|
+
return new SignalNode(options?.name);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/core/api/nodes/sync/flowConstant.ts
|
|
500
|
+
/**
|
|
501
|
+
* Creates a constant reactive value that initializes lazily and never recomputes.
|
|
502
|
+
*
|
|
503
|
+
* The initializer function runs once on first access, and the result is cached permanently.
|
|
504
|
+
* Unlike state or derivations, constants never react to changes - they represent immutable values
|
|
505
|
+
* in the reactive graph. Useful for expensive one-time computations or static configuration.
|
|
506
|
+
*
|
|
507
|
+
* @param initializer - Function that computes the constant value on first access
|
|
508
|
+
* @param options - Optional settings; `name` identifies this primitive in error messages, `equals` overrides the default `===` change detection
|
|
509
|
+
* @returns A FlowConstant that provides read-only access to the cached value
|
|
510
|
+
*
|
|
511
|
+
* @public
|
|
512
|
+
*/
|
|
513
|
+
function constant(initializer, options) {
|
|
514
|
+
return new ConstantNode(syncCore(initializer, options), "constant");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region src/core/api/nodes/sync/flowState.ts
|
|
519
|
+
function state(valueOrInitializer, options) {
|
|
520
|
+
return new StateNode(syncCore(valueOrInitializer, options), "state");
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/core/api/nodes/sync/flowWritableDerivation.ts
|
|
525
|
+
/**
|
|
526
|
+
* Creates a derived value that recomputes automatically but can also be manually overridden.
|
|
527
|
+
*
|
|
528
|
+
* The compute function tracks dependencies and recomputes when they change, just like a regular derivation.
|
|
529
|
+
* However, you can also call `set()` to override the computed value. Use this for values that are normally
|
|
530
|
+
* derived but need occasional manual adjustments, such as editable calculated fields or user-correctable totals.
|
|
531
|
+
*
|
|
532
|
+
* @param compute - Function that accesses dependencies and computes the derived value
|
|
533
|
+
* @param options - Optional settings; `name` identifies this primitive in error messages, `equals` overrides the
|
|
534
|
+
* default `===` change detection, `eager` recomputes on dirty instead of on read (#118)
|
|
535
|
+
* @returns A FlowWritableDerivation that provides both reactive computation and manual control
|
|
536
|
+
*
|
|
537
|
+
* @public
|
|
538
|
+
*/
|
|
539
|
+
function writableDerivation(compute, options) {
|
|
540
|
+
return new WritableDerivationNode(syncCore(compute, options, true), "writableDerivation");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
//#endregion
|
|
544
|
+
export { PicoFlowCycleError, PicoFlowDisposedError, PicoFlowError, PicoFlowInternalError, PicoFlowPreconditionError, array, constant, constantAsync, derivation, derivationAsync, isDisposable, map, onFlushError, reset, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|