@ersbeth/picoflow 1.0.1 → 1.1.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.
Files changed (180) hide show
  1. package/.cursor/plans/unifier-flowresource-avec-flowderivation-c9506e24.plan.md +372 -0
  2. package/README.md +17 -1
  3. package/biome.json +4 -1
  4. package/dist/picoflow.js +1129 -661
  5. package/dist/types/flow/base/flowDisposable.d.ts +67 -0
  6. package/dist/types/flow/base/flowDisposable.d.ts.map +1 -0
  7. package/dist/types/flow/base/flowEffect.d.ts +127 -0
  8. package/dist/types/flow/base/flowEffect.d.ts.map +1 -0
  9. package/dist/types/flow/base/flowGraph.d.ts +97 -0
  10. package/dist/types/flow/base/flowGraph.d.ts.map +1 -0
  11. package/dist/types/flow/base/flowSignal.d.ts +134 -0
  12. package/dist/types/flow/base/flowSignal.d.ts.map +1 -0
  13. package/dist/types/flow/base/flowTracker.d.ts +15 -0
  14. package/dist/types/flow/base/flowTracker.d.ts.map +1 -0
  15. package/dist/types/flow/base/index.d.ts +7 -0
  16. package/dist/types/flow/base/index.d.ts.map +1 -0
  17. package/dist/types/flow/base/utils.d.ts +20 -0
  18. package/dist/types/flow/base/utils.d.ts.map +1 -0
  19. package/dist/types/{advanced/array.d.ts → flow/collections/flowArray.d.ts} +50 -12
  20. package/dist/types/flow/collections/flowArray.d.ts.map +1 -0
  21. package/dist/types/flow/collections/flowMap.d.ts +224 -0
  22. package/dist/types/flow/collections/flowMap.d.ts.map +1 -0
  23. package/dist/types/flow/collections/index.d.ts +3 -0
  24. package/dist/types/flow/collections/index.d.ts.map +1 -0
  25. package/dist/types/flow/index.d.ts +4 -0
  26. package/dist/types/flow/index.d.ts.map +1 -0
  27. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts +137 -0
  28. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts.map +1 -0
  29. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts +137 -0
  30. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts.map +1 -0
  31. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts +343 -0
  32. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts.map +1 -0
  33. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts +81 -0
  34. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts.map +1 -0
  35. package/dist/types/flow/nodes/async/flowStateAsync.d.ts +111 -0
  36. package/dist/types/flow/nodes/async/flowStateAsync.d.ts.map +1 -0
  37. package/dist/types/flow/nodes/async/index.d.ts +6 -0
  38. package/dist/types/flow/nodes/async/index.d.ts.map +1 -0
  39. package/dist/types/flow/nodes/index.d.ts +3 -0
  40. package/dist/types/flow/nodes/index.d.ts.map +1 -0
  41. package/dist/types/flow/nodes/sync/flowConstant.d.ts +108 -0
  42. package/dist/types/flow/nodes/sync/flowConstant.d.ts.map +1 -0
  43. package/dist/types/flow/nodes/sync/flowDerivation.d.ts +100 -0
  44. package/dist/types/flow/nodes/sync/flowDerivation.d.ts.map +1 -0
  45. package/dist/types/flow/nodes/sync/flowNode.d.ts +314 -0
  46. package/dist/types/flow/nodes/sync/flowNode.d.ts.map +1 -0
  47. package/dist/types/flow/nodes/sync/flowReadonly.d.ts +57 -0
  48. package/dist/types/flow/nodes/sync/flowReadonly.d.ts.map +1 -0
  49. package/dist/types/flow/nodes/sync/flowState.d.ts +96 -0
  50. package/dist/types/flow/nodes/sync/flowState.d.ts.map +1 -0
  51. package/dist/types/flow/nodes/sync/index.d.ts +6 -0
  52. package/dist/types/flow/nodes/sync/index.d.ts.map +1 -0
  53. package/dist/types/index.d.ts +1 -4
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/dist/types/solid/converters.d.ts +34 -44
  56. package/dist/types/solid/converters.d.ts.map +1 -1
  57. package/dist/types/solid/primitives.d.ts +1 -0
  58. package/dist/types/solid/primitives.d.ts.map +1 -1
  59. package/docs/.vitepress/config.mts +1 -1
  60. package/docs/api/typedoc-sidebar.json +81 -1
  61. package/package.json +60 -58
  62. package/src/flow/base/flowDisposable.ts +71 -0
  63. package/src/flow/base/flowEffect.ts +171 -0
  64. package/src/flow/base/flowGraph.ts +288 -0
  65. package/src/flow/base/flowSignal.ts +207 -0
  66. package/src/flow/base/flowTracker.ts +17 -0
  67. package/src/flow/base/index.ts +6 -0
  68. package/src/flow/base/utils.ts +19 -0
  69. package/src/flow/collections/flowArray.ts +409 -0
  70. package/src/flow/collections/flowMap.ts +398 -0
  71. package/src/flow/collections/index.ts +2 -0
  72. package/src/flow/index.ts +3 -0
  73. package/src/flow/nodes/async/flowConstantAsync.ts +142 -0
  74. package/src/flow/nodes/async/flowDerivationAsync.ts +143 -0
  75. package/src/flow/nodes/async/flowNodeAsync.ts +474 -0
  76. package/src/flow/nodes/async/flowReadonlyAsync.ts +81 -0
  77. package/src/flow/nodes/async/flowStateAsync.ts +116 -0
  78. package/src/flow/nodes/async/index.ts +5 -0
  79. package/src/flow/nodes/await/advanced/index.ts +5 -0
  80. package/src/{advanced → flow/nodes/await/advanced}/resource.ts +37 -3
  81. package/src/{advanced → flow/nodes/await/advanced}/resourceAsync.ts +35 -3
  82. package/src/{advanced → flow/nodes/await/advanced}/stream.ts +40 -2
  83. package/src/{advanced → flow/nodes/await/advanced}/streamAsync.ts +38 -3
  84. package/src/flow/nodes/await/flowConstantAwait.ts +154 -0
  85. package/src/flow/nodes/await/flowDerivationAwait.ts +154 -0
  86. package/src/flow/nodes/await/flowNodeAwait.ts +508 -0
  87. package/src/flow/nodes/await/flowReadonlyAwait.ts +89 -0
  88. package/src/flow/nodes/await/flowStateAwait.ts +130 -0
  89. package/src/flow/nodes/await/index.ts +5 -0
  90. package/src/flow/nodes/index.ts +3 -0
  91. package/src/flow/nodes/sync/flowConstant.ts +111 -0
  92. package/src/flow/nodes/sync/flowDerivation.ts +105 -0
  93. package/src/flow/nodes/sync/flowNode.ts +439 -0
  94. package/src/flow/nodes/sync/flowReadonly.ts +57 -0
  95. package/src/flow/nodes/sync/flowState.ts +101 -0
  96. package/src/flow/nodes/sync/index.ts +5 -0
  97. package/src/index.ts +1 -47
  98. package/src/solid/converters.ts +59 -198
  99. package/src/solid/primitives.ts +4 -0
  100. package/test/base/flowEffect.test.ts +108 -0
  101. package/test/base/flowGraph.test.ts +485 -0
  102. package/test/base/flowSignal.test.ts +372 -0
  103. package/test/collections/flowArray.asyncStates.test.ts +1553 -0
  104. package/test/collections/flowArray.scalars.test.ts +1129 -0
  105. package/test/collections/flowArray.states.test.ts +1365 -0
  106. package/test/collections/flowMap.asyncStates.test.ts +1105 -0
  107. package/test/collections/flowMap.scalars.test.ts +877 -0
  108. package/test/collections/flowMap.states.test.ts +1097 -0
  109. package/test/nodes/async/flowConstantAsync.test.ts +860 -0
  110. package/test/nodes/async/flowDerivationAsync.test.ts +1517 -0
  111. package/test/nodes/async/flowStateAsync.test.ts +1387 -0
  112. package/test/{resource.test.ts → nodes/await/advanced/resource.test.ts} +21 -19
  113. package/test/{resourceAsync.test.ts → nodes/await/advanced/resourceAsync.test.ts} +3 -1
  114. package/test/{stream.test.ts → nodes/await/advanced/stream.test.ts} +30 -28
  115. package/test/{streamAsync.test.ts → nodes/await/advanced/streamAsync.test.ts} +16 -14
  116. package/test/nodes/await/flowConstantAwait.test.ts +643 -0
  117. package/test/nodes/await/flowDerivationAwait.test.ts +1583 -0
  118. package/test/nodes/await/flowStateAwait.test.ts +999 -0
  119. package/test/nodes/mixed/derivation.test.ts +1527 -0
  120. package/test/nodes/sync/flowConstant.test.ts +620 -0
  121. package/test/nodes/sync/flowDerivation.test.ts +1373 -0
  122. package/test/nodes/sync/flowState.test.ts +945 -0
  123. package/test/solid/converters.test.ts +721 -0
  124. package/test/solid/primitives.test.ts +1031 -0
  125. package/tsconfig.json +2 -1
  126. package/vitest.config.ts +7 -1
  127. package/IMPLEMENTATION_GUIDE.md +0 -1578
  128. package/dist/types/advanced/array.d.ts.map +0 -1
  129. package/dist/types/advanced/index.d.ts +0 -9
  130. package/dist/types/advanced/index.d.ts.map +0 -1
  131. package/dist/types/advanced/map.d.ts +0 -166
  132. package/dist/types/advanced/map.d.ts.map +0 -1
  133. package/dist/types/advanced/resource.d.ts +0 -78
  134. package/dist/types/advanced/resource.d.ts.map +0 -1
  135. package/dist/types/advanced/resourceAsync.d.ts +0 -56
  136. package/dist/types/advanced/resourceAsync.d.ts.map +0 -1
  137. package/dist/types/advanced/stream.d.ts +0 -117
  138. package/dist/types/advanced/stream.d.ts.map +0 -1
  139. package/dist/types/advanced/streamAsync.d.ts +0 -97
  140. package/dist/types/advanced/streamAsync.d.ts.map +0 -1
  141. package/dist/types/basic/constant.d.ts +0 -60
  142. package/dist/types/basic/constant.d.ts.map +0 -1
  143. package/dist/types/basic/derivation.d.ts +0 -89
  144. package/dist/types/basic/derivation.d.ts.map +0 -1
  145. package/dist/types/basic/disposable.d.ts +0 -82
  146. package/dist/types/basic/disposable.d.ts.map +0 -1
  147. package/dist/types/basic/effect.d.ts +0 -67
  148. package/dist/types/basic/effect.d.ts.map +0 -1
  149. package/dist/types/basic/index.d.ts +0 -10
  150. package/dist/types/basic/index.d.ts.map +0 -1
  151. package/dist/types/basic/observable.d.ts +0 -83
  152. package/dist/types/basic/observable.d.ts.map +0 -1
  153. package/dist/types/basic/signal.d.ts +0 -69
  154. package/dist/types/basic/signal.d.ts.map +0 -1
  155. package/dist/types/basic/state.d.ts +0 -47
  156. package/dist/types/basic/state.d.ts.map +0 -1
  157. package/dist/types/basic/trackingContext.d.ts +0 -33
  158. package/dist/types/basic/trackingContext.d.ts.map +0 -1
  159. package/dist/types/creators.d.ts +0 -340
  160. package/dist/types/creators.d.ts.map +0 -1
  161. package/src/advanced/array.ts +0 -222
  162. package/src/advanced/index.ts +0 -12
  163. package/src/advanced/map.ts +0 -193
  164. package/src/basic/constant.ts +0 -97
  165. package/src/basic/derivation.ts +0 -147
  166. package/src/basic/disposable.ts +0 -86
  167. package/src/basic/effect.ts +0 -104
  168. package/src/basic/index.ts +0 -9
  169. package/src/basic/observable.ts +0 -109
  170. package/src/basic/signal.ts +0 -145
  171. package/src/basic/state.ts +0 -60
  172. package/src/basic/trackingContext.ts +0 -45
  173. package/src/creators.ts +0 -395
  174. package/test/array.test.ts +0 -600
  175. package/test/constant.test.ts +0 -44
  176. package/test/derivation.test.ts +0 -539
  177. package/test/effect.test.ts +0 -29
  178. package/test/map.test.ts +0 -240
  179. package/test/signal.test.ts +0 -72
  180. package/test/state.test.ts +0 -212
@@ -0,0 +1,409 @@
1
+ import { FlowGraph, isDisposable } from "../base";
2
+ import { FlowNode, type FlowState, state } from "../nodes";
3
+
4
+ /**
5
+ * Represents the actions that can be performed on a FlowArray.
6
+ * @public
7
+ */
8
+ export type FlowArrayAction<T> =
9
+ | {
10
+ type: "set";
11
+ items: T[];
12
+ }
13
+ | {
14
+ type: "update";
15
+ index: number;
16
+ item: T;
17
+ }
18
+ | {
19
+ type: "push";
20
+ item: T;
21
+ }
22
+ | {
23
+ type: "pop";
24
+ }
25
+ | {
26
+ type: "unshift";
27
+ item: T;
28
+ }
29
+ | {
30
+ type: "shift";
31
+ }
32
+ | {
33
+ type: "splice";
34
+ start: number;
35
+ deleteCount: number;
36
+ items: T[];
37
+ }
38
+ | {
39
+ type: "clear";
40
+ };
41
+
42
+ /**
43
+ * Represents a reactive array.
44
+ * @public
45
+ */
46
+ export class FlowArray<T> extends FlowNode<T[]> {
47
+ /**
48
+ * Last action performed on the FlowArray.
49
+ * @public
50
+ */
51
+ $lastAction: FlowState<FlowArrayAction<T>>;
52
+ protected declare _value: T[];
53
+
54
+ /**
55
+ * Creates an instance of FlowArray.
56
+ * @param value - Initial array value.
57
+ * @public
58
+ */
59
+ constructor(value: T[] = []) {
60
+ super(value);
61
+ this.$lastAction = state<FlowArrayAction<T>>({
62
+ type: "set",
63
+ items: value,
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Gets the current length of the array.
69
+ * @returns The length of the array.
70
+ * @public
71
+ */
72
+ get length(): number {
73
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
74
+ return this._value.length;
75
+ }
76
+
77
+ /**
78
+ * Internal method to get the raw value.
79
+ * @internal
80
+ */
81
+ protected _getRaw(): T[] {
82
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
83
+ return [...this._value]; // Ensure nobody can modify the original array
84
+ }
85
+
86
+ /**
87
+ * Replaces the entire array with new items.
88
+ * @param items - The new array of items.
89
+ * @public
90
+ */
91
+ override async set(items: T[]): Promise<void> {
92
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
93
+ const update = () => {
94
+ // compute current value
95
+ const currentValue = this._value;
96
+
97
+ if (currentValue !== items) {
98
+ // dispose old items
99
+ currentValue.forEach((item) => {
100
+ if (isDisposable(item)) item.dispose({ self: true });
101
+ });
102
+
103
+ // assign new items
104
+ this._value = items;
105
+
106
+ // emit last action
107
+ this.$lastAction.set({ type: "set", items: items });
108
+ }
109
+
110
+ // return value changed check
111
+ return currentValue !== items;
112
+ };
113
+
114
+ const notify = () => {
115
+ // call super method to avoid setting dirty flag back to true
116
+ super._notify();
117
+ };
118
+
119
+ return FlowGraph.requestWrite(notify, update);
120
+ }
121
+
122
+ /**
123
+ * Replaces an item at a specific index.
124
+ * @param index - The index of the item to replace.
125
+ * @param item - The new item.
126
+ * @public
127
+ */
128
+ async update(index: number, item: T): Promise<void> {
129
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
130
+ const update = () => {
131
+ if (index < 0 || index >= this._value.length) {
132
+ throw new Error("[PicoFlow] Index out of bounds");
133
+ }
134
+
135
+ // compute current value
136
+ const currentValue = this._value[index];
137
+
138
+ if (currentValue !== item) {
139
+ // dispose item
140
+ if (isDisposable(currentValue)) {
141
+ currentValue.dispose({ self: true });
142
+ }
143
+
144
+ // update item
145
+ this._value[index] = item;
146
+
147
+ // emit last action
148
+ this.$lastAction.set({ type: "update", index: index, item: item });
149
+ }
150
+
151
+ // return value changed check
152
+ return currentValue !== item;
153
+ };
154
+
155
+ const notify = () => {
156
+ // call super method to avoid setting dirty flag back to true
157
+ super._notify();
158
+ };
159
+
160
+ return FlowGraph.requestWrite(notify, update);
161
+ }
162
+
163
+ /**
164
+ * Appends an item to the end of the array.
165
+ * @param item - The item to append.
166
+ * @public
167
+ */
168
+ async push(item: T): Promise<void> {
169
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
170
+ const update = () => {
171
+ // push item
172
+ this._value.push(item);
173
+
174
+ // emit last action
175
+ this.$lastAction.set({ type: "push", item: item });
176
+
177
+ // return value changed check
178
+ return true;
179
+ };
180
+
181
+ const notify = () => {
182
+ // call super method to avoid setting dirty flag back to true
183
+ super._notify();
184
+ };
185
+
186
+ return FlowGraph.requestWrite(notify, update);
187
+ }
188
+
189
+ /**
190
+ * Removes the last item from the array.
191
+ * @public
192
+ */
193
+ async pop(): Promise<void> {
194
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
195
+
196
+ const update = () => {
197
+ // pop item
198
+ const item = this._value.pop();
199
+
200
+ if (item !== undefined) {
201
+ // dispose item
202
+ if (isDisposable(item)) {
203
+ item.dispose({ self: true });
204
+ }
205
+
206
+ // emit last action
207
+ this.$lastAction.set({ type: "pop" });
208
+
209
+ // return value changed check
210
+ return true;
211
+ }
212
+ return false;
213
+ };
214
+
215
+ const notify = () => {
216
+ // call super method to avoid setting dirty flag back to true
217
+ super._notify();
218
+ };
219
+
220
+ return FlowGraph.requestWrite(notify, update);
221
+ }
222
+
223
+ /**
224
+ * Inserts an item at the beginning of the array.
225
+ * @param item - The item to insert.
226
+ * @public
227
+ */
228
+ async unshift(item: T): Promise<void> {
229
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
230
+ const update = () => {
231
+ // unshift item
232
+ this._value.unshift(item);
233
+
234
+ // emit last action
235
+ this.$lastAction.set({ type: "unshift", item: item });
236
+
237
+ // return value changed check
238
+ return true;
239
+ };
240
+
241
+ const notify = () => {
242
+ // call super method to avoid setting dirty flag back to true
243
+ super._notify();
244
+ };
245
+
246
+ return FlowGraph.requestWrite(notify, update);
247
+ }
248
+
249
+ /**
250
+ * Removes the first item from the array.
251
+ * @public
252
+ */
253
+ async shift(): Promise<void> {
254
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
255
+ const update = () => {
256
+ // shift item
257
+ const item = this._value.shift();
258
+
259
+ if (item !== undefined) {
260
+ // dispose item
261
+ if (isDisposable(item)) {
262
+ item.dispose({ self: true });
263
+ }
264
+ // emit last action
265
+ this.$lastAction.set({ type: "shift" });
266
+
267
+ // return value changed check
268
+ return true;
269
+ }
270
+
271
+ return false;
272
+ };
273
+
274
+ const notify = () => {
275
+ // call super method to avoid setting dirty flag back to true
276
+ super._notify();
277
+ };
278
+
279
+ return FlowGraph.requestWrite(notify, update);
280
+ }
281
+
282
+ /**
283
+ * Changes the content of the array.
284
+ * @param start - The starting index.
285
+ * @param deleteCount - Number of items to remove.
286
+ * @param newItems - New items to add.
287
+ * @public
288
+ */
289
+ async splice(
290
+ start: number,
291
+ deleteCount: number,
292
+ ...newItems: T[]
293
+ ): Promise<void> {
294
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
295
+ const update = () => {
296
+ // splice items
297
+ const items = this._value.splice(start, deleteCount, ...newItems);
298
+
299
+ // dispose items
300
+ items.forEach((item) => {
301
+ if (isDisposable(item)) item.dispose({ self: true });
302
+ });
303
+
304
+ // emit last action
305
+ this.$lastAction.set({
306
+ type: "splice",
307
+ start: start,
308
+ deleteCount: deleteCount,
309
+ items: newItems,
310
+ });
311
+
312
+ // return value changed check
313
+ return true;
314
+ };
315
+
316
+ const notify = () => {
317
+ // call super method to avoid setting dirty flag back to true
318
+ super._notify();
319
+ };
320
+
321
+ return FlowGraph.requestWrite(notify, update);
322
+ }
323
+
324
+ /**
325
+ * Clears all items from the array.
326
+ * @public
327
+ */
328
+ async clear(): Promise<void> {
329
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
330
+ const update = () => {
331
+ // dispose old items
332
+ const items = [...this._value];
333
+ items.forEach((item) => {
334
+ if (isDisposable(item)) item.dispose({ self: true });
335
+ });
336
+
337
+ // clear array
338
+ this._value = [];
339
+
340
+ // emit last action
341
+ this.$lastAction.set({ type: "clear" });
342
+
343
+ // return value changed check
344
+ return true;
345
+ };
346
+
347
+ const notify = () => {
348
+ // call super method to avoid setting dirty flag back to true
349
+ super._notify();
350
+ };
351
+
352
+ return FlowGraph.requestWrite(notify, update);
353
+ }
354
+
355
+ /**
356
+ * Disposes the FlowArray and its items.
357
+ * @param options - Disposal options.
358
+ * @public
359
+ */
360
+ override dispose(options?: { self: boolean }): void {
361
+ super.dispose(options);
362
+ this._value.forEach((item) => {
363
+ if (isDisposable(item)) item.dispose(options);
364
+ });
365
+ this._value = [];
366
+ }
367
+
368
+ /* INTERNAL */
369
+ }
370
+
371
+ /**
372
+ * Creates a new reactive array with mutation methods and fine-grained action tracking.
373
+ *
374
+ * @typeParam T - The type of the array elements.
375
+ * @param initial - An optional array of initial values.
376
+ * @returns A new instance of {@link FlowArray}.
377
+ *
378
+ * @remarks
379
+ * A reactive array provides array-like mutation methods (push, pop, shift, unshift, splice)
380
+ * and tracks the last operation performed via `$lastAction`. This enables both whole-array
381
+ * reactivity and fine-grained tracking of specific mutations.
382
+ *
383
+ * The array automatically disposes disposable items when they are removed (if they implement
384
+ * the FlowDisposable interface).
385
+ *
386
+ * @example
387
+ * ```typescript
388
+ * const $items = array([1, 2, 3]);
389
+ *
390
+ * // Track the whole array
391
+ * effect((t) => {
392
+ * console.log('Items:', $items.get(t));
393
+ * });
394
+ *
395
+ * // Track the last action
396
+ * effect((t) => {
397
+ * const action = $items.$lastAction.get(t);
398
+ * console.log('Action:', action.type);
399
+ * });
400
+ *
401
+ * $items.push(4); // Logs action: "push"
402
+ * $items.pop(); // Logs action: "pop"
403
+ * ```
404
+ *
405
+ * @public
406
+ */
407
+ export function array<T>(initial?: T[]): FlowArray<T> {
408
+ return new FlowArray<T>(initial);
409
+ }