repos / starfx

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

commit
5b6cce2
parent
2a61af0
author
Eric Bower
date
2024-08-16 19:32:28 +0000 UTC
docs: more content
7 files changed,  +90, -20
M docs/posts/controllers.md
+4, -0
 1@@ -11,5 +11,9 @@ of business logic. This could be as simple as making a single API endpoint and
 2 caching the results or as complex as making multiple dependent API calls and
 3 combinatory logic.
 4 
 5+Not only do have a centralized place for handling complex business logic,
 6+fetching API data, and updating our FE global state, but we also have a robust
 7+middleware system similar to `express` or `koa`!
 8+
 9 In the following sections we will discuss how to create controllers and the
10 different use cases for them inside `starfx`.
M docs/posts/learn.md
+4, -0
 1@@ -41,6 +41,10 @@ of hooks. Then entire system is designed, from the ground up, to not need
 2 subscribing to and publishing events. Those events could come from `react`, but
 3 they could also come from anywhere.
 4 
 5+Thirdly, we have taken the best part about `express` and `koa` and applied it to
 6+fetching API data on the front-end. What this means is that we have a powerful
 7+middleware system that we can leverage on the front-end.
 8+
 9 # Why does `starfx` use js generators?
10 
11 Generators give us -- the library authors -- more control over how side-effects
M docs/posts/loader.md
+22, -5
 1@@ -13,11 +13,8 @@ want.
 2 
 3 # Usage
 4 
 5-For endpoints, when you use `storeMdw.store()`, loaders automatically track
 6-fetch requests.
 7-
 8-For thunks you can use `storeMdw.loader()` which will track the status of a
 9-thunk.
10+For endpoints, loaders are installed automatically and track fetch requests.
11+Loader success is determined by `Response.ok` or if `fetch` throws an error.
12 
13 You can also use loaders manually:
14 
15@@ -33,6 +30,26 @@ function* fn() {
16 }
17 ```
18 
19+For thunks you can use `mdw.loader()` which will track the status of a thunk.
20+
21+```ts
22+import { createThunks, mdw } from "starfx";
23+// imaginary schema
24+import { initialState, schema } from "./schema";
25+
26+const thunks = createThunks();
27+thunks.use(mdw.loader(schema));
28+thunks.use(thunks.routes());
29+
30+const go = thunks.create("go", function* (ctx, next) {
31+  throw new Error("boom!");
32+});
33+
34+const store = createStore({ initialState });
35+store.dispatch(go());
36+schema.loaders.selectById(store.getState(), { id: `${go}` }); // status = "error"; message = "boom!"
37+```
38+
39 # Shape
40 
41 ```ts
M docs/posts/models.md
+4, -4
 1@@ -6,8 +6,8 @@ description: State management in starfx
 2 Once core component of an MVC framework is the Model.
 3 
 4 Since data normalization is a first-class citizen inside `starfx`, we built a
 5-custom react database for front-end web apps. Like a backend MVC framework, we
 6-want to think of managing the FE store like managing a database. So while
 7+custom, reactive database for front-end web apps. Like a backend MVC framework,
 8+we want to think of managing the FE store like managing a database. So while
 9 thinking about models as separate entities, you create all your models by
10 creating a single schema.
11 
12@@ -15,7 +15,7 @@ Managing models in `starfx` leverages two primary concepts: schema and store.
13 
14 The store is a single, global, and reactive object that was built to make
15 updating views easy. It is essentially an event emitter with a javascript object
16-attached to it.
17+that is updated in a very particular way (via `schema.update`).
18 
19 Because the goal of this library is to create scalable web apps, we want users
20-to create all their models at the same time inside of a single schema.
21+to create all their models at the same time inside a single schema.
M docs/posts/schema.md
+2, -0
1@@ -17,6 +17,8 @@ functions to:
2 - Update the value
3 - Query for data within the value
4 
5+Our schema implementation was heavily inspired by [zod](https://zod.dev/).
6+
7 # Schema assumptions
8 
9 `createSchema` requires two slices by default in order for it and everything
M docs/posts/store.md
+11, -4
 1@@ -3,10 +3,17 @@ title: Store
 2 Description: An immutable store that acts like a reactive, in-memory database
 3 ---
 4 
 5-I love `redux`. I know it gets sniped for having too much boilerplate when
 6+Features:
 7+
 8+- A single, global javascript object
 9+- Reactive
10+- Normalized
11+- Acts like a database
12+
13+We love `redux`. We know it gets sniped for having too much boilerplate when
14 alternatives like `zustand` and `react-query` exist that cut through the
15 ceremony of managing state. However, `redux` was never designed to be easy to
16-use, it was designed to be scalable, debuggable, and maintainable. Yes, setting
17+use; it was designed to be scalable, debuggable, and maintainable. Yes, setting
18 up a `redux` store is work, but that is in an effort to serve its
19 maintainability.
20 
21@@ -19,13 +26,13 @@ Fast forward to `redux-toolkit` and we have `createSlice` which leverages
22 `immer` under-the-hood to ensure immutability. So we no longer need reducers for
23 immutability.
24 
25-Further, I argue, placing the business logic for updating state inside reducers
26+Further, we argue, placing the business logic for updating state inside reducers
27 (via switch-cases) makes understanding business logic harder. Instead of having
28 a single function that updates X state slices, we have X functions (reducers)
29 that we need to piece together in our heads to understand what is being updated
30 when an action is dispatched.
31 
32-With all of this in mind, `starfx/store` takes all the good parts of `redux` and
33+With all of this in mind, `starfx` takes all the good parts of `redux` and
34 removes the need for reducers entirely. We still have a single state object that
35 contains everything from API data, UX, and a way to create memoized functions
36 (e.g. selectors). We maintain immutability (using `immer`) and also have a
M docs/posts/thunks.md
+43, -7
 1@@ -7,16 +7,18 @@ Thunks are the foundational central processing units. They have access to all
 2 the actions being dispatched from the view as well as your global state. They
 3 also wield the full power of structured concurrency.
 4 
 5-As I've been developing these specialized thunks, I'm starting to think of them
 6-more like micro-controllers. Only thunks and endpoints have the ability to
 7-update state (think MVC). However, thunks are not tied to any particular view
 8-and in that way are more composable. Thunks can call other thunks and you have
 9-the async flow control tools from `effection` to facilitate coordination.
10+> Endpoints are specialized thunks as you will see later in the docs
11+
12+Think of thunks as micro-controllers. Only thunks and endpoints have the ability
13+to update state (or a model in MVC terms). However, thunks are not tied to any
14+particular view and in that way are more composable. Thunks can call other
15+thunks and you have the async flow control tools from `effection` to facilitate
16+coordination and cleanup.
17 
18 Every thunk that's created requires a unique id -- user provided string. This
19-provides us with a handful of benefits:
20+provides us with some benefits:
21 
22-- User hand-labels each thunk created
23+- User hand-labels each thunk
24 - Better traceability
25 - Easier to debug async and side-effects
26 - Build abstractions off naming conventions (e.g. creating routers
27@@ -62,6 +64,34 @@ store.dispatch(log("sending log message"));
28 // after all remaining middleware have run
29 ```
30 
31+# Anatomy of thunk middleware
32+
33+Thunks are a composition of middleware functions in a stack. Therefore, every
34+single middleware function shares the exact same type signature:
35+
36+```ts
37+// for demonstration purposes we are copy/pasting these types which can
38+// normally be imported from:
39+//   import type { ThunkCtx, Next } from "starfx";
40+type Next = () => Operation<void>;
41+
42+interface ThunkCtx<P = any> extends Payload<P> {
43+  name: string;
44+  key: string;
45+  action: ActionWithPayload<CreateActionPayload<P>>;
46+  actionFn: IfAny<
47+    P,
48+    CreateAction<ThunkCtx>,
49+    CreateActionWithPayload<ThunkCtx<P>, P>
50+  >;
51+  result: Result<void>;
52+}
53+
54+function* myMiddleware(ctx: ThunkCtx, next: Next) {
55+  yield* next();
56+}
57+```
58+
59 # Thunk action
60 
61 When creating a thunk, the return value is just an action creator:
62@@ -74,9 +104,15 @@ console.log(log("sending log message"));
63 }
64 ```
65 
66+An action is the "event" being emitted from `startfx` and subscribes to a very
67+particular type signature.
68+
69 A thunk action adheres to the
70 [flux standard action spec](https://github.com/redux-utilities/flux-standard-action).
71 
72+> While not strictly necessary, it is highly recommended to keep actions JSON
73+> serializable
74+
75 # Thunk payload
76 
77 When calling a thunk, the user can provide a payload that is strictly enforced