bunja 2.1.1 → 3.0.0-alpha.4
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 +135 -4
- package/bunja.ts +831 -215
- package/deno.json +1 -1
- package/dist/{bunja-BvZKLiEP.d.ts → bunja-BxbzuHdH.d.cts} +73 -18
- package/dist/bunja-C_hneAUK.cjs +685 -0
- package/dist/bunja-D0Qa6gsc.js +637 -0
- package/dist/{bunja-CPUl4ZRK.d.cts → bunja-DEFeIlpt.d.ts} +73 -18
- package/dist/bunja.cjs +1 -1
- package/dist/bunja.d.cts +2 -2
- package/dist/bunja.d.ts +2 -2
- package/dist/bunja.js +1 -1
- package/dist/react.cjs +16 -4
- package/dist/react.d.cts +3 -2
- package/dist/react.d.ts +3 -2
- package/dist/react.js +17 -4
- package/dist/solid.cjs +1 -1
- package/dist/solid.d.cts +2 -2
- package/dist/solid.d.ts +2 -2
- package/dist/solid.js +1 -1
- package/package.json +2 -2
- package/react.ts +34 -8
- package/solid.ts +4 -3
- package/test.ts +533 -4
- package/dist/bunja-Ce8RwebF.cjs +0 -437
- package/dist/bunja-DhBgerdn.js +0 -389
package/README.md
CHANGED
|
@@ -69,6 +69,26 @@ function MyComponent() {
|
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
#### Seeding the first instance
|
|
73
|
+
|
|
74
|
+
If a bunja needs creation-time data, use `bunja.withSeed`. A default seed is
|
|
75
|
+
required when the bunja is declared, so callers may still omit the seed.
|
|
76
|
+
|
|
77
|
+
The seed is used only when a matching bunja instance is first created. It is not
|
|
78
|
+
part of the bunja instance identity. If the matching instance already exists,
|
|
79
|
+
later seeds are ignored.
|
|
80
|
+
|
|
81
|
+
Seeds can be supplied to `store.get`, `useBunja`, `bunja.use`, or `bunja.will`.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const formBunja = bunja.withSeed({ title: "" }, (seed) => {
|
|
85
|
+
const titleAtom = atom(seed.title);
|
|
86
|
+
return { titleAtom };
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
useBunja({ bunja: formBunja, seed: { title: "Draft" } });
|
|
90
|
+
```
|
|
91
|
+
|
|
72
92
|
### Defining a Bunja that relies on other Bunja
|
|
73
93
|
|
|
74
94
|
If you want to manage a state with a broad lifetime and another state with a
|
|
@@ -156,6 +176,112 @@ either `resourceFooBunja` or `resourceBarBunja`, since they depend on
|
|
|
156
176
|
>
|
|
157
177
|
> See: <https://github.com/facebook/react/issues/16728>
|
|
158
178
|
|
|
179
|
+
#### Bunja init rules
|
|
180
|
+
|
|
181
|
+
Inside a bunja initialization function, call `bunja.use` and `bunja.will`
|
|
182
|
+
unconditionally and in the same order every time, similar to React's
|
|
183
|
+
[Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks). Do not put
|
|
184
|
+
them inside `if` statements, loops, or callbacks.
|
|
185
|
+
|
|
186
|
+
The target passed to `bunja.use` or `bunja.will` must be static. If you pass
|
|
187
|
+
scope value pairs as the second argument or through a bunja ref's `with` field,
|
|
188
|
+
the list of scope bindings must also be static. Do not choose a different bunja
|
|
189
|
+
or add/remove scope bindings based on runtime conditions.
|
|
190
|
+
|
|
191
|
+
`seed` is the exception. A bunja ref may provide a dynamic `seed` value because
|
|
192
|
+
seed is used only when creating the first matching bunja instance and is not
|
|
193
|
+
recorded in the dependency graph.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
const consumerBunja = bunja.withSeed({ currentUserId: "" }, (seed) => {
|
|
197
|
+
const getProfile = bunja.will({
|
|
198
|
+
// Must stay the same on every init.
|
|
199
|
+
bunja: profileBunja,
|
|
200
|
+
with: [
|
|
201
|
+
// Must stay the same on every init.
|
|
202
|
+
LocaleScope.bind("en-US"),
|
|
203
|
+
],
|
|
204
|
+
// May change for each first instance creation.
|
|
205
|
+
seed: { currentUserId: seed.currentUserId },
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return getProfile();
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
If a dependency should only be used for one branch, declare it with `bunja.will`
|
|
213
|
+
unconditionally, then branch on whether to call the returned thunk.
|
|
214
|
+
|
|
215
|
+
#### Conditional dependencies
|
|
216
|
+
|
|
217
|
+
Use `bunja.will` when a bunja may depend on another bunja only for a selected
|
|
218
|
+
branch. `bunja.will` declares a possible dependency and returns a thunk. Only a
|
|
219
|
+
called thunk becomes an active dependency and is mounted.
|
|
220
|
+
|
|
221
|
+
The thunk may only be called during the same bunja initialization function that
|
|
222
|
+
created it.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
const resourceA = bunja(() => {
|
|
226
|
+
const { send } = bunja.use(websocketBunja);
|
|
227
|
+
bunja.effect(() => {
|
|
228
|
+
send("subscribe-a");
|
|
229
|
+
return () => send("unsubscribe-a");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const resourceB = bunja(() => {
|
|
234
|
+
const { send } = bunja.use(websocketBunja);
|
|
235
|
+
bunja.effect(() => {
|
|
236
|
+
send("subscribe-b");
|
|
237
|
+
return () => send("unsubscribe-b");
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const selectedResourceBunja = bunja(() => {
|
|
242
|
+
const selected = bunja.use(SelectedResourceScope);
|
|
243
|
+
const useA = bunja.will(resourceA);
|
|
244
|
+
const useB = bunja.will(resourceB);
|
|
245
|
+
|
|
246
|
+
return selected === "a" ? useA() : useB();
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
When a `bunja.will` thunk is called, the selected dependency becomes part of the
|
|
251
|
+
current bunja instance. If that selected dependency resolves to a different
|
|
252
|
+
instance because of scope values, the current bunja also resolves to a different
|
|
253
|
+
instance.
|
|
254
|
+
|
|
255
|
+
A declared but uncalled `bunja.will` dependency has no effect on the current
|
|
256
|
+
bunja instance.
|
|
257
|
+
|
|
258
|
+
#### Prebaking the dependency graph
|
|
259
|
+
|
|
260
|
+
During normal `store.get` and `useBunja`, only the selected `bunja.will` branch
|
|
261
|
+
is baked. If a declared `bunja.will` dependency is not called, that dependency's
|
|
262
|
+
own dependencies may still be unknown. `store.prebake` can be used by devtools
|
|
263
|
+
or debugging code to visit those declared dependencies and fill in the graph.
|
|
264
|
+
|
|
265
|
+
Prebaking runs bunja init functions in a dry graph-collection mode. It does not
|
|
266
|
+
create ref-counted bunja instances, it does not mount dependencies, and it does
|
|
267
|
+
not run `bunja.effect` callbacks. Prebake still calls bunja init functions, so
|
|
268
|
+
put external resource creation and subscriptions inside `bunja.effect` if they
|
|
269
|
+
must not run during graph collection.
|
|
270
|
+
|
|
271
|
+
`store.prebake` rejects root bunja refs that provide a seed. During dry-run
|
|
272
|
+
initialization, bunja refs are converted to graph refs, so every prebaked bunja
|
|
273
|
+
is initialized with its declared default seed.
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
const result = store.prebake(selectedResourceBunja, readScope);
|
|
277
|
+
|
|
278
|
+
result.relatedBunjas;
|
|
279
|
+
result.requiredScopes;
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The normal `store.get` and `useBunja` paths do not prebake automatically. They
|
|
283
|
+
still mount only the active `bunja.will` branch.
|
|
284
|
+
|
|
159
285
|
### Dependency injection using Scope
|
|
160
286
|
|
|
161
287
|
You can use a bunja for local state management.\
|
|
@@ -239,6 +365,11 @@ const UrlContext = createContext("https://example.com/");
|
|
|
239
365
|
const UrlScope = createScopeFromContext(UrlContext);
|
|
240
366
|
```
|
|
241
367
|
|
|
368
|
+
When using React 19, Bunja reads scope contexts lazily with `React.use`, so only
|
|
369
|
+
the contexts needed by the active branch are read. With React 18, Bunja must
|
|
370
|
+
read all bound contexts before resolving the bunja because `useContext` cannot
|
|
371
|
+
be called conditionally. In React 18, call `bindScope` before rendering.
|
|
372
|
+
|
|
242
373
|
#### Injecting dependencies directly into the scope
|
|
243
374
|
|
|
244
375
|
You might want to use a bunja directly within a React component where the values
|
|
@@ -260,15 +391,15 @@ function MyComponent() {
|
|
|
260
391
|
|
|
261
392
|
##### Doing the same thing inside a bunja
|
|
262
393
|
|
|
263
|
-
You can
|
|
264
|
-
initialization function.
|
|
394
|
+
You can pass scope value pairs as the second argument to `bunja.use` to override
|
|
395
|
+
scope values from within a bunja initialization function.
|
|
265
396
|
|
|
266
397
|
```ts
|
|
267
398
|
const myBunja = bunja(() => {
|
|
268
|
-
const fooData = bunja.
|
|
399
|
+
const fooData = bunja.use(fetchBunja, [
|
|
269
400
|
UrlScope.bind("https://example.com/foo"),
|
|
270
401
|
]);
|
|
271
|
-
const barData = bunja.
|
|
402
|
+
const barData = bunja.use(fetchBunja, [
|
|
272
403
|
UrlScope.bind("https://example.com/bar"),
|
|
273
404
|
]);
|
|
274
405
|
|