nanoeffects 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -55
- package/dist/{action-rqZEnRIp.d.ts → action-creator-D48fp13T.d.ts} +66 -74
- package/dist/actions.d.ts +54 -20
- package/dist/actions.js +1 -1
- package/dist/bus-Cho463Sq.js +1 -0
- package/dist/dev.d.ts +2 -2
- package/dist/effects.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{module-BSCdR9D1.d.ts → module-DVX8aYlq.d.ts} +39 -25
- package/dist/notrace/{action-rqZEnRIp.d.ts → action-creator-D48fp13T.d.ts} +66 -74
- package/dist/notrace/actions.d.ts +90 -42
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/bus-B12SvxLv.js +1 -0
- package/dist/notrace/effects.d.ts +1 -1
- package/dist/notrace/index.d.ts +2 -2
- package/dist/notrace/index.js +1 -1
- package/dist/notrace/policies.d.ts +1 -1
- package/dist/notrace/redux.d.ts +1 -1
- package/dist/notrace/redux.js +1 -1
- package/dist/observable.d.ts +1 -1
- package/dist/policies.d.ts +1 -1
- package/dist/redux.d.ts +1 -1
- package/dist/redux.js +1 -1
- package/dist/trace.d.ts +1 -1
- package/docs/README.md +24 -9
- package/docs/actions.md +118 -42
- package/docs/context.md +27 -8
- package/docs/effects.md +152 -73
- package/docs/errors.md +23 -10
- package/docs/guide.md +236 -129
- package/docs/hmr.md +25 -13
- package/docs/observable.md +10 -9
- package/docs/package.md +14 -0
- package/docs/policies.md +92 -30
- package/docs/semantics.md +5 -3
- package/docs/stores.md +96 -22
- package/docs/tracing.md +69 -54
- package/package.json +5 -4
- package/dist/bus-CRnlwmQ4.js +0 -1
- package/dist/notrace/bus-Dx0l9X6h.js +0 -1
- package/dist/notrace/event-0RIBGYqS.js +0 -1
package/README.md
CHANGED
|
@@ -5,35 +5,46 @@
|
|
|
5
5
|
[](https://bundlephobia.com/package/nanoeffects)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
A small
|
|
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`.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
import { createBus } from "nanoeffects";
|
|
10
|
+
## Install
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
```bash
|
|
13
|
+
npm install nanoeffects
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
ESM only, no dependencies, types included.
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
## Create a bus
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
Subscribe with `on`, send with `emit`. `on` returns a function that unsubscribes.
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import { createBus } from "nanoeffects";
|
|
24
|
+
|
|
25
|
+
const bus = createBus(); // event bus; each handler call is a task
|
|
26
|
+
|
|
27
|
+
const unbind = bus.on("greet", (name) => {
|
|
28
|
+
console.log(`hello ${name}`);
|
|
29
|
+
});
|
|
18
30
|
|
|
19
31
|
bus.emit("greet", "Ada"); // hello Ada
|
|
20
|
-
unbind();
|
|
32
|
+
unbind();
|
|
21
33
|
```
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
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).
|
|
24
36
|
|
|
25
|
-
|
|
26
|
-
bus.emit("greet", "Ada");
|
|
27
|
-
bus.emit({ type: "greet", payload: "Ada" });
|
|
28
|
-
bus.emit(double, 21); // a command: runs, returns 42
|
|
29
|
-
```
|
|
37
|
+
## Abort overlapping work
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
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).
|
|
32
40
|
|
|
33
|
-
```
|
|
41
|
+
```js
|
|
42
|
+
import { createBus } from "nanoeffects";
|
|
34
43
|
import { latest } from "nanoeffects/policies";
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
const bus = createBus();
|
|
46
|
+
|
|
47
|
+
// latest — a newer run aborts the older one
|
|
37
48
|
bus.on(
|
|
38
49
|
"search",
|
|
39
50
|
latest(async (query, ctx) => {
|
|
@@ -43,55 +54,39 @@ bus.on(
|
|
|
43
54
|
);
|
|
44
55
|
```
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
- **Cancellation first.** Aborting a task aborts its children and `ctx.on` subscriptions and drops its result.
|
|
48
|
-
- **One model** instead of `AbortController`, lodash `debounce`, `p-queue` and a hand-written retry.
|
|
49
|
-
- **Typed** commands, actions, jobs and modules; **traced** with Redux DevTools; the trace can be cut out of the build.
|
|
50
|
-
- **Checked** against lodash on random call sequences, and against known issues of RxJS, p-queue, p-retry and effector.
|
|
51
|
-
|
|
52
|
-
## Install
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npm install nanoeffects
|
|
56
|
-
```
|
|
57
|
+
Same `on` / `emit`. Replace `api.search` with your own request.
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
## Get a result from `emit`
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
A **command** is like an event, but `emit` runs its handler and returns the result (a promise if the handler is async).
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
| `nanoeffects/policies` | `latest`, `leading`, `debounce`, `throttle`, `queue` |
|
|
66
|
-
| `nanoeffects/effects` | `retry`, `timeout`, `poll`, `delay`, `take`, `all`, `allSettled`, `race` |
|
|
67
|
-
| `nanoeffects/actions` | `createAction`, `createCommand`, `createJob`, `createModule`, `modulePlugin` |
|
|
68
|
-
| `nanoeffects/redux` | `createEffectsMiddleware` |
|
|
69
|
-
| `nanoeffects/observable` | `createObserve`: an event as an Observable (RxJS) |
|
|
70
|
-
| `nanoeffects/dev` | `devPlugin`: warnings about listeners that pile up and messages about modules; a no-op in production |
|
|
71
|
-
| `nanoeffects/trace` | `createTracer` and the loggers |
|
|
63
|
+
```js
|
|
64
|
+
import { createBus } from "nanoeffects";
|
|
65
|
+
import { createCommand } from "nanoeffects/actions";
|
|
72
66
|
|
|
73
|
-
|
|
67
|
+
const bus = createBus();
|
|
74
68
|
|
|
75
|
-
|
|
69
|
+
// createCommand — emit runs the handler and returns what it returns
|
|
70
|
+
const double = createCommand("math/double", (value) => value * 2);
|
|
76
71
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
| `signal`, `abort` | the signal of the task, and a way to abort it |
|
|
80
|
-
| `emit` | emit as this task; does nothing after an abort |
|
|
81
|
-
| `on`, `off` | subscribe; the subscription ends when the task aborts |
|
|
82
|
-
| `run(fn, ...)` | a child task, aborted with this one |
|
|
83
|
-
| `type`, `payload` | the event the task works on |
|
|
72
|
+
const answer = bus.emit(double(21)); // 42
|
|
73
|
+
```
|
|
84
74
|
|
|
85
|
-
|
|
75
|
+
More (contracts, jobs, modules): [Actions](docs/actions.md).
|
|
86
76
|
|
|
87
77
|
## Docs
|
|
88
78
|
|
|
89
|
-
[
|
|
79
|
+
Start with the [Guide](docs/guide.md) (bus → policies → effects → modules). Or jump by need:
|
|
90
80
|
|
|
91
|
-
|
|
81
|
+
| Need | Page |
|
|
82
|
+
| ---- | ---- |
|
|
83
|
+
| Async UI races (search, submit) | [Policies](docs/policies.md) |
|
|
84
|
+
| Retry, timeout, wait for events | [Effects](docs/effects.md) |
|
|
85
|
+
| Domain logic off the UI | [Actions, commands, modules](docs/actions.md) |
|
|
86
|
+
| Redux / Zustand / MobX | [Stores](docs/stores.md) |
|
|
92
87
|
|
|
93
|
-
|
|
94
|
-
pnpm verify # what CI runs: types, lint, tests, build, integration, package checks, size
|
|
95
|
-
```
|
|
88
|
+
[Full index, package entries, tracing](docs/README.md) · [Examples](examples)
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
- **Tiny.** `createBus` is about 1.3 kB gzip; other entries load only when imported.
|
|
91
|
+
- **Cancellation first.** Aborting a task aborts its children and drops its result.
|
|
92
|
+
- **One model** instead of hand-rolled `AbortController`, lodash debounce, and retry glue.
|
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
//#region src/core/types.d.ts
|
|
2
|
-
type PayloadOf<P> = undefined extends P ? [payload?: P] : [payload: P];
|
|
3
|
-
type EventMap = Record<string, unknown>;
|
|
4
|
-
type Keys<E> = keyof E & string;
|
|
5
|
-
type Task<P, E extends EventMap, R = unknown> = (payload: P, ctx: Context<E>) => R;
|
|
6
|
-
type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command";
|
|
7
|
-
type Phase = "start" | "done" | "error" | "cancel" | "drop";
|
|
8
|
-
type TraceEvent<E = Record<string, unknown>> = { [K in Keys<E>]: {
|
|
9
|
-
id: number;
|
|
10
|
-
parent?: number;
|
|
11
|
-
type: K;
|
|
12
|
-
effect: EffectName;
|
|
13
|
-
phase: Phase;
|
|
14
|
-
payload: E[K];
|
|
15
|
-
error?: unknown;
|
|
16
|
-
}; }[Keys<E>];
|
|
17
|
-
/**
|
|
18
|
-
* The contract of every trace logger: receives each task phase of a bus.
|
|
19
|
-
* Adapters in `nanoeffects/trace` are factories that return one; pass it
|
|
20
|
-
* through `createTracer` to `createBus({ trace })`.
|
|
21
|
-
*/
|
|
22
|
-
type TraceLogger<E = Record<string, unknown>> = (event: TraceEvent<E>) => void;
|
|
23
|
-
/** One logger or several; a throwing logger never breaks the task or the others. */
|
|
24
|
-
type Trace<E = Record<string, unknown>> = TraceLogger<E> | readonly TraceLogger<E>[];
|
|
25
|
-
type UnsubscribeCallback = () => void;
|
|
26
|
-
//#endregion
|
|
27
1
|
//#region src/core/store.d.ts
|
|
28
2
|
declare const anyEventType = "*";
|
|
29
3
|
//#endregion
|
|
@@ -68,27 +42,35 @@ type Action<E extends EventMap> = {
|
|
|
68
42
|
type: EventKey<E>;
|
|
69
43
|
payload: EventPayload<E>;
|
|
70
44
|
};
|
|
71
|
-
type WithPayload<E extends EventMap, K extends EventKey<E>> = undefined extends E[K] ? {
|
|
72
|
-
type: K;
|
|
73
|
-
payload?: E[K];
|
|
74
|
-
} : {
|
|
75
|
-
type: K;
|
|
76
|
-
payload: E[K];
|
|
77
|
-
};
|
|
78
45
|
type EventLike<E extends EventMap> = { [K in EventKey<E>]: WithPayload<E, K>; }[Keys<E>];
|
|
79
46
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
47
|
+
* Brands `{ type, payload }` from a command call so `emit` can run the command.
|
|
48
|
+
* Not enumerable on the object.
|
|
49
|
+
*/
|
|
50
|
+
declare const commandCall: unique symbol;
|
|
51
|
+
/**
|
|
52
|
+
* What `command(payload)` returns: an action-shaped value that `emit` runs as
|
|
53
|
+
* that command. A plain `{ type, payload }` without this brand is notify-only.
|
|
54
|
+
*/
|
|
55
|
+
type CommandCall<P = unknown, R = unknown> = {
|
|
56
|
+
type: string;
|
|
57
|
+
payload: P;
|
|
58
|
+
readonly [commandCall]: Command<P, R>;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* A typed request for a result. Calling it builds `{ type, payload }` like
|
|
62
|
+
* `createAction`; `bus.emit(command(payload))` (or `bus.emit(command, payload)`)
|
|
63
|
+
* runs the handler as a task and returns what it returns. Made by
|
|
82
64
|
* `createCommand` in `nanoeffects/actions`.
|
|
83
65
|
*/
|
|
84
66
|
type Command<P = never, R = unknown> = {
|
|
85
|
-
(payload: P
|
|
67
|
+
(payload: P): CommandCall<P, R>;
|
|
86
68
|
readonly type: string;
|
|
87
69
|
/** Wraps the handler that really runs, e.g. to route its outcome. */
|
|
88
70
|
readonly wrap?: (run: (payload: never, ctx: never) => unknown) => (payload: never, ctx: never) => unknown;
|
|
89
71
|
};
|
|
90
72
|
/**
|
|
91
|
-
* What the bus needs to run commands. The implementation, `
|
|
73
|
+
* What the bus needs to run commands. The implementation, `commandsPlugin` in
|
|
92
74
|
* `nanoeffects/actions`, holds the handlers bound to commands and runs the
|
|
93
75
|
* right one, so a bus without `commands` carries none of that.
|
|
94
76
|
*/
|
|
@@ -96,11 +78,13 @@ interface Commands {
|
|
|
96
78
|
run(command: Command, payload: unknown, ctx: unknown): unknown;
|
|
97
79
|
}
|
|
98
80
|
/** Anything `emit` takes as its first argument: an event type, an action or a command. */
|
|
99
|
-
type Emittable<E extends EventMap> = EventKey<E> | Command | EventLike<E>;
|
|
81
|
+
type Emittable<E extends EventMap> = EventKey<E> | Command | CommandCall | EventLike<E>;
|
|
100
82
|
type Emit<E extends EventMap> = {
|
|
101
83
|
<K extends EventKey<E>>(event: K, ...payload: PayloadOf<E[K]>): void;
|
|
102
|
-
|
|
84
|
+
/** Before Handler: a command is callable too, and must return its result type. */
|
|
103
85
|
<P, R>(command: Command<P, R>, ...payload: PayloadOf<P>): R;
|
|
86
|
+
<P, R>(call: CommandCall<P, R>): R;
|
|
87
|
+
<K extends EventKey<E>, R>(event: Handler<E, R, K>, ...payload: PayloadOf<E[K]>): R;
|
|
104
88
|
(event: EventLike<E>): void;
|
|
105
89
|
};
|
|
106
90
|
type On<E extends EventMap> = {
|
|
@@ -119,6 +103,8 @@ type On<E extends EventMap> = {
|
|
|
119
103
|
}, fn: Task<E[K], E>): UnsubscribeCallback;
|
|
120
104
|
};
|
|
121
105
|
type Off<E extends EventMap> = {
|
|
106
|
+
/** Removes every listener of every event (including `"*"`). */
|
|
107
|
+
(): void;
|
|
122
108
|
(event: AnyEvent): void;
|
|
123
109
|
(event: AnyEvent, fn: Task<EventPayload<E>, E>): void;
|
|
124
110
|
<K extends EventKey<E>>(event: K): void;
|
|
@@ -173,6 +159,11 @@ type Context<E extends EventMap> = EventLike<E> & ContextOf<E> & {
|
|
|
173
159
|
};
|
|
174
160
|
interface Bus<E extends EventMap> {
|
|
175
161
|
on: On<E>;
|
|
162
|
+
/**
|
|
163
|
+
* Like `on`, but the handler runs at most once: it is removed before it is
|
|
164
|
+
* invoked, so a nested emit of the same event does not re-enter it.
|
|
165
|
+
*/
|
|
166
|
+
once: On<E>;
|
|
176
167
|
off: Off<E>;
|
|
177
168
|
emit: Emit<E>;
|
|
178
169
|
/**
|
|
@@ -183,7 +174,7 @@ interface Bus<E extends EventMap> {
|
|
|
183
174
|
use<R>(plugin: (bus: Bus<E>) => R): R;
|
|
184
175
|
/** `BusOptions.context` and `.trace`, and `commands`, for what makes tasks of its own, like `modulePlugin`. */
|
|
185
176
|
readonly context?: ContextOption<E> | undefined;
|
|
186
|
-
/** What runs the commands; `
|
|
177
|
+
/** What runs the commands; `commandsPlugin` and `modulePlugin` set it. Without one a command runs its own handler. */
|
|
187
178
|
commands?: Commands | undefined;
|
|
188
179
|
readonly trace?: Tracer | undefined;
|
|
189
180
|
}
|
|
@@ -223,7 +214,40 @@ type BusOptions<E extends EventMap> = {
|
|
|
223
214
|
};
|
|
224
215
|
declare const createBus: <Events extends EventMap>(options?: BusOptions<Events>) => Bus<Events>;
|
|
225
216
|
//#endregion
|
|
226
|
-
//#region src/
|
|
217
|
+
//#region src/core/types.d.ts
|
|
218
|
+
type PayloadOf<P> = undefined extends P ? [payload?: P] : [payload: P];
|
|
219
|
+
type EventMap = Record<string, unknown>;
|
|
220
|
+
type Keys<E> = keyof E & string;
|
|
221
|
+
type Task<P, E extends EventMap, R = unknown> = (payload: P, ctx: Context<E>) => R;
|
|
222
|
+
type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command";
|
|
223
|
+
type Phase = "start" | "done" | "error" | "cancel" | "drop";
|
|
224
|
+
type TraceEvent<E = Record<string, unknown>> = { [K in Keys<E>]: {
|
|
225
|
+
id: number;
|
|
226
|
+
parent?: number;
|
|
227
|
+
type: K;
|
|
228
|
+
effect: EffectName;
|
|
229
|
+
phase: Phase;
|
|
230
|
+
payload: E[K];
|
|
231
|
+
error?: unknown;
|
|
232
|
+
}; }[Keys<E>];
|
|
233
|
+
/**
|
|
234
|
+
* The contract of every trace logger: receives each task phase of a bus.
|
|
235
|
+
* Adapters in `nanoeffects/trace` are factories that return one; pass it
|
|
236
|
+
* through `createTracer` to `createBus({ trace })`.
|
|
237
|
+
*/
|
|
238
|
+
type TraceLogger<E = Record<string, unknown>> = (event: TraceEvent<E>) => void;
|
|
239
|
+
/** One logger or several; a throwing logger never breaks the task or the others. */
|
|
240
|
+
type Trace<E = Record<string, unknown>> = TraceLogger<E> | readonly TraceLogger<E>[];
|
|
241
|
+
type UnsubscribeCallback = () => void;
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/core/action-creator.d.ts
|
|
244
|
+
type WithPayload<E extends EventMap, K extends Keys<E>> = undefined extends E[K] ? {
|
|
245
|
+
type: K;
|
|
246
|
+
payload?: E[K];
|
|
247
|
+
} : {
|
|
248
|
+
type: K;
|
|
249
|
+
payload: E[K];
|
|
250
|
+
};
|
|
227
251
|
type Absent<P> = [P] extends [void] ? undefined : P;
|
|
228
252
|
type Made<P, T extends string> = WithPayload<{ [K in T]: Absent<P>; }, T>;
|
|
229
253
|
declare const free: unique symbol;
|
|
@@ -267,37 +291,5 @@ interface ActionCreator<P = void, T extends string = string, A extends unknown[]
|
|
|
267
291
|
}): action is Made<P, T>;
|
|
268
292
|
toString(): T;
|
|
269
293
|
}
|
|
270
|
-
/**
|
|
271
|
-
* Creates an action creator, as in Redux Toolkit.
|
|
272
|
-
*
|
|
273
|
-
* - `createAction("user/loaded")` needs no generics: the payload type comes
|
|
274
|
-
* from the event map of the bus that the action goes to, and the literal
|
|
275
|
-
* type of the action is kept.
|
|
276
|
-
* - `createAction<User>("user/loaded")` gives the payload type, for a bus
|
|
277
|
-
* without an event map or to check the calls of the creator. The type of
|
|
278
|
-
* the action is then `string`; to keep the literal, give it as the second
|
|
279
|
-
* generic: `createAction<User, "user/loaded">("user/loaded")`.
|
|
280
|
-
* - With `prepare` the arguments of the creator can differ from the payload,
|
|
281
|
-
* and both types are inferred.
|
|
282
|
-
*
|
|
283
|
-
* @example
|
|
284
|
-
* ```ts
|
|
285
|
-
* const added = createAction<number>("counter/added");
|
|
286
|
-
* added(2); // { type: "counter/added", payload: 2 }
|
|
287
|
-
* added.type; // "counter/added"
|
|
288
|
-
* added.match(action); // true for actions of this type
|
|
289
|
-
*
|
|
290
|
-
* const renamed = createAction("user/renamed", (id: string, name: string) => ({
|
|
291
|
-
* payload: { id, name },
|
|
292
|
-
* }));
|
|
293
|
-
*
|
|
294
|
-
* bus.emit(added(2));
|
|
295
|
-
* bus.on(added, (n) => console.log(n));
|
|
296
|
-
* ```
|
|
297
|
-
*/
|
|
298
|
-
declare function createAction<P = Free, T extends string = string>(type: T): [P] extends [Free] ? FreeActionCreator<T> : ActionCreator<P, T>;
|
|
299
|
-
declare function createAction<A extends unknown[], P, T extends string = string>(type: T, prepare: (...args: A) => {
|
|
300
|
-
payload: P;
|
|
301
|
-
}): ActionCreator<P, T, A>;
|
|
302
294
|
//#endregion
|
|
303
|
-
export {
|
|
295
|
+
export { Handler as A, ContextOf as C, EventKey as D, Emittable as E, WithType as F, commandCall as I, createBus as L, Off as M, On as N, EventLike as O, WithContext as P, Tracer as R, Context as S, Emit as T, Bus as _, EffectName as a, CommandCall as b, PayloadOf as c, Trace as d, TraceEvent as f, AnyEvent as g, Action as h, WithPayload as i, HandlerRef as j, EventPayload as k, Phase as l, UnsubscribeCallback as m, Free as n, EventMap as o, TraceLogger as p, FreeActionCreator as r, Keys as s, ActionCreator as t, Task as u, BusOptions as v, ContextOption as w, Commands as x, Command as y, anyEventType as z };
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as modulePlugin, c as
|
|
1
|
+
import { n as Free, o as EventMap, r as FreeActionCreator, t as ActionCreator, u as Task } from "./action-creator-D48fp13T.js";
|
|
2
|
+
import { a as modulePlugin, c as bindHandler, i as createModule, l as commandsPlugin, n as ModuleApi, o as Binding, r as ModuleContext, s as MadeCommand, t as Module, u as createCommand } from "./module-DVX8aYlq.js";
|
|
3
|
+
//#region src/actions/action.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Creates an action creator, as in Redux Toolkit.
|
|
6
|
+
*
|
|
7
|
+
* - `createAction("user/loaded")` needs no generics: the payload type comes
|
|
8
|
+
* from the event map of the bus that the action goes to, and the literal
|
|
9
|
+
* type of the action is kept.
|
|
10
|
+
* - `createAction<User>("user/loaded")` gives the payload type, for a bus
|
|
11
|
+
* without an event map or to check the calls of the creator. The type of
|
|
12
|
+
* the action is then `string`; to keep the literal, give it as the second
|
|
13
|
+
* generic: `createAction<User, "user/loaded">("user/loaded")`.
|
|
14
|
+
* - With `prepare` the arguments of the creator can differ from the payload,
|
|
15
|
+
* and both types are inferred.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const added = createAction<number>("counter/added");
|
|
20
|
+
* added(2); // { type: "counter/added", payload: 2 }
|
|
21
|
+
* added.type; // "counter/added"
|
|
22
|
+
* added.match(action); // true for actions of this type
|
|
23
|
+
*
|
|
24
|
+
* const renamed = createAction("user/renamed", (id: string, name: string) => ({
|
|
25
|
+
* payload: { id, name },
|
|
26
|
+
* }));
|
|
27
|
+
*
|
|
28
|
+
* bus.emit(added(2));
|
|
29
|
+
* bus.on(added, (n) => console.log(n));
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function createAction<P = Free, T extends string = string>(type: T): [P] extends [Free] ? FreeActionCreator<T> : ActionCreator<P, T>;
|
|
33
|
+
export declare function createAction<A extends unknown[], P, T extends string = string>(type: T, prepare: (...args: A) => {
|
|
34
|
+
payload: P;
|
|
35
|
+
}): ActionCreator<P, T, A>;
|
|
36
|
+
//#endregion
|
|
3
37
|
//#region src/actions/job.d.ts
|
|
4
38
|
type Creator = {
|
|
5
39
|
(...args: never[]): {
|
|
@@ -9,43 +43,43 @@ type Creator = {
|
|
|
9
43
|
readonly type: string;
|
|
10
44
|
};
|
|
11
45
|
type Outcome = string | Creator;
|
|
12
|
-
/** What `createJob` returns: the command of the job
|
|
13
|
-
type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
14
|
-
command: Command<P, Promise<Awaited<R> | undefined>> & {
|
|
15
|
-
readonly type: T;
|
|
16
|
-
};
|
|
46
|
+
/** What `createJob` returns: the command of the job, with its two actions as properties. */
|
|
47
|
+
type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCommand<P, Promise<Awaited<R> | undefined>, T> & {
|
|
17
48
|
/** Made from a string with the result as its payload, or the creator that was given. */
|
|
18
|
-
|
|
49
|
+
readonly success: S extends string ? ActionCreator<Awaited<R>, S> : S;
|
|
19
50
|
/** Made from a string with the error as its payload, or the creator that was given. */
|
|
20
|
-
|
|
51
|
+
readonly fail: F extends string ? ActionCreator<unknown, F> : F;
|
|
21
52
|
};
|
|
22
53
|
/**
|
|
23
54
|
* Creates a job: a command whose outcome is sent on as actions. When the
|
|
24
|
-
* handler succeeds, `
|
|
25
|
-
* fails, `
|
|
55
|
+
* handler succeeds, `job.success(result)` is emitted on the bus, and when it
|
|
56
|
+
* fails, `job.fail(error)`. It is made for the case where nobody waits for
|
|
26
57
|
* the result and the bus carries it on, like `createAsyncThunk` or an effector
|
|
27
|
-
* effect. It returns the
|
|
58
|
+
* effect. It returns the command, with the two actions on it: `job.success` and
|
|
59
|
+
* `job.fail`.
|
|
28
60
|
*
|
|
29
61
|
* `success` and `fail` are a type string, from which the job makes an action
|
|
30
62
|
* creator with `createAction`, or an action creator that exists already, which
|
|
31
63
|
* is used and returned as it is. A missing one is made with the type
|
|
32
64
|
* `<type>/success` or `<type>/fail`.
|
|
33
65
|
*
|
|
34
|
-
* `bus.emit(
|
|
35
|
-
* handler always counts as handled: it goes to `
|
|
66
|
+
* `bus.emit(job(payload))` returns a promise of the result. An error of the
|
|
67
|
+
* handler always counts as handled: it goes to `job.fail` and the promise
|
|
36
68
|
* resolves with `undefined`; a command is the tool for a result that must reject.
|
|
37
69
|
* The actions are emitted as the task that ran the job, also for a handler bound
|
|
38
70
|
* with `commands.handle`, and are skipped once the task is aborted. A missing
|
|
39
71
|
* handler is not an outcome: `emit` throws `MissingHandlerError`. Calling the
|
|
40
|
-
* command
|
|
72
|
+
* job builds `{ type, payload }` like a command; it does not run the handler.
|
|
41
73
|
*
|
|
42
74
|
* @example
|
|
43
75
|
* ```ts
|
|
44
|
-
* const
|
|
45
|
-
*
|
|
76
|
+
* const loadUser = createJob("user/load", fetchUser, {
|
|
77
|
+
* success: "user/loaded",
|
|
78
|
+
* fail: "user/failed",
|
|
79
|
+
* });
|
|
46
80
|
*
|
|
47
|
-
* bus.on(
|
|
48
|
-
* bus.emit(loadUser
|
|
81
|
+
* bus.on(loadUser.success, (user) => render(user));
|
|
82
|
+
* bus.emit(loadUser("1")); // loadUser.success(user) or loadUser.fail(error) comes later
|
|
49
83
|
* ```
|
|
50
84
|
*/
|
|
51
85
|
export declare const createJob: <P = void, R = void, T extends string = string, E extends EventMap = EventMap, S extends Outcome = `${T}/success`, F extends Outcome = `${T}/fail`>(type: T, handler?: Task<P, E, R>, outcomes?: {
|
|
@@ -55,4 +89,4 @@ export declare const createJob: <P = void, R = void, T extends string = string,
|
|
|
55
89
|
fail?: F;
|
|
56
90
|
}) => Job<P, R, T, S, F>;
|
|
57
91
|
//#endregion
|
|
58
|
-
export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler,
|
|
92
|
+
export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, commandsPlugin, createCommand, createModule, modulePlugin };
|
package/dist/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-DlA80GOA.js";import{t as r}from"./event-0RIBGYqS.js";function
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-DlA80GOA.js";import{t as r}from"./event-0RIBGYqS.js";import{r as i}from"./bus-Cho463Sq.js";function a(e,t){return Object.assign((...n)=>({type:e,payload:t?t(...n).payload:n[0]}),{type:e,match:t=>t.type===e,toString:()=>e})}var o=class extends Error{};const s=(e,t)=>{let n=t=>t.type===e;function r(n,r){if(typeof r==`object`){if(!t)throw new o(`No handler for command "${e}"`);return t(n,r)}return i(a,e,n)}let a=Object.assign(r,{type:e,match:n});return a},c=(e,t)=>[e,t],l=(e=[])=>t=>{let n=/* @__PURE__ */ new Map,i=(e,t)=>{let r={fn:t};return n.set(e,[...n.get(e)??[],r]),()=>{let t=(n.get(e)??[]).filter(e=>e!==r);t.length>0?n.set(e,t):n.delete(e)}};function a(e,t){if(t)return i(r(e),t);let n=e.map(([e,t])=>i(r(e),t));return()=>{for(let e of n)e()}}a(e);let o={run:(e,t,r)=>{let i=(n.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,r])},handle:a};return t.commands=o,o},u=e=>typeof e==`string`?a(e):e,d=(e,t,n)=>{let r=e.emit;r(Reflect.apply(t,null,[n]))},f=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof o)throw e;return Promise.reject(e)}},p=(e,t,n={})=>{let r=s(e,t),a=u(n.success??`${e}/success`),o=u(n.fail??`${e}/fail`),c=(e,t,n)=>{let r=n;return f(()=>Reflect.apply(e,null,[t,n])).then(e=>(d(r,a,e),e),e=>{d(r,o,e)})},l=e=>(t,n)=>c(e,t,n);function p(t,n){return typeof n==`object`?c(r,t,n):i(m,e,t)}let m=Object.assign(p,{type:e,match:r.match,wrap:l,success:a,fail:o});return m},m=(e,t)=>({name:e,setup:t}),h=(r=[])=>i=>{let a=i.commands??l()(i),o={handle:(...e)=>{if(!a.handle)throw Error("modulePlugin: the `commands` of the bus is not a registry from `commandsPlugin()`");return Reflect.apply(a.handle,null,e)}},s=n({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),c=n=>{let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map;return{mount:a=>{let s=r.get(a);if(s)return s;let l=i.get(a.name);l&&r.get(l)?.();let u=t(n,a.name),d=u.ctx,f=c(d),p=()=>{r.delete(a),i.get(a.name)===a&&i.delete(a.name),d.abort(),u.unlink()};r.set(a,p),i.set(a.name,a);let m=(t,n)=>{let r=Reflect.apply(o.handle,null,[t,n]);return e(d,r),r},h=(...e)=>{let t=e.map(e=>typeof e==`function`?m(e,e):f.mount(e));return()=>{for(let e of t)e()}},g=(e,...t)=>{d.run(e,...t).catch(e=>{if(!d.signal.aborted)throw e})};try{a.setup(Object.assign(d,{handle:m,use:h,spawn:g}))}catch(e){throw p(),e}return p},unmount:e=>r.get(e)?.(),isMounted:e=>r.has(e)}},u=c(s.ctx);for(let e of r)u.mount(e);return{commands:a,mount:e=>(s.ctx.signal.throwIfAborted(),u.mount(e)),unmount:u.unmount,isMounted:u.isMounted,dispose:()=>s.ctx.abort()}};export{c as bindHandler,l as commandsPlugin,a as createAction,s as createCommand,p as createJob,m as createModule,h as modulePlugin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./onAbort-BgMfspK4.js";import{a as t,n,r}from"./context-DlA80GOA.js";import{n as i}from"./promise-CSLpzwSR.js";import{n as a,t as o}from"./event-0RIBGYqS.js";const s=`*`,c=()=>{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()};return{get:n=>(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]),add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>{r(e,t)}},remove:r,clear:()=>{for(let t of Reflect.ownKeys(e))delete e[t];for(let e of Reflect.ownKeys(t))delete t[e]}}},l=Symbol(`nanoeffects.commandCall`),u=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,l,{value:e,enumerable:!1}),r},d=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&l in e)return e[l]},f=(e,t,n)=>Reflect.apply(e,null,[t,n]),p=(s={})=>{let l=c(),u=(e,t)=>l.add(o(e),t),p=(e,t)=>{let n=u(e,(e,r)=>(n(),t(e,r)));return n},m=(e,t)=>{e===void 0?l.clear():l.remove(o(e),t)},h=(t,a)=>{let{onError:o}=s,{ctx:c}=n({event:a,bus:g,tracer:s.trace,parent:{ref:r()}});if(!o){t(a.payload,c);return}let l=t=>{try{o(t,a)}catch(t){e(`onError failed`,t)}};try{let e=t(a.payload,c);i(e)&&Promise.resolve(e).catch(l)}catch(e){l(e)}},g={on:u,once:p,off:m,emit:(e,i)=>{let c=o(e),u=a(e,i),p={type:c,payload:u},m=d(e),_=[...l.get(c),...l.get(`*`)];_.length===0&&!m&&s.onUnhandled?.(p);for(let e of _)h(e,p);if(m){let{ctx:e}=n({event:p,bus:g,tracer:s.trace,parent:{ref:r()}}),i=g.commands?.run??f;return t(e,`command`,()=>i(m,u,e))}},use:e=>e(g),context:s.context,trace:s.trace};return g};export{s as i,p as n,u as r,l as t};
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ModuleApi } from "./module-
|
|
1
|
+
import { _ as Bus, m as UnsubscribeCallback, o as EventMap } from "./action-creator-D48fp13T.js";
|
|
2
|
+
import { n as ModuleApi } from "./module-DVX8aYlq.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/effects.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as Context, o as EventMap, s as Keys, u as Task } from "./action-creator-D48fp13T.js";
|
|
2
2
|
//#region src/utils/delay.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Resolves after `ms`, or rejects early if `ctx.signal` aborts. `ms` can be a
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type Commands, type Context, type ContextOf, type ContextOption, type EffectName, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type Handler, type HandlerRef, type Keys, type Off, type On, type PayloadOf, type Phase, type Task, type Trace, type TraceEvent, type TraceLogger, type Tracer, type UnsubscribeCallback, type WithContext, type WithPayload, type WithType, anyEventType, createBus };
|
|
1
|
+
import { A as Handler, C as ContextOf, D as EventKey, E as Emittable, F as WithType, I as commandCall, L as createBus, M as Off, N as On, O as EventLike, P as WithContext, R as Tracer, S as Context, T as Emit, _ as Bus, a as EffectName, b as CommandCall, c as PayloadOf, d as Trace, f as TraceEvent, g as AnyEvent, h as Action, i as WithPayload, j as HandlerRef, k as EventPayload, l as Phase, m as UnsubscribeCallback, o as EventMap, p as TraceLogger, s as Keys, u as Task, v as BusOptions, w as ContextOption, x as Commands, y as Command, z as anyEventType } from "./action-creator-D48fp13T.js";
|
|
2
|
+
export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type CommandCall, type Commands, type Context, type ContextOf, type ContextOption, type EffectName, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type Handler, type HandlerRef, type Keys, type Off, type On, type PayloadOf, type Phase, type Task, type Trace, type TraceEvent, type TraceLogger, type Tracer, type UnsubscribeCallback, type WithContext, type WithPayload, type WithType, anyEventType, commandCall, createBus };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{i as anyEventType,t as commandCall,n as createBus}from"./bus-Cho463Sq.js";
|