nanoeffects 0.26.0 → 0.28.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.
Files changed (67) hide show
  1. package/README.md +85 -30
  2. package/dist/{action-creator-Bpa8BUS9.d.ts → action-creator-CcAEvUnw.d.ts} +90 -23
  3. package/dist/actions.d.ts +54 -21
  4. package/dist/actions.js +1 -1
  5. package/dist/bus-DlpEluzd.js +1 -0
  6. package/dist/{context-types-BKIH5a8s.d.ts → context-types-hCKgU8sC.d.ts} +1 -1
  7. package/dist/context.d.ts +2 -2
  8. package/dist/dev.d.ts +2 -2
  9. package/dist/dev.js +1 -1
  10. package/dist/effects.d.ts +2 -2
  11. package/dist/event-DVcW0mME.js +1 -0
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +1 -1
  14. package/dist/match.d.ts +1 -1
  15. package/dist/{module-B6rg5NIb.d.ts → module-C_oEXw1n.d.ts} +40 -57
  16. package/dist/notrace/{action-creator-D2CotugT.d.ts → action-creator-DxOvVo8I.d.ts} +90 -23
  17. package/dist/notrace/actions.d.ts +114 -98
  18. package/dist/notrace/actions.js +1 -1
  19. package/dist/notrace/bus-DmKn4SDD.js +1 -0
  20. package/dist/notrace/effects.d.ts +2 -2
  21. package/dist/notrace/index.d.ts +2 -2
  22. package/dist/notrace/index.js +1 -1
  23. package/dist/notrace/{plugin-Dz6hq8Jg.d.ts → plugin-lJjjd-SN.d.ts} +1 -1
  24. package/dist/notrace/policies.d.ts +1 -1
  25. package/dist/notrace/policies.js +1 -1
  26. package/dist/notrace/redux.d.ts +1 -1
  27. package/dist/observable.d.ts +6 -6
  28. package/dist/oop.d.ts +2 -2
  29. package/dist/oop.js +1 -1
  30. package/dist/{plugin-D-j1DarG.d.ts → plugin-CuvIgjnX.d.ts} +2 -2
  31. package/dist/policies.d.ts +2 -2
  32. package/dist/react.d.ts +2 -2
  33. package/dist/redux.d.ts +1 -1
  34. package/dist/scope.d.ts +2 -2
  35. package/dist/signal.d.ts +1 -1
  36. package/dist/signal.js +1 -1
  37. package/dist/svelte.d.ts +1 -1
  38. package/dist/trace.d.ts +1 -1
  39. package/dist/{types-Bkuhnkq6.d.ts → types-FV8fuvI9.d.ts} +2 -2
  40. package/dist/vue.d.ts +1 -1
  41. package/dist/{with-policy-BeH8wCkX.d.ts → with-policy-3bhFLEXs.d.ts} +1 -1
  42. package/docs/README.md +5 -4
  43. package/docs/actions.md +94 -53
  44. package/docs/chat-example.md +8 -8
  45. package/docs/chat-widget.md +135 -0
  46. package/docs/context.md +1 -1
  47. package/docs/errors.md +1 -1
  48. package/docs/guide.md +26 -26
  49. package/docs/hmr.md +1 -1
  50. package/docs/motivation.md +10 -4
  51. package/docs/oop.md +1 -1
  52. package/docs/package.md +2 -2
  53. package/docs/plugins.md +23 -23
  54. package/docs/query-libraries.md +14 -14
  55. package/docs/scope.md +22 -22
  56. package/docs/server.md +12 -12
  57. package/docs/stability.md +2 -2
  58. package/docs/starter.md +3 -3
  59. package/docs/tracing.md +5 -5
  60. package/docs/typescript.md +16 -16
  61. package/llms-full.txt +380 -194
  62. package/llms.txt +3 -2
  63. package/package.json +5 -4
  64. package/dist/bus-hHp-T7JV.js +0 -1
  65. package/dist/event-0RIBGYqS.js +0 -1
  66. package/dist/notrace/bus-BdyvumpX.js +0 -1
  67. package/dist/notrace/promise-CSLpzwSR.js +0 -1
package/docs/server.md CHANGED
@@ -16,9 +16,9 @@ The fix is one `AbortController` for each unit of work that the client owns, and
16
16
  | A WebSocket | the socket connects | the socket closes |
17
17
  | A queued job | the job is accepted | the server shuts down |
18
18
 
19
- ## Make the bus and the command
19
+ ## Make the bus and the action
20
20
 
21
- The bus and the command live in files that know nothing about HTTP, so the same command can run in a route, a test or a worker.
21
+ The bus and the action live in files that know nothing about HTTP, so the same action can run in a route, a test or a worker.
22
22
 
23
23
  ```js
24
24
  // bus.js
@@ -30,15 +30,15 @@ import { signalPlugin } from "nanoeffects/signal";
30
30
  export const bus = createBus().use(signalPlugin());
31
31
  ```
32
32
 
33
- The command loads a user. It tries the request up to three times, and gives up on each try after five seconds. `ctx.signal` is the signal of the task: it aborts when the controller of the request aborts.
33
+ The action loads a user. It tries the request up to three times, and gives up on each try after five seconds. `ctx.signal` is the signal of the task: it aborts when the controller of the request aborts.
34
34
 
35
35
  ```js
36
36
  // user.js
37
- import { createCommand } from "nanoeffects/actions";
37
+ import { createAction } from "nanoeffects/actions";
38
38
  import { retry, timeout } from "nanoeffects/effects";
39
39
 
40
- // createCommand — a command with a handler: emit returns what the handler returns
41
- export const getUser = createCommand("user/get", async (id, ctx) => {
40
+ // createAction — a command with a handler: emit returns what the handler returns
41
+ export const getUser = createAction("user/get", async (id, ctx) => {
42
42
  // one try: the request, with the signal of timeout so it can stop it
43
43
  const load = ({ signal }) => api.getUser(id, { signal });
44
44
  // timeout — fail the try after 5 s
@@ -91,7 +91,7 @@ app.get("/users/:id", async (request, reply) => {
91
91
  });
92
92
  ```
93
93
 
94
- After an abort the command rejects with the abort reason. The `catch` above ignores it when the connection is gone, because there is nobody to answer.
94
+ After an abort the action rejects with the abort reason. The `catch` above ignores it when the connection is gone, because there is nobody to answer.
95
95
 
96
96
  ## Stop the server
97
97
 
@@ -123,17 +123,17 @@ The route above fills `inFlight` and empties it in the `close` listener. The [ap
123
123
 
124
124
  A socket lasts minutes and carries several tasks (a subscription, a heartbeat), so the controller belongs to the socket and every `emit` for it gets the same signal. Closing the socket stops all of them.
125
125
 
126
- First the command. It sends a price now and then every second, until its task is aborted.
126
+ First the action. It sends a price now and then every second, until its task is aborted.
127
127
 
128
128
  ```js
129
129
  // prices.js
130
- import { createCommand } from "nanoeffects/actions";
130
+ import { createAction } from "nanoeffects/actions";
131
131
  import { poll } from "nanoeffects/effects";
132
132
  import { latest } from "nanoeffects/policies";
133
133
 
134
- // createCommand — a command with a handler
134
+ // createAction — a command with a handler
135
135
  // latest — a new subscription stops the old one; key — but only the old one of the same socket
136
- export const watchPrices = createCommand(
136
+ export const watchPrices = createAction(
137
137
  "prices/watch",
138
138
  latest(
139
139
  async (call, ctx) => {
@@ -147,7 +147,7 @@ export const watchPrices = createCommand(
147
147
  );
148
148
  ```
149
149
 
150
- Without the `key`, one policy serves the whole command, and a new subscription on one socket would stop the subscription on another.
150
+ Without the `key`, one policy serves the whole action, and a new subscription on one socket would stop the subscription on another.
151
151
 
152
152
  Then the route. Each socket gets an id and a controller.
153
153
 
package/docs/stability.md CHANGED
@@ -11,7 +11,7 @@ A stable entry follows the rules below. An unstable entry can change in any mino
11
11
  | `nanoeffects` | Stable | `createBus` and the types of a bus |
12
12
  | `nanoeffects/effects` | Stable | `retry`, `timeout`, `poll`, `take`, `delay`, `all`, `allSettled`, `race`, `backoff`, `cancellable`, `effectsPlugin` |
13
13
  | `nanoeffects/policies` | Stable | `latest`, `leading`, `debounce`, `throttle`, `queue`, `withPolicy` |
14
- | `nanoeffects/actions` | Stable | `createAction`, `createCommand`, `createJob`, `createModule` and their plugins |
14
+ | `nanoeffects/actions` | Stable | `createAction`, `createJob`, `createModule` and their plugins |
15
15
  | `nanoeffects/trace` | Stable: the contract | `Tracer`, `Trace`, `TraceEvent`, `TraceLogger`, `Phase`, `EffectName` and `tracePlugin` |
16
16
  | `nanoeffects/trace` | Unstable: the tools | `createTracer`, `consoleLogger`, `performanceLogger`, `createTraceRecorder`, `reduxDevtools` and their option types |
17
17
  | `nanoeffects/scope` | Unstable | `scopePlugin`, `effect` |
@@ -54,7 +54,7 @@ A deprecated export keeps working and is removed only in a major release. `RaceS
54
54
  | ---- | --------- |
55
55
  | Node.js, the library | 18, 20, 22 and 24 (a smoke test runs the built package on each) |
56
56
  | Node.js, the dev tools and the trace in a browser | 22 and newer |
57
- | TypeScript | The types are written and checked with TypeScript 7. TypeScript 5.9 is used for the Vue tools and for the test of the exports. Older versions are not tested. |
57
+ | TypeScript | 5.9 and 7. The built declaration files are type-checked with TypeScript 5.9 in CI, and the source with TypeScript 7. Older versions are not tested. |
58
58
  | React | 18 or newer (`nanoeffects/react`) |
59
59
  | Vue | 3.3 or newer (`nanoeffects/vue`) |
60
60
  | Svelte | 5 or newer (`nanoeffects/svelte`) |
package/docs/starter.md CHANGED
@@ -46,7 +46,7 @@ Pass `--template` to skip the question. With npm, put `--` before the option.
46
46
  | `react` | The same handler with `useEmitter` and `useEventState`, and `usePolicy` for a debounced save in a component | You build a React app |
47
47
  | `vanilla-ts` | The `vanilla` app in TypeScript | You want the compiler to check events and handlers |
48
48
  | `react-ts` | The `react` app in TypeScript | You build a React app in TypeScript |
49
- | `domains-ts` | Commands, a job and modules, with the logic split into domain folders and React as a thin layer | You build an app with more than one feature and want the logic off the UI |
49
+ | `domains-ts` | Actions with handlers, a job and modules, with the logic split into domain folders and React as a thin layer | You build an app with more than one feature and want the logic off the UI |
50
50
  | `vue-ts` | The `react-ts` app in Vue 3, with `watchEmitter` and `useEventRef` from `nanoeffects/vue` | You build a Vue 3 app |
51
51
  | `svelte-ts` | The `react-ts` app in Svelte 5, with `eventStore` from `nanoeffects/svelte` | You build a Svelte app |
52
52
 
@@ -94,13 +94,13 @@ npm create nanoeffects@latest my-app -- --template domains-ts
94
94
  | ---- | --- |
95
95
  | `src/bus.ts` | Creates the bus. It imports no events and no domain. |
96
96
  | `src/app.ts` | Mounts the domains with `modulePlugin`. |
97
- | `src/domains/catalog/` | Searching products: `catalog.ts` (events and a command), `catalog-handlers.ts` (`debounce` and `latest`), `catalog-module.ts`. |
97
+ | `src/domains/catalog/` | Searching products: `catalog.ts` (events and an action with a handler), `catalog-handlers.ts` (`debounce` and `latest`), `catalog-module.ts`. |
98
98
  | `src/domains/checkout/` | Placing an order: `checkout.ts` (a job), `checkout-handlers.ts` (`leading` and `retry`), `checkout-store.ts` (state written from the job's outcomes), `checkout-module.ts`. |
99
99
  | `src/ui/` | The components. None of them calls the fake server in `src/api.ts`. |
100
100
 
101
101
  For a bigger example, a chat in which parts react to each other, see [Build a chat with entities, features, modules and a small server](chat-example.md). It has more layers than `domains-ts`: when domains must react to each other, it keeps them from importing each other by splitting them into entities and features.
102
102
 
103
- The commands, jobs and modules themselves are explained in [Actions, commands, jobs and modules](actions.md). The template keeps found products in a Context provider with one `useEventState`, and the order state in a store of its own: `useEventState` is [not a store](react.md#show-the-last-value-of-an-event-as-state).
103
+ The actions, jobs and modules themselves are explained in [Actions, jobs and modules](actions.md). The template keeps found products in a Context provider with one `useEventState`, and the order state in a store of its own: `useEventState` is [not a store](react.md#show-the-last-value-of-an-event-as-state).
104
104
 
105
105
  ## Read the files
106
106
 
package/docs/tracing.md CHANGED
@@ -132,13 +132,13 @@ Every event of a task below another one has `rootId`, the id of the root of the
132
132
 
133
133
  **Problem.** A flow such as a checkout has several roots (a click, a timer, a store) and many branches. Parent ids give a tree, but you want to filter the log by the flow.
134
134
 
135
- **When.** One business flow runs through several commands, and you read the log by flow.
135
+ **When.** One business flow runs through several actions, and you read the log by flow.
136
136
 
137
- Give the flow a `traceGroupId` in the scope of the command, the job or the action that starts it. Every task that it starts through `ctx.emit` or `ctx.run` has the same `traceGroupId`, and so does every trace event of those tasks. A task that declares its own `traceGroupId` uses it for itself and for what it starts.
137
+ Give the flow a `traceGroupId` in the scope of the action or the job that starts it. Every task that it starts through `ctx.emit` or `ctx.run` has the same `traceGroupId`, and so does every trace event of those tasks. A task that declares its own `traceGroupId` uses it for itself and for what it starts.
138
138
 
139
139
  ```js
140
140
  import { createBus } from "nanoeffects";
141
- import { createCommand } from "nanoeffects/actions";
141
+ import { createAction } from "nanoeffects/actions";
142
142
  import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
143
143
 
144
144
  // createTracer: sends each trace event to the logger; consoleLogger: prints one line each
@@ -149,7 +149,7 @@ const bus = createBus({ trace })
149
149
  .use(tracePlugin());
150
150
 
151
151
  // the scope names the flow; traceGroupId is for the tracer, not a field of ctx
152
- const pay = createCommand("checkout/pay", async () => {}, {
152
+ const pay = createAction("checkout/pay", async () => {}, {
153
153
  traceGroupId: "checkout",
154
154
  });
155
155
  ```
@@ -202,7 +202,7 @@ The names work wherever an event or a listener is made:
202
202
  | A listener, `bus.on` and `bus.once` | `{ traceId }` as the third argument |
203
203
  | A listener of a module or of an instance of `extendBus` | the same third argument of its `on` and `once` |
204
204
  | A method with `@On` or `@Once` | `{ traceId }` in the decorator |
205
- | A command, a job or an action | `traceGroupId` in its scope |
205
+ | An action or a job | `traceGroupId` in its scope |
206
206
 
207
207
  ## Turn the names off
208
208
 
@@ -1,20 +1,20 @@
1
1
  # Use nanoeffects in a TypeScript app with many files
2
2
 
3
- Read this page if your app is in TypeScript and the commands and modules live in different files. It shows how to write a command so that TypeScript infers its payload and result, and what to add when the handlers need the fields of your bus (`ctx.store`, the effects of `effectsPlugin`). For one file or for JavaScript you do not need it.
3
+ Read this page if your app is in TypeScript and the handlers and modules live in different files. It shows how to write an action with a handler so that TypeScript infers its payload and result, and what to add when the handlers need the fields of your bus (`ctx.store`, the effects of `effectsPlugin`). For one file or for JavaScript you do not need it.
4
4
 
5
- ## Write a command with its handler
5
+ ## Write an action with its handler
6
6
 
7
- Give `createCommand` the handler and write nothing else. The payload comes from the annotated parameter, the result from the body. You do not write a generic.
7
+ Give `createAction` the handler and write nothing else. The payload comes from the annotated parameter, the result from the body. You do not write a generic.
8
8
 
9
9
  <!-- docs-check: skip - a file of examples/typescript, type-checked there -->
10
10
  ```ts
11
11
  // user.ts
12
- import { createCommand, createModule } from "nanoeffects/actions";
12
+ import { createAction, createModule } from "nanoeffects/actions";
13
13
 
14
14
  export type User = { id: string; name: string };
15
15
 
16
16
  // payload: string, from `id: string`; result: Promise<User>, from the body
17
- export const getUser = createCommand(
17
+ export const getUser = createAction(
18
18
  "user/get",
19
19
  async (id: string, { signal }) => {
20
20
  const response = await fetch(`/api/users/${id}`, { signal });
@@ -56,11 +56,11 @@ A wrong payload at the call (`getUser(1)`) is an error. Annotate the parameters,
56
56
 
57
57
  ## Give `ctx` the fields of your bus
58
58
 
59
- Types of a bus live on the bus. `contextPlugin({ store })` and `effectsPlugin({ race })` make `ctx.store` and `ctx.race` known to the handlers of **that bus**. A command made in another file does not see the bus, so its `ctx` has no `store`:
59
+ Types of a bus live on the bus. `contextPlugin({ store })` and `effectsPlugin({ race })` make `ctx.store` and `ctx.race` known to the handlers of **that bus**. An action with a handler made in another file does not see the bus, so its `ctx` has no `store`:
60
60
 
61
61
  <!-- docs-check: skip - shows the error on purpose -->
62
62
  ```ts
63
- export const getUser = createCommand("user/get", (id: string, ctx) => {
63
+ export const getUser = createAction("user/get", (id: string, ctx) => {
64
64
  ctx.store; // error: Property 'store' does not exist
65
65
  });
66
66
  ```
@@ -93,7 +93,7 @@ export type AppContext = Context<AppEvents>; // ctx of a handler of this bus: ha
93
93
  | Type | What it is | Why you name it |
94
94
  | ---- | ---------- | --------------- |
95
95
  | `EventsOf<typeof bus>` | the event map of the bus, with the context fields of the plugins (and of `WithContext`, if you use it) | the source of `AppContext` |
96
- | `Context<AppEvents>` | the `ctx` that a handler of the bus gets | annotate the `ctx` of a handler given to `createCommand` |
96
+ | `Context<AppEvents>` | the `ctx` that a handler of the bus gets | annotate the `ctx` of a handler given to `createAction` |
97
97
 
98
98
  These types change nothing at runtime. They only tell the checker what the bus already does.
99
99
 
@@ -102,10 +102,10 @@ Annotate `ctx` with `AppContext` where the handler reads a field of the bus, and
102
102
  <!-- docs-check: skip - a file of examples/typescript-context, type-checked there -->
103
103
  ```ts
104
104
  // user.ts
105
- import { createCommand } from "nanoeffects/actions";
105
+ import { createAction } from "nanoeffects/actions";
106
106
  import { type AppContext, createModule } from "./bus";
107
107
 
108
- export const getUser = createCommand(
108
+ export const getUser = createAction(
109
109
  "user/get",
110
110
  (id: string, { store, signal }: AppContext) => store.fetchUser(id, { signal }),
111
111
  );
@@ -145,14 +145,14 @@ Pass `ctx` (or `{ signal }`) when you call it. The effects of `nanoeffects/effec
145
145
 
146
146
  ## Keep the contract apart from the code
147
147
 
148
- When the file with the command must not import the code that implements it, make the command without a handler. Here you write the payload and the result once, as generics, and the handler goes to a module, where it needs no annotation:
148
+ When the file with the action must not import the code that implements it, make the action without a handler. Here you write the payload and the result once, as a `Call`, and the handler goes to a module, where it needs no annotation. `Call` is a promise of yours: nothing checks it against the handler, so keep the handler typed from it in the module:
149
149
 
150
150
  <!-- docs-check: skip - fragment of the file above -->
151
151
  ```ts
152
- export const getUser = createCommand<string, Promise<User>>("user/get"); // the contract
152
+ export const getUser = createAction<Call<string, Promise<User>>>("user/get"); // the contract
153
153
 
154
154
  createModule("user", (m) => {
155
- // payload and result come from the command, ctx from the app: no annotation
155
+ // payload and result come from the Call, ctx from the app: no annotation
156
156
  m.handle(getUser, (id, { store, signal }) => store.fetchUser(id, { signal }));
157
157
  });
158
158
  ```
@@ -161,8 +161,8 @@ A wrong payload, a wrong field of `ctx` and a wrong result are errors.
161
161
 
162
162
  ## Check your setup
163
163
 
164
- - **A field is missing on `ctx`.** The `ctx` of a handler given to `createCommand` is not annotated: add `: AppContext`. A handler written inline in `m.handle` or `m.on` of a module made with the `createModule` of `bus.ts` does not need it. The default `ctx` has no fields of your bus.
165
- - **`EventsOf<typeof bus>` is `never`.** `bus` is not a bus. `bus.use(commandsPlugin())` and `bus.use(modulePlugin(...))` return the registry or the app, not the bus: keep the bus in its own constant (`bus`, then `app = bus.use(modulePlugin())`).
164
+ - **A field is missing on `ctx`.** The `ctx` of a handler given to `createAction` is not annotated: add `: AppContext`. A handler written inline in `m.handle` or `m.on` of a module made with the `createModule` of `bus.ts` does not need it. The default `ctx` has no fields of your bus.
165
+ - **`EventsOf<typeof bus>` is `never`.** `bus` is not a bus. `bus.use(actionsPlugin())` and `bus.use(modulePlugin(...))` return the registry or the app, not the bus: keep the bus in its own constant (`bus`, then `app = bus.use(modulePlugin())`).
166
166
  - **`effectsPlugin` is not in the type.** `use` must come before you read `typeof bus`. Chain `createBus(...).use(effectsPlugin(...))` in one binding.
167
167
  - **A policy loses the types.** It does not: `latest(handler)` keeps the payload, the result and the context of `handler`.
168
- - **Do not pass the context as a generic of `createCommand`.** It is the fourth generic, after the payload, the result and the type string, so you would have to write all four. Annotate `ctx` with `AppContext`, or put the handler in a module made with the `createModule` of `bus.ts`.
168
+ - **Do not pass the context as a generic of `createAction`.** It is the fourth generic of the handler form, after the payload, the result and the type string, so you would have to write all four. Annotate `ctx` with `AppContext`, or put the handler in a module made with the `createModule` of `bus.ts`.