storion 0.5.0 → 0.7.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 +318 -25
- package/dist/async/async.d.ts +49 -0
- package/dist/async/async.d.ts.map +1 -1
- package/dist/async/index.js +50 -0
- package/dist/collection.d.ts +13 -21
- package/dist/collection.d.ts.map +1 -1
- package/dist/core/container.d.ts.map +1 -1
- package/dist/core/createResolver.d.ts.map +1 -1
- package/dist/core/disposable.d.ts +18 -0
- package/dist/core/disposable.d.ts.map +1 -0
- package/dist/core/effect.d.ts +20 -0
- package/dist/core/effect.d.ts.map +1 -1
- package/dist/core/storeContext.d.ts.map +1 -1
- package/dist/devtools/index.js +2 -1
- package/dist/effect-DPAYSuW3.js +294 -0
- package/dist/emitter-BA44OHdL.js +228 -0
- package/dist/react/index.js +27 -20
- package/dist/react/useStore.d.ts.map +1 -1
- package/dist/{store-CwA4YTVb.js → store-BroaE7p4.js} +46 -241
- package/dist/storion.js +32 -304
- package/dist/test/util.d.ts +2 -0
- package/dist/test/util.d.ts.map +1 -0
- package/dist/types.d.ts +63 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -282,9 +282,7 @@ export const settingsStore = store({
|
|
|
282
282
|
|
|
283
283
|
return {
|
|
284
284
|
// Direct value
|
|
285
|
-
setTheme
|
|
286
|
-
setTheme(theme);
|
|
287
|
-
},
|
|
285
|
+
setTheme,
|
|
288
286
|
|
|
289
287
|
// Reducer - returns new value
|
|
290
288
|
toggleTheme: () => {
|
|
@@ -387,6 +385,60 @@ export const syncStore = store({
|
|
|
387
385
|
});
|
|
388
386
|
```
|
|
389
387
|
|
|
388
|
+
### Effect Re-run
|
|
389
|
+
|
|
390
|
+
Effects automatically re-run when their tracked state changes. There are three ways an effect can be triggered to re-run:
|
|
391
|
+
|
|
392
|
+
**1. Tracked state changes** — The most common case. When any state property read during the effect's execution changes, the effect re-runs automatically.
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
effect(() => {
|
|
396
|
+
// This effect tracks `state.count` and re-runs when it changes
|
|
397
|
+
console.log("Count is:", state.count);
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**2. Calling `ctx.refresh()` asynchronously** — You can manually trigger a re-run from async code (promises, setTimeout, event handlers).
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
effect((ctx) => {
|
|
405
|
+
// Schedule a refresh after some async work
|
|
406
|
+
ctx.safe(fetchData()).then(() => {
|
|
407
|
+
ctx.refresh(); // Re-runs the effect
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Or from a setTimeout
|
|
411
|
+
setTimeout(() => {
|
|
412
|
+
ctx.refresh();
|
|
413
|
+
}, 1000);
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**3. Returning `ctx.refresh`** — For synchronous refresh requests, return `ctx.refresh` from the effect. The effect will re-run after the current execution completes.
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
effect((ctx) => {
|
|
421
|
+
const data = async.wait(state.asyncData); // May throw a promise
|
|
422
|
+
// If we get here, data is available
|
|
423
|
+
state.result = transform(data);
|
|
424
|
+
|
|
425
|
+
// Return ctx.refresh to request a re-run after this execution
|
|
426
|
+
if (needsAnotherRun) {
|
|
427
|
+
return ctx.refresh;
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
> **Important:** Effects cannot re-run while already executing. Calling `ctx.refresh()` synchronously during effect execution throws an error:
|
|
433
|
+
>
|
|
434
|
+
> ```ts
|
|
435
|
+
> effect((ctx) => {
|
|
436
|
+
> ctx.refresh(); // ❌ Error: Effect is already running, cannot refresh
|
|
437
|
+
> });
|
|
438
|
+
> ```
|
|
439
|
+
>
|
|
440
|
+
> This prevents infinite loops and ensures predictable execution. Use the return pattern or async scheduling instead.
|
|
441
|
+
|
|
390
442
|
### Effect with Safe Async
|
|
391
443
|
|
|
392
444
|
**The problem:** When an effect re-runs before an async operation completes, you get stale data or "state update on unmounted component" warnings. Managing this manually is error-prone.
|
|
@@ -745,17 +797,14 @@ function Dashboard() {
|
|
|
745
797
|
|
|
746
798
|
// 3c. With custom deps - refetch when deps change
|
|
747
799
|
function UserById({ userId }: { userId: string }) {
|
|
748
|
-
const { user } = useStore(
|
|
749
|
-
|
|
750
|
-
const [state, actions] = get(userStore);
|
|
800
|
+
const { user } = useStore(({ get, trigger }) => {
|
|
801
|
+
const [state, actions] = get(userStore);
|
|
751
802
|
|
|
752
|
-
|
|
753
|
-
|
|
803
|
+
// Refetch when userId prop changes
|
|
804
|
+
trigger(actions.currentUser.dispatch, [userId]);
|
|
754
805
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
[userId] // selector deps for proper tracking
|
|
758
|
-
);
|
|
806
|
+
return { user: state.currentUser };
|
|
807
|
+
});
|
|
759
808
|
|
|
760
809
|
return <div>{user.data?.name}</div>;
|
|
761
810
|
}
|
|
@@ -824,7 +873,7 @@ function SearchBox() {
|
|
|
824
873
|
│ │ │
|
|
825
874
|
│ ├── Once ever? ────► trigger(fn, []) │
|
|
826
875
|
│ │ │
|
|
827
|
-
│ ├── Every visit? ──► trigger(fn, [id])
|
|
876
|
+
│ ├── Every visit? ──► trigger(fn, [context.id]) │
|
|
828
877
|
│ │ │
|
|
829
878
|
│ └── When deps change? ► trigger(fn, [dep1, dep2]) │
|
|
830
879
|
│ │
|
|
@@ -888,9 +937,9 @@ function Dashboard() {
|
|
|
888
937
|
const [postState, postActions] = get(postStore);
|
|
889
938
|
const [commentState, commentActions] = get(commentStore);
|
|
890
939
|
|
|
891
|
-
trigger(userActions.fetch
|
|
892
|
-
trigger(postActions.fetch
|
|
893
|
-
trigger(commentActions.fetch
|
|
940
|
+
trigger(userActions.fetch);
|
|
941
|
+
trigger(postActions.fetch);
|
|
942
|
+
trigger(commentActions.fetch);
|
|
894
943
|
|
|
895
944
|
// Wait for ALL async states - suspends until all are ready
|
|
896
945
|
const [user, posts, comments] = async.all(
|
|
@@ -919,10 +968,8 @@ function FastestResult() {
|
|
|
919
968
|
const { result } = useStore(({ get, trigger }) => {
|
|
920
969
|
const [state, actions] = get(searchStore);
|
|
921
970
|
|
|
922
|
-
trigger(
|
|
923
|
-
|
|
924
|
-
actions.searchAPI2(query);
|
|
925
|
-
}, [query]);
|
|
971
|
+
trigger(actions.searchAPI1, [], query);
|
|
972
|
+
trigger(actions.searchAPI2, [], query);
|
|
926
973
|
|
|
927
974
|
// Returns whichever finishes first
|
|
928
975
|
return {
|
|
@@ -946,6 +993,156 @@ function FastestResult() {
|
|
|
946
993
|
| `async.isLoading(state)` | `boolean` | Loading indicator |
|
|
947
994
|
| `async.isError(state)` | `boolean` | Error check |
|
|
948
995
|
|
|
996
|
+
### Derived Async State with `async.derive()`
|
|
997
|
+
|
|
998
|
+
**The problem:** You need to compute a value from multiple async states. If any source is loading, the derived value should be loading. If any errors, it should error. Writing this logic manually is verbose and error-prone.
|
|
999
|
+
|
|
1000
|
+
**With Storion:** Use `async.derive()` to create computed async state. It uses `async.wait()` internally, so it automatically handles pending/error states and re-computes when sources change.
|
|
1001
|
+
|
|
1002
|
+
```ts
|
|
1003
|
+
import { store } from "storion";
|
|
1004
|
+
import { async, type AsyncState } from "storion/async";
|
|
1005
|
+
|
|
1006
|
+
interface User {
|
|
1007
|
+
id: string;
|
|
1008
|
+
name: string;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
interface Post {
|
|
1012
|
+
id: string;
|
|
1013
|
+
title: string;
|
|
1014
|
+
authorId: string;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
export const dashboardStore = store({
|
|
1018
|
+
name: "dashboard",
|
|
1019
|
+
state: {
|
|
1020
|
+
user: async.fresh<User>(),
|
|
1021
|
+
posts: async.fresh<Post[]>(),
|
|
1022
|
+
// Derived async state - computed from user + posts
|
|
1023
|
+
summary: async.fresh<{ userName: string; postCount: number }>(),
|
|
1024
|
+
},
|
|
1025
|
+
setup({ state, focus }) {
|
|
1026
|
+
// Fetch actions
|
|
1027
|
+
const userActions = async(focus("user"), async (ctx, userId: string) => {
|
|
1028
|
+
const res = await fetch(`/api/users/${userId}`, { signal: ctx.signal });
|
|
1029
|
+
return res.json();
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
const postsActions = async(focus("posts"), async (ctx, userId: string) => {
|
|
1033
|
+
const res = await fetch(`/api/users/${userId}/posts`, {
|
|
1034
|
+
signal: ctx.signal,
|
|
1035
|
+
});
|
|
1036
|
+
return res.json();
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
// Derive summary from user + posts
|
|
1040
|
+
// - If user OR posts is pending → summary is pending
|
|
1041
|
+
// - If user OR posts has error → summary has error
|
|
1042
|
+
// - If both succeed → summary is success with computed value
|
|
1043
|
+
async.derive(focus("summary"), () => {
|
|
1044
|
+
const user = async.wait(state.user);
|
|
1045
|
+
const posts = async.wait(state.posts);
|
|
1046
|
+
return {
|
|
1047
|
+
userName: user.name,
|
|
1048
|
+
postCount: posts.length,
|
|
1049
|
+
};
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
return {
|
|
1053
|
+
fetchUser: userActions.dispatch,
|
|
1054
|
+
fetchPosts: postsActions.dispatch,
|
|
1055
|
+
};
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
**Conditional dependencies:**
|
|
1061
|
+
|
|
1062
|
+
```ts
|
|
1063
|
+
// Derive with conditional async sources
|
|
1064
|
+
async.derive(focus("result"), () => {
|
|
1065
|
+
const type = async.wait(state.type);
|
|
1066
|
+
|
|
1067
|
+
// Dynamically choose which async state to wait for
|
|
1068
|
+
if (type === "user") {
|
|
1069
|
+
return async.wait(state.userData);
|
|
1070
|
+
} else {
|
|
1071
|
+
return async.wait(state.guestData);
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
**Parallel waiting with `async.all()`:**
|
|
1077
|
+
|
|
1078
|
+
```ts
|
|
1079
|
+
// Wait for multiple states in parallel (more efficient)
|
|
1080
|
+
async.derive(focus("combined"), () => {
|
|
1081
|
+
const [user, posts, comments] = async.all(
|
|
1082
|
+
state.user,
|
|
1083
|
+
state.posts,
|
|
1084
|
+
state.comments
|
|
1085
|
+
);
|
|
1086
|
+
return { user, posts, comments };
|
|
1087
|
+
});
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
**Stale mode - preserve data during recomputation:**
|
|
1091
|
+
|
|
1092
|
+
```ts
|
|
1093
|
+
state: {
|
|
1094
|
+
// Stale mode: keeps previous computed value while recomputing
|
|
1095
|
+
summary: async.stale({ userName: "Loading...", postCount: 0 }),
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// The derive will preserve stale data during pending/error
|
|
1099
|
+
async.derive(focus("summary"), () => {
|
|
1100
|
+
const user = async.wait(state.user);
|
|
1101
|
+
const posts = async.wait(state.posts);
|
|
1102
|
+
return { userName: user.name, postCount: posts.length };
|
|
1103
|
+
});
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
**Key behaviors:**
|
|
1107
|
+
|
|
1108
|
+
| Source State | Derived State |
|
|
1109
|
+
| ------------------ | ----------------------------------- |
|
|
1110
|
+
| Any source pending | `pending` (stale: preserves data) |
|
|
1111
|
+
| Any source error | `error` (stale: preserves data) |
|
|
1112
|
+
| All sources ready | `success` with computed value |
|
|
1113
|
+
| Sources change | Auto-recomputes via effect tracking |
|
|
1114
|
+
|
|
1115
|
+
**`async.derive()` vs manual effects:**
|
|
1116
|
+
|
|
1117
|
+
```ts
|
|
1118
|
+
// ❌ Manual - verbose, error-prone
|
|
1119
|
+
effect(() => {
|
|
1120
|
+
if (state.user.status === "pending" || state.posts.status === "pending") {
|
|
1121
|
+
state.summary = asyncState("fresh", "pending");
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (state.user.status === "error") {
|
|
1125
|
+
state.summary = asyncState("fresh", "error", state.user.error);
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (state.posts.status === "error") {
|
|
1129
|
+
state.summary = asyncState("fresh", "error", state.posts.error);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
state.summary = asyncState("fresh", "success", {
|
|
1133
|
+
userName: state.user.data.name,
|
|
1134
|
+
postCount: state.posts.data.length,
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
// ✅ With async.derive - clean and automatic
|
|
1139
|
+
async.derive(focus("summary"), () => {
|
|
1140
|
+
const user = async.wait(state.user);
|
|
1141
|
+
const posts = async.wait(state.posts);
|
|
1142
|
+
return { userName: user.name, postCount: posts.length };
|
|
1143
|
+
});
|
|
1144
|
+
```
|
|
1145
|
+
|
|
949
1146
|
### Dependency Injection
|
|
950
1147
|
|
|
951
1148
|
**The problem:** Your stores need shared services (API clients, loggers, config) but importing singletons directly causes issues:
|
|
@@ -1026,6 +1223,72 @@ testApp.clear(); // Clear all cached instances
|
|
|
1026
1223
|
testApp.dispose(); // Dispose container and all instances
|
|
1027
1224
|
```
|
|
1028
1225
|
|
|
1226
|
+
### Parameterized Factories with `create()`
|
|
1227
|
+
|
|
1228
|
+
**The problem:** Some services need configuration at creation time (database connections, loggers with namespaces, API clients with different endpoints). But `get()` only works with parameterless factories since it caches instances.
|
|
1229
|
+
|
|
1230
|
+
**With Storion:** Use `create()` for parameterized factories. Unlike `get()`, `create()` always returns fresh instances and supports additional arguments.
|
|
1231
|
+
|
|
1232
|
+
```ts
|
|
1233
|
+
import { store, container, type Resolver } from "storion";
|
|
1234
|
+
|
|
1235
|
+
// Parameterized factory - receives resolver + custom args
|
|
1236
|
+
function createLogger(resolver: Resolver, namespace: string) {
|
|
1237
|
+
return {
|
|
1238
|
+
info: (msg: string) => console.log(`[${namespace}] INFO: ${msg}`),
|
|
1239
|
+
error: (msg: string) => console.error(`[${namespace}] ERROR: ${msg}`),
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function createDatabase(
|
|
1244
|
+
resolver: Resolver,
|
|
1245
|
+
config: { host: string; port: number }
|
|
1246
|
+
) {
|
|
1247
|
+
return {
|
|
1248
|
+
query: (sql: string) =>
|
|
1249
|
+
fetch(`http://${config.host}:${config.port}/query`, {
|
|
1250
|
+
method: "POST",
|
|
1251
|
+
body: sql,
|
|
1252
|
+
}),
|
|
1253
|
+
close: () => {
|
|
1254
|
+
/* cleanup */
|
|
1255
|
+
},
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// Use in store setup
|
|
1260
|
+
const userStore = store({
|
|
1261
|
+
name: "user",
|
|
1262
|
+
state: { users: [] as User[] },
|
|
1263
|
+
setup({ create }) {
|
|
1264
|
+
// Each call creates a fresh instance with specific config
|
|
1265
|
+
const logger = create(createLogger, "user-store");
|
|
1266
|
+
const db = create(createDatabase, { host: "localhost", port: 5432 });
|
|
1267
|
+
|
|
1268
|
+
return {
|
|
1269
|
+
fetchUsers: async () => {
|
|
1270
|
+
logger.info("Fetching users...");
|
|
1271
|
+
await db.query("SELECT * FROM users");
|
|
1272
|
+
},
|
|
1273
|
+
};
|
|
1274
|
+
},
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
// Also works with container directly
|
|
1278
|
+
const app = container();
|
|
1279
|
+
const authLogger = app.create(createLogger, "auth");
|
|
1280
|
+
const adminDb = app.create(createDatabase, { host: "admin.db", port: 5433 });
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
**Key differences between `get()` and `create()`:**
|
|
1284
|
+
|
|
1285
|
+
| Feature | `get()` | `create()` |
|
|
1286
|
+
| ---------- | --------------------------- | --------------------------------------------- |
|
|
1287
|
+
| Caching | Yes (singleton per factory) | No (always fresh) |
|
|
1288
|
+
| Arguments | None (parameterless only) | Supports additional arguments |
|
|
1289
|
+
| Use case | Shared services | Configured instances, child stores |
|
|
1290
|
+
| Middleware | Applied | Applied (without args) / Bypassed (with args) |
|
|
1291
|
+
|
|
1029
1292
|
### Middleware
|
|
1030
1293
|
|
|
1031
1294
|
**The problem:** You need cross-cutting behavior (logging, persistence, devtools) applied to some or all stores, without modifying each store individually.
|
|
@@ -1145,8 +1408,11 @@ const myStore = store({
|
|
|
1145
1408
|
```ts
|
|
1146
1409
|
interface StoreContext<TState, TActions> {
|
|
1147
1410
|
state: TState; // First-level props only (state.x = y)
|
|
1148
|
-
get<T>(spec: StoreSpec<T>): StoreTuple; // Get dependency store
|
|
1149
|
-
get<T>(factory: Factory<T>): T; // Get DI service
|
|
1411
|
+
get<T>(spec: StoreSpec<T>): StoreTuple; // Get dependency store (cached)
|
|
1412
|
+
get<T>(factory: Factory<T>): T; // Get DI service (cached)
|
|
1413
|
+
create<T>(spec: StoreSpec<T>): StoreInstance<T>; // Create child store (fresh)
|
|
1414
|
+
create<T>(factory: Factory<T>): T; // Create service (fresh)
|
|
1415
|
+
create<R, A>(factory: (r, ...a: A) => R, ...a: A): R; // Parameterized factory
|
|
1150
1416
|
focus<P extends Path>(path: P): Focus; // Lens-like accessor
|
|
1151
1417
|
update(fn: (draft: TState) => void): void; // For nested/array mutations
|
|
1152
1418
|
dirty(prop?: keyof TState): boolean; // Check if state changed
|
|
@@ -1157,6 +1423,29 @@ interface StoreContext<TState, TActions> {
|
|
|
1157
1423
|
|
|
1158
1424
|
> **Note:** `state` allows direct assignment only for first-level properties. Use `update()` for nested objects, arrays, or batch updates.
|
|
1159
1425
|
|
|
1426
|
+
**`get()` vs `create()` — When to use each:**
|
|
1427
|
+
|
|
1428
|
+
| Method | Caching | Use case |
|
|
1429
|
+
| ---------- | -------- | ------------------------------------------------------ |
|
|
1430
|
+
| `get()` | Cached | Shared dependencies, singleton services |
|
|
1431
|
+
| `create()` | No cache | Child stores, parameterized factories, fresh instances |
|
|
1432
|
+
|
|
1433
|
+
```ts
|
|
1434
|
+
setup({ get, create }) {
|
|
1435
|
+
// get() - cached, same instance every time
|
|
1436
|
+
const api = get(apiService); // Singleton
|
|
1437
|
+
|
|
1438
|
+
// create() - fresh instance each call
|
|
1439
|
+
const childStore = create(childSpec); // New store instance
|
|
1440
|
+
|
|
1441
|
+
// create() with arguments - parameterized factory
|
|
1442
|
+
const db = create(createDatabase, { host: 'localhost', port: 5432 });
|
|
1443
|
+
const logger = create(createLogger, 'auth-store');
|
|
1444
|
+
|
|
1445
|
+
return { /* ... */ };
|
|
1446
|
+
}
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1160
1449
|
### React (`storion/react`)
|
|
1161
1450
|
|
|
1162
1451
|
| Export | Description |
|
|
@@ -1171,10 +1460,13 @@ interface StoreContext<TState, TActions> {
|
|
|
1171
1460
|
#### useStore Selector
|
|
1172
1461
|
|
|
1173
1462
|
```ts
|
|
1174
|
-
// Selector receives context with get()
|
|
1175
|
-
const result = useStore(({ get, mixin, once }) => {
|
|
1463
|
+
// Selector receives context with get(), create(), mixin(), once()
|
|
1464
|
+
const result = useStore(({ get, create, mixin, once }) => {
|
|
1176
1465
|
const [state, actions] = get(myStore);
|
|
1177
|
-
const service = get(myFactory);
|
|
1466
|
+
const service = get(myFactory); // Cached
|
|
1467
|
+
|
|
1468
|
+
// create() for parameterized factories (fresh instance each render)
|
|
1469
|
+
const logger = create(createLogger, "my-component");
|
|
1178
1470
|
|
|
1179
1471
|
// Run once on mount
|
|
1180
1472
|
once(() => actions.init());
|
|
@@ -1194,6 +1486,7 @@ const result = useStore(({ get, mixin, once }) => {
|
|
|
1194
1486
|
| `async.all(...states)` | Wait for all states to be ready |
|
|
1195
1487
|
| `async.any(...states)` | Get first ready state |
|
|
1196
1488
|
| `async.race(states)` | Race between states |
|
|
1489
|
+
| `async.derive(focus, computeFn)` | Derive async state from other async states |
|
|
1197
1490
|
| `async.hasData(state)` | Check if state has data |
|
|
1198
1491
|
| `async.isLoading(state)` | Check if state is loading |
|
|
1199
1492
|
| `async.isError(state)` | Check if state has error |
|
package/dist/async/async.d.ts
CHANGED
|
@@ -114,6 +114,55 @@ export declare namespace async {
|
|
|
114
114
|
status: "error";
|
|
115
115
|
error: Error;
|
|
116
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* Derive an async state from other async states using a synchronous computation.
|
|
119
|
+
* The computation function uses `async.wait()` to extract data from async states,
|
|
120
|
+
* which throws promises when states are pending.
|
|
121
|
+
*
|
|
122
|
+
* Key behaviors:
|
|
123
|
+
* - If `computeFn` throws a promise (via `async.wait`), sets focus to pending and re-runs after promise settles
|
|
124
|
+
* - If `computeFn` throws an error, sets focus to error state
|
|
125
|
+
* - If `computeFn` returns a value, sets focus to success state
|
|
126
|
+
* - If `computeFn` returns a promise (not throws), throws an error - must be synchronous
|
|
127
|
+
* - Uses a single wrapper promise to avoid cascading re-renders
|
|
128
|
+
*
|
|
129
|
+
* @param focus - Focus to update with derived async state
|
|
130
|
+
* @param computeFn - Synchronous computation function that uses `async.wait()` to read async states
|
|
131
|
+
* @returns Dispose function to stop the derivation
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* // Basic usage - derive from multiple async states
|
|
135
|
+
* async.derive(focus('c'), () => {
|
|
136
|
+
* const a = async.wait(state.a);
|
|
137
|
+
* const b = async.wait(state.b);
|
|
138
|
+
* return a + b;
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* // Conditional dependencies
|
|
143
|
+
* async.derive(focus('result'), () => {
|
|
144
|
+
* const type = async.wait(state.type);
|
|
145
|
+
* if (type === 'user') {
|
|
146
|
+
* return async.wait(state.userData);
|
|
147
|
+
* } else {
|
|
148
|
+
* return async.wait(state.guestData);
|
|
149
|
+
* }
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* // For parallel waiting, use async.all
|
|
154
|
+
* async.derive(focus('combined'), () => {
|
|
155
|
+
* const [a, b, c] = async.all(state.a, state.b, state.c);
|
|
156
|
+
* return { a, b, c };
|
|
157
|
+
* });
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* // With stale mode - preserves data during loading/error
|
|
161
|
+
* async.derive(focus('result'), () => {
|
|
162
|
+
* return async.wait(state.userData);
|
|
163
|
+
* });
|
|
164
|
+
*/
|
|
165
|
+
function derive<T, M extends AsyncMode = "fresh">(focus: Focus<AsyncState<T, M>>, computeFn: () => T): VoidFunction;
|
|
117
166
|
}
|
|
118
167
|
export {};
|
|
119
168
|
//# sourceMappingURL=async.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/async/async.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EAET,YAAY,EACZ,YAAY,EACZ,YAAY,EAEZ,kBAAkB,EAElB,cAAc,EAEd,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,QAAQ,EACR,cAAc,EAEf,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/async/async.ts"],"names":[],"mappings":"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"}
|
package/dist/async/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { e as effect } from "../effect-DPAYSuW3.js";
|
|
2
|
+
import { u as untrack } from "../emitter-BA44OHdL.js";
|
|
1
3
|
class AsyncNotReadyError extends Error {
|
|
2
4
|
constructor(message, status) {
|
|
3
5
|
super(message);
|
|
@@ -523,6 +525,54 @@ function asyncState(mode, status, dataOrError, errorOrExtra, extra) {
|
|
|
523
525
|
return state.status === "error";
|
|
524
526
|
}
|
|
525
527
|
async2.isError = isError;
|
|
528
|
+
function derive(focus, computeFn) {
|
|
529
|
+
const [getState, setState] = focus;
|
|
530
|
+
let hasSetPending = false;
|
|
531
|
+
return effect((ctx) => {
|
|
532
|
+
const currentState = untrack(getState);
|
|
533
|
+
const mode = currentState.mode;
|
|
534
|
+
const staleData = mode === "stale" ? currentState.data : currentState.status === "success" ? currentState.data : void 0;
|
|
535
|
+
try {
|
|
536
|
+
const result = computeFn();
|
|
537
|
+
if (result !== null && result !== void 0 && typeof result.then === "function") {
|
|
538
|
+
throw new Error(
|
|
539
|
+
"async.derive: computeFn must be synchronous. Use async.wait() for async values, not async/await or returning promises."
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
hasSetPending = false;
|
|
543
|
+
if (mode === "stale") {
|
|
544
|
+
setState(asyncState("stale", "success", result));
|
|
545
|
+
} else {
|
|
546
|
+
setState(asyncState("fresh", "success", result));
|
|
547
|
+
}
|
|
548
|
+
} catch (ex) {
|
|
549
|
+
if (ex !== null && ex !== void 0 && typeof ex.then === "function") {
|
|
550
|
+
if (!hasSetPending) {
|
|
551
|
+
hasSetPending = true;
|
|
552
|
+
if (mode === "stale") {
|
|
553
|
+
setState(
|
|
554
|
+
asyncState("stale", "pending", staleData)
|
|
555
|
+
);
|
|
556
|
+
} else {
|
|
557
|
+
setState(asyncState("fresh", "pending"));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
ctx.safe(ex).then(ctx.refresh, ctx.refresh);
|
|
561
|
+
} else {
|
|
562
|
+
hasSetPending = false;
|
|
563
|
+
const error = ex instanceof Error ? ex : new Error(String(ex));
|
|
564
|
+
if (mode === "stale") {
|
|
565
|
+
setState(
|
|
566
|
+
asyncState("stale", "error", staleData, error)
|
|
567
|
+
);
|
|
568
|
+
} else {
|
|
569
|
+
setState(asyncState("fresh", "error", error));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
async2.derive = derive;
|
|
526
576
|
})(async || (async = {}));
|
|
527
577
|
export {
|
|
528
578
|
AsyncAggregateError,
|
package/dist/collection.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export interface Collection<TKey, TValue> {
|
|
2
|
+
with(key: TKey, callback: (item: TValue) => void): this;
|
|
3
|
+
has(key: TKey): boolean;
|
|
4
|
+
get(key: TKey): TValue;
|
|
5
|
+
set(key: TKey, value: TValue): this;
|
|
6
|
+
size: number;
|
|
7
|
+
clear(): this;
|
|
8
|
+
delete(key: TKey): this;
|
|
9
|
+
keys(): IterableIterator<TKey>;
|
|
10
|
+
values(): IterableIterator<TValue>;
|
|
11
|
+
entries(): IterableIterator<[TKey, TValue]>;
|
|
12
|
+
}
|
|
1
13
|
/**
|
|
2
14
|
* Lazy-instantiation Map wrapper.
|
|
3
15
|
*
|
|
@@ -18,25 +30,5 @@
|
|
|
18
30
|
* @param initialItems - Optional initial entries
|
|
19
31
|
* @returns A Map-like object with lazy instantiation on `get()`
|
|
20
32
|
*/
|
|
21
|
-
export declare function collection<TKey, TValue>(createItem: (key: TKey) => TValue, initialItems?: readonly [TKey, TValue][]):
|
|
22
|
-
with(key: TKey, callback: (item: TValue) => void): /*elided*/ any;
|
|
23
|
-
/** Check if key exists (does NOT create item) */
|
|
24
|
-
has(key: TKey): boolean;
|
|
25
|
-
/** Get item by key, creating it if it doesn't exist */
|
|
26
|
-
get(key: TKey): NonNullable<TValue>;
|
|
27
|
-
/** Explicitly set an item */
|
|
28
|
-
set(key: TKey, value: TValue): /*elided*/ any;
|
|
29
|
-
/** Number of items in the collection */
|
|
30
|
-
readonly size: number;
|
|
31
|
-
/** Remove all items */
|
|
32
|
-
clear(): /*elided*/ any;
|
|
33
|
-
/** Remove a specific item */
|
|
34
|
-
delete(key: TKey): /*elided*/ any;
|
|
35
|
-
/** Iterate over keys */
|
|
36
|
-
keys(): MapIterator<TKey>;
|
|
37
|
-
/** Iterate over values */
|
|
38
|
-
values(): MapIterator<TValue>;
|
|
39
|
-
/** Iterate over [key, value] pairs */
|
|
40
|
-
entries(): MapIterator<[TKey, TValue]>;
|
|
41
|
-
};
|
|
33
|
+
export declare function collection<TKey, TValue>(createItem: (key: TKey) => TValue, initialItems?: readonly [TKey, TValue][]): Collection<TKey, TValue>;
|
|
42
34
|
//# sourceMappingURL=collection.d.ts.map
|
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU,CAAC,IAAI,EAAE,MAAM;IACtC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACxD,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC;IACxB,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,IAAI,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,IAAI,gBAAgB,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EACrC,UAAU,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,MAAM,EACjC,YAAY,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,GACvC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CA8D1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/core/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEhB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/core/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEhB,MAAM,UAAU,CAAC;AAYlB,UAAU,uBAAuB;IAC/B,sDAAsD;IACtD,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC;IACnB,qDAAqD;IACrD,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC;CACrB;AAYD;;GAEG;AACH,UAAU,WAAW;IACnB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAAC;IAC7C;;OAEG;IACH,QAAQ,EAAE;QACR,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;QACzC,KAAK,IAAI,IAAI,CAAC;KACf,CAAC;CACH;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,WAkLvB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createResolver.d.ts","sourceRoot":"","sources":["../../src/core/createResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EACV,OAAO,EAEP,UAAU,EACV,QAAQ,EACR,eAAe,EAIhB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"createResolver.d.ts","sourceRoot":"","sources":["../../src/core/createResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EACV,OAAO,EAEP,UAAU,EACV,QAAQ,EACR,eAAe,EAIhB,MAAM,UAAU,CAAC;AAKlB,YAAY,EACV,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,QAAQ,EACR,eAAe,GAChB,MAAM,UAAU,CAAC;AAmClB;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CA6JtE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,EACxC,UAAU,EAAE,UAAU,GACrB,UAAU,CAOZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,SAAa,GAAG,UAAU,CAUvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GACpD,UAAU,CAMZ"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Disposable } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Attempts to dispose a value if it has a `dispose` property.
|
|
5
|
+
*
|
|
6
|
+
* Supports multiple disposal patterns:
|
|
7
|
+
* - If `dispose` is a function, it will be called directly
|
|
8
|
+
* - If `dispose` is an array, each item will be processed:
|
|
9
|
+
* - Functions are called directly
|
|
10
|
+
* - Objects are recursively disposed via `tryDispose`
|
|
11
|
+
*
|
|
12
|
+
* @param value - The value to attempt disposal on. Can be any type;
|
|
13
|
+
* non-disposable values are safely ignored.
|
|
14
|
+
*/
|
|
15
|
+
export declare function tryDispose(value: unknown): void;
|
|
16
|
+
export declare function isDisposable(value: unknown): value is Disposable;
|
|
17
|
+
export declare function willDispose(value: unknown): VoidFunction;
|
|
18
|
+
//# sourceMappingURL=disposable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../../src/core/disposable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,QAoBxC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAMhE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAExD"}
|
package/dist/core/effect.d.ts
CHANGED
|
@@ -115,6 +115,26 @@ export interface EffectContext {
|
|
|
115
115
|
* });
|
|
116
116
|
*/
|
|
117
117
|
safe<TArgs extends unknown[], TReturn>(callback: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Manually trigger a re-run of the effect.
|
|
120
|
+
* Useful for async.derive pattern where thrown promises should trigger re-computation.
|
|
121
|
+
*
|
|
122
|
+
* Note: If called while the effect is currently running, the refresh will be
|
|
123
|
+
* scheduled for after the current run completes.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* effect((ctx) => {
|
|
127
|
+
* try {
|
|
128
|
+
* const data = async.wait(state.asyncData); // throws if pending
|
|
129
|
+
* state.computed = transform(data);
|
|
130
|
+
* } catch (ex) {
|
|
131
|
+
* if (isPromise(ex)) {
|
|
132
|
+
* ctx.safe(ex).then(ctx.refresh, ctx.refresh);
|
|
133
|
+
* }
|
|
134
|
+
* }
|
|
135
|
+
* });
|
|
136
|
+
*/
|
|
137
|
+
refresh(): void;
|
|
118
138
|
}
|
|
119
139
|
/**
|
|
120
140
|
* Effect function type.
|