@ersbeth/picoflow 0.2.4 → 1.0.1
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/.cursor/plans/update-js-e795d61b.plan.md +567 -0
- package/.gitlab-ci.yml +24 -0
- package/.vscode/settings.json +3 -3
- package/CHANGELOG.md +51 -0
- package/IMPLEMENTATION_GUIDE.md +1578 -0
- package/README.md +9 -134
- package/biome.json +32 -32
- package/dist/picoflow.js +610 -436
- package/dist/types/advanced/array.d.ts +0 -6
- package/dist/types/advanced/array.d.ts.map +1 -1
- package/dist/types/advanced/index.d.ts +5 -5
- package/dist/types/advanced/index.d.ts.map +1 -1
- package/dist/types/advanced/map.d.ts +114 -23
- package/dist/types/advanced/map.d.ts.map +1 -1
- package/dist/types/advanced/resource.d.ts +51 -12
- package/dist/types/advanced/resource.d.ts.map +1 -1
- package/dist/types/advanced/resourceAsync.d.ts +28 -13
- package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
- package/dist/types/advanced/stream.d.ts +74 -16
- package/dist/types/advanced/stream.d.ts.map +1 -1
- package/dist/types/advanced/streamAsync.d.ts +69 -15
- package/dist/types/advanced/streamAsync.d.ts.map +1 -1
- package/dist/types/basic/constant.d.ts +44 -16
- package/dist/types/basic/constant.d.ts.map +1 -1
- package/dist/types/basic/derivation.d.ts +73 -24
- package/dist/types/basic/derivation.d.ts.map +1 -1
- package/dist/types/basic/disposable.d.ts +65 -6
- package/dist/types/basic/disposable.d.ts.map +1 -1
- package/dist/types/basic/effect.d.ts +27 -16
- package/dist/types/basic/effect.d.ts.map +1 -1
- package/dist/types/basic/index.d.ts +7 -8
- package/dist/types/basic/index.d.ts.map +1 -1
- package/dist/types/basic/observable.d.ts +62 -13
- package/dist/types/basic/observable.d.ts.map +1 -1
- package/dist/types/basic/signal.d.ts +35 -6
- package/dist/types/basic/signal.d.ts.map +1 -1
- package/dist/types/basic/state.d.ts +25 -4
- package/dist/types/basic/state.d.ts.map +1 -1
- package/dist/types/basic/trackingContext.d.ts +33 -0
- package/dist/types/basic/trackingContext.d.ts.map +1 -0
- package/dist/types/creators.d.ts +271 -26
- package/dist/types/creators.d.ts.map +1 -1
- package/dist/types/index.d.ts +60 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/solid/converters.d.ts +5 -5
- package/dist/types/solid/converters.d.ts.map +1 -1
- package/dist/types/solid/index.d.ts +2 -2
- package/dist/types/solid/index.d.ts.map +1 -1
- package/dist/types/solid/primitives.d.ts +96 -4
- package/dist/types/solid/primitives.d.ts.map +1 -1
- package/docs/.vitepress/config.mts +110 -0
- package/docs/api/classes/FlowArray.md +489 -0
- package/docs/api/classes/FlowConstant.md +350 -0
- package/docs/api/classes/FlowDerivation.md +334 -0
- package/docs/api/classes/FlowEffect.md +100 -0
- package/docs/api/classes/FlowMap.md +512 -0
- package/docs/api/classes/FlowObservable.md +306 -0
- package/docs/api/classes/FlowResource.md +380 -0
- package/docs/api/classes/FlowResourceAsync.md +362 -0
- package/docs/api/classes/FlowSignal.md +160 -0
- package/docs/api/classes/FlowState.md +368 -0
- package/docs/api/classes/FlowStream.md +367 -0
- package/docs/api/classes/FlowStreamAsync.md +364 -0
- package/docs/api/classes/SolidDerivation.md +75 -0
- package/docs/api/classes/SolidResource.md +91 -0
- package/docs/api/classes/SolidState.md +71 -0
- package/docs/api/classes/TrackingContext.md +33 -0
- package/docs/api/functions/array.md +58 -0
- package/docs/api/functions/constant.md +45 -0
- package/docs/api/functions/derivation.md +53 -0
- package/docs/api/functions/effect.md +49 -0
- package/docs/api/functions/from.md +220 -0
- package/docs/api/functions/isDisposable.md +49 -0
- package/docs/api/functions/map.md +57 -0
- package/docs/api/functions/resource.md +52 -0
- package/docs/api/functions/resourceAsync.md +50 -0
- package/docs/api/functions/signal.md +36 -0
- package/docs/api/functions/state.md +47 -0
- package/docs/api/functions/stream.md +53 -0
- package/docs/api/functions/streamAsync.md +50 -0
- package/docs/api/index.md +118 -0
- package/docs/api/interfaces/FlowDisposable.md +65 -0
- package/docs/api/interfaces/SolidObservable.md +19 -0
- package/docs/api/type-aliases/FlowArrayAction.md +49 -0
- package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
- package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
- package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
- package/docs/api/type-aliases/NotPromise.md +18 -0
- package/docs/api/type-aliases/SolidGetter.md +17 -0
- package/docs/api/typedoc-sidebar.json +1 -0
- package/docs/examples/examples.md +2313 -0
- package/docs/examples/patterns.md +649 -0
- package/docs/guide/advanced/disposal.md +426 -0
- package/docs/guide/advanced/solidjs.md +221 -0
- package/docs/guide/advanced/upgrading.md +464 -0
- package/docs/guide/introduction/concepts.md +56 -0
- package/docs/guide/introduction/conventions.md +61 -0
- package/docs/guide/introduction/getting-started.md +134 -0
- package/docs/guide/introduction/lifecycle.md +371 -0
- package/docs/guide/primitives/array.md +400 -0
- package/docs/guide/primitives/constant.md +380 -0
- package/docs/guide/primitives/derivations.md +348 -0
- package/docs/guide/primitives/effects.md +458 -0
- package/docs/guide/primitives/map.md +387 -0
- package/docs/guide/primitives/overview.md +175 -0
- package/docs/guide/primitives/resources.md +858 -0
- package/docs/guide/primitives/signal.md +259 -0
- package/docs/guide/primitives/state.md +368 -0
- package/docs/guide/primitives/streams.md +931 -0
- package/docs/index.md +47 -0
- package/docs/public/logo.svg +1 -0
- package/package.json +57 -41
- package/src/advanced/array.ts +208 -210
- package/src/advanced/index.ts +7 -7
- package/src/advanced/map.ts +178 -68
- package/src/advanced/resource.ts +87 -43
- package/src/advanced/resourceAsync.ts +62 -42
- package/src/advanced/stream.ts +113 -50
- package/src/advanced/streamAsync.ts +120 -61
- package/src/basic/constant.ts +82 -49
- package/src/basic/derivation.ts +128 -84
- package/src/basic/disposable.ts +74 -15
- package/src/basic/effect.ts +85 -77
- package/src/basic/index.ts +7 -8
- package/src/basic/observable.ts +94 -36
- package/src/basic/signal.ts +133 -105
- package/src/basic/state.ts +46 -25
- package/src/basic/trackingContext.ts +45 -0
- package/src/creators.ts +297 -54
- package/src/index.ts +96 -43
- package/src/solid/converters.ts +186 -67
- package/src/solid/index.ts +8 -2
- package/src/solid/primitives.ts +167 -65
- package/test/array.test.ts +592 -612
- package/test/constant.test.ts +31 -33
- package/test/derivation.test.ts +531 -536
- package/test/effect.test.ts +21 -21
- package/test/map.test.ts +233 -137
- package/test/resource.test.ts +119 -121
- package/test/resourceAsync.test.ts +98 -100
- package/test/signal.test.ts +51 -55
- package/test/state.test.ts +186 -168
- package/test/stream.test.ts +189 -189
- package/test/streamAsync.test.ts +186 -186
- package/tsconfig.json +19 -18
- package/typedoc.json +37 -0
- package/vite.config.ts +23 -23
- package/vitest.config.ts +7 -7
- package/api/doc/index.md +0 -31
- package/api/doc/picoflow.array.md +0 -55
- package/api/doc/picoflow.constant.md +0 -55
- package/api/doc/picoflow.derivation.md +0 -55
- package/api/doc/picoflow.effect.md +0 -55
- package/api/doc/picoflow.flowarray._constructor_.md +0 -49
- package/api/doc/picoflow.flowarray._lastaction.md +0 -13
- package/api/doc/picoflow.flowarray.clear.md +0 -17
- package/api/doc/picoflow.flowarray.dispose.md +0 -55
- package/api/doc/picoflow.flowarray.get.md +0 -19
- package/api/doc/picoflow.flowarray.length.md +0 -13
- package/api/doc/picoflow.flowarray.md +0 -273
- package/api/doc/picoflow.flowarray.pop.md +0 -17
- package/api/doc/picoflow.flowarray.push.md +0 -53
- package/api/doc/picoflow.flowarray.set.md +0 -53
- package/api/doc/picoflow.flowarray.setitem.md +0 -69
- package/api/doc/picoflow.flowarray.shift.md +0 -17
- package/api/doc/picoflow.flowarray.splice.md +0 -85
- package/api/doc/picoflow.flowarray.unshift.md +0 -53
- package/api/doc/picoflow.flowarrayaction.md +0 -37
- package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
- package/api/doc/picoflow.flowconstant.get.md +0 -25
- package/api/doc/picoflow.flowconstant.md +0 -88
- package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
- package/api/doc/picoflow.flowderivation.get.md +0 -23
- package/api/doc/picoflow.flowderivation.md +0 -86
- package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
- package/api/doc/picoflow.flowdisposable.md +0 -43
- package/api/doc/picoflow.floweffect._constructor_.md +0 -54
- package/api/doc/picoflow.floweffect.dispose.md +0 -21
- package/api/doc/picoflow.floweffect.disposed.md +0 -13
- package/api/doc/picoflow.floweffect.md +0 -131
- package/api/doc/picoflow.flowgetter.md +0 -15
- package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
- package/api/doc/picoflow.flowmap._lastset.md +0 -21
- package/api/doc/picoflow.flowmap.delete.md +0 -61
- package/api/doc/picoflow.flowmap.md +0 -133
- package/api/doc/picoflow.flowmap.setat.md +0 -77
- package/api/doc/picoflow.flowobservable.get.md +0 -19
- package/api/doc/picoflow.flowobservable.md +0 -68
- package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
- package/api/doc/picoflow.flowresource._constructor_.md +0 -49
- package/api/doc/picoflow.flowresource.fetch.md +0 -27
- package/api/doc/picoflow.flowresource.get.md +0 -23
- package/api/doc/picoflow.flowresource.md +0 -100
- package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
- package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
- package/api/doc/picoflow.flowresourceasync.get.md +0 -23
- package/api/doc/picoflow.flowresourceasync.md +0 -100
- package/api/doc/picoflow.flowsignal.dispose.md +0 -59
- package/api/doc/picoflow.flowsignal.disposed.md +0 -18
- package/api/doc/picoflow.flowsignal.md +0 -112
- package/api/doc/picoflow.flowsignal.trigger.md +0 -21
- package/api/doc/picoflow.flowstate.md +0 -52
- package/api/doc/picoflow.flowstate.set.md +0 -61
- package/api/doc/picoflow.flowstream._constructor_.md +0 -49
- package/api/doc/picoflow.flowstream.dispose.md +0 -21
- package/api/doc/picoflow.flowstream.get.md +0 -23
- package/api/doc/picoflow.flowstream.md +0 -100
- package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
- package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
- package/api/doc/picoflow.flowstreamasync.get.md +0 -23
- package/api/doc/picoflow.flowstreamasync.md +0 -100
- package/api/doc/picoflow.flowstreamdisposer.md +0 -13
- package/api/doc/picoflow.flowstreamsetter.md +0 -13
- package/api/doc/picoflow.flowstreamupdater.md +0 -19
- package/api/doc/picoflow.flowwatcher.md +0 -15
- package/api/doc/picoflow.from.md +0 -55
- package/api/doc/picoflow.from_1.md +0 -55
- package/api/doc/picoflow.from_2.md +0 -55
- package/api/doc/picoflow.from_3.md +0 -55
- package/api/doc/picoflow.from_4.md +0 -55
- package/api/doc/picoflow.from_5.md +0 -55
- package/api/doc/picoflow.isdisposable.md +0 -55
- package/api/doc/picoflow.map.md +0 -59
- package/api/doc/picoflow.md +0 -544
- package/api/doc/picoflow.resource.md +0 -55
- package/api/doc/picoflow.resourceasync.md +0 -55
- package/api/doc/picoflow.signal.md +0 -19
- package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
- package/api/doc/picoflow.solidderivation.get.md +0 -13
- package/api/doc/picoflow.solidderivation.md +0 -94
- package/api/doc/picoflow.solidgetter.md +0 -13
- package/api/doc/picoflow.solidobservable.get.md +0 -13
- package/api/doc/picoflow.solidobservable.md +0 -57
- package/api/doc/picoflow.solidresource._constructor_.md +0 -49
- package/api/doc/picoflow.solidresource.get.md +0 -13
- package/api/doc/picoflow.solidresource.latest.md +0 -13
- package/api/doc/picoflow.solidresource.md +0 -157
- package/api/doc/picoflow.solidresource.refetch.md +0 -13
- package/api/doc/picoflow.solidresource.state.md +0 -13
- package/api/doc/picoflow.solidstate._constructor_.md +0 -49
- package/api/doc/picoflow.solidstate.get.md +0 -13
- package/api/doc/picoflow.solidstate.md +0 -115
- package/api/doc/picoflow.solidstate.set.md +0 -13
- package/api/doc/picoflow.state.md +0 -55
- package/api/doc/picoflow.stream.md +0 -55
- package/api/doc/picoflow.streamasync.md +0 -55
- package/api/picoflow.public.api.md +0 -244
- package/api-extractor.json +0 -61
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
# Upgrading from v0.x
|
|
2
|
+
|
|
3
|
+
PicoFlow v1.0.0 introduces a new API based on an explicit tracking context instead of getter and watcher functions. This change brings several benefits:
|
|
4
|
+
|
|
5
|
+
1. **More intuitive:** The method-based API (`$state.get(t)`) is more natural than `get($state)`
|
|
6
|
+
2. **Better chaining:** Nested access is much more readable: `$a.get(t).b.get(t)` vs `get(get($a).b)`
|
|
7
|
+
3. **Explicit control:** Clear distinction between reactive (`.get(t)`) and non-reactive (`.pick()`) reads
|
|
8
|
+
4. **Simpler architecture:** Fewer internal abstractions make the library easier to understand and maintain
|
|
9
|
+
5. **More flexible:** Fine-grained control over dependencies within a single effect
|
|
10
|
+
|
|
11
|
+
We believe this change makes PicoFlow better for both new and experienced users, and we're excited for you to try it!
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
The core change is simple:
|
|
16
|
+
|
|
17
|
+
**Before (v0.x):**
|
|
18
|
+
```typescript
|
|
19
|
+
effect((get, watch) => {
|
|
20
|
+
const value = get($state);
|
|
21
|
+
watch($signal);
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**After (v1.0.0):**
|
|
26
|
+
```typescript
|
|
27
|
+
effect((t) => {
|
|
28
|
+
const value = $state.get(t);
|
|
29
|
+
$signal.watch(t);
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Breaking Changes Summary
|
|
34
|
+
|
|
35
|
+
| v0.x | v1.0.0 | Notes |
|
|
36
|
+
|------|--------|-------|
|
|
37
|
+
| `effect((get, watch) => {})` | `effect((t) => {})` | Single tracking context parameter |
|
|
38
|
+
| `derivation((get, watch) => {})` | `derivation((t) => {})` | Single tracking context parameter |
|
|
39
|
+
| `get($observable)` | `$observable.get(t)` | Method-based API |
|
|
40
|
+
| `watch($signal)` | `$signal.watch(t)` | Method-based API |
|
|
41
|
+
| `FlowGetter` type | `TrackingContext` type | New type for tracking context |
|
|
42
|
+
| `FlowWatcher` type | `TrackingContext` type | New type for tracking context |
|
|
43
|
+
| N/A | `$observable.pick()` | New: non-reactive reads |
|
|
44
|
+
|
|
45
|
+
## TypeScript Type Changes
|
|
46
|
+
|
|
47
|
+
If you've imported these types, update them:
|
|
48
|
+
|
|
49
|
+
**Before:**
|
|
50
|
+
```typescript
|
|
51
|
+
import type { FlowGetter, FlowWatcher } from '@ersbeth/picoflow';
|
|
52
|
+
|
|
53
|
+
function myHelper(get: FlowGetter, watch: FlowWatcher) {
|
|
54
|
+
// ...
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**After:**
|
|
59
|
+
```typescript
|
|
60
|
+
import type { TrackingContext } from '@ersbeth/picoflow';
|
|
61
|
+
|
|
62
|
+
function myHelper(t: TrackingContext) {
|
|
63
|
+
// ...
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Step-by-Step Migration
|
|
68
|
+
|
|
69
|
+
### 1. Update PicoFlow
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install @ersbeth/picoflow@1.0.0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Replace Effect and Derivation Parameters
|
|
76
|
+
|
|
77
|
+
Find all `effect()` and `derivation()` calls in your codebase:
|
|
78
|
+
|
|
79
|
+
**Pattern to find:** `(get, watch) =>` or `(get) =>`
|
|
80
|
+
**Replace with:** `(t) =>`
|
|
81
|
+
|
|
82
|
+
### 3. Update Observable Reads
|
|
83
|
+
|
|
84
|
+
Replace getter function calls with method calls:
|
|
85
|
+
|
|
86
|
+
**Pattern to find:** `get($observable)`
|
|
87
|
+
**Replace with:** `$observable.get(t)`
|
|
88
|
+
|
|
89
|
+
### 4. Update Signal Watches
|
|
90
|
+
|
|
91
|
+
Replace watcher function calls with method calls:
|
|
92
|
+
|
|
93
|
+
**Pattern to find:** `watch($signal)`
|
|
94
|
+
**Replace with:** `$signal.watch(t)`
|
|
95
|
+
|
|
96
|
+
### 5. Test Your Application
|
|
97
|
+
|
|
98
|
+
Run your tests and verify everything works as expected.
|
|
99
|
+
|
|
100
|
+
## Common Migration Patterns
|
|
101
|
+
|
|
102
|
+
### Pattern 1: Simple Effect with State
|
|
103
|
+
|
|
104
|
+
**Before:**
|
|
105
|
+
```typescript
|
|
106
|
+
import { state, effect } from '@ersbeth/picoflow';
|
|
107
|
+
|
|
108
|
+
const $count = state(0);
|
|
109
|
+
|
|
110
|
+
const $effect = effect((get) => {
|
|
111
|
+
console.log('Count:', get($count));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
$count.set(42);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**After:**
|
|
118
|
+
```typescript
|
|
119
|
+
import { state, effect } from '@ersbeth/picoflow';
|
|
120
|
+
|
|
121
|
+
const $count = state(0);
|
|
122
|
+
|
|
123
|
+
const $effect = effect((t) => {
|
|
124
|
+
console.log('Count:', $count.get(t));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
$count.set(42);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Pattern 2: Effect with Signal Watch
|
|
131
|
+
|
|
132
|
+
**Before:**
|
|
133
|
+
```typescript
|
|
134
|
+
import { signal, effect } from '@ersbeth/picoflow';
|
|
135
|
+
|
|
136
|
+
const $signal = signal();
|
|
137
|
+
|
|
138
|
+
const $effect = effect((get, watch) => {
|
|
139
|
+
watch($signal);
|
|
140
|
+
console.log('Signal triggered!');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
$signal.trigger();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**After:**
|
|
147
|
+
```typescript
|
|
148
|
+
import { signal, effect } from '@ersbeth/picoflow';
|
|
149
|
+
|
|
150
|
+
const $signal = signal();
|
|
151
|
+
|
|
152
|
+
const $effect = effect((t) => {
|
|
153
|
+
$signal.watch(t);
|
|
154
|
+
console.log('Signal triggered!');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
$signal.trigger();
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Pattern 3: Derivations
|
|
161
|
+
|
|
162
|
+
**Before:**
|
|
163
|
+
```typescript
|
|
164
|
+
import { state, derivation } from '@ersbeth/picoflow';
|
|
165
|
+
|
|
166
|
+
const $firstName = state('John');
|
|
167
|
+
const $lastName = state('Doe');
|
|
168
|
+
|
|
169
|
+
const $fullName = derivation((get) => {
|
|
170
|
+
return `${get($firstName)} ${get($lastName)}`;
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**After:**
|
|
175
|
+
```typescript
|
|
176
|
+
import { state, derivation } from '@ersbeth/picoflow';
|
|
177
|
+
|
|
178
|
+
const $firstName = state('John');
|
|
179
|
+
const $lastName = state('Doe');
|
|
180
|
+
|
|
181
|
+
const $fullName = derivation((t) => {
|
|
182
|
+
return `${$firstName.get(t)} ${$lastName.get(t)}`;
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Pattern 4: Nested States (Chained Access)
|
|
187
|
+
|
|
188
|
+
This is where the new API really shines!
|
|
189
|
+
|
|
190
|
+
**Before:**
|
|
191
|
+
```typescript
|
|
192
|
+
const $user = state({
|
|
193
|
+
profile: $profile,
|
|
194
|
+
settings: $settings
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
effect((get) => {
|
|
198
|
+
const name = get(get($user).profile).name;
|
|
199
|
+
console.log(name);
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**After:**
|
|
204
|
+
```typescript
|
|
205
|
+
const $user = state({
|
|
206
|
+
profile: $profile,
|
|
207
|
+
settings: $settings
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
effect((t) => {
|
|
211
|
+
const name = $user.get(t).profile.get(t).name;
|
|
212
|
+
console.log(name);
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Much more readable!
|
|
217
|
+
|
|
218
|
+
### Pattern 5: Non-Reactive Reads (NEW CAPABILITY!)
|
|
219
|
+
|
|
220
|
+
v1.0.0 introduces `pick()` for reading values without creating dependencies:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const $data = state({ count: 0 });
|
|
224
|
+
const $trigger = signal();
|
|
225
|
+
|
|
226
|
+
effect((t) => {
|
|
227
|
+
// Only react to $trigger, not to $data
|
|
228
|
+
$trigger.watch(t);
|
|
229
|
+
const snapshot = $data.pick(); // Non-reactive read!
|
|
230
|
+
console.log('Triggered, current data:', snapshot);
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
You can also use `get(null)` for the same effect:
|
|
235
|
+
```typescript
|
|
236
|
+
const snapshot = $data.get(null); // Equivalent to pick()
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Pattern 6: Arrays and Collections
|
|
240
|
+
|
|
241
|
+
**Before:**
|
|
242
|
+
```typescript
|
|
243
|
+
import { array, effect } from '@ersbeth/picoflow';
|
|
244
|
+
|
|
245
|
+
const $items = array([1, 2, 3]);
|
|
246
|
+
|
|
247
|
+
effect((get) => {
|
|
248
|
+
const items = get($items);
|
|
249
|
+
console.log('Items:', items);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
$items.push(4);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**After:**
|
|
256
|
+
```typescript
|
|
257
|
+
import { array, effect } from '@ersbeth/picoflow';
|
|
258
|
+
|
|
259
|
+
const $items = array([1, 2, 3]);
|
|
260
|
+
|
|
261
|
+
effect((t) => {
|
|
262
|
+
const items = $items.get(t);
|
|
263
|
+
console.log('Items:', items);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
$items.push(4);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Pattern 7: Reactive Maps
|
|
270
|
+
|
|
271
|
+
**Before:**
|
|
272
|
+
```typescript
|
|
273
|
+
import { map, effect } from '@ersbeth/picoflow';
|
|
274
|
+
|
|
275
|
+
const $map = map({ foo: 'bar' });
|
|
276
|
+
|
|
277
|
+
effect((get) => {
|
|
278
|
+
const data = get($map);
|
|
279
|
+
console.log('Map:', data);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
$map.setAt('key', 'value');
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**After:**
|
|
286
|
+
```typescript
|
|
287
|
+
import { map, effect } from '@ersbeth/picoflow';
|
|
288
|
+
|
|
289
|
+
const $map = map({ foo: 'bar' });
|
|
290
|
+
|
|
291
|
+
effect((t) => {
|
|
292
|
+
const data = $map.get(t);
|
|
293
|
+
console.log('Map:', data);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
$map.setAt('key', 'value');
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Pattern 8: Async Resources
|
|
300
|
+
|
|
301
|
+
**Before:**
|
|
302
|
+
```typescript
|
|
303
|
+
import { resource, effect } from '@ersbeth/picoflow';
|
|
304
|
+
|
|
305
|
+
const $data = resource(fetchData, {});
|
|
306
|
+
|
|
307
|
+
effect((get) => {
|
|
308
|
+
const data = get($data);
|
|
309
|
+
console.log('Data loaded:', data);
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**After:**
|
|
314
|
+
```typescript
|
|
315
|
+
import { resource, effect } from '@ersbeth/picoflow';
|
|
316
|
+
|
|
317
|
+
const $data = resource(fetchData, {});
|
|
318
|
+
|
|
319
|
+
effect((t) => {
|
|
320
|
+
const data = $data.get(t);
|
|
321
|
+
console.log('Data loaded:', data);
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Pattern 9: Streams
|
|
326
|
+
|
|
327
|
+
**Before:**
|
|
328
|
+
```typescript
|
|
329
|
+
import { stream, effect } from '@ersbeth/picoflow';
|
|
330
|
+
|
|
331
|
+
const $messages = stream((set) => {
|
|
332
|
+
const ws = new WebSocket('ws://...');
|
|
333
|
+
ws.onmessage = (e) => set(e.data);
|
|
334
|
+
return () => ws.close();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
effect((get) => {
|
|
338
|
+
console.log('Message:', get($messages));
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**After:**
|
|
343
|
+
```typescript
|
|
344
|
+
import { stream, effect } from '@ersbeth/picoflow';
|
|
345
|
+
|
|
346
|
+
const $messages = stream((set) => {
|
|
347
|
+
const ws = new WebSocket('ws://...');
|
|
348
|
+
ws.onmessage = (e) => set(e.data);
|
|
349
|
+
return () => ws.close();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
effect((t) => {
|
|
353
|
+
console.log('Message:', $messages.get(t));
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Pattern 10: SolidJS Integration
|
|
358
|
+
|
|
359
|
+
If you're using `picoflow/solid`:
|
|
360
|
+
|
|
361
|
+
**Before:**
|
|
362
|
+
```typescript
|
|
363
|
+
import { from } from '@ersbeth/picoflow/solid';
|
|
364
|
+
import { state } from '@ersbeth/picoflow';
|
|
365
|
+
|
|
366
|
+
const $picoState = state(42);
|
|
367
|
+
|
|
368
|
+
// Convert to SolidJS signal
|
|
369
|
+
const solidSignal = from($picoState);
|
|
370
|
+
|
|
371
|
+
// Or with a getter function
|
|
372
|
+
const solidDerived = from((get) => {
|
|
373
|
+
return get($stateA) + get($stateB);
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**After:**
|
|
378
|
+
```typescript
|
|
379
|
+
import { from } from '@ersbeth/picoflow/solid';
|
|
380
|
+
import { state } from '@ersbeth/picoflow';
|
|
381
|
+
|
|
382
|
+
const $picoState = state(42);
|
|
383
|
+
|
|
384
|
+
// Convert to SolidJS signal
|
|
385
|
+
const solidSignal = from($picoState);
|
|
386
|
+
|
|
387
|
+
// Or with a getter function
|
|
388
|
+
const solidDerived = from((t) => {
|
|
389
|
+
return $stateA.get(t) + $stateB.get(t);
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Advanced: Mixed Reactive and Non-Reactive Reads
|
|
394
|
+
|
|
395
|
+
One powerful new feature is the ability to mix reactive and non-reactive reads in the same effect:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
const $config = state({ debug: true });
|
|
399
|
+
const $data = state({ value: 0 });
|
|
400
|
+
|
|
401
|
+
effect((t) => {
|
|
402
|
+
// Reactive: effect re-runs when $data changes
|
|
403
|
+
const data = $data.get(t);
|
|
404
|
+
|
|
405
|
+
// Non-reactive: effect does NOT re-run when $config changes
|
|
406
|
+
const config = $config.pick();
|
|
407
|
+
|
|
408
|
+
if (config.debug) {
|
|
409
|
+
console.log('Debug:', data);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// This triggers the effect:
|
|
414
|
+
$data.set({ value: 1 });
|
|
415
|
+
|
|
416
|
+
// This does NOT trigger the effect:
|
|
417
|
+
$config.set({ debug: false });
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
This is useful for:
|
|
421
|
+
- Reading configuration that rarely changes
|
|
422
|
+
- Accessing reference data without creating dependencies
|
|
423
|
+
- Performance optimization by avoiding unnecessary re-runs
|
|
424
|
+
|
|
425
|
+
## Troubleshooting
|
|
426
|
+
|
|
427
|
+
### Error: "Cannot read property 'get' of undefined"
|
|
428
|
+
|
|
429
|
+
Make sure you're passing the tracking context to the `get()` method:
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// ❌ Wrong
|
|
433
|
+
effect((t) => {
|
|
434
|
+
const value = $state.get(); // Missing parameter
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// ✅ Correct
|
|
438
|
+
effect((t) => {
|
|
439
|
+
const value = $state.get(t);
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Error: "watch is not a function"
|
|
444
|
+
|
|
445
|
+
You're trying to use the old API. Update to the new method-based syntax:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// ❌ Wrong (old API)
|
|
449
|
+
effect((get, watch) => {
|
|
450
|
+
watch($signal);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// ✅ Correct (new API)
|
|
454
|
+
effect((t) => {
|
|
455
|
+
$signal.watch(t);
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### My effect runs too often / too rarely
|
|
460
|
+
|
|
461
|
+
Check if you're using `.get(t)` vs `.pick()` correctly:
|
|
462
|
+
- Use `.get(t)` to create a reactive dependency
|
|
463
|
+
- Use `.pick()` or `.get(null)` for snapshot reads without dependencies
|
|
464
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
Understand the fundamentals of reactive programming and how it simplifies managing your application's state and behavior.
|
|
4
|
+
|
|
5
|
+
## What is Reactive Programming?
|
|
6
|
+
|
|
7
|
+
Imagine a **spreadsheet** where you write `=A1 + B1` in cell C1. When you change A1 or B1, C1 automatically updates. That's reactive programming - values that automatically update when their dependencies change.
|
|
8
|
+
|
|
9
|
+
## Imperative Approach
|
|
10
|
+
|
|
11
|
+
In traditional programming, you manually update everything:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Imperative approach
|
|
15
|
+
let count = 0
|
|
16
|
+
let doubledCount = count * 2
|
|
17
|
+
|
|
18
|
+
function increment() {
|
|
19
|
+
count++
|
|
20
|
+
doubledCount = count * 2 // Must remember to update!
|
|
21
|
+
updateUI(count, doubledCount) // Must remember to update UI!
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
flowchart LR
|
|
28
|
+
A[count = 5] --> B[Manually update doubled]
|
|
29
|
+
B --> C[Manually update UI]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Reactive Approach
|
|
33
|
+
|
|
34
|
+
With reactive programming, dependencies update automatically:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Reactive approach with PicoFlow
|
|
38
|
+
const $count = state(0)
|
|
39
|
+
const $doubled = derivation((t) => $count.get(t) * 2)
|
|
40
|
+
|
|
41
|
+
effect((t) => {
|
|
42
|
+
updateUI($count.get(t), $doubled.get(t)) // Runs automatically!
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
function increment() {
|
|
46
|
+
$count.set(n => n + 1) // Everything else updates automatically!
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```mermaid
|
|
51
|
+
flowchart LR
|
|
52
|
+
A2[$count.set 5] --> C2[$doubled updates]
|
|
53
|
+
C2 --> D2[UI effect runs]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**The benefit:** You define relationships once, and they stay synchronized automatically!
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Conventions
|
|
2
|
+
|
|
3
|
+
PicoFlow follows simple, consistent conventions that make your reactive code easier to read and maintain. These conventions help you quickly identify reactive values and understand data flow at a glance.
|
|
4
|
+
|
|
5
|
+
## The $ Prefix
|
|
6
|
+
|
|
7
|
+
You'll see variables prefixed with `$` throughout PicoFlow examples:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const $count = state(0)
|
|
11
|
+
const $double = derivation((t) => $count.get(t) * 2)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This is a **naming convention** to make reactive values stand out. It helps you quickly identify:
|
|
15
|
+
- Which values are reactive
|
|
16
|
+
- Which values will cause re-executions when changed
|
|
17
|
+
- Where your state lives
|
|
18
|
+
|
|
19
|
+
Think of `$` as a visual marker: "This value is special - it's reactive!"
|
|
20
|
+
|
|
21
|
+
### Examples
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// Reactive values - use $ prefix
|
|
25
|
+
const $userName = state('Alice')
|
|
26
|
+
const $userAge = state(25)
|
|
27
|
+
const $isAdult = derivation((t) => $userAge.get(t) >= 18)
|
|
28
|
+
|
|
29
|
+
// Plain values - no $ prefix
|
|
30
|
+
const currentName = $userName.pick() // Snapshot of current value
|
|
31
|
+
const maxAge = 100 // Constant
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Benefits
|
|
35
|
+
|
|
36
|
+
**1. Visual Clarity**
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Clear distinction between reactive and non-reactive
|
|
40
|
+
function updateProfile(newName: string) {
|
|
41
|
+
$userName.set(newName) // $ = reactive, will trigger effects
|
|
42
|
+
const timestamp = Date.now() // no $ = plain value
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**2. Prevents Confusion**
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Easy to see what's reactive
|
|
50
|
+
effect((t) => {
|
|
51
|
+
const user = $currentUser.get(t) // $ = will re-run on change
|
|
52
|
+
const config = appConfig // no $ = static value
|
|
53
|
+
|
|
54
|
+
fetchUserData(user.id, config)
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**3. Code Review**
|
|
59
|
+
|
|
60
|
+
When reviewing code, the `$` prefix makes it immediately obvious which variables should be part of the reactive system and which should be regular JavaScript values.
|
|
61
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Welcome to PicoFlow! This guide will help you install PicoFlow and build your first reactive application in just a few minutes.
|
|
4
|
+
|
|
5
|
+
## Why PicoFlow?
|
|
6
|
+
|
|
7
|
+
PicoFlow is a lightweight reactive library with a focus on **explicit control** and **simplicity**. Unlike some reactive libraries that track everything automatically, PicoFlow gives you precise control over what is reactive and what isn't.
|
|
8
|
+
|
|
9
|
+
### Key Philosophy
|
|
10
|
+
|
|
11
|
+
1. **Explicit Tracking** - You decide what to track with `.get(t)`
|
|
12
|
+
2. **Fine-grained Control** - React to specific operations, not just "something changed"
|
|
13
|
+
3. **No Magic** - Simple, predictable behavior
|
|
14
|
+
4. **TypeScript First** - Full type safety and inference
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install PicoFlow using your preferred package manager:
|
|
19
|
+
|
|
20
|
+
::: code-group
|
|
21
|
+
|
|
22
|
+
```bash [pnpm]
|
|
23
|
+
pnpm add @ersbeth/picoflow
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash [npm]
|
|
27
|
+
npm install @ersbeth/picoflow
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash [yarn]
|
|
31
|
+
yarn add @ersbeth/picoflow
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
:::
|
|
35
|
+
|
|
36
|
+
If you plan to use the SolidJS integration, install SolidJS as a peer dependency:
|
|
37
|
+
|
|
38
|
+
::: code-group
|
|
39
|
+
|
|
40
|
+
```bash [pnpm]
|
|
41
|
+
pnpm add solid-js
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash [npm]
|
|
45
|
+
npm install solid-js
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```bash [yarn]
|
|
49
|
+
yarn add solid-js
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
:::
|
|
53
|
+
|
|
54
|
+
Import the primitives you need:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { state, effect, derivation, signal } from '@ersbeth/picoflow'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For SolidJS integration:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { from } from '@ersbeth/picoflow/solid'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Your First PicoFlow App
|
|
67
|
+
|
|
68
|
+
Let's build a simple counter with PicoFlow:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { state, derivation, effect } from '@ersbeth/picoflow'
|
|
72
|
+
|
|
73
|
+
// Create reactive state (prefix with $ by convention)
|
|
74
|
+
const $count = state(0)
|
|
75
|
+
|
|
76
|
+
// Create a computed value
|
|
77
|
+
const $isEven = derivation((t) => {
|
|
78
|
+
return $count.get(t) % 2 === 0
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// React to changes
|
|
82
|
+
effect((t) => {
|
|
83
|
+
const count = $count.get(t)
|
|
84
|
+
const even = $isEven.get(t)
|
|
85
|
+
console.log(`Count is ${count}, which is ${even ? 'even' : 'odd'}`)
|
|
86
|
+
})
|
|
87
|
+
// Logs "Count is 0, wich is even"
|
|
88
|
+
|
|
89
|
+
// Update the state
|
|
90
|
+
$count.set(1) // Logs: "Count is 1, which is odd"
|
|
91
|
+
$count.set(2) // Logs: "Count is 2, which is even"
|
|
92
|
+
$count.set(3) // Logs: "Count is 3, which is odd"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### What Just Happened?
|
|
96
|
+
|
|
97
|
+
1. **State** (`$count`) - Holds a mutable value
|
|
98
|
+
2. **Derivation** (`$isEven`) - Computes a value based on state
|
|
99
|
+
3. **Effect** - Runs automatically when dependencies change
|
|
100
|
+
4. **`.get(t)`** - Reads a value AND creates a dependency
|
|
101
|
+
5. **`.set()`** - Updates state, triggering effects
|
|
102
|
+
|
|
103
|
+
### Data Flow
|
|
104
|
+
|
|
105
|
+
Here's what happens when you call `$count.set(1)`:
|
|
106
|
+
|
|
107
|
+
```mermaid
|
|
108
|
+
sequenceDiagram
|
|
109
|
+
participant User
|
|
110
|
+
participant $count
|
|
111
|
+
participant $isEven
|
|
112
|
+
participant Effect
|
|
113
|
+
|
|
114
|
+
User->>$count: set(1)
|
|
115
|
+
activate $count
|
|
116
|
+
Note over $count: Updates value<br/>0 → 1
|
|
117
|
+
$count->>$isEven: notify()
|
|
118
|
+
Note over $isEven: Marks as dirty<br/>(lazy recompute)
|
|
119
|
+
$count->>Effect: notify()
|
|
120
|
+
deactivate $count
|
|
121
|
+
|
|
122
|
+
activate Effect
|
|
123
|
+
Effect->>$count: get(t)
|
|
124
|
+
$count-->>Effect: 1
|
|
125
|
+
Effect->>$isEven: get(t)
|
|
126
|
+
activate $isEven
|
|
127
|
+
Note over $isEven: Recomputes because dirty
|
|
128
|
+
$isEven->>$count: get(t)
|
|
129
|
+
$count-->>$isEven: 1
|
|
130
|
+
$isEven-->>Effect: false
|
|
131
|
+
deactivate $isEven
|
|
132
|
+
Note over Effect: Logs: "Count is 1,<br/>which is odd"
|
|
133
|
+
deactivate Effect
|
|
134
|
+
```
|