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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![size](https://img.shields.io/bundlephobia/minzip/nanoeffects)](https://bundlephobia.com/package/nanoeffects)
6
6
  [![license](https://img.shields.io/npm/l/nanoeffects)](LICENSE)
7
7
 
8
- A small event bus for JavaScript apps. Each handler runs as a **task** with an `AbortSignal`, so you can cancel in-flight work. Policies (`latest`, `debounce`, …) and effects (`retry`, `timeout`, …) are plain functions you pass to `on`.
8
+ Move business logic and side effects out of React, Vue and Svelte components into plain `async` functions, with cancellation built in.
9
9
 
10
10
  ## Install
11
11
 
@@ -15,38 +15,37 @@ npm install nanoeffects
15
15
 
16
16
  ESM only, no dependencies, types included.
17
17
 
18
- Starting a new project? `npm create nanoeffects@latest my-app` makes a Vite app with a bus ([Docs](docs/starter.md)).
19
-
20
- Want to try it first? [Open the playground](https://stackblitz.com/github/nanoeffects/nanoeffects/tree/main/examples/playground): a search box with `debounce` and `latest`, running in your browser, no install. More playgrounds (React) and recipes: [Examples](examples/README.md).
18
+ New project: `npm create nanoeffects@latest my-app` ([Docs](docs/starter.md)). Try it first: [playground](https://stackblitz.com/github/nanoeffects/nanoeffects/tree/main/examples/playground) in your browser, no install.
21
19
 
22
20
  ## Create a bus
23
21
 
24
- Subscribe with `on`, send with `emit`. `on` returns a function that unsubscribes.
22
+ The project is built on the event emitter pattern, also known as [publish–subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern): every handler is an `on` listener and every message an `emit`. You rarely need to think about it, because policies, actions and modules (below) do the work. Full API of the bus (`once`, `off`, options): [Guide](docs/guide.md#1-create-a-bus).
25
23
 
26
24
  ```js
27
25
  import { createBus } from "nanoeffects";
28
26
 
29
27
  const bus = createBus(); // event bus; each handler call is a task
28
+ const cart = [];
30
29
 
31
- const unbind = bus.on("greet", (name) => {
32
- console.log(`hello ${name}`);
33
- });
30
+ // two independent listeners of one event; on returns the function that unsubscribes
31
+ const unbind = bus.on("cart/added", (item) => analytics.track("add", item));
32
+ bus.on("cart/added", (item) => cart.push(item));
34
33
 
35
- bus.emit("greet", "Ada"); // hello Ada
36
- unbind();
34
+ bus.emit("cart/added", { id: "p1" }); // both listeners run, cart has the item
35
+ unbind(); // stop tracking
37
36
  ```
38
37
 
39
- That is enough for a small app or a widget. The handler’s second argument is a task context (`signal`, `emit`, …). See the [Guide](docs/guide.md).
38
+ ## Keep the logic out of the component
40
39
 
41
- ## Abort overlapping work
42
-
43
- In a SPA, a newer search should abort the older one. Wrap the handler in `latest` and pass `ctx.signal` to `fetch` (and similar APIs).
40
+ A newer search should abort the older one. Put it in a handler outside the component, wrapped in `latest`.
44
41
 
45
42
  ```js
43
+ // search.js — no React, Vue or Svelte in this file
46
44
  import { createBus } from "nanoeffects";
47
45
  import { latest } from "nanoeffects/policies";
48
46
 
49
- const bus = createBus();
47
+ // createBus — one bus for the logic of the app
48
+ export const bus = createBus();
50
49
 
51
50
  // latest — a newer run aborts the older one
52
51
  bus.on(
@@ -59,40 +58,96 @@ bus.on(
59
58
  );
60
59
  ```
61
60
 
62
- Same `on` / `emit`. Replace `api.search` with your own request.
61
+ `ctx.signal` is a standard [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal), built into browsers and Node: `fetch` and many other APIs accept it and stop when it aborts.
63
62
 
64
- ## Get a result from `emit`
63
+ The handler knows nothing about the UI: it only emits `results`. The component sends `search` and listens to `results`. React has hooks for it (`useEventState`, `useEmitter`: [React](docs/react.md#show-the-last-value-of-an-event-as-state)), and [Vue](docs/vue.md) and [Svelte](docs/svelte.md) have their own helpers:
65
64
 
66
- A **command** is like an event, but `emit` runs its handler and returns the result (a promise if the handler is async).
65
+ ```jsx
66
+ // SearchBox.jsx
67
+ import { useEffect, useState } from "react";
68
+ import { bus } from "./search";
69
+
70
+ export function SearchBox() {
71
+ const [items, setItems] = useState([]);
72
+
73
+ // on returns the unbind function; returning it removes the listener on unmount
74
+ useEffect(() => bus.on("results", setItems), []);
75
+
76
+ return (
77
+ <>
78
+ <input onChange={(event) => bus.emit("search", event.target.value)} />
79
+ <ul>
80
+ {items.map((item) => (
81
+ <li key={item.id}>{item.title}</li>
82
+ ))}
83
+ </ul>
84
+ </>
85
+ );
86
+ }
87
+ ```
88
+
89
+ ## Load data and await the result
90
+
91
+ Give an action a handler, and `emit` returns its result. The caller does not import the request.
67
92
 
68
93
  ```js
69
94
  import { createBus } from "nanoeffects";
70
- import { createCommand } from "nanoeffects/actions";
95
+ import { createAction } from "nanoeffects/actions";
71
96
 
72
97
  const bus = createBus();
73
98
 
74
- // createCommand — emit runs the handler and returns what it returns
75
- const double = createCommand("math/double", (value) => value * 2);
99
+ // createAction with a handler — emit returns what the handler returns
100
+ const getUser = createAction("user/get", async (id, ctx) => {
101
+ const response = await fetch(`/users/${id}`, { signal: ctx.signal });
102
+ return response.json();
103
+ });
76
104
 
77
- const answer = bus.emit(double(21)); // 42
105
+ const user = await bus.emit(getUser("1"));
78
106
  ```
79
107
 
80
- More (contracts, jobs, modules): [Actions](docs/actions.md).
108
+ This is one way to load data. If you already use React Query or SWR, keep them: see [Query libraries](docs/query-libraries.md).
109
+
110
+ ## Take dependencies from the context
111
+
112
+ Put an API client, a store or a logger on every task once. A test passes a fake one. Here the client is an [axios](https://axios-http.com) instance, but any object works.
113
+
114
+ ```js
115
+ import axios from "axios";
116
+ import { createBus } from "nanoeffects";
117
+
118
+ const api = axios.create({ baseURL: "/api" });
119
+
120
+ // context — every task gets these fields: the bus is the container for your dependencies
121
+ const bus = createBus({ context: { api } });
122
+
123
+ bus.on("save", async (draft, ctx) => {
124
+ await ctx.api.put("/drafts", draft, { signal: ctx.signal });
125
+ });
126
+ ```
81
127
 
82
128
  ## Docs
83
129
 
84
- Start with the [Guide](docs/guide.md) (bus → policies → effects → modules). Or jump by need:
130
+ Start with the [Guide](docs/guide.md), or jump by need:
85
131
 
86
132
  | Need | Page |
87
133
  | ---- | ---- |
88
134
  | Async UI races (search, submit) | [Policies](docs/policies.md) |
89
135
  | Retry, timeout, wait for events | [Effects](docs/effects.md) |
90
- | Domain logic off the UI | [Actions, commands, modules](docs/actions.md) |
91
- | Redux / Zustand / MobX | [Stores](docs/stores.md) |
136
+ | Domain logic off the UI | [Actions, handlers, modules](docs/actions.md) |
137
+ | Redux / Zustand / MobX / Pinia next to the bus | [Stores](docs/stores.md) |
138
+ | Coming from redux-saga, RxJS or a small emitter | [redux-saga](docs/migrating-from-saga.md), [RxJS](docs/migrating-from-rxjs.md), [mitt and nanoevents](docs/migrating-from-mitt.md) |
92
139
  | See what ran and why (console, Redux DevTools) | [Tracing](docs/tracing.md) |
93
140
 
94
- [Full index and package entries](docs/README.md) · [Examples](examples/README.md) · [Why nanoeffects exists](docs/motivation.md)
141
+ [Full index and package entries](docs/README.md) · [Examples](examples/README.md)
142
+
143
+ ## Why
144
+
145
+ Requests, timers and races pile up in components, so the logic goes into services. Those need cancel, retry, shared dependencies and a way to see what ran. [redux-saga](https://redux-saga.js.org) does it with generators, [RxJS](https://rxjs.dev) with streams. nanoeffects keeps plain `async` functions and adds one idea, the built-in [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
146
+
147
+ The bus is only the heart: any emitter ([`nanoevents`](https://github.com/ai/nanoevents) is ~100 bytes, [many more](https://github.com/gajus/sister#similar-libraries)) can send events. On top come cancellable tasks, a context for dependencies, actions and modules per domain, and tracing with Redux DevTools.
148
+
149
+ - **Plain `async` functions.** No generators, no streams.
150
+ - **Fits what you have.** Recipes for [Redux Toolkit, Zustand, MobX and Pinia](docs/stores.md), an [RxJS](docs/migrating-from-rxjs.md) bridge, helpers for [React](docs/react.md), [Vue](docs/vue.md) and [Svelte](docs/svelte.md).
151
+ - **Tiny, no dependencies.** `createBus` is about 1.4 kB gzip (1.3 kB brotli). Safe inside an SDK.
95
152
 
96
- - **Tiny.** `createBus` is about 1.4 kB gzip (1.3 kB brotli); other entries load only when imported.
97
- - **Cancellation first.** Aborting a task aborts its children and drops its result.
98
- - **One model** instead of hand-rolled `AbortController`, lodash debounce, and retry glue.
153
+ More: [Why nanoeffects exists](docs/motivation.md).
@@ -36,6 +36,27 @@ type UnsubscribeCallback = () => void;
36
36
  //#endregion
37
37
  //#region src/core/store.d.ts
38
38
  declare const anyEventType = "*";
39
+ type AnyType = "*";
40
+ interface UnsubscribeTask<E extends EventMap> {
41
+ (task: Task<DataOf<E[Keys<E>]>, E>): void;
42
+ <K extends AnyType>(key: K, task?: Task<DataOf<E[Keys<E>]>, E>): void;
43
+ <K extends Keys<E>>(key: K, task?: Task<DataOf<E[K]>, E>): void;
44
+ }
45
+ type Store<E extends EventMap> = {
46
+ get: {
47
+ /** Every listener of every event, in the order of the events. */
48
+ (): Task<DataOf<E[Keys<E>]>, E>[];
49
+ (key: AnyType): Task<DataOf<E[Keys<E>]>, E>[];
50
+ <K extends Keys<E>>(key: K): Task<DataOf<E[K]>, E>[];
51
+ };
52
+ add: {
53
+ (key: AnyType, task: Task<DataOf<E[Keys<E>]>, E>): UnsubscribeCallback;
54
+ <K extends Keys<E>>(key: K, task: Task<DataOf<E[K]>, E>): UnsubscribeCallback;
55
+ };
56
+ remove: UnsubscribeTask<E>;
57
+ /** Drops every key; lists already handed out by `get` stay as they were. */
58
+ clear: () => void;
59
+ };
39
60
  //#endregion
40
61
  //#region src/core/tracer.d.ts
41
62
  /**
@@ -71,7 +92,7 @@ interface Tracer<Ref extends object = object> {
71
92
  /** Subscribes to every event; handlers receive the payload of any of them. */
72
93
  type AnyEvent = typeof anyEventType;
73
94
  type EventKey<E extends EventMap> = Keys<E>;
74
- type EventPayload<E extends EventMap> = E[EventKey<E>];
95
+ type EventPayload<E extends EventMap> = DataOf<E[EventKey<E>]>;
75
96
  type WithType<K extends string> = {
76
97
  type: K;
77
98
  };
@@ -91,7 +112,7 @@ type WithSignal = {
91
112
  type HandlerRef<E extends EventMap, Response = unknown> = (action: Action<E>) => Response;
92
113
  type Handler<E extends EventMap, Response = unknown, K extends EventKey<E> = EventKey<E>> = { [P in K]: {
93
114
  type: P;
94
- (payload: E[P]): Response;
115
+ (payload: DataOf<E[P]>): Response;
95
116
  }; }[K];
96
117
  type Action<E extends EventMap> = {
97
118
  type: EventKey<E>;
@@ -125,35 +146,45 @@ type Command<P = never, R = unknown> = {
125
146
  readonly wrap?: (run: (payload: never, ctx: never) => unknown) => (payload: never, ctx: never) => unknown;
126
147
  };
127
148
  /**
128
- * What the bus needs to run commands. The implementation, `commandsPlugin` in
129
- * `nanoeffects/actions`, holds the handlers bound to commands and runs the
130
- * right one, so a bus without `commands` carries none of that.
149
+ * What the bus needs to run the handlers of actions. The implementation,
150
+ * `actionsPlugin` in `nanoeffects/actions`, holds the handlers bound to
151
+ * actions and runs the right one, so a bus without `actions` carries none of
152
+ * that. `bound` tells, for an event type, whether a handler is bound to it: an
153
+ * action with no handler of its own is a command only then. A runner that has
154
+ * no `bound` runs commands only.
131
155
  */
132
- interface Commands {
156
+ interface Actions {
133
157
  run(command: Command, payload: unknown, ctx: unknown): unknown;
158
+ bound?(type: string): Command | undefined;
134
159
  }
135
160
  /** Anything `emit` takes as its first argument: an event type, an action or a command. */
136
161
  type Emittable<E extends EventMap> = EventKey<E> | Command | CommandCall | EventLike<E>;
137
162
  type Emit<E extends EventMap> = {
138
- <K extends EventKey<E>>(event: K, ...payload: PayloadOf<E[K]>): void;
163
+ /**
164
+ * On a bus without an event map the caller can say what a handler bound for
165
+ * the type returns: `bus.emit<string>("user/getId")`. With a map the parameter
166
+ * is `never`, so this form is not taken and no check of names or payloads is lost.
167
+ */
168
+ <R = void>(type: string extends EventKey<E> ? string : never, ...payload: unknown[]): R;
169
+ <K extends EventKey<E>>(event: K, ...payload: PayloadOf<DataOf<E[K]>>): ResultOf<E[K]>;
139
170
  /** Before Handler: a command is callable too, and must return its result type. */
140
171
  <P, R>(command: Command<P, R>, ...payload: PayloadOf<P>): R;
141
172
  /** A scope as the second argument replaces context fields for the tasks of this emit; see `scopePlugin`. */
142
173
  <P, R, F>(call: CommandCall<P, R> & WithScope<F>, scope?: EmitScope<E, F>): R;
143
174
  <P, R>(call: CommandCall<P, R>, scope?: EmitScope<E>): R;
144
- <K extends EventKey<E>, R>(event: Handler<E, R, K>, ...payload: PayloadOf<E[K]>): R;
175
+ <K extends EventKey<E>, R>(event: Handler<E, R, K>, ...payload: PayloadOf<DataOf<E[K]>>): R;
145
176
  <F>(event: EventLike<E> & WithScope<F>, scope?: EmitScope<E, F>): void;
146
177
  (event: EventLike<E>, scope?: EmitScope<E>): void;
147
178
  };
148
179
  type On<E extends EventMap> = {
149
180
  (event: AnyEvent, fn: Task<EventPayload<E>, E>, options?: ListenerOptions): UnsubscribeCallback;
150
- <K extends EventKey<E>>(event: K, fn: Task<E[K], E>, options?: ListenerOptions): UnsubscribeCallback;
181
+ <K extends EventKey<E>>(event: K, fn: Task<DataOf<E[K]>, E>, options?: ListenerOptions): UnsubscribeCallback;
151
182
  /** An action creator without a payload type: the payload comes from the event map. */
152
183
  /** The creators and commands that carry a scope come first: the handler sees their fields. */
153
- <K extends EventKey<E>, F>(creator: FreeActionCreator<K, F> & WithScope<F>, fn: Task<E[K], WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
184
+ <K extends EventKey<E>, F>(creator: FreeActionCreator<K, F> & WithScope<F>, fn: Task<DataOf<E[K]>, WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
154
185
  <P, F>(creator: ActionCreator<P, string, PayloadOf<P>, F> & WithScope<F>, fn: Task<P, WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
155
186
  <P, F>(command: Command<P, unknown> & WithScope<F>, fn: Task<P, WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
156
- <K extends EventKey<E>>(creator: FreeActionCreator<K>, fn: Task<E[K], E>, options?: ListenerOptions): UnsubscribeCallback;
187
+ <K extends EventKey<E>>(creator: FreeActionCreator<K>, fn: Task<DataOf<E[K]>, E>, options?: ListenerOptions): UnsubscribeCallback;
157
188
  /** An action creator: the payload comes from it, so an event map is not needed. */
158
189
  <P>(creator: ActionCreator<P>, fn: Task<P, E>, options?: ListenerOptions): UnsubscribeCallback;
159
190
  /** A command: the payload comes from it too. */
@@ -162,7 +193,7 @@ type On<E extends EventMap> = {
162
193
  type: K;
163
194
  }>(event: Event & {
164
195
  type: K;
165
- }, fn: Task<E[K], E>, options?: ListenerOptions): UnsubscribeCallback;
196
+ }, fn: Task<DataOf<E[K]>, E>, options?: ListenerOptions): UnsubscribeCallback;
166
197
  };
167
198
  type Off<E extends EventMap> = {
168
199
  /** Removes every listener of every event (including `"*"`). */
@@ -170,12 +201,12 @@ type Off<E extends EventMap> = {
170
201
  (event: AnyEvent): void;
171
202
  (event: AnyEvent, fn: Task<EventPayload<E>, E>): void;
172
203
  <K extends EventKey<E>>(event: K): void;
173
- <K extends EventKey<E>>(event: K, fn: Task<E[K], E>): void;
204
+ <K extends EventKey<E>>(event: K, fn: Task<DataOf<E[K]>, E>): void;
174
205
  <K extends EventKey<E>, Event extends {
175
206
  type: K;
176
207
  }>(event: Event & {
177
208
  type: K;
178
- }, fn: Task<E[K], E>): void;
209
+ }, fn: Task<DataOf<E[K]>, E>): void;
179
210
  <P>(command: Command<P, unknown>, fn?: Task<P, E>): void;
180
211
  };
181
212
  declare const contextKey: unique symbol;
@@ -239,6 +270,24 @@ interface Bus<E extends EventMap> {
239
270
  */
240
271
  once: On<E>;
241
272
  off: Off<E>;
273
+ /**
274
+ * The listeners an event has right now, in the order they run: what `on`,
275
+ * `once`, `ctx.on` and the `on` of a module registered, as the bus holds them
276
+ * (a plugin may have wrapped a function, so compare the length, not the
277
+ * functions). It takes the event name as a string, as the bus stores it, not
278
+ * an action creator: use `creator.type`. `"*"` gives the any-event listeners,
279
+ * which a named event does not include. The array is not changed afterwards: a later `on` or unbind
280
+ * gives the next `get` a new one.
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * const unbind = bus.on("saved", save);
285
+ * bus.get("saved").length; // 1
286
+ * unbind();
287
+ * bus.get("saved").length; // 0
288
+ * ```
289
+ */
290
+ get: Store<E>["get"];
242
291
  emit: Emit<E>;
243
292
  /**
244
293
  * Calls `plugin` with the bus and returns what it returns: an api, a
@@ -246,10 +295,10 @@ interface Bus<E extends EventMap> {
246
295
  * no list of them. See `modulePlugin` in `nanoeffects/actions`.
247
296
  */
248
297
  use<R>(plugin: (bus: Bus<E>) => R): R;
249
- /** `BusOptions.context` and `.trace`, and `commands`, for what makes tasks of its own, like `modulePlugin`. */
298
+ /** `BusOptions.context` and `.trace`, and `actions`, for what makes tasks of its own, like `modulePlugin`. */
250
299
  readonly ctx?: ContextOption<E> | undefined;
251
- /** What runs the commands; `commandsPlugin` and `modulePlugin` set it. Without one a command runs its own handler. */
252
- commands?: Commands | undefined;
300
+ /** What runs the handlers of actions; `actionsPlugin` and `modulePlugin` set it. Without one a command runs its own handler. */
301
+ actions?: Actions | undefined;
253
302
  readonly trace?: Tracer | undefined;
254
303
  /** Called with every new task context to add fields to it, for what needs the task, like `effectsPlugin`. Chain to the previous one. */
255
304
  addContext?: ((ctx: object) => void) | undefined;
@@ -267,8 +316,8 @@ type BusOptions<E extends EventMap> = {
267
316
  onError?: (error: unknown, event: Action<E>) => void;
268
317
  /**
269
318
  * Called when an event is emitted and nobody listens to it: the usual sign
270
- * of a typo in an event name. Not called for commands, which always have a
271
- * handler to run. Typically a warning in development only.
319
+ * of a typo in an event name. Not called for commands, nor for an action with
320
+ * a handler bound in `actions`, which always have a handler to run. Typically a warning in development only.
272
321
  *
273
322
  * @example
274
323
  * ```ts
@@ -360,15 +409,33 @@ type Merge<A, B> = Omit<A, keyof B> & B;
360
409
  type WithFields<E extends EventMap, F> = [unknown] extends [F] ? E : Omit<E, keyof WithContext<object>> & WithContext<Merge<ContextOf<E>, F>>;
361
410
  //#endregion
362
411
  //#region src/core/action-creator.d.ts
363
- type WithPayload<E extends EventMap, K extends Keys<E>> = undefined extends E[K] ? {
412
+ type WithPayload<E extends EventMap, K extends Keys<E>> = undefined extends DataOf<E[K]> ? {
364
413
  type: K;
365
- payload?: E[K];
414
+ payload?: DataOf<E[K]>;
366
415
  } : {
367
416
  type: K;
368
- payload: E[K];
417
+ payload: DataOf<E[K]>;
369
418
  };
370
419
  type Absent<P> = [P] extends [void] ? undefined : P;
371
420
  type Made<P, T extends string> = WithPayload<{ [K in T]: Absent<P>; }, T>;
421
+ declare const callBrand: unique symbol;
422
+ /**
423
+ * A payload type together with the type of what a handler of the action
424
+ * returns, for an event map or `createAction`:
425
+ * `type Events = { "user/getId": Call<void, string> }`, and then
426
+ * `bus.emit("user/getId")` is a `string`. A type only: it adds nothing to a
427
+ * bundle, and an entry that is not a `Call` means a payload, with no result.
428
+ */
429
+ type Call<P = void, R = void> = {
430
+ readonly [callBrand]: {
431
+ payload: P;
432
+ result: R;
433
+ };
434
+ };
435
+ /** The payload type of an entry of an event map: the `P` of a `Call<P, R>`, the entry itself otherwise. */
436
+ type DataOf<T> = T extends Call<infer P, unknown> ? P : T;
437
+ /** What `emit` returns for an entry of an event map: the `R` of a `Call<P, R>`, `void` otherwise. */
438
+ type ResultOf<T> = T extends Call<unknown, infer R> ? R : void;
372
439
  declare const free: unique symbol;
373
440
  type Free = typeof free;
374
441
  /**
@@ -411,4 +478,4 @@ interface ActionCreator<P = void, T extends string = string, A extends unknown[]
411
478
  toString(): T;
412
479
  }
413
480
  //#endregion
414
- export { HandlerRef as A, Keys as B, Emit as C, EventPayload as D, EventLike as E, WithType as F, Trace as G, PayloadOf as H, createBus as I, UnsubscribeCallback as J, TraceEvent as K, Tracer as L, On as M, WithContext as N, EventsOf as O, WithSignal as P, EffectName as R, ContextOption as S, EventKey as T, Phase as U, ListenerOptions as V, Task as W, Command as _, EmitScope as a, Context as b, WithScope as c, Effects as d, Signaled as f, BusOptions as g, Bus as h, WithPayload as i, Off as j, Handler as k, BoundForm as l, AnyEvent as m, Free as n, Scope as o, Action as p, TraceLogger as q, FreeActionCreator as r, WithFields as s, ActionCreator as t, Effect as u, CommandCall as v, Emittable as w, ContextOf as x, Commands as y, EventMap as z };
481
+ export { EventsOf as A, Tracer as B, ContextOf as C, EventKey as D, Emittable as E, WithContext as F, PayloadOf as G, EventMap as H, WithSignal as I, Trace as J, Phase as K, WithType as L, HandlerRef as M, Off as N, EventLike as O, On as P, commandCall as R, Context as S, Emit as T, Keys as U, EffectName as V, ListenerOptions as W, TraceLogger as X, TraceEvent as Y, UnsubscribeCallback as Z, AnyEvent as _, FreeActionCreator as a, Command as b, Scope as c, BoundForm as d, Effect as f, Actions as g, Action as h, Free as i, Handler as j, EventPayload as k, WithFields as l, Signaled as m, Call as n, WithPayload as o, Effects as p, Task as q, DataOf as r, EmitScope as s, ActionCreator as t, WithScope as u, Bus as v, ContextOption as w, CommandCall as x, BusOptions as y, createBus as z };
package/dist/actions.d.ts CHANGED
@@ -1,24 +1,37 @@
1
- import { H as PayloadOf, T as EventKey, W as Task, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { r as Scoped, t as FieldsOfScope } from "./types-Bkuhnkq6.js";
3
- import { a as createModule, c as CarryingCalls, d as commandsPlugin, f as createCommand, l as MadeCommand, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as bindHandler } from "./module-B6rg5NIb.js";
1
+ import { D as EventKey, G as PayloadOf, H as EventMap, a as FreeActionCreator, c as Scope, i as Free, n as Call, q as Task, t as ActionCreator, u as WithScope, v as Bus } from "./action-creator-CcAEvUnw.js";
2
+ import { r as Scoped, t as FieldsOfScope } from "./types-FV8fuvI9.js";
3
+ import { a as createModule, c as CarryingCalls, d as actionsPlugin, f as setHandler, l as MadeCommand, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as ScopedCommand } from "./module-C_oEXw1n.js";
4
4
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
5
5
  //#region src/actions/action.d.ts
6
+ type Reserved = "handle" | "prepare";
7
+ type Made<P, T extends string> = [P] extends [Free] ? FreeActionCreator<T> : [P] extends [Call<infer Q, infer R>] ? MadeCommand<Q, R, T> : ActionCreator<P, T>;
6
8
  type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCreator<T, F> & WithScope<F> : ActionCreator<P, T, PayloadOf<P>, F> & WithScope<F>;
9
+ type ScopeOf<S> = Omit<S, Reserved>;
10
+ type PreparedCommand<P, R, T extends string, A extends unknown[], F> = ((...args: A) => ReturnType<MadeCommand<P, R, T>> & WithScope<F>) & Omit<MadeCommand<P, R, T>, never> & WithScope<F>;
7
11
  /**
8
- * Creates an action creator, as in Redux Toolkit.
12
+ * Creates an action creator, as in Redux Toolkit. With a handler it makes a
13
+ * command: `bus.emit(getUser("1"))` runs the handler and returns its result.
9
14
  *
10
15
  * - `createAction("user/loaded")` needs no generics: the payload type comes
11
16
  * from the event map of the bus that the action goes to, and the literal
12
- * type of the action is kept.
17
+ * type of the action is kept. An action with no handler is an event.
13
18
  * - `createAction<User>("user/loaded")` gives the payload type, for a bus
14
19
  * without an event map or to check the calls of the creator. The type of
15
20
  * the action is then `string`; to keep the literal, give it as the second
16
21
  * generic: `createAction<User, "user/loaded">("user/loaded")`.
17
- * - With `prepare` the arguments of the creator can differ from the payload,
18
- * and both types are inferred.
19
- * - A scope as the last argument, `{ cache, $effects: { race } }`, gives the
20
- * listeners of its calls those context fields; it works on a bus that uses `scopePlugin`
21
- * from `nanoeffects/scope` and is ignored on any other.
22
+ * - `createAction<Call<Mail, void>>("mail/send")` also says what a handler
23
+ * bound later returns; see `Call`.
24
+ * - A function as the second argument is the handler: it gets the payload and
25
+ * the context of the task, and `emit` returns what it returns. It can be
26
+ * wrapped in a policy, e.g. `latest(fn)`. A handler bound for the type in
27
+ * `actionsPlugin` wins over it.
28
+ * - The options object, as the second argument or the third after a handler,
29
+ * is `{ handle, prepare, ...scope }`: `handle` is the handler, `prepare`
30
+ * builds the payload from several arguments (both types are inferred), and
31
+ * every other field is the scope. A scope as the last argument,
32
+ * `{ cache, $effects: { race } }`, puts those fields on the context of the
33
+ * handler and of the listeners of its calls; it works on a bus that uses
34
+ * `scopePlugin` from `nanoeffects/scope` and is ignored on any other.
22
35
  *
23
36
  * @example
24
37
  * ```ts
@@ -27,22 +40,42 @@ type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCrea
27
40
  * added.type; // "counter/added"
28
41
  * added.match(action); // true for actions of this type
29
42
  *
30
- * const renamed = createAction("user/renamed", (id: string, name: string) => ({
31
- * payload: { id, name },
32
- * }));
43
+ * const renamed = createAction("user/renamed", {
44
+ * prepare: (id: string, name: string) => ({ payload: { id, name } }),
45
+ * });
46
+ *
47
+ * const getUser = createAction("user/get", (id: string, { signal }) =>
48
+ * fetchUser(id, { signal }),
49
+ * );
50
+ * const user = await bus.emit(getUser("1"));
33
51
  *
34
52
  * bus.emit(added(2));
35
53
  * bus.on(added, (n) => console.log(n));
36
54
  * ```
37
55
  */
38
- export declare function createAction<P = Free, T extends string = string>(type: T): [P] extends [Free] ? FreeActionCreator<T> : ActionCreator<P, T>;
39
- export declare function createAction<A extends unknown[], P, T extends string = string>(type: T, prepare: (...args: A) => {
40
- payload: P;
41
- }): ActionCreator<P, T, A>;
56
+ export declare function createAction<P = Free, T extends string = string>(type: T): Made<P, T>;
57
+ export declare function createAction<P = void, R = void, T extends string = string, E extends EventMap = EventMap>(type: T, handler: Task<P, E, R>): MadeCommand<P, R, T>;
58
+ export declare function createAction<P, R, A extends unknown[], T extends string, S extends Scope = Record<never, never>>(type: T, handler: Task<P, Scoped<EventMap, ScopeOf<S>>, R>, options: {
59
+ prepare: (...args: A) => {
60
+ payload: P;
61
+ };
62
+ } & S): PreparedCommand<P, R, T, A, FieldsOfScope<EventMap, ScopeOf<S>>>;
63
+ export declare function createAction<P, R, A extends unknown[], T extends string, S extends Scope = Record<never, never>>(type: T, options: {
64
+ handle: Task<P, Scoped<EventMap, ScopeOf<S>>, R>;
65
+ prepare: (...args: A) => {
66
+ payload: P;
67
+ };
68
+ } & S): PreparedCommand<P, R, T, A, FieldsOfScope<EventMap, ScopeOf<S>>>;
69
+ export declare function createAction<P, R, T extends string, S extends Scope = Record<never, never>>(type: T, handler: Task<P, Scoped<EventMap, S>, R>, scope: S): ScopedCommand<P, R, T, FieldsOfScope<EventMap, S>>;
70
+ export declare function createAction<P, R, T extends string, S extends Scope = Record<never, never>>(type: T, options: {
71
+ handle: Task<P, Scoped<EventMap, ScopeOf<S>>, R>;
72
+ } & S): ScopedCommand<P, R, T, FieldsOfScope<EventMap, ScopeOf<S>>>;
73
+ export declare function createAction<A extends unknown[], P, T extends string, S extends Scope = Record<never, never>>(type: T, options: {
74
+ prepare: (...args: A) => {
75
+ payload: P;
76
+ };
77
+ } & S): ActionCreator<P, T, A, FieldsOfScope<EventMap, ScopeOf<S>>> & WithScope<FieldsOfScope<EventMap, ScopeOf<S>>>;
42
78
  export declare function createAction<P = Free, T extends string = string, S extends Scope = Record<never, never>>(type: T, scope: S): ScopedCreator<P, T, FieldsOfScope<EventMap, S>>;
43
- export declare function createAction<A extends unknown[], P, T extends string, S extends Scope>(type: T, prepare: (...args: A) => {
44
- payload: P;
45
- }, scope: S): ActionCreator<P, T, A, FieldsOfScope<EventMap, S>> & WithScope<FieldsOfScope<EventMap, S>>;
46
79
  //#endregion
47
80
  //#region src/actions/emitter.d.ts
48
81
  /** What `emitterPlugin` adds to the bus. */
@@ -161,4 +194,4 @@ export declare const createJob: <P = void, R = void, T extends string = string,
161
194
  /** Context values and `$effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
162
195
  scope?: X) => CarryingCalls<Job<P, R, T, S, F, PE, CE>, P, Promise<Awaited<R> | undefined>, FieldsOfScope<E, X>>;
163
196
  //#endregion
164
- export { type ActionCreator, type Binding, type EmitterApi, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, commandsPlugin, createCommand, createModule, modulePlugin };
197
+ export { type ActionCreator, type Binding, type EmitterApi, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, actionsPlugin, createModule, modulePlugin, setHandler };
package/dist/actions.js CHANGED
@@ -1 +1 @@
1
- import{d as e,i as t,l as n,n as r,u as i}from"./context-UcmrAIhP.js";import{t as a}from"./event-0RIBGYqS.js";import{r as o}from"./bus-hHp-T7JV.js";import{n as s,t as c}from"./lifecycle-key-8iX8RUVD.js";import{t as l}from"./origin-key-BbWN3gmN.js";import{t as u}from"./scope-key-B7l7mUS5.js";import{t as d}from"./mount-each-D54UqAhZ.js";const f=(e,t)=>(t&&Object.defineProperty(e,u,{value:t}),e),p=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),m=e=>Reflect.get(e,u);function h(e,t,n){let r=typeof t==`function`?t:void 0,i=typeof t==`function`?n:t;return Object.assign(f((...t)=>f({type:e,payload:r?r(...t).payload:t[0]},i),i),{type:e,match:t=>t.type===e,toString:()=>e})}var g=class extends Error{};function _(e,t,n){let r=t=>t.type===e;function i(r,i){if(typeof i==`object`){if(!t)throw new g(`No handler for command "${e}"`);return t(r,i)}return f(o(s,e,r),n)}let a=Object.assign(i,{type:e,match:r}),s=f(a,n);return s}const v=(e,t)=>[e,t],y=e=>typeof Reflect.get(e??{},`handle`)==`function`,b=(e=[])=>t=>{let n=t.commands;if(y(n))return n.handle(e),n;let r=/* @__PURE__ */ new Map,i=(e,t)=>{let n={fn:t};return r.set(e,[...r.get(e)??[],n]),()=>{let t=(r.get(e)??[]).filter(e=>e!==n);t.length>0?r.set(e,t):r.delete(e)}};function o(e,t){if(t)return i(a(e),t);let n=e.map(([e,t])=>i(a(e),t));return()=>{for(let e of n)e()}}o(e);let s={run:(e,t,n)=>{let i=(r.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,n])},handle:o};return t.commands=s,s},x=()=>e=>{function t(t,n){return(...r)=>{let i=t(...r),a=Reflect.get(e,l);a?a(i,n):e.emit(i)}}return Object.assign(e,{emitter:t})},S=e=>typeof e==`string`?h(e):e,C=(e,t,n,r)=>{let i=m(t);e.emit(Reflect.apply(t,null,[n]),r&&i?p(r,i):r)},w=(t,r,i,a)=>{let o=t;o[n]=!0;try{C(t,r,i,a)}catch(t){e(`cancel listener`,t)}o[n]=!1},T=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof g)throw e;return Promise.reject(e)}},E=(t,n,r={},a)=>{let l=_(t,n),u=e=>S(r[e]??`${t}/${e}`),d=u(`pending`),p=u(`success`),m=u(`fail`),h=u(`cancel`),g=(t,n,r,o)=>{let s=r,l=`idle`,u=()=>{},f=()=>{l=`closed`,u()},g=e=>{l===`started`&&(f(),w(s,h,e,a))},_=()=>{l===`idle`&&(l=`started`,u=i(s,g),C(s,d,n,a))},v=e=>{l===`started`&&(f(),s.signal.aborted?w(s,h,s.signal.reason,a):`error`in e?C(s,m,e.error,a):C(s,p,e.value,a))};return Reflect.set(s,c,{start:_,cancel:g,done:v,drop:()=>{l===`idle`&&(l=`closed`)}}),T(()=>(o||_(),Reflect.apply(t,null,[n,r]))).then(e=>(v({value:e}),e),t=>{if(l===`idle`)try{_()}catch(t){e(`pending listener`,t)}v({error:t})})},v=e=>{let t=Reflect.get(e,s)===!0;return(n,r)=>g(e,n,r,t)},y=Reflect.get(n??{},s)===!0;function b(e,r){return typeof r==`object`?n?g(l,e,r,y):Reflect.apply(l,null,[e,r]):f(o(E,t,e),a)}let x=Object.assign(b,{type:t,match:l.match,wrap:v,pending:d,success:p,fail:m,cancel:h}),E=f(x,a);return E},D=e=>typeof e==`object`&&!!e,O=e=>{if(typeof e==`string`)return e;if(D(e)||typeof e==`function`){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},k=(e,t)=>{let[n,r]=e;return n===void 0||Object.is(O(n),O(t[0]))&&(r===void 0||Object.is(r,t[1]))},A=e=>{let t={on:e.on,once:e.once,off:e.off},n=[],r=!0,i=(t,n)=>Reflect.apply(t,e,n),a=(e,t)=>(...a)=>{if(!r)return i(t,a);let o={kind:e,args:a,dropped:!1};return n.push(o),()=>{o.live?o.live():o.dropped=!0}},o=e=>(...t)=>{if(!r)return i(e,t);for(let e of n)e.kind!==`off`&&k(t,e.args)&&(e.dropped=!0);n.push({kind:`off`,args:t,dropped:!1})},s=a(`on`,t.on),c=a(`once`,t.once),l=o(t.off);return e.on=s,e.once=c,e.off=l,{release:()=>{r=!1,e.on===s&&(e.on=t.on),e.once===c&&(e.once=t.once),e.off===l&&(e.off=t.off)},replay:()=>{for(let e of n.splice(0))if(!e.dropped){if(e.kind===`off`)i(t.off,e.args);else{let n=e.kind===`on`?t.on:t.once;e.live=i(n,e.args)}}}}};function j(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const M=(e,t)=>n=>t?e.fitScope?.(t,n)??n:n,N=(e,t,n)=>{n&&e.applyScope?.(t,n)},P=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],F=(e,t)=>{let n=e.on;return(...e)=>{let[r,i,...a]=e;return Reflect.apply(n,null,[r,t(i),...a])}},I=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},L=(n=[])=>a=>{let o=a.commands??b()(a),s={handle:(...e)=>{if(!o.handle)throw Error("modulePlugin: the `commands` of the bus is not a registry from `commandsPlugin()`");return Reflect.apply(o.handle,null,e)}},c=e=>{let n=/* @__PURE__ */ new Map,r=/* @__PURE__ */ new Map;return{mount:o=>{let l=n.get(o);if(l)return l;let u=r.get(o.name);u&&n.get(u)?.();let d=t(e,o.name),f=d.ctx,p=c(f),m=()=>{n.delete(o),r.get(o.name)===o&&r.delete(o.name),f.abort(),d.unlink()};n.set(o,m),r.set(o.name,o);let h=o.scope,g=M(a,h),_=(e,t)=>{let n=Reflect.apply(s.handle,null,P(g,e,t));return i(f,n),n},v=(...e)=>{let t=e.map(e=>typeof e==`function`?_(e,e):p.mount(e));return()=>{for(let e of t)e()}},y=I(f),b=F(f,g);try{N(a,f,h),o.setup(Object.assign(f,{handle:_,use:v,spawn:y,on:b}))}catch(e){throw m(),e}return m},unmount:e=>n.get(e)?.(),isMounted:e=>n.has(e)}},l=A(a),u=a.emit,f,p=(e=!0)=>{if(f)return f;l.release(),a.emit===m&&(a.emit=u);let t=r({event:{type:`app`,payload:void 0},bus:a,tracer:a.trace});f={root:t,apps:d(c(t.ctx))};try{e&&f.apps.mount(...n)}finally{l.replay()}return f},m=(...e)=>(p(),Reflect.apply(u,a,e));a.emit=m,n.length>0&&queueMicrotask(()=>{try{p()}catch(t){e(`the modules of the app could not start`,t)}});let h={get commands(){return p(),o},mount:(...e)=>{let{root:t,apps:n}=p();return t.ctx.signal.throwIfAborted(),n.mount(...e)},unmount:(...e)=>p().apps.unmount(...e),isMounted:e=>p().apps.isMounted(e),dispose:()=>p(!1).root.ctx.abort(),createModule:j};return a.app=h,h};export{v as bindHandler,b as commandsPlugin,h as createAction,_ as createCommand,E as createJob,j as createModule,x as emitterPlugin,L as modulePlugin};
1
+ import{d as e,i as t,l as n,n as r,u as i}from"./context-UcmrAIhP.js";import{t as a}from"./event-DVcW0mME.js";import{r as o}from"./bus-DlpEluzd.js";import{n as s,t as c}from"./lifecycle-key-8iX8RUVD.js";import{t as l}from"./origin-key-BbWN3gmN.js";import{t as u}from"./scope-key-B7l7mUS5.js";import{t as d}from"./mount-each-D54UqAhZ.js";const f=(e,t)=>(t&&Object.defineProperty(e,u,{value:t}),e),p=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),m=e=>Reflect.get(e,u),h=e=>{let t=Object.assign(n=>o(t,e,n),{type:e});return t},g=e=>typeof e==`object`&&!!e&&Reflect.get(e,`signal`)instanceof AbortSignal&&typeof Reflect.get(e,`emit`)==`function`;var _=class extends Error{};function v(e,t,n){return y(e,t,n)}function y(e,t,n,r){let i=t=>t.type===e,a=e=>r?e.length===2&&g(e[1]):typeof e[1]==`object`;function s(...i){let[s,c]=i;if(a(i)){if(!t)throw new _(`No handler for command "${e}"`);return t(s,c)}let u=r&&Reflect.apply(r,null,i),d=u?u.payload:s;return f(o(l,e,d),n)}let c=Object.assign(s,{type:e,match:i,toString:()=>e}),l=f(c,n);return l}function b(e,t){return[e,t]}const x=e=>typeof Reflect.get(e??{},`handle`)==`function`,S=(e=[])=>t=>{let n=t.actions;if(x(n))return n.handle(e),n;let r=/* @__PURE__ */ new Map,i=(e,t)=>{let n={fn:t};return r.set(e,[...r.get(e)??[],n]),()=>{let t=(r.get(e)??[]).filter(e=>e!==n);t.length>0?r.set(e,t):r.delete(e)}};function o(e,t){if(t)return i(a(e),t);let n=e.map(([e,t])=>i(a(e),t));return()=>{for(let e of n)e()}}o(e);let s={run:(e,t,n)=>{let i=(r.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,n])},handle:o,bound:e=>r.has(e)?h(e):void 0};return t.actions=s,s};function C(e,t,n){let{handle:r,prepare:i,scope:a}=w(typeof t==`function`?n:t),o=typeof t==`function`?t:r;return o?y(e,o,a,i):Object.assign(f((...t)=>{let n=i&&Reflect.apply(i,null,t);return f({type:e,payload:n?n.payload:t[0]},a)},a),{type:e,match:t=>t.type===e,toString:()=>e})}const w=e=>{if(!e)return{};let{handle:t,prepare:n,...r}=e,i=typeof t==`function`,a=typeof n==`function`;if(!i&&!a)return{scope:e};let o=Object.keys(r).length>0?r:void 0;return{...i&&{handle:t},...a&&{prepare:n},...o&&{scope:o}}},T=()=>e=>{function t(t,n){return(...r)=>{let i=t(...r),a=Reflect.get(e,l);a?a(i,n):e.emit(i)}}return Object.assign(e,{emitter:t})},E=e=>typeof e==`string`?C(e):e,D=(e,t,n,r)=>{let i=m(t);e.emit(Reflect.apply(t,null,[n]),r&&i?p(r,i):r)},O=(t,r,i,a)=>{let o=t;o[n]=!0;try{D(t,r,i,a)}catch(t){e(`cancel listener`,t)}o[n]=!1},k=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof _)throw e;return Promise.reject(e)}},A=(t,n,r={},a)=>{let l=v(t,n),u=e=>E(r[e]??`${t}/${e}`),d=u(`pending`),p=u(`success`),m=u(`fail`),h=u(`cancel`),g=(t,n,r,o)=>{let s=r,l=`idle`,u=()=>{},f=()=>{l=`closed`,u()},g=e=>{l===`started`&&(f(),O(s,h,e,a))},_=()=>{l===`idle`&&(l=`started`,u=i(s,g),D(s,d,n,a))},v=e=>{l===`started`&&(f(),s.signal.aborted?O(s,h,s.signal.reason,a):`error`in e?D(s,m,e.error,a):D(s,p,e.value,a))};return Reflect.set(s,c,{start:_,cancel:g,done:v,drop:()=>{l===`idle`&&(l=`closed`)}}),k(()=>(o||_(),Reflect.apply(t,null,[n,r]))).then(e=>(v({value:e}),e),t=>{if(l===`idle`)try{_()}catch(t){e(`pending listener`,t)}v({error:t})})},_=e=>{let t=Reflect.get(e,s)===!0;return(n,r)=>g(e,n,r,t)},y=Reflect.get(n??{},s)===!0;function b(e,r){return typeof r==`object`?n?g(l,e,r,y):Reflect.apply(l,null,[e,r]):f(o(S,t,e),a)}let x=Object.assign(b,{type:t,match:l.match,wrap:_,pending:d,success:p,fail:m,cancel:h}),S=f(x,a);return S},j=e=>typeof e==`object`&&!!e,M=e=>{if(typeof e==`string`)return e;if(j(e)||typeof e==`function`){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},N=(e,t)=>{let[n,r]=e;return n===void 0||Object.is(M(n),M(t[0]))&&(r===void 0||Object.is(r,t[1]))},P=e=>{let t={on:e.on,once:e.once,off:e.off},n=[],r=!0,i=(t,n)=>Reflect.apply(t,e,n),a=(e,t)=>(...a)=>{if(!r)return i(t,a);let o={kind:e,args:a,dropped:!1};return n.push(o),()=>{o.live?o.live():o.dropped=!0}},o=e=>(...t)=>{if(!r)return i(e,t);for(let e of n)e.kind!==`off`&&N(t,e.args)&&(e.dropped=!0);n.push({kind:`off`,args:t,dropped:!1})},s=a(`on`,t.on),c=a(`once`,t.once),l=o(t.off);return e.on=s,e.once=c,e.off=l,{release:()=>{r=!1,e.on===s&&(e.on=t.on),e.once===c&&(e.once=t.once),e.off===l&&(e.off=t.off)},replay:()=>{for(let e of n.splice(0))if(!e.dropped){if(e.kind===`off`)i(t.off,e.args);else{let n=e.kind===`on`?t.on:t.once;e.live=i(n,e.args)}}}}};function F(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const I=(e,t)=>n=>t?e.fitScope?.(t,n)??n:n,L=(e,t,n)=>{n&&e.applyScope?.(t,n)},R=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],z=(e,t)=>{let n=e.on;return(...e)=>{let[r,i,...a]=e;return Reflect.apply(n,null,[r,t(i),...a])}},B=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},V=(n=[])=>a=>{let o=a.actions??S()(a),s={handle:(...e)=>{if(!o.handle)throw Error("modulePlugin: the `actions` of the bus is not a registry from `actionsPlugin()`");return Reflect.apply(o.handle,null,e)}},c=e=>{let n=/* @__PURE__ */ new Map,r=/* @__PURE__ */ new Map;return{mount:o=>{let l=n.get(o);if(l)return l;let u=r.get(o.name);u&&n.get(u)?.();let d=t(e,o.name),f=d.ctx,p=c(f),m=()=>{n.delete(o),r.get(o.name)===o&&r.delete(o.name),f.abort(),d.unlink()};n.set(o,m),r.set(o.name,o);let h=o.scope,g=I(a,h),_=(e,t)=>{let n=Reflect.apply(s.handle,null,R(g,e,t));return i(f,n),n},v=(...e)=>{let t=e.map(e=>typeof e==`function`?_(e,e):p.mount(e));return()=>{for(let e of t)e()}},y=B(f),b=z(f,g);try{L(a,f,h),o.setup(Object.assign(f,{handle:_,use:v,spawn:y,on:b}))}catch(e){throw m(),e}return m},unmount:e=>n.get(e)?.(),isMounted:e=>n.has(e)}},l=P(a),u=a.emit,f=a.get,p,m=(e=!0)=>{if(p)return p;l.release(),a.emit===h&&(a.emit=u);let t=r({event:{type:`app`,payload:void 0},bus:a,tracer:a.trace});p={root:t,apps:d(c(t.ctx))};try{e&&p.apps.mount(...n)}finally{l.replay()}return p},h=(...e)=>(m(),Reflect.apply(u,a,e));a.emit=h,a.get=((...e)=>(m(),Reflect.apply(f,a,e))),n.length>0&&queueMicrotask(()=>{try{m()}catch(t){e(`the modules of the app could not start`,t)}});let g={get actions(){return m(),o},mount:(...e)=>{let{root:t,apps:n}=m();return t.ctx.signal.throwIfAborted(),n.mount(...e)},unmount:(...e)=>m().apps.unmount(...e),isMounted:e=>m().apps.isMounted(e),dispose:()=>m(!1).root.ctx.abort(),createModule:F};return a.app=g,g};export{S as actionsPlugin,C as createAction,A as createJob,F as createModule,T as emitterPlugin,V as modulePlugin,b as setHandler};
@@ -0,0 +1 @@
1
+ import{d as e,n as t,o as n,r}from"./context-UcmrAIhP.js";import{n as i,t as a}from"./event-DVcW0mME.js";const o=()=>{let e={},t={},n=n=>(t[n]=void 0,e[n]??=/* @__PURE__ */ new Map,e[n]),r=(e,t)=>{let r=n(e),i=r.get(t)??0;i>1?r.set(t,i-1):t?r.delete(t):r.clear()},i=()=>{e={},t={}},a=n=>n===void 0?Object.keys(e).flatMap(a):(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]);return{get:a,add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>r(e,t)},remove:r,clear:i}},s=Symbol(),c=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,s,{value:e,enumerable:!1}),r},l=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&s in e)return e[s]},u=(e,t,n)=>Reflect.apply(e,null,[t,n]),d=(s={})=>{let c=o(),d=(e,t)=>c.add(a(e),t),f=(e,t)=>{let n=d(e,(e,r)=>(n(),t(e,r)));return n},p=(e,t)=>{e===void 0?c.clear():c.remove(a(e),t)},m=e=>t({event:e,bus:g,tracer:s.trace,parent:{ref:r()}}).ctx,h=(t,n)=>{let{onError:r}=s,i=m(n);if(!r){t(n.payload,i);return}let a=t=>{try{r(t,n)}catch(t){e(`onError failed`,t)}};try{let e=t(n.payload,i);typeof e?.then==`function`&&Promise.resolve(e).catch(a)}catch(e){a(e)}},g={get:c.get,on:d,off:p,once:f,emit:(e,t)=>{let r=a(e),o=i(e,t),d={type:r,payload:o},f=l(e);f||=g.actions?.bound?.(r);let p=[...c.get(r),...c.get(`*`)];p.length===0&&!f&&s.onUnhandled?.(d);for(let e of p)h(e,d);if(f){let e=m(d),t=g.actions?.run??u;return n(e,`command`,()=>t(f,o,e))}},use:e=>e(g),ctx:s.context,trace:s.trace};return g};export{d as n,c as r,l as t};
@@ -1,4 +1,4 @@
1
- import { x as ContextOf, z as EventMap } from "./action-creator-Bpa8BUS9.js";
1
+ import { C as ContextOf, H as EventMap } from "./action-creator-CcAEvUnw.js";
2
2
  //#region src/core/context-types.d.ts
3
3
  /** What a function that makes the fields of a task gets to see. */
4
4
  type ContextTask = {
package/dist/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { N as WithContext, h as Bus, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { n as NoClash, t as ContextTask } from "./context-types-BKIH5a8s.js";
1
+ import { F as WithContext, H as EventMap, v as Bus } from "./action-creator-CcAEvUnw.js";
2
+ import { n as NoClash, t as ContextTask } from "./context-types-hCKgU8sC.js";
3
3
  //#region src/context/plugin.d.ts
4
4
  /**
5
5
  * Puts `fields` on the context of every task of the bus: a store, a client, a
package/dist/dev.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { J as UnsubscribeCallback, h as Bus, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { i as WithApp, n as ModuleApi } from "./module-B6rg5NIb.js";
1
+ import { H as EventMap, Z as UnsubscribeCallback, v as Bus } from "./action-creator-CcAEvUnw.js";
2
+ import { i as WithApp, n as ModuleApi } from "./module-C_oEXw1n.js";
3
3
  //#region src/dev/types.d.ts
4
4
  /** The parts of what `modulePlugin` returned that the diagnostics report. */
5
5
  type ReportedApp<E extends EventMap> = Pick<ModuleApi<E>, "mount" | "unmount" | "isMounted">;
package/dist/dev.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./event-0RIBGYqS.js";import{t}from"./mount-each-D54UqAhZ.js";const n=`[nanoeffects]`,r=(e={})=>t=>{let{maxListeners:n=10,leaks:r=!0,modules:i=!0,app:s}=e,c=e.log??(e=>console.warn(e)),l=e.log??(e=>console.info(e)),u=[];return r&&u.push(a(t,n,c)),i&&u.push(o(t,s,l)),()=>{for(let e of u.splice(0))e()}},i=t=>typeof t==`function`&&typeof Reflect.get(t,`type`)!=`string`?`*`:e(t),a=(e,t,r)=>{let a=e.on,o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Set,c=!0,l=(...l)=>{if(!c)return Reflect.apply(a,e,l);let[u]=l,d=i(u),f=(o.get(d)??0)+1;o.set(d,f),f>t&&!s.has(d)&&(s.add(d),r(`${n} ${f} listeners of "${d}" were added and not unsubscribed (more than ${t}). A file that is reloaded and subscribes again, or an unsubscribe that is never called?`));let p=Reflect.apply(a,e,l),m=!1;return()=>{if(!m){m=!0;let e=(o.get(d)??1)-1;o.set(d,e),e<=t&&s.delete(d)}p()}};return e.on=l,()=>{c=!1,e.on===l&&(e.on=a)}},o=(e,t,n)=>{let r=t??e.app;if(r)return s(r,n);let i,a;return Object.defineProperty(e,"app",{configurable:!0,enumerable:!0,get:()=>i,set:e=>{a?.(),i=e,a=e?s(e,n):void 0}}),()=>{a?.(),Object.defineProperty(e,"app",{configurable:!0,enumerable:!0,writable:!0,value:i})}},s=(e,r)=>{let{mount:i,unmount:a}=e,o=/* @__PURE__ */ new Map,s=t({mount:t=>{let a=o.get(t.name);if(a===t&&e.isMounted(t))return i(t);let s=a!==void 0&&e.isMounted(a),c=i(t);return o.set(t.name,t),r(`${n} module "${t.name}" ${s?`replaced`:`mounted`}`),()=>{e.isMounted(t)&&r(`${n} module "${t.name}" unmounted`),c()}},unmount:t=>{e.isMounted(t)&&r(`${n} module "${t.name}" unmounted`),a(t)},isMounted:t=>e.isMounted(t)});return e.mount=s.mount,e.unmount=s.unmount,()=>{e.mount=i,e.unmount=a}};export{r as devPlugin};
1
+ import{t as e}from"./event-DVcW0mME.js";import{t}from"./mount-each-D54UqAhZ.js";const n=`[nanoeffects]`,r=(e={})=>t=>{let{maxListeners:n=10,leaks:r=!0,modules:i=!0,app:s}=e,c=e.log??(e=>console.warn(e)),l=e.log??(e=>console.info(e)),u=[];return r&&u.push(a(t,n,c)),i&&u.push(o(t,s,l)),()=>{for(let e of u.splice(0))e()}},i=t=>typeof t==`function`&&typeof Reflect.get(t,`type`)!=`string`?`*`:e(t),a=(e,t,r)=>{let a=e.on,o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Set,c=!0,l=(...l)=>{if(!c)return Reflect.apply(a,e,l);let[u]=l,d=i(u),f=(o.get(d)??0)+1;o.set(d,f),f>t&&!s.has(d)&&(s.add(d),r(`${n} ${f} listeners of "${d}" were added and not unsubscribed (more than ${t}). A file that is reloaded and subscribes again, or an unsubscribe that is never called?`));let p=Reflect.apply(a,e,l),m=!1;return()=>{if(!m){m=!0;let e=(o.get(d)??1)-1;o.set(d,e),e<=t&&s.delete(d)}p()}};return e.on=l,()=>{c=!1,e.on===l&&(e.on=a)}},o=(e,t,n)=>{let r=t??e.app;if(r)return s(r,n);let i,a;return Object.defineProperty(e,"app",{configurable:!0,enumerable:!0,get:()=>i,set:e=>{a?.(),i=e,a=e?s(e,n):void 0}}),()=>{a?.(),Object.defineProperty(e,"app",{configurable:!0,enumerable:!0,writable:!0,value:i})}},s=(e,r)=>{let{mount:i,unmount:a}=e,o=/* @__PURE__ */ new Map,s=t({mount:t=>{let a=o.get(t.name);if(a===t&&e.isMounted(t))return i(t);let s=a!==void 0&&e.isMounted(a),c=i(t);return o.set(t.name,t),r(`${n} module "${t.name}" ${s?`replaced`:`mounted`}`),()=>{e.isMounted(t)&&r(`${n} module "${t.name}" unmounted`),c()}},unmount:t=>{e.isMounted(t)&&r(`${n} module "${t.name}" unmounted`),a(t)},isMounted:t=>e.isMounted(t)});return e.mount=s.mount,e.unmount=s.unmount,()=>{e.mount=i,e.unmount=a}};export{r as devPlugin};
package/dist/effects.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as WithSignal, W as Task, b as Context, u as Effect, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { a as RaceStarter, c as all, d as GroupOptions, f as Starter, i as take, l as ChildOf, m as delay, n as effectsPlugin, o as race, p as StarterOf, r as TakeOptions, s as allSettled, u as EffectContext } from "./plugin-D-j1DarG.js";
1
+ import { H as EventMap, I as WithSignal, S as Context, f as Effect, q as Task } from "./action-creator-CcAEvUnw.js";
2
+ import { a as RaceStarter, c as all, d as GroupOptions, f as Starter, i as take, l as ChildOf, m as delay, n as effectsPlugin, o as race, p as StarterOf, r as TakeOptions, s as allSettled, u as EffectContext } from "./plugin-CuvIgjnX.js";
3
3
  //#region src/utils/onAbort.d.ts
4
4
  export declare const onAbort: (ctx: {
5
5
  signal?: AbortSignal | undefined;
@@ -0,0 +1 @@
1
+ const e=e=>e.type??e,t=(e,t)=>typeof e==`object`?e.payload:t;export{t as n,e as t};
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as HandlerRef, B as Keys, C as Emit, D as EventPayload, E as EventLike, F as WithType, H as PayloadOf, I as createBus, J as UnsubscribeCallback, M as On, N as WithContext, O as EventsOf, P as WithSignal, S as ContextOption, T as EventKey, W as Task, _ as Command, b as Context, g as BusOptions, h as Bus, i as WithPayload, j as Off, k as Handler, m as AnyEvent, p as Action, v as CommandCall, w as Emittable, x as ContextOf, y as Commands, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type CommandCall, type Commands, type Context, type ContextOf, type ContextOption, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type EventsOf, type Handler, type HandlerRef, type Keys, type Off, type On, type PayloadOf, type Task, type UnsubscribeCallback, type WithContext, type WithPayload, type WithSignal, type WithType, createBus };
1
+ import { A as EventsOf, C as ContextOf, D as EventKey, E as Emittable, F as WithContext, G as PayloadOf, H as EventMap, I as WithSignal, L as WithType, M as HandlerRef, N as Off, O as EventLike, P as On, S as Context, T as Emit, U as Keys, Z as UnsubscribeCallback, _ as AnyEvent, b as Command, g as Actions, h as Action, j as Handler, k as EventPayload, n as Call, o as WithPayload, q as Task, v as Bus, w as ContextOption, x as CommandCall, y as BusOptions, z as createBus } from "./action-creator-CcAEvUnw.js";
2
+ export { type Action, type Actions, type AnyEvent, type Bus, type BusOptions, type Call, type Command, type CommandCall, type Context, type ContextOf, type ContextOption, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type EventsOf, type Handler, type HandlerRef, type Keys, type Off, type On, type PayloadOf, type Task, type UnsubscribeCallback, type WithContext, type WithPayload, type WithSignal, type WithType, createBus };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export{n as createBus}from"./bus-hHp-T7JV.js";
1
+ export{n as createBus}from"./bus-DlpEluzd.js";