nanoeffects 0.2.0 → 0.3.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 +1 -1
- package/dist/{action-rqZEnRIp.d.ts → action-Bdfp-Wvd.d.ts} +2 -2
- package/dist/actions.d.ts +18 -18
- package/dist/actions.js +1 -1
- package/dist/dev.d.ts +2 -2
- package/dist/effects.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/{module-BSCdR9D1.d.ts → module-I1ixyJzx.d.ts} +31 -18
- package/dist/notrace/{action-rqZEnRIp.d.ts → action-Bdfp-Wvd.d.ts} +2 -2
- package/dist/notrace/actions.d.ts +45 -32
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/effects.d.ts +1 -1
- package/dist/notrace/index.d.ts +1 -1
- package/dist/notrace/policies.d.ts +1 -1
- package/dist/notrace/redux.d.ts +2 -28
- 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 +2 -28
- package/dist/redux.js +1 -1
- package/dist/trace.d.ts +1 -1
- package/docs/actions.md +10 -10
- package/docs/guide.md +2 -2
- package/docs/stores.md +0 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ ESM only, no dependencies, types included.
|
|
|
65
65
|
| `nanoeffects/policies` | `latest`, `leading`, `debounce`, `throttle`, `queue` |
|
|
66
66
|
| `nanoeffects/effects` | `retry`, `timeout`, `poll`, `delay`, `take`, `all`, `allSettled`, `race` |
|
|
67
67
|
| `nanoeffects/actions` | `createAction`, `createCommand`, `createJob`, `createModule`, `modulePlugin` |
|
|
68
|
-
| `nanoeffects/redux` | `createEffectsMiddleware
|
|
68
|
+
| `nanoeffects/redux` | `createEffectsMiddleware` |
|
|
69
69
|
| `nanoeffects/observable` | `createObserve`: an event as an Observable (RxJS) |
|
|
70
70
|
| `nanoeffects/dev` | `devPlugin`: warnings about listeners that pile up and messages about modules; a no-op in production |
|
|
71
71
|
| `nanoeffects/trace` | `createTracer` and the loggers |
|
|
@@ -88,7 +88,7 @@ type Command<P = never, R = unknown> = {
|
|
|
88
88
|
readonly wrap?: (run: (payload: never, ctx: never) => unknown) => (payload: never, ctx: never) => unknown;
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
|
-
* What the bus needs to run commands. The implementation, `
|
|
91
|
+
* What the bus needs to run commands. The implementation, `commandsPlugin` in
|
|
92
92
|
* `nanoeffects/actions`, holds the handlers bound to commands and runs the
|
|
93
93
|
* right one, so a bus without `commands` carries none of that.
|
|
94
94
|
*/
|
|
@@ -183,7 +183,7 @@ interface Bus<E extends EventMap> {
|
|
|
183
183
|
use<R>(plugin: (bus: Bus<E>) => R): R;
|
|
184
184
|
/** `BusOptions.context` and `.trace`, and `commands`, for what makes tasks of its own, like `modulePlugin`. */
|
|
185
185
|
readonly context?: ContextOption<E> | undefined;
|
|
186
|
-
/** What runs the commands; `
|
|
186
|
+
/** What runs the commands; `commandsPlugin` and `modulePlugin` set it. Without one a command runs its own handler. */
|
|
187
187
|
commands?: Commands | undefined;
|
|
188
188
|
readonly trace?: Tracer | undefined;
|
|
189
189
|
}
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { N as Task,
|
|
2
|
-
import { a as modulePlugin, c as
|
|
1
|
+
import { N as Task, k as EventMap, n as FreeActionCreator, r as createAction, t as ActionCreator } from "./action-Bdfp-Wvd.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-I1ixyJzx.js";
|
|
3
3
|
//#region src/actions/job.d.ts
|
|
4
4
|
type Creator = {
|
|
5
5
|
(...args: never[]): {
|
|
@@ -9,22 +9,20 @@ type Creator = {
|
|
|
9
9
|
readonly type: string;
|
|
10
10
|
};
|
|
11
11
|
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
|
-
};
|
|
12
|
+
/** What `createJob` returns: the command of the job, with its two actions as properties. */
|
|
13
|
+
type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCommand<P, Promise<Awaited<R> | undefined>, T> & {
|
|
17
14
|
/** Made from a string with the result as its payload, or the creator that was given. */
|
|
18
|
-
|
|
15
|
+
readonly success: S extends string ? ActionCreator<Awaited<R>, S> : S;
|
|
19
16
|
/** Made from a string with the error as its payload, or the creator that was given. */
|
|
20
|
-
|
|
17
|
+
readonly fail: F extends string ? ActionCreator<unknown, F> : F;
|
|
21
18
|
};
|
|
22
19
|
/**
|
|
23
20
|
* Creates a job: a command whose outcome is sent on as actions. When the
|
|
24
|
-
* handler succeeds, `
|
|
25
|
-
* fails, `
|
|
21
|
+
* handler succeeds, `job.success(result)` is emitted on the bus, and when it
|
|
22
|
+
* fails, `job.fail(error)`. It is made for the case where nobody waits for
|
|
26
23
|
* the result and the bus carries it on, like `createAsyncThunk` or an effector
|
|
27
|
-
* effect. It returns the
|
|
24
|
+
* effect. It returns the command, with the two actions on it: `job.success` and
|
|
25
|
+
* `job.fail`.
|
|
28
26
|
*
|
|
29
27
|
* `success` and `fail` are a type string, from which the job makes an action
|
|
30
28
|
* creator with `createAction`, or an action creator that exists already, which
|
|
@@ -32,7 +30,7 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
|
32
30
|
* `<type>/success` or `<type>/fail`.
|
|
33
31
|
*
|
|
34
32
|
* `bus.emit(command, payload)` returns a promise of the result. An error of the
|
|
35
|
-
* handler always counts as handled: it goes to `
|
|
33
|
+
* handler always counts as handled: it goes to `job.fail` and the promise
|
|
36
34
|
* resolves with `undefined`; a command is the tool for a result that must reject.
|
|
37
35
|
* The actions are emitted as the task that ran the job, also for a handler bound
|
|
38
36
|
* with `commands.handle`, and are skipped once the task is aborted. A missing
|
|
@@ -41,11 +39,13 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
|
41
39
|
*
|
|
42
40
|
* @example
|
|
43
41
|
* ```ts
|
|
44
|
-
* const
|
|
45
|
-
*
|
|
42
|
+
* const loadUser = createJob("user/load", fetchUser, {
|
|
43
|
+
* success: "user/loaded",
|
|
44
|
+
* fail: "user/failed",
|
|
45
|
+
* });
|
|
46
46
|
*
|
|
47
|
-
* bus.on(
|
|
48
|
-
* bus.emit(loadUser, "1"); //
|
|
47
|
+
* bus.on(loadUser.success, (user) => render(user));
|
|
48
|
+
* bus.emit(loadUser, "1"); // loadUser.success(user) or loadUser.fail(error) comes later
|
|
49
49
|
* ```
|
|
50
50
|
*/
|
|
51
51
|
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 +55,4 @@ export declare const createJob: <P = void, R = void, T extends string = string,
|
|
|
55
55
|
fail?: F;
|
|
56
56
|
}) => Job<P, R, T, S, F>;
|
|
57
57
|
//#endregion
|
|
58
|
-
export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, createAction, createCommand,
|
|
58
|
+
export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, commandsPlugin, createAction, 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 i(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 a=class extends Error{};const o=(e,t)=>Object.assign((r,i)=>{if(!t)throw new a(`No handler for command "${e}"`);return t(r,i??n({event:{type:e,payload:r}}).ctx)},{type:e}),s=(e,t)=>[e,t],c=(e
|
|
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 i(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 a=class extends Error{};const o=(e,t)=>Object.assign((r,i)=>{if(!t)throw new a(`No handler for command "${e}"`);return t(r,i??n({event:{type:e,payload:r}}).ctx)},{type:e,match:t=>t.type===e}),s=(e,t)=>[e,t],c=(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},l=e=>typeof e==`string`?i(e):e,u=(e,t,n)=>{let r=e.emit;r(Reflect.apply(t,null,[n]))},d=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof a)throw e;return Promise.reject(e)}},f=(e,t,n={})=>{let r=o(e,t),i=l(n.success??`${e}/success`),a=l(n.fail??`${e}/fail`),s=(e,t,n)=>{let r=n;return d(()=>Reflect.apply(e,null,[t,n])).then(e=>(u(r,i,e),e),e=>{u(r,a,e)})};return Object.assign((e,t)=>t?s(r,e,t):d(()=>r(e)),{type:e,match:r.match,wrap:e=>(t,n)=>s(e,t,n),success:i,fail:a})},p=(e,t)=>({name:e,setup:t}),m=(r=[])=>i=>{let a=i.commands??c()(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}),l=n=>{let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map;return{mount:a=>{let s=r.get(a);if(s)return s;let c=i.get(a.name);c&&r.get(c)?.();let u=t(n,a.name),d=u.ctx,f=l(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=l(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{s as bindHandler,c as commandsPlugin,i as createAction,o as createCommand,f as createJob,p as createModule,m as modulePlugin};
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as UnsubscribeCallback, k as EventMap, o as Bus } from "./action-
|
|
2
|
-
import { n as ModuleApi } from "./module-
|
|
1
|
+
import { L as UnsubscribeCallback, k as EventMap, o as Bus } from "./action-Bdfp-Wvd.js";
|
|
2
|
+
import { n as ModuleApi } from "./module-I1ixyJzx.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 { A as Keys, N as Task, k as EventMap, u as Context } from "./action-
|
|
1
|
+
import { A as Keys, N as Task, k as EventMap, u as Context } from "./action-Bdfp-Wvd.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 Keys, C as WithPayload, D as anyEventType, E as Tracer, F as TraceEvent, I as TraceLogger, L as UnsubscribeCallback, M as Phase, N as Task, O as EffectName, P as Trace, S as WithContext, T as createBus, _ as EventPayload, a as AnyEvent, b as Off, c as Command, d as ContextOf, f as ContextOption, g as EventLike, h as EventKey, i as Action, j as PayloadOf, k as EventMap, l as Commands, m as Emittable, o as Bus, p as Emit, s as BusOptions, u as Context, v as Handler, w as WithType, x as On, y as HandlerRef } from "./action-
|
|
1
|
+
import { A as Keys, C as WithPayload, D as anyEventType, E as Tracer, F as TraceEvent, I as TraceLogger, L as UnsubscribeCallback, M as Phase, N as Task, O as EffectName, P as Trace, S as WithContext, T as createBus, _ as EventPayload, a as AnyEvent, b as Off, c as Command, d as ContextOf, f as ContextOption, g as EventLike, h as EventKey, i as Action, j as PayloadOf, k as EventMap, l as Commands, m as Emittable, o as Bus, p as Emit, s as BusOptions, u as Context, v as Handler, w as WithType, x as On, y as HandlerRef } from "./action-Bdfp-Wvd.js";
|
|
2
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,5 +1,17 @@
|
|
|
1
|
-
import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as EventMap, o as Bus, u as Context } from "./action-
|
|
1
|
+
import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as EventMap, o as Bus, u as Context } from "./action-Bdfp-Wvd.js";
|
|
2
2
|
//#region src/actions/command.d.ts
|
|
3
|
+
/** What `createCommand` returns: a command that also tells whether an action is of its type. */
|
|
4
|
+
type MadeCommand<P, R, T extends string> = Command<P, R> & {
|
|
5
|
+
readonly type: T;
|
|
6
|
+
/** Tells whether an action has the type of this command, and narrows its type. */
|
|
7
|
+
match(action: {
|
|
8
|
+
type: string;
|
|
9
|
+
payload?: unknown;
|
|
10
|
+
}): action is {
|
|
11
|
+
type: T;
|
|
12
|
+
payload: P;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
3
15
|
/**
|
|
4
16
|
* Creates a command: a typed request for a result. `bus.emit(command, payload)`
|
|
5
17
|
* runs its handler as a task, with a context, and returns what the handler
|
|
@@ -7,11 +19,13 @@ import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as
|
|
|
7
19
|
*
|
|
8
20
|
* The handler is optional. A command without one is only a contract, and the
|
|
9
21
|
* bus gets its implementation from the `commands` registry (see
|
|
10
|
-
* `
|
|
22
|
+
* `commandsPlugin` and `bindHandler`). A handler bound there wins over the one
|
|
11
23
|
* the command was created with, so a test can give a bus its own implementation.
|
|
12
24
|
*
|
|
13
25
|
* A handler can be wrapped in a policy, e.g. `latest(fn)`. Calling the command
|
|
14
26
|
* directly, `command(payload)`, runs its own handler outside of any bus.
|
|
27
|
+
* `command.match(action)` tells whether an action has the type of the command,
|
|
28
|
+
* as it does for an action creator.
|
|
15
29
|
*
|
|
16
30
|
* @example
|
|
17
31
|
* ```ts
|
|
@@ -20,34 +34,33 @@ import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as
|
|
|
20
34
|
* );
|
|
21
35
|
* const sendMail = createCommand<Mail, void>("mail/send"); // implemented elsewhere
|
|
22
36
|
*
|
|
23
|
-
*
|
|
37
|
+
* bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)]));
|
|
24
38
|
* const bus = createBus({ commands });
|
|
25
39
|
* const user = await bus.emit(getUser, "1");
|
|
26
40
|
*
|
|
27
41
|
* commands.handle(getUser, async () => fakeUser); // in a test
|
|
28
42
|
* ```
|
|
29
43
|
*/
|
|
30
|
-
declare const createCommand: <P = void, R = void, T extends string = string, E extends EventMap = EventMap>(type: T, handler?: Task<P, E, R>) =>
|
|
31
|
-
readonly type: T;
|
|
32
|
-
};
|
|
44
|
+
declare const createCommand: <P = void, R = void, T extends string = string, E extends EventMap = EventMap>(type: T, handler?: Task<P, E, R>) => MadeCommand<P, R, T>;
|
|
33
45
|
/** A command (or its type) with the handler to bind to it; `bindHandler` makes one. */
|
|
34
46
|
type Binding<E extends EventMap = EventMap> = readonly [Command<never, unknown> | string, Task<never, E, unknown>];
|
|
35
47
|
/**
|
|
36
|
-
* Pairs a command with the handler to bind to it, for `
|
|
48
|
+
* Pairs a command with the handler to bind to it, for `commandsPlugin`. It
|
|
37
49
|
* checks that the handler fits the command.
|
|
38
50
|
*
|
|
39
51
|
* @example
|
|
40
52
|
* ```ts
|
|
41
|
-
*
|
|
53
|
+
* bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)]));
|
|
42
54
|
* ```
|
|
43
55
|
*/
|
|
44
56
|
declare const bindHandler: <P, R, E extends EventMap>(command: Command<P, R>, fn: Task<P, E, R>) => Binding<E>;
|
|
45
57
|
/**
|
|
46
|
-
* The
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
58
|
+
* The plugin that gives a bus the handlers bound to commands:
|
|
59
|
+
* `bus.use(commandsPlugin(bindings))` makes the registry, sets it as the
|
|
60
|
+
* commands of the bus and returns it. Each bus has its own, so each test can
|
|
61
|
+
* have its own implementation. `handle` binds more later and returns the
|
|
62
|
+
* function that unbinds. A second `commandsPlugin` on the same bus replaces
|
|
63
|
+
* the registry of that bus.
|
|
51
64
|
*
|
|
52
65
|
* What the bus runs for a command: the handler bound last, else the one the
|
|
53
66
|
* command was created with, else it throws `No handler for command "..."`.
|
|
@@ -55,13 +68,13 @@ declare const bindHandler: <P, R, E extends EventMap>(command: Command<P, R>, fn
|
|
|
55
68
|
* @example
|
|
56
69
|
* ```ts
|
|
57
70
|
* const bus = createBus<Events>();
|
|
58
|
-
* const commands =
|
|
71
|
+
* const commands = bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)]));
|
|
59
72
|
*
|
|
60
73
|
* const unbind = commands.handle(getUser, async () => fakeUser);
|
|
61
74
|
* unbind(); // the command's own handler is back
|
|
62
75
|
* ```
|
|
63
76
|
*/
|
|
64
|
-
declare const
|
|
77
|
+
declare const commandsPlugin: <E extends EventMap>(bindings?: readonly Binding<E>[]) => (bus: Bus<E>) => {
|
|
65
78
|
run: (command: Command, payload: unknown, ctx: unknown) => unknown;
|
|
66
79
|
handle: {
|
|
67
80
|
<P, R>(command: Command<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
|
|
@@ -121,7 +134,7 @@ type Module<E extends EventMap = EventMap> = {
|
|
|
121
134
|
* ```
|
|
122
135
|
*/
|
|
123
136
|
declare const createModule: <E extends EventMap = EventMap>(name: string, setup: (m: ModuleContext<E>) => void) => Module<E>;
|
|
124
|
-
type Registry<E extends EventMap> = ReturnType<typeof
|
|
137
|
+
type Registry<E extends EventMap> = ReturnType<ReturnType<typeof commandsPlugin<E>>>;
|
|
125
138
|
/** What `modulePlugin` returns. */
|
|
126
139
|
type ModuleApi<E extends EventMap = EventMap> = {
|
|
127
140
|
/** The registry of command handlers of the bus: the one it was made with, or the app's own. */
|
|
@@ -137,7 +150,7 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
137
150
|
* The plugin that assembles an app from modules: `bus.use(modulePlugin(modules))`
|
|
138
151
|
* mounts `modules` at once, in the order given, and returns `mount`, `unmount`,
|
|
139
152
|
* `isMounted`, `dispose` and `commands` for the modules that come and go later. The bus runs the commands that the
|
|
140
|
-
* modules bind: if `
|
|
153
|
+
* modules bind: if `bus.use(commandsPlugin())` was called before, that registry is
|
|
141
154
|
* used, otherwise the app attaches its own, as `app.commands`.
|
|
142
155
|
*
|
|
143
156
|
* A module is known by its `name`: mounting a module whose name is taken
|
|
@@ -164,4 +177,4 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
164
177
|
*/
|
|
165
178
|
declare const modulePlugin: <E extends EventMap>(modules?: readonly Module<E>[]) => (bus: Bus<E>) => ModuleApi<E>;
|
|
166
179
|
//#endregion
|
|
167
|
-
export { modulePlugin as a,
|
|
180
|
+
export { modulePlugin as a, bindHandler as c, createModule as i, commandsPlugin as l, ModuleApi as n, Binding as o, ModuleContext as r, MadeCommand as s, Module as t, createCommand as u };
|
|
@@ -88,7 +88,7 @@ type Command<P = never, R = unknown> = {
|
|
|
88
88
|
readonly wrap?: (run: (payload: never, ctx: never) => unknown) => (payload: never, ctx: never) => unknown;
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
|
-
* What the bus needs to run commands. The implementation, `
|
|
91
|
+
* What the bus needs to run commands. The implementation, `commandsPlugin` in
|
|
92
92
|
* `nanoeffects/actions`, holds the handlers bound to commands and runs the
|
|
93
93
|
* right one, so a bus without `commands` carries none of that.
|
|
94
94
|
*/
|
|
@@ -183,7 +183,7 @@ interface Bus<E extends EventMap> {
|
|
|
183
183
|
use<R>(plugin: (bus: Bus<E>) => R): R;
|
|
184
184
|
/** `BusOptions.context` and `.trace`, and `commands`, for what makes tasks of its own, like `modulePlugin`. */
|
|
185
185
|
readonly context?: ContextOption<E> | undefined;
|
|
186
|
-
/** What runs the commands; `
|
|
186
|
+
/** What runs the commands; `commandsPlugin` and `modulePlugin` set it. Without one a command runs its own handler. */
|
|
187
187
|
commands?: Commands | undefined;
|
|
188
188
|
readonly trace?: Tracer | undefined;
|
|
189
189
|
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as EventMap, n as FreeActionCreator, o as Bus, r as createAction, t as ActionCreator, u as Context } from "./action-
|
|
1
|
+
import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as EventMap, n as FreeActionCreator, o as Bus, r as createAction, t as ActionCreator, u as Context } from "./action-Bdfp-Wvd.js";
|
|
2
2
|
//#region src/actions/command.d.ts
|
|
3
|
+
/** What `createCommand` returns: a command that also tells whether an action is of its type. */
|
|
4
|
+
type MadeCommand<P, R, T extends string> = Command<P, R> & {
|
|
5
|
+
readonly type: T;
|
|
6
|
+
/** Tells whether an action has the type of this command, and narrows its type. */
|
|
7
|
+
match(action: {
|
|
8
|
+
type: string;
|
|
9
|
+
payload?: unknown;
|
|
10
|
+
}): action is {
|
|
11
|
+
type: T;
|
|
12
|
+
payload: P;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
3
15
|
/**
|
|
4
16
|
* Creates a command: a typed request for a result. `bus.emit(command, payload)`
|
|
5
17
|
* runs its handler as a task, with a context, and returns what the handler
|
|
@@ -7,11 +19,13 @@ import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as
|
|
|
7
19
|
*
|
|
8
20
|
* The handler is optional. A command without one is only a contract, and the
|
|
9
21
|
* bus gets its implementation from the `commands` registry (see
|
|
10
|
-
* `
|
|
22
|
+
* `commandsPlugin` and `bindHandler`). A handler bound there wins over the one
|
|
11
23
|
* the command was created with, so a test can give a bus its own implementation.
|
|
12
24
|
*
|
|
13
25
|
* A handler can be wrapped in a policy, e.g. `latest(fn)`. Calling the command
|
|
14
26
|
* directly, `command(payload)`, runs its own handler outside of any bus.
|
|
27
|
+
* `command.match(action)` tells whether an action has the type of the command,
|
|
28
|
+
* as it does for an action creator.
|
|
15
29
|
*
|
|
16
30
|
* @example
|
|
17
31
|
* ```ts
|
|
@@ -20,34 +34,33 @@ import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as
|
|
|
20
34
|
* );
|
|
21
35
|
* const sendMail = createCommand<Mail, void>("mail/send"); // implemented elsewhere
|
|
22
36
|
*
|
|
23
|
-
*
|
|
37
|
+
* bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)]));
|
|
24
38
|
* const bus = createBus({ commands });
|
|
25
39
|
* const user = await bus.emit(getUser, "1");
|
|
26
40
|
*
|
|
27
41
|
* commands.handle(getUser, async () => fakeUser); // in a test
|
|
28
42
|
* ```
|
|
29
43
|
*/
|
|
30
|
-
export declare const createCommand: <P = void, R = void, T extends string = string, E extends EventMap = EventMap>(type: T, handler?: Task<P, E, R>) =>
|
|
31
|
-
readonly type: T;
|
|
32
|
-
};
|
|
44
|
+
export declare const createCommand: <P = void, R = void, T extends string = string, E extends EventMap = EventMap>(type: T, handler?: Task<P, E, R>) => MadeCommand<P, R, T>;
|
|
33
45
|
/** A command (or its type) with the handler to bind to it; `bindHandler` makes one. */
|
|
34
46
|
type Binding<E extends EventMap = EventMap> = readonly [Command<never, unknown> | string, Task<never, E, unknown>];
|
|
35
47
|
/**
|
|
36
|
-
* Pairs a command with the handler to bind to it, for `
|
|
48
|
+
* Pairs a command with the handler to bind to it, for `commandsPlugin`. It
|
|
37
49
|
* checks that the handler fits the command.
|
|
38
50
|
*
|
|
39
51
|
* @example
|
|
40
52
|
* ```ts
|
|
41
|
-
*
|
|
53
|
+
* bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)]));
|
|
42
54
|
* ```
|
|
43
55
|
*/
|
|
44
56
|
export declare const bindHandler: <P, R, E extends EventMap>(command: Command<P, R>, fn: Task<P, E, R>) => Binding<E>;
|
|
45
57
|
/**
|
|
46
|
-
* The
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
58
|
+
* The plugin that gives a bus the handlers bound to commands:
|
|
59
|
+
* `bus.use(commandsPlugin(bindings))` makes the registry, sets it as the
|
|
60
|
+
* commands of the bus and returns it. Each bus has its own, so each test can
|
|
61
|
+
* have its own implementation. `handle` binds more later and returns the
|
|
62
|
+
* function that unbinds. A second `commandsPlugin` on the same bus replaces
|
|
63
|
+
* the registry of that bus.
|
|
51
64
|
*
|
|
52
65
|
* What the bus runs for a command: the handler bound last, else the one the
|
|
53
66
|
* command was created with, else it throws `No handler for command "..."`.
|
|
@@ -55,13 +68,13 @@ export declare const bindHandler: <P, R, E extends EventMap>(command: Command<P,
|
|
|
55
68
|
* @example
|
|
56
69
|
* ```ts
|
|
57
70
|
* const bus = createBus<Events>();
|
|
58
|
-
* const commands =
|
|
71
|
+
* const commands = bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)]));
|
|
59
72
|
*
|
|
60
73
|
* const unbind = commands.handle(getUser, async () => fakeUser);
|
|
61
74
|
* unbind(); // the command's own handler is back
|
|
62
75
|
* ```
|
|
63
76
|
*/
|
|
64
|
-
export declare const
|
|
77
|
+
export declare const commandsPlugin: <E extends EventMap>(bindings?: readonly Binding<E>[]) => (bus: Bus<E>) => {
|
|
65
78
|
run: (command: Command, payload: unknown, ctx: unknown) => unknown;
|
|
66
79
|
handle: {
|
|
67
80
|
<P, R>(command: Command<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
|
|
@@ -79,22 +92,20 @@ type Creator = {
|
|
|
79
92
|
readonly type: string;
|
|
80
93
|
};
|
|
81
94
|
type Outcome = string | Creator;
|
|
82
|
-
/** What `createJob` returns: the command of the job
|
|
83
|
-
type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
84
|
-
command: Command<P, Promise<Awaited<R> | undefined>> & {
|
|
85
|
-
readonly type: T;
|
|
86
|
-
};
|
|
95
|
+
/** What `createJob` returns: the command of the job, with its two actions as properties. */
|
|
96
|
+
type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCommand<P, Promise<Awaited<R> | undefined>, T> & {
|
|
87
97
|
/** Made from a string with the result as its payload, or the creator that was given. */
|
|
88
|
-
|
|
98
|
+
readonly success: S extends string ? ActionCreator<Awaited<R>, S> : S;
|
|
89
99
|
/** Made from a string with the error as its payload, or the creator that was given. */
|
|
90
|
-
|
|
100
|
+
readonly fail: F extends string ? ActionCreator<unknown, F> : F;
|
|
91
101
|
};
|
|
92
102
|
/**
|
|
93
103
|
* Creates a job: a command whose outcome is sent on as actions. When the
|
|
94
|
-
* handler succeeds, `
|
|
95
|
-
* fails, `
|
|
104
|
+
* handler succeeds, `job.success(result)` is emitted on the bus, and when it
|
|
105
|
+
* fails, `job.fail(error)`. It is made for the case where nobody waits for
|
|
96
106
|
* the result and the bus carries it on, like `createAsyncThunk` or an effector
|
|
97
|
-
* effect. It returns the
|
|
107
|
+
* effect. It returns the command, with the two actions on it: `job.success` and
|
|
108
|
+
* `job.fail`.
|
|
98
109
|
*
|
|
99
110
|
* `success` and `fail` are a type string, from which the job makes an action
|
|
100
111
|
* creator with `createAction`, or an action creator that exists already, which
|
|
@@ -102,7 +113,7 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
|
102
113
|
* `<type>/success` or `<type>/fail`.
|
|
103
114
|
*
|
|
104
115
|
* `bus.emit(command, payload)` returns a promise of the result. An error of the
|
|
105
|
-
* handler always counts as handled: it goes to `
|
|
116
|
+
* handler always counts as handled: it goes to `job.fail` and the promise
|
|
106
117
|
* resolves with `undefined`; a command is the tool for a result that must reject.
|
|
107
118
|
* The actions are emitted as the task that ran the job, also for a handler bound
|
|
108
119
|
* with `commands.handle`, and are skipped once the task is aborted. A missing
|
|
@@ -111,11 +122,13 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
|
111
122
|
*
|
|
112
123
|
* @example
|
|
113
124
|
* ```ts
|
|
114
|
-
* const
|
|
115
|
-
*
|
|
125
|
+
* const loadUser = createJob("user/load", fetchUser, {
|
|
126
|
+
* success: "user/loaded",
|
|
127
|
+
* fail: "user/failed",
|
|
128
|
+
* });
|
|
116
129
|
*
|
|
117
|
-
* bus.on(
|
|
118
|
-
* bus.emit(loadUser, "1"); //
|
|
130
|
+
* bus.on(loadUser.success, (user) => render(user));
|
|
131
|
+
* bus.emit(loadUser, "1"); // loadUser.success(user) or loadUser.fail(error) comes later
|
|
119
132
|
* ```
|
|
120
133
|
*/
|
|
121
134
|
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?: {
|
|
@@ -176,7 +189,7 @@ type Module<E extends EventMap = EventMap> = {
|
|
|
176
189
|
* ```
|
|
177
190
|
*/
|
|
178
191
|
export declare const createModule: <E extends EventMap = EventMap>(name: string, setup: (m: ModuleContext<E>) => void) => Module<E>;
|
|
179
|
-
type Registry<E extends EventMap> = ReturnType<typeof
|
|
192
|
+
type Registry<E extends EventMap> = ReturnType<ReturnType<typeof commandsPlugin<E>>>;
|
|
180
193
|
/** What `modulePlugin` returns. */
|
|
181
194
|
type ModuleApi<E extends EventMap = EventMap> = {
|
|
182
195
|
/** The registry of command handlers of the bus: the one it was made with, or the app's own. */
|
|
@@ -192,7 +205,7 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
192
205
|
* The plugin that assembles an app from modules: `bus.use(modulePlugin(modules))`
|
|
193
206
|
* mounts `modules` at once, in the order given, and returns `mount`, `unmount`,
|
|
194
207
|
* `isMounted`, `dispose` and `commands` for the modules that come and go later. The bus runs the commands that the
|
|
195
|
-
* modules bind: if `
|
|
208
|
+
* modules bind: if `bus.use(commandsPlugin())` was called before, that registry is
|
|
196
209
|
* used, otherwise the app attaches its own, as `app.commands`.
|
|
197
210
|
*
|
|
198
211
|
* A module is known by its `name`: mounting a module whose name is taken
|
package/dist/notrace/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e,n as t,s as n}from"./context-yAWj0BFZ.js";import{t as r}from"./event-0RIBGYqS.js";function i(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 a=class extends Error{};const o=(e,n)=>Object.assign((r,i)=>{if(!n)throw new a(`No handler for command "${e}"`);return n(r,i??t({event:{type:e,payload:r}}).ctx)},{type:e}),s=(e,t)=>[e,t],c=(e
|
|
1
|
+
import{i as e,n as t,s as n}from"./context-yAWj0BFZ.js";import{t as r}from"./event-0RIBGYqS.js";function i(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 a=class extends Error{};const o=(e,n)=>Object.assign((r,i)=>{if(!n)throw new a(`No handler for command "${e}"`);return n(r,i??t({event:{type:e,payload:r}}).ctx)},{type:e,match:t=>t.type===e}),s=(e,t)=>[e,t],c=(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},l=e=>typeof e==`string`?i(e):e,u=(e,t,n)=>{let r=e.emit;r(Reflect.apply(t,null,[n]))},d=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof a)throw e;return Promise.reject(e)}},f=(e,t,n={})=>{let r=o(e,t),i=l(n.success??`${e}/success`),a=l(n.fail??`${e}/fail`),s=(e,t,n)=>{let r=n;return d(()=>Reflect.apply(e,null,[t,n])).then(e=>(u(r,i,e),e),e=>{u(r,a,e)})};return Object.assign((e,t)=>t?s(r,e,t):d(()=>r(e)),{type:e,match:r.match,wrap:e=>(t,n)=>s(e,t,n),success:i,fail:a})},p=(e,t)=>({name:e,setup:t}),m=(r=[])=>i=>{let a=i.commands??c()(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=t({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),l=t=>{let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map;return{mount:a=>{let s=r.get(a);if(s)return s;let c=i.get(a.name);c&&r.get(c)?.();let u=e(t,a.name),d=u.ctx,f=l(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=(e,t)=>{let r=Reflect.apply(o.handle,null,[e,t]);return n(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=l(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{s as bindHandler,c as commandsPlugin,i as createAction,o as createCommand,f as createJob,p as createModule,m as modulePlugin};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as Keys, N as Task, k as EventMap, u as Context } from "./action-
|
|
1
|
+
import { A as Keys, N as Task, k as EventMap, u as Context } from "./action-Bdfp-Wvd.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/notrace/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as Keys, C as WithPayload, D as anyEventType, E as Tracer, F as TraceEvent, I as TraceLogger, L as UnsubscribeCallback, M as Phase, N as Task, O as EffectName, P as Trace, S as WithContext, T as createBus, _ as EventPayload, a as AnyEvent, b as Off, c as Command, d as ContextOf, f as ContextOption, g as EventLike, h as EventKey, i as Action, j as PayloadOf, k as EventMap, l as Commands, m as Emittable, o as Bus, p as Emit, s as BusOptions, u as Context, v as Handler, w as WithType, x as On, y as HandlerRef } from "./action-
|
|
1
|
+
import { A as Keys, C as WithPayload, D as anyEventType, E as Tracer, F as TraceEvent, I as TraceLogger, L as UnsubscribeCallback, M as Phase, N as Task, O as EffectName, P as Trace, S as WithContext, T as createBus, _ as EventPayload, a as AnyEvent, b as Off, c as Command, d as ContextOf, f as ContextOption, g as EventLike, h as EventKey, i as Action, j as PayloadOf, k as EventMap, l as Commands, m as Emittable, o as Bus, p as Emit, s as BusOptions, u as Context, v as Handler, w as WithType, x as On, y as HandlerRef } from "./action-Bdfp-Wvd.js";
|
|
2
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 };
|
package/dist/notrace/redux.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as EventMap, o as Bus, s as BusOptions } from "./action-
|
|
1
|
+
import { k as EventMap, o as Bus, s as BusOptions } from "./action-Bdfp-Wvd.js";
|
|
2
2
|
//#region src/store/redux/middleware.d.ts
|
|
3
3
|
/** What Redux passes through a middleware: an action is a plain object with a `type`. */
|
|
4
4
|
type ReduxAction = {
|
|
@@ -9,32 +9,6 @@ type NanoeffectsMiddlewareOptions = {
|
|
|
9
9
|
/** Return `false` for the actions that the bus should not get. Default: all of them. */
|
|
10
10
|
filter?: (action: ReduxAction) => boolean;
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
13
|
-
* A Redux middleware that sends every action to the bus, after the reducers
|
|
14
|
-
* have handled it, so a handler that reads the store sees the new state. It
|
|
15
|
-
* does not depend on Redux: it only has the shape of a middleware. Values that
|
|
16
|
-
* are not plain actions (the functions of redux-thunk) go through untouched.
|
|
17
|
-
*
|
|
18
|
-
* The other way needs no adapter: give the store to the tasks with
|
|
19
|
-
* `createBus({ context: { store } })` and dispatch from a handler.
|
|
20
|
-
*
|
|
21
|
-
* Take care of loops: a handler must not dispatch the action it handles.
|
|
22
|
-
* Errors of handlers reach the caller of `dispatch`, as for `bus.emit`; set
|
|
23
|
-
* `onError` on the bus to isolate them.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* const store = configureStore({
|
|
28
|
-
* reducer,
|
|
29
|
-
* middleware: (getDefault) => getDefault().concat(nanoeffectsMiddleware(bus)),
|
|
30
|
-
* });
|
|
31
|
-
*
|
|
32
|
-
* bus.on(userRequested, latest(async (id, { store, signal }) => {
|
|
33
|
-
* store.dispatch(userLoaded(await fetchUser(id, { signal })));
|
|
34
|
-
* }));
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export declare const nanoeffectsMiddleware: <E extends EventMap>(bus: Bus<E>, options?: NanoeffectsMiddlewareOptions) => () => (next: (action: unknown) => unknown) => (action: unknown) => unknown;
|
|
38
12
|
//#endregion
|
|
39
13
|
//#region src/store/redux/effects.d.ts
|
|
40
14
|
/** What Redux gives a middleware: the parts of the store that it may use. */
|
|
@@ -89,4 +63,4 @@ export declare const createEffectsMiddleware: <E extends EventMap = EventMap>(op
|
|
|
89
63
|
middleware: (storeApi: ReduxStoreApi) => (next: (action: unknown) => unknown) => (action: unknown) => unknown;
|
|
90
64
|
};
|
|
91
65
|
//#endregion
|
|
92
|
-
export type { EffectsMiddlewareOptions,
|
|
66
|
+
export type { EffectsMiddlewareOptions, ReduxStoreApi };
|
package/dist/notrace/redux.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e}from"./context-yAWj0BFZ.js";import{t}from"./bus-Dx0l9X6h.js";const n=e=>typeof e==`object`&&!!e&&`type`in e,r=(e,t={})=>{let r=e.emit;return()=>e=>i=>{let a=e(i);return n(i)&&(t.filter?.(i)??!0)&&r(i),a}},i=(n={})=>{let{filter:i,context:a,onError:o,...s}=n,c,l=/* @__PURE__ */ new WeakMap,u=e=>({...a,store:c&&{getState:c.getState,dispatch:t=>{typeof t==`object`&&t&&l.set(t,e.type);let n=t;return c?.dispatch(n)}}}),d=t({...s,onError:o??((t,{type:n})=>{e(`handler of "${n}" failed`,t)}),context:u}),f=r(d,{filter:e=>l.get(e)!==e.type&&(i?.(e)??!0)});return{bus:d,middleware:e=>(c=e,f())}};export{i as createEffectsMiddleware
|
|
1
|
+
import{c as e}from"./context-yAWj0BFZ.js";import{t}from"./bus-Dx0l9X6h.js";const n=e=>typeof e==`object`&&!!e&&`type`in e,r=(e,t={})=>{let r=e.emit;return()=>e=>i=>{let a=e(i);return n(i)&&(t.filter?.(i)??!0)&&r(i),a}},i=(n={})=>{let{filter:i,context:a,onError:o,...s}=n,c,l=/* @__PURE__ */ new WeakMap,u=e=>({...a,store:c&&{getState:c.getState,dispatch:t=>{typeof t==`object`&&t&&l.set(t,e.type);let n=t;return c?.dispatch(n)}}}),d=t({...s,onError:o??((t,{type:n})=>{e(`handler of "${n}" failed`,t)}),context:u}),f=r(d,{filter:e=>l.get(e)!==e.type&&(i?.(e)??!0)});return{bus:d,middleware:e=>(c=e,f())}};export{i as createEffectsMiddleware};
|
package/dist/observable.d.ts
CHANGED
package/dist/policies.d.ts
CHANGED
package/dist/redux.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as EventMap, o as Bus, s as BusOptions } from "./action-
|
|
1
|
+
import { k as EventMap, o as Bus, s as BusOptions } from "./action-Bdfp-Wvd.js";
|
|
2
2
|
//#region src/store/redux/middleware.d.ts
|
|
3
3
|
/** What Redux passes through a middleware: an action is a plain object with a `type`. */
|
|
4
4
|
type ReduxAction = {
|
|
@@ -9,32 +9,6 @@ type NanoeffectsMiddlewareOptions = {
|
|
|
9
9
|
/** Return `false` for the actions that the bus should not get. Default: all of them. */
|
|
10
10
|
filter?: (action: ReduxAction) => boolean;
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
13
|
-
* A Redux middleware that sends every action to the bus, after the reducers
|
|
14
|
-
* have handled it, so a handler that reads the store sees the new state. It
|
|
15
|
-
* does not depend on Redux: it only has the shape of a middleware. Values that
|
|
16
|
-
* are not plain actions (the functions of redux-thunk) go through untouched.
|
|
17
|
-
*
|
|
18
|
-
* The other way needs no adapter: give the store to the tasks with
|
|
19
|
-
* `createBus({ context: { store } })` and dispatch from a handler.
|
|
20
|
-
*
|
|
21
|
-
* Take care of loops: a handler must not dispatch the action it handles.
|
|
22
|
-
* Errors of handlers reach the caller of `dispatch`, as for `bus.emit`; set
|
|
23
|
-
* `onError` on the bus to isolate them.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* const store = configureStore({
|
|
28
|
-
* reducer,
|
|
29
|
-
* middleware: (getDefault) => getDefault().concat(nanoeffectsMiddleware(bus)),
|
|
30
|
-
* });
|
|
31
|
-
*
|
|
32
|
-
* bus.on(userRequested, latest(async (id, { store, signal }) => {
|
|
33
|
-
* store.dispatch(userLoaded(await fetchUser(id, { signal })));
|
|
34
|
-
* }));
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export declare const nanoeffectsMiddleware: <E extends EventMap>(bus: Bus<E>, options?: NanoeffectsMiddlewareOptions) => () => (next: (action: unknown) => unknown) => (action: unknown) => unknown;
|
|
38
12
|
//#endregion
|
|
39
13
|
//#region src/store/redux/effects.d.ts
|
|
40
14
|
/** What Redux gives a middleware: the parts of the store that it may use. */
|
|
@@ -89,4 +63,4 @@ export declare const createEffectsMiddleware: <E extends EventMap = EventMap>(op
|
|
|
89
63
|
middleware: (storeApi: ReduxStoreApi) => (next: (action: unknown) => unknown) => (action: unknown) => unknown;
|
|
90
64
|
};
|
|
91
65
|
//#endregion
|
|
92
|
-
export type { EffectsMiddlewareOptions,
|
|
66
|
+
export type { EffectsMiddlewareOptions, ReduxStoreApi };
|
package/dist/redux.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./onAbort-BgMfspK4.js";import{t}from"./bus-CRnlwmQ4.js";const n=e=>typeof e==`object`&&!!e&&`type`in e,r=(e,t={})=>{let r=e.emit;return()=>e=>i=>{let a=e(i);return n(i)&&(t.filter?.(i)??!0)&&r(i),a}},i=(n={})=>{let{filter:i,context:a,onError:o,...s}=n,c,l=/* @__PURE__ */ new WeakMap,u=e=>({...a,store:c&&{getState:c.getState,dispatch:t=>{typeof t==`object`&&t&&l.set(t,e.type);let n=t;return c?.dispatch(n)}}}),d=t({...s,onError:o??((t,{type:n})=>{e(`handler of "${n}" failed`,t)}),context:u}),f=r(d,{filter:e=>l.get(e)!==e.type&&(i?.(e)??!0)});return{bus:d,middleware:e=>(c=e,f())}};export{i as createEffectsMiddleware
|
|
1
|
+
import{n as e}from"./onAbort-BgMfspK4.js";import{t}from"./bus-CRnlwmQ4.js";const n=e=>typeof e==`object`&&!!e&&`type`in e,r=(e,t={})=>{let r=e.emit;return()=>e=>i=>{let a=e(i);return n(i)&&(t.filter?.(i)??!0)&&r(i),a}},i=(n={})=>{let{filter:i,context:a,onError:o,...s}=n,c,l=/* @__PURE__ */ new WeakMap,u=e=>({...a,store:c&&{getState:c.getState,dispatch:t=>{typeof t==`object`&&t&&l.set(t,e.type);let n=t;return c?.dispatch(n)}}}),d=t({...s,onError:o??((t,{type:n})=>{e(`handler of "${n}" failed`,t)}),context:u}),f=r(d,{filter:e=>l.get(e)!==e.type&&(i?.(e)??!0)});return{bus:d,middleware:e=>(c=e,f())}};export{i as createEffectsMiddleware};
|
package/dist/trace.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as Tracer, F as TraceEvent, I as TraceLogger, P as Trace } from "./action-
|
|
1
|
+
import { E as Tracer, F as TraceEvent, I as TraceLogger, P as Trace } from "./action-Bdfp-Wvd.js";
|
|
2
2
|
//#region src/trace/console.d.ts
|
|
3
3
|
type ConsoleLoggerOptions = {
|
|
4
4
|
/** Defaults to `globalThis.console`. */
|
package/docs/actions.md
CHANGED
|
@@ -18,8 +18,8 @@ const getUser = createCommand("user/get", (id: string, { signal }) => fetchUser(
|
|
|
18
18
|
// a contract without a handler: the code that sends mail is elsewhere
|
|
19
19
|
const sendMail = createCommand<Mail, void>("mail/send");
|
|
20
20
|
|
|
21
|
-
// a command whose outcome is sent on as actions:
|
|
22
|
-
const
|
|
21
|
+
// a command whose outcome is sent on as actions: they are its properties, loadUser.success and loadUser.fail
|
|
22
|
+
const loadUser = createJob("user/load", fetchUser, {
|
|
23
23
|
success: userLoaded, // an action creator that exists: used and returned as it is
|
|
24
24
|
fail: "user/failed", // a string: the job makes the action creator
|
|
25
25
|
});
|
|
@@ -27,26 +27,26 @@ const { command: loadUser, successAction, failAction } = createJob("user/load",
|
|
|
27
27
|
|
|
28
28
|
**Actions.** `createAction(type)` returns a creator, with no generics: it keeps the literal type, and the bus checks the payload against its event map (`bus.emit(userLoaded(5))` is an error when `"user/loaded"` is a `User`). Without an event map give the payload type, `createAction<User>("user/loaded")`. The creator: `userLoaded(user)` is `{ type, payload }`, `userLoaded.type` is the type, `userLoaded.match(action)` narrows an action. `createAction(type, prepare)` builds the payload from several arguments. A creator works as the event of its type: `bus.emit(userLoaded(user))`, `bus.on(userLoaded, fn)`.
|
|
29
29
|
|
|
30
|
-
**Commands.** `bus.emit(command, payload)` tells the listeners of the command's type, then runs its handler as a task, with a context, and returns the result. The handler can be wrapped in a policy, `createCommand("search", latest(fn))`. Calling `command(payload)` runs its own handler without a bus.
|
|
30
|
+
**Commands.** `bus.emit(command, payload)` tells the listeners of the command's type, then runs its handler as a task, with a context, and returns the result. The handler can be wrapped in a policy, `createCommand("search", latest(fn))`. Calling `command(payload)` runs its own handler without a bus. `command.match(action)` tells whether an action has the command's type and narrows it, as `match` of an action creator does.
|
|
31
31
|
|
|
32
|
-
**Where handlers are registered.** The command may carry a handler. The bus gets the handlers bound to commands from a registry, `
|
|
32
|
+
**Where handlers are registered.** The command may carry a handler. The bus gets the handlers bound to commands from a registry, `commandsPlugin`, and a handler bound there wins over the command's own. The registry belongs to one bus, as an effector scope does with `fork({ handlers })`, so each bus (each test) can have its own.
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
|
-
import {
|
|
35
|
+
import { commandsPlugin, bindHandler } from "nanoeffects/actions";
|
|
36
36
|
|
|
37
37
|
const bus = createBus<Events>();
|
|
38
|
-
const commands =
|
|
38
|
+
const commands = bus.use(commandsPlugin([bindHandler(sendMail, smtpSend)])); // attached to the bus
|
|
39
39
|
|
|
40
40
|
const unbind = commands.handle(getUser, async () => fakeUser); // later, e.g. in a test
|
|
41
41
|
unbind(); // the command's own handler is back; the latest binding wins
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
A command with no handler anywhere throws `No handler for command "..."` from `emit`. The registry lives in `nanoeffects/actions`, so a bus without one carries none of this code; without it `emit(command, payload)` simply runs the command's own handler. `
|
|
44
|
+
A command with no handler anywhere throws `No handler for command "..."` from `emit`. The registry lives in `nanoeffects/actions`, so a bus without one carries none of this code; without it `emit(command, payload)` simply runs the command's own handler. `bus.use(commandsPlugin())` sets the registry as `bus.commands`, and a second one on the same bus replaces it. There is no `commands` option in `createBus`; a custom runner is assigned, `bus.commands = { run }`.
|
|
45
45
|
|
|
46
|
-
**Jobs.** `createJob(type, handler, { success, fail })` makes a command that sends its outcome on as actions, for when nobody waits for the result, and returns
|
|
46
|
+
**Jobs.** `createJob(type, handler, { success, fail })` makes a command that sends its outcome on as actions, for when nobody waits for the result, and returns the command itself, with the two actions on it: `loadUser.success` and `loadUser.fail`. When the handler succeeds, `loadUser.success(result)` is emitted on the bus, and when it fails, `loadUser.fail(error)`.
|
|
47
47
|
|
|
48
48
|
- `success` and `fail` are a string, from which the job makes an action creator with `createAction` (the payload is the result, or the error), or an action creator that exists already, which is used and returned as it is. A missing one is made with the type `<type>/success` or `<type>/fail`.
|
|
49
|
-
- `bus.emit(command, payload)` returns a promise of the result. An error of the handler always counts as handled: it goes to `
|
|
49
|
+
- `bus.emit(command, payload)` returns a promise of the result. An error of the handler always counts as handled: it goes to `loadUser.fail` and the promise resolves with `undefined`. For a result that must reject, use a command.
|
|
50
50
|
- The actions are emitted as the task that ran the job, also for a handler bound with `commands.handle`, and are not sent once the task is aborted. A missing handler is not an outcome: `emit` throws `No handler for command "..."`.
|
|
51
51
|
- Calling `command(payload)` directly runs only the handler.
|
|
52
52
|
|
|
@@ -72,7 +72,7 @@ app.dispose(); // unmounts every module
|
|
|
72
72
|
|
|
73
73
|
- **Unmounting undoes everything** the module did: its subscriptions, its bound commands, its sub-modules and its background tasks, which are aborted.
|
|
74
74
|
- **A module is known by its name.** Mounting a module whose name is taken replaces the mounted one. That is what hot reload needs: see [hmr.md](hmr.md).
|
|
75
|
-
- **The registry.** If `
|
|
75
|
+
- **The registry.** If `bus.use(commandsPlugin())` was called, that registry is used; otherwise the module plugin attaches its own. Either way it is `bus.commands` and `app.commands`, so the commands that modules bind are the ones the bus runs.
|
|
76
76
|
- A disposed app cannot mount modules.
|
|
77
77
|
- A module context is a task context plus `handle` (give a command a handler: for a contract, or to override), `use` (bring sub-modules and commands that carry a handler: `m.use(getUser, saveUser, subModule)`, in any order; the returned function undoes just that call) and `spawn` (a background task, aborted on unmount).
|
|
78
78
|
- A command that carries a handler keeps answering after its module is unmounted: the handler is its own, and `use` only brings the command and tracks it in the registry. Use `handle` for a handler that should live only as long as the module.
|
package/docs/guide.md
CHANGED
|
@@ -85,12 +85,12 @@ Strings are for the plain case, objects for actions that other code already make
|
|
|
85
85
|
A command made with a function carries its handler, as above. More often it is only a contract, a name with the types of the request and the result, and the code that does the work is registered separately: in another file, or a fake one in a test.
|
|
86
86
|
|
|
87
87
|
```ts
|
|
88
|
-
import { createCommand,
|
|
88
|
+
import { createCommand, commandsPlugin } from "nanoeffects/actions";
|
|
89
89
|
|
|
90
90
|
const getUser = createCommand<string, Promise<User>>("user/get"); // no handler yet
|
|
91
91
|
|
|
92
92
|
const bus = createBus<Events>();
|
|
93
|
-
const commands =
|
|
93
|
+
const commands = bus.use(commandsPlugin()); // the handlers of this bus
|
|
94
94
|
|
|
95
95
|
bus.emit(getUser, "1"); // throws: No handler for command "user/get"
|
|
96
96
|
|
package/docs/stores.md
CHANGED
|
@@ -27,8 +27,6 @@ It looks after what is easy to get wrong:
|
|
|
27
27
|
|
|
28
28
|
Options are those of `createBus` (except `context`, which adds fields next to `store`) and `filter`, which keeps the actions it rejects away from the bus. Values that are not plain actions (thunks) go through untouched. Nothing here depends on Redux.
|
|
29
29
|
|
|
30
|
-
To use your own bus, `nanoeffectsMiddleware(bus, { filter })` is the middleware alone: it sends actions to the bus after the reducers and nothing more. Give the store to the tasks yourself with `createBus({ context: { store } })`, or lazily, `context: { get store() { return store; } }` (typed with `WithContext`), since the store needs the middleware and the middleware needs the bus.
|
|
31
|
-
|
|
32
30
|
## Zustand, MobX and other stores
|
|
33
31
|
|
|
34
32
|
No adapter is needed: a store tells about its changes, and `bus.emit` is a call.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nanoeffects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Typed event bus where every handler is a task with an AbortSignal, plus policies (latest, debounce, throttle, queue), effects (retry, timeout, poll) and tracing. About 1.2 kB gzip.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"event-bus",
|