repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

commit
187ff1a
parent
d1df279
author
Eric Bower
date
2024-02-16 15:08:38 +0000 UTC
docs: copy
4 files changed,  +172, -1
M docs/posts/endpoints.md
+59, -0
 1@@ -38,6 +38,65 @@ store.dispatch(fetchUsers());
 2 store.dispatch(updateUser({ id: "1", name: "bobby" }));
 3 ```
 4 
 5+# Enforcing fetch response type
 6+
 7+When using `createApi` and `mdw.fetch` we can provide the type that we think
 8+will be returned by the fetch response:
 9+
10+```ts
11+interface Success {
12+  users: User[];
13+}
14+
15+interface Err {
16+  error: string;
17+}
18+
19+const fetchUsers = api.get<never, Success, Err>(
20+  "/users",
21+  function* (ctx, next) {
22+    yield* next();
23+
24+    if (!ctx.json.ok) {
25+      // we have an error type
26+      console.log(ctx.json.value.error);
27+      return;
28+    }
29+
30+    // we have a success type
31+    console.log(ctx.json.value.users);
32+  },
33+);
34+```
35+
36+When calling `createApi` you can also pass it a generic error type that all
37+endpoints inherit:
38+
39+```ts
40+import type { ApiCtx } from "starfx";
41+
42+type MyApiCtx<P = any, S = any> = ApiCtx<P, S, { error: string }>;
43+
44+const api = createApi<MyApiCtx>();
45+
46+// this will inherit the types from `MyApiCtx`
47+const fetchUsers = api.get<never, Success>(
48+  "/users",
49+  function* (ctx, next) {
50+    yield* next();
51+
52+    if (!ctx.json.ok) {
53+      // we have an error type
54+      console.log(ctx.json.value.error);
55+      return;
56+    }
57+
58+    // we have a success type
59+    console.log(ctx.json.value.users);
60+  },
61+);
62+```
63+
64 # The same API endpoints but different logic
65 
66 It is very common to have the same endpoint with different business logic
M docs/posts/schema.md
+1, -1
1@@ -183,7 +183,7 @@ in your system.
2 
3 [Read more about entity factories.](https://bower.sh/entity-factories)
4 
5-# `slice.loader`
6+# `loader`
7 
8 This is a specialized database table specific to managing loaders in `starfx`.
9 [Read more about loaders here](/loader).
M docs/posts/store.md
+51, -0
 1@@ -89,3 +89,54 @@ const store = configureStore(schema);
 2 store.run(api.bootup);
 3 store.dispatch(fetchUsers());
 4 ```
 5+
 6+# How to update state
 7+
 8+There are **three** ways to update state, each with varying degrees of type
 9+safety:
10+
11+```ts
12+import { updateStore } from "starfx/store";
13+
14+function*() {
15+  // good types
16+  yield* schema.update([/* ... */]);
17+  // no types
18+  yield* updateStore([/* ... */]);
19+}
20+
21+store.run(function*() {
22+  // no types
23+  yield* store.update([/* ... */]);
24+});
25+```
26+
27+`schema.update` has the highest type safety because it knows your state shape.
28+The other methods are more generic and the user will have to provide types to
29+them manually.
30+
31+# Updating state from view
32+
33+You cannot directly update state from the view, users can only manipulate state
34+from a thunk, endpoint, or a delimited continuation.
35+
36+This is a design decision that forces everything to route through our mini
37+controllers.
38+
39+However, it is very easy to create a controller to do simple tasks like updating
40+state:
41+
42+```ts
43+import type { StoreUpdater } from "starfx/store";
44+
45+const updater = thunks.create<StoreUpdater[]>("update", function* (ctx, next) {
46+  yield* updateStore(ctx.payload);
47+  yield* next();
48+});
49+
50+store.dispatch(
51+  updater([
52+    schema.users.add({ [user1.id]: user }),
53+  ]),
54+);
55+```
M docs/posts/thunks.md
+61, -0
 1@@ -61,3 +61,64 @@ store.dispatch(log("sending log message"));
 2 // last mdw in the stack
 3 // after all remaining middleware have run
 4 ```
 5+
 6+# Thunk action
 7+
 8+When creating a thunk, the return value is just an action creator:
 9+
10+```ts
11+console.log(log("sending log message"));
12+{
13+  type: "log",
14+  payload: "sending log message"
15+}
16+```
17+
18+A thunk action adheres to the
19+[flux standard action spec](https://github.com/redux-utilities/flux-standard-action).
20+
21+# Thunk payload
22+
23+When calling a thunk, the user can provide a payload that is strictly enforced
24+and accessible via the `ctx.payload` property:
25+
26+```ts
27+const makeItSo = api.get<{ id: string }>("make-it-so", function* (ctx, next) {
28+  console.log(ctx.payload);
29+  yield* next();
30+});
31+
32+makeItSo(); // type error!
33+makeItSo("123"); // type error!
34+makeItSo({ id: "123" }); // nice!
35+```
36+
37+# Custom `ctx`
38+
39+End-users are able to provide a custom `ctx` object to their thunks. It must
40+extend `ThunkCtx` in order for it to pass, but otherwise you are free to add
41+whatever properties you want:
42+
43+```ts
44+import { createThunks, type ThunkCtx } from "starfx";
45+
46+interface MyCtx extends ThunkCtx {
47+  wow: bool;
48+}
49+
50+const thunks = createThunks<MyCtx>();
51+
52+// we recommend a mdw that ensures the property exists since we cannot
53+// make that guarentee
54+thunks.use(function* (ctx, next) {
55+  if (!Object.hasOwn(ctx, "wow")) {
56+    ctx.wow = false;
57+  }
58+  yield* next();
59+});
60+
61+const log = thunks.create("log", function* (ctx, next) {
62+  ctx.wow = true;
63+  yield* next();
64+});
65+```