nanoeffects 0.33.0 → 0.34.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 +9 -1
- package/dist/actions.d.ts +2 -2
- package/dist/actions.js +1 -1
- package/dist/dev.d.ts +1 -1
- package/dist/{module-DPKjzfRk.d.ts → module-Fk4OtkZk.d.ts} +10 -27
- package/dist/notrace/actions.d.ts +10 -27
- package/dist/notrace/actions.js +1 -1
- package/docs/actions.md +13 -7
- package/docs/guide.md +6 -6
- package/docs/ru/actions.md +13 -7
- package/docs/ru/guide.md +6 -6
- package/llms-full.txt +19 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,15 @@ Move business logic and side effects out of React, Vue and Svelte components int
|
|
|
13
13
|
npm install nanoeffects
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add nanoeffects
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
yarn add nanoeffects
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The package is ESM only (use `import`, not `require`), has no dependencies and includes its types. It runs on Node 18 or newer and in any bundler.
|
|
17
25
|
|
|
18
26
|
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.
|
|
19
27
|
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { G as PayloadOf, H as EventMap, T as EventKey, 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-DprJV-pN.js";
|
|
2
2
|
import { r as Scoped, t as FieldsOfScope } from "./types-DxPZDzZG.js";
|
|
3
|
-
import { a as createModule, c as
|
|
3
|
+
import { a as createModule, c as MadeHandled, l as ScopedHandled, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as CarryingCalls, t as Module, u as actionsPlugin } from "./module-Fk4OtkZk.js";
|
|
4
4
|
import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
|
|
5
5
|
//#region src/actions/action.d.ts
|
|
6
6
|
type Reserved = "handle" | "prepare";
|
|
@@ -194,4 +194,4 @@ export declare const createJob: <P = void, R = void, T extends string = string,
|
|
|
194
194
|
/** Context values and `$effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
|
|
195
195
|
scope?: X) => CarryingCalls<Job<P, R, T, S, F, PE, CE>, P, Promise<Awaited<R> | undefined>, FieldsOfScope<E, X>>;
|
|
196
196
|
//#endregion
|
|
197
|
-
export { type ActionCreator, type
|
|
197
|
+
export { type ActionCreator, type EmitterApi, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, actionsPlugin, createModule, modulePlugin };
|
package/dist/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e,t}from"./caller-DULg_ZYV.js";import{n,t as r}from"./onAbort-BgMfspK4.js";import{i,n as a}from"./context-Dp_QRXzb.js";import{t as o}from"./emit-key-C6FL7Ewn.js";import{t as s}from"./event-DVcW0mME.js";import{r as c}from"./bus-Cmk4U1hQ.js";import{n as l,t as u}from"./lifecycle-key-8iX8RUVD.js";import{t as d}from"./origin-key-BbWN3gmN.js";import{t as f}from"./scope-key-B7l7mUS5.js";import{t as p}from"./mount-each-D54UqAhZ.js";const m=(e,t)=>(t&&Object.defineProperty(e,f,{value:t}),e),h=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),g=e=>Reflect.get(e,f),_=e=>{let t=Object.assign(n=>c(t,e,n),{type:e});return t},v=e=>typeof e==`object`&&!!e&&Reflect.get(e,`signal`)instanceof AbortSignal&&typeof Reflect.get(e,`emit`)==`function`;var y=class extends Error{};function b(e,t,n){return x(e,t,n)}function x(e,t,n,r){let i=t=>t.type===e,a=e=>r?e.length===2&&v(e[1]):typeof e[1]==`object`;function o(...i){let[o,s]=i;if(a(i)){if(!t)throw new y(`No handler for action "${e}"`);return t(o,s)}let u=r&&Reflect.apply(r,null,i),d=u?u.payload:o;return m(c(l,e,d),n)}let s=Object.assign(o,{type:e,match:i,toString:()=>e}),l=m(s,n);return l}
|
|
1
|
+
import{i as e,t}from"./caller-DULg_ZYV.js";import{n,t as r}from"./onAbort-BgMfspK4.js";import{i,n as a}from"./context-Dp_QRXzb.js";import{t as o}from"./emit-key-C6FL7Ewn.js";import{t as s}from"./event-DVcW0mME.js";import{r as c}from"./bus-Cmk4U1hQ.js";import{n as l,t as u}from"./lifecycle-key-8iX8RUVD.js";import{t as d}from"./origin-key-BbWN3gmN.js";import{t as f}from"./scope-key-B7l7mUS5.js";import{t as p}from"./mount-each-D54UqAhZ.js";const m=(e,t)=>(t&&Object.defineProperty(e,f,{value:t}),e),h=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),g=e=>Reflect.get(e,f),_=e=>{let t=Object.assign(n=>c(t,e,n),{type:e});return t},v=e=>typeof e==`object`&&!!e&&Reflect.get(e,`signal`)instanceof AbortSignal&&typeof Reflect.get(e,`emit`)==`function`;var y=class extends Error{};function b(e,t,n){return x(e,t,n)}function x(e,t,n,r){let i=t=>t.type===e,a=e=>r?e.length===2&&v(e[1]):typeof e[1]==`object`;function o(...i){let[o,s]=i;if(a(i)){if(!t)throw new y(`No handler for action "${e}"`);return t(o,s)}let u=r&&Reflect.apply(r,null,i),d=u?u.payload:o;return m(c(l,e,d),n)}let s=Object.assign(o,{type:e,match:i,toString:()=>e}),l=m(s,n);return l}const S=e=>typeof Reflect.get(e??{},`handle`)==`function`,C=()=>e=>{let t=e.actions;if(S(t))return t;let n=/* @__PURE__ */ new Map,r=(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 i(e,t){return r(s(e),t)}let a={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:i,bound:e=>n.has(e)?_(e):void 0};return e.actions=a,a};function w(e,t,n){let{handle:r,prepare:i,scope:a}=T(typeof t==`function`?n:t),o=typeof t==`function`?t:r;return o?x(e,o,a,i):Object.assign(m((...t)=>{let n=i&&Reflect.apply(i,null,t);return m({type:e,payload:n?n.payload:t[0]},a)},a),{type:e,match:t=>t.type===e,toString:()=>e})}const T=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}}},E=()=>e=>{function t(t,n){return(...r)=>{let i=t(...r),a=Reflect.get(e,d);a?a(i,n):e.emit(i)}}return Object.assign(e,{emitter:t})},D=e=>typeof e==`string`?w(e):e,O=(e,t,n,r)=>{let i=g(t);e.emit(Reflect.apply(t,null,[n]),r&&i?h(r,i):r)},k=(e,t,r,i)=>{let a=e;a[o]=!0;try{O(e,t,r,i)}catch(e){n(`cancel listener`,e)}a[o]=!1},A=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof y)throw e;return Promise.reject(e)}},j=(e,t,i={},a)=>{let o=b(e,t),s=t=>D(i[t]??`${e}/${t}`),d=s(`pending`),f=s(`success`),p=s(`fail`),h=s(`cancel`),g=(e,t,i,o)=>{let s=i,c=`idle`,l=()=>{},m=()=>{c=`closed`,l()},g=e=>{c===`started`&&(m(),k(s,h,e,a))},_=()=>{c===`idle`&&(c=`started`,l=r(s,g),O(s,d,t,a))},v=e=>{c===`started`&&(m(),s.signal.aborted?k(s,h,s.signal.reason,a):`error`in e?O(s,p,e.error,a):O(s,f,e.value,a))};return Reflect.set(s,u,{start:_,cancel:g,done:v,drop:()=>{c===`idle`&&(c=`closed`)}}),A(()=>(o||_(),Reflect.apply(e,null,[t,i]))).then(e=>(v({value:e}),e),e=>{if(c===`idle`)try{_()}catch(e){n(`pending listener`,e)}v({error:e})})},_=e=>{let t=Reflect.get(e,l)===!0;return(n,r)=>g(e,n,r,t)},v=Reflect.get(t??{},l)===!0;function y(n,r){return typeof r==`object`?t?g(o,n,r,v):Reflect.apply(o,null,[n,r]):m(c(S,e,n),a)}let x=Object.assign(y,{type:e,match:o.match,wrap:_,pending:d,success:f,fail:p,cancel:h}),S=m(x,a);return S},M=e=>typeof e==`object`&&!!e,N=e=>{if(typeof e==`string`)return e;if(M(e)||typeof e==`function`){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},P=(e,t)=>{let[n,r]=e;return n===void 0||Object.is(N(n),N(t[0]))&&(r===void 0||Object.is(r,t[1]))},F=n=>{let r={on:n.on,once:n.once,off:n.off},i=[],a=!0,o=(e,t)=>Reflect.apply(e,n,t),s=(e,n)=>(...r)=>{if(!a)return o(n,r);let s={kind:e,args:r,stack:t(),dropped:!1};return i.push(s),()=>{s.live?s.live():s.dropped=!0}},c=e=>(...t)=>{if(!a)return o(e,t);for(let e of i)e.kind!==`off`&&P(t,e.args)&&(e.dropped=!0);i.push({kind:`off`,args:t,dropped:!1})},l=s(`on`,r.on),u=s(`once`,r.once),d=c(r.off);return n.on=l,n.once=u,n.off=d,{release:()=>{a=!1,n.on===l&&(n.on=r.on),n.once===u&&(n.once=r.once),n.off===d&&(n.off=r.off)},replay:()=>{for(let t of i.splice(0))if(!t.dropped){if(t.kind===`off`)o(r.off,t.args);else{let n=t.kind===`on`?r.on:r.once;t.live=e(()=>o(n,t.args),t.stack)}}}}};function I(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const L=(e,t)=>n=>t?e.fitScope?.(t,n)??n:n,R=(e,t,n)=>{n&&e.applyScope?.(t,n)},z=(e,t,n)=>[t,e(n)],B=(t,n)=>{let r=t.on;return(...t)=>{let[i,a,...o]=t;return e(()=>Reflect.apply(r,null,[i,n(a),...o]))}},V=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},H=(e=[])=>t=>{let o=t.actions??C()(t),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,a=/* @__PURE__ */ new Map;return{mount:o=>{let l=n.get(o);if(l)return l;let u=a.get(o.name);u&&n.get(u)?.();let d=i(e,o.name),f=d.ctx,p=c(f),m=()=>{n.delete(o),a.get(o.name)===o&&a.delete(o.name),f.abort(),d.unlink()};n.set(o,m),a.set(o.name,o);let h=o.scope,g=L(t,h),_=(e,t)=>{let n=Reflect.apply(s.handle,null,z(g,e,t));return r(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=V(f),b=B(f,g);try{R(t,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=F(t),u=t.emit,d=t.get,f,m=(n=!0)=>{if(f)return f;l.release(),t.emit===h&&(t.emit=u);let r=a({event:{type:`app`,payload:void 0},bus:t,tracer:t.trace});f={root:r,apps:p(c(r.ctx))};try{n&&f.apps.mount(...e)}finally{l.replay()}return f},h=(...e)=>(m(),Reflect.apply(u,t,e));t.emit=h,t.get=((...e)=>(m(),Reflect.apply(d,t,e))),e.length>0&&queueMicrotask(()=>{try{m()}catch(e){n(`the modules of the app could not start`,e)}});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:I};return t.app=g,g};export{C as actionsPlugin,w as createAction,j as createJob,I as createModule,E as emitterPlugin,H as modulePlugin};
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { H as EventMap, Q as UnsubscribeCallback, v as Bus } from "./action-creator-DprJV-pN.js";
|
|
2
|
-
import { i as WithApp, n as ModuleApi } from "./module-
|
|
2
|
+
import { i as WithApp, n as ModuleApi } from "./module-Fk4OtkZk.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">;
|
|
@@ -17,22 +17,6 @@ type MadeHandled<P, R, T extends string> = Handled<P, R> & {
|
|
|
17
17
|
type CarryingCalls<Made, P, R, F> = [unknown] extends [F] ? Made : ((payload: P) => HandledCall<P, R> & WithScope<F>) & Made & WithScope<F>;
|
|
18
18
|
/** An action made with a scope: its calls carry the fields of that scope, for the checker. */
|
|
19
19
|
type ScopedHandled<P, R, T extends string, F> = ((payload: P) => HandledCall<P, R> & WithScope<F>) & MadeHandled<P, R, T> & WithScope<F>;
|
|
20
|
-
/** An action (or its type) with the handler to bind to it; `setHandler` makes one. */
|
|
21
|
-
type Binding<E extends EventMap = EventMap> = readonly [Handled<never, unknown> | string, Task<never, E, unknown>];
|
|
22
|
-
/**
|
|
23
|
-
* Pairs an action with the handler to bind to it, for `actionsPlugin`. For a
|
|
24
|
-
* action (an action made with a handler, or with a `Call` type) it checks that
|
|
25
|
-
* the handler fits; any other action is accepted with a handler of any payload.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* bus.use(actionsPlugin([setHandler(sendMail, smtpSend)]));
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
declare function setHandler<P, R, E extends EventMap>(handled: Handled<P, R>, fn: Task<P, E, R>): Binding<E>;
|
|
33
|
-
declare function setHandler<A extends {
|
|
34
|
-
readonly type: string;
|
|
35
|
-
}, E extends EventMap>(action: A, fn: LooseHandler<A, E>): Binding<E>;
|
|
36
20
|
/**
|
|
37
21
|
* The handler an action that is not an action can have: any. An action
|
|
38
22
|
* (a creator whose calls are calls of an action with a handler) is not accepted here, so its own
|
|
@@ -49,15 +33,14 @@ interface Registry$1<E extends EventMap> extends Actions {
|
|
|
49
33
|
readonly type: string;
|
|
50
34
|
}>(action: A, fn: LooseHandler<A, E>): UnsubscribeCallback;
|
|
51
35
|
handle(type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
|
|
52
|
-
handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
|
|
53
36
|
}
|
|
54
37
|
/**
|
|
55
38
|
* The plugin that gives a bus the handlers bound to actions:
|
|
56
|
-
* `bus.use(actionsPlugin(
|
|
39
|
+
* `bus.use(actionsPlugin())` makes the registry, sets it as the
|
|
57
40
|
* actions of the bus and returns it. Each bus has its own, so each test can
|
|
58
|
-
* have its own implementation. `handle` binds
|
|
59
|
-
* function that unbinds. A second `actionsPlugin` on the same bus
|
|
60
|
-
*
|
|
41
|
+
* have its own implementation. `handle` binds a handler and returns the
|
|
42
|
+
* function that unbinds. A second `actionsPlugin` on the same bus returns the
|
|
43
|
+
* registry that is there, so the order of the
|
|
61
44
|
* plugins does not matter. A `actions` that is not such a registry (a custom
|
|
62
45
|
* runner) is replaced.
|
|
63
46
|
*
|
|
@@ -67,13 +50,14 @@ interface Registry$1<E extends EventMap> extends Actions {
|
|
|
67
50
|
* @example
|
|
68
51
|
* ```ts
|
|
69
52
|
* const bus = createBus<Events>();
|
|
70
|
-
* const actions = bus.use(actionsPlugin(
|
|
53
|
+
* const actions = bus.use(actionsPlugin());
|
|
54
|
+
* actions.handle(sendMail, smtpSend);
|
|
71
55
|
*
|
|
72
56
|
* const unbind = actions.handle(getUser, async () => fakeUser);
|
|
73
57
|
* unbind(); // the action's own handler is back
|
|
74
58
|
* ```
|
|
75
59
|
*/
|
|
76
|
-
declare const actionsPlugin: <E extends EventMap>(
|
|
60
|
+
declare const actionsPlugin: <E extends EventMap>() => (bus: Bus<E>) => Registry$1<E>;
|
|
77
61
|
//#endregion
|
|
78
62
|
//#region src/actions/module.d.ts
|
|
79
63
|
/**
|
|
@@ -83,14 +67,13 @@ declare const actionsPlugin: <E extends EventMap>(bindings?: readonly Binding<E>
|
|
|
83
67
|
* module does is undone when it is unmounted.
|
|
84
68
|
*/
|
|
85
69
|
type ModuleContext<E extends EventMap = EventMap> = Context<E> & {
|
|
86
|
-
/** Binds the handler of an action
|
|
70
|
+
/** Binds the handler of an action; undone on unmount. */
|
|
87
71
|
handle<P, R>(handled: Handled<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
|
|
88
72
|
/** Also an action made without a handler: the handler answers it as it would one made with a handler. */
|
|
89
73
|
handle<A extends {
|
|
90
74
|
readonly type: string;
|
|
91
75
|
}>(action: A, fn: LooseHandler<A, E>): UnsubscribeCallback;
|
|
92
76
|
handle(type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
|
|
93
|
-
handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
|
|
94
77
|
/**
|
|
95
78
|
* Brings sub-modules and actions, in any order. A sub-module is mounted and
|
|
96
79
|
* unmounted with this one. An action is bound to the handler it carries, for
|
|
@@ -124,7 +107,7 @@ type Module<E extends EventMap = EventMap> = {
|
|
|
124
107
|
* ```ts
|
|
125
108
|
* export const userModule = createModule<Events>("user", (m) => {
|
|
126
109
|
* m.handle(getUser, fetchUser);
|
|
127
|
-
* m.handle(
|
|
110
|
+
* m.handle(sendMail, smtpSend);
|
|
128
111
|
* m.use(authModule);
|
|
129
112
|
*
|
|
130
113
|
* m.on(loginRequested, (user, ctx) => ctx.emit(userLoaded(user)));
|
|
@@ -220,4 +203,4 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
220
203
|
*/
|
|
221
204
|
declare const modulePlugin: <E extends EventMap>(modules?: readonly Module<E>[]) => (bus: Bus<E> & ScopeSlots & WithApp) => ModuleApi<E>;
|
|
222
205
|
//#endregion
|
|
223
|
-
export { createModule as a,
|
|
206
|
+
export { createModule as a, MadeHandled as c, WithApp as i, ScopedHandled as l, ModuleApi as n, modulePlugin as o, ModuleContext as r, CarryingCalls as s, Module as t, actionsPlugin as u };
|
|
@@ -40,22 +40,6 @@ type MadeHandled<P, R, T extends string> = Handled<P, R> & {
|
|
|
40
40
|
type CarryingCalls<Made, P, R, F> = [unknown] extends [F] ? Made : ((payload: P) => HandledCall<P, R> & WithScope<F>) & Made & WithScope<F>;
|
|
41
41
|
/** An action made with a scope: its calls carry the fields of that scope, for the checker. */
|
|
42
42
|
type ScopedHandled<P, R, T extends string, F> = ((payload: P) => HandledCall<P, R> & WithScope<F>) & MadeHandled<P, R, T> & WithScope<F>;
|
|
43
|
-
/** An action (or its type) with the handler to bind to it; `setHandler` makes one. */
|
|
44
|
-
type Binding<E extends EventMap = EventMap> = readonly [Handled<never, unknown> | string, Task<never, E, unknown>];
|
|
45
|
-
/**
|
|
46
|
-
* Pairs an action with the handler to bind to it, for `actionsPlugin`. For a
|
|
47
|
-
* action (an action made with a handler, or with a `Call` type) it checks that
|
|
48
|
-
* the handler fits; any other action is accepted with a handler of any payload.
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```ts
|
|
52
|
-
* bus.use(actionsPlugin([setHandler(sendMail, smtpSend)]));
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export declare function setHandler<P, R, E extends EventMap>(handled: Handled<P, R>, fn: Task<P, E, R>): Binding<E>;
|
|
56
|
-
export declare function setHandler<A extends {
|
|
57
|
-
readonly type: string;
|
|
58
|
-
}, E extends EventMap>(action: A, fn: LooseHandler<A, E>): Binding<E>;
|
|
59
43
|
/**
|
|
60
44
|
* The handler an action that is not an action can have: any. An action
|
|
61
45
|
* (a creator whose calls are calls of an action with a handler) is not accepted here, so its own
|
|
@@ -72,15 +56,14 @@ interface Registry$1<E extends EventMap> extends Actions {
|
|
|
72
56
|
readonly type: string;
|
|
73
57
|
}>(action: A, fn: LooseHandler<A, E>): UnsubscribeCallback;
|
|
74
58
|
handle(type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
|
|
75
|
-
handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
|
|
76
59
|
}
|
|
77
60
|
/**
|
|
78
61
|
* The plugin that gives a bus the handlers bound to actions:
|
|
79
|
-
* `bus.use(actionsPlugin(
|
|
62
|
+
* `bus.use(actionsPlugin())` makes the registry, sets it as the
|
|
80
63
|
* actions of the bus and returns it. Each bus has its own, so each test can
|
|
81
|
-
* have its own implementation. `handle` binds
|
|
82
|
-
* function that unbinds. A second `actionsPlugin` on the same bus
|
|
83
|
-
*
|
|
64
|
+
* have its own implementation. `handle` binds a handler and returns the
|
|
65
|
+
* function that unbinds. A second `actionsPlugin` on the same bus returns the
|
|
66
|
+
* registry that is there, so the order of the
|
|
84
67
|
* plugins does not matter. A `actions` that is not such a registry (a custom
|
|
85
68
|
* runner) is replaced.
|
|
86
69
|
*
|
|
@@ -90,13 +73,14 @@ interface Registry$1<E extends EventMap> extends Actions {
|
|
|
90
73
|
* @example
|
|
91
74
|
* ```ts
|
|
92
75
|
* const bus = createBus<Events>();
|
|
93
|
-
* const actions = bus.use(actionsPlugin(
|
|
76
|
+
* const actions = bus.use(actionsPlugin());
|
|
77
|
+
* actions.handle(sendMail, smtpSend);
|
|
94
78
|
*
|
|
95
79
|
* const unbind = actions.handle(getUser, async () => fakeUser);
|
|
96
80
|
* unbind(); // the action's own handler is back
|
|
97
81
|
* ```
|
|
98
82
|
*/
|
|
99
|
-
export declare const actionsPlugin: <E extends EventMap>(
|
|
83
|
+
export declare const actionsPlugin: <E extends EventMap>() => (bus: Bus<E>) => Registry$1<E>;
|
|
100
84
|
//#endregion
|
|
101
85
|
//#region src/actions/action.d.ts
|
|
102
86
|
type Reserved = "handle" | "prepare";
|
|
@@ -298,14 +282,13 @@ scope?: X) => CarryingCalls<Job<P, R, T, S, F, PE, CE>, P, Promise<Awaited<R> |
|
|
|
298
282
|
* module does is undone when it is unmounted.
|
|
299
283
|
*/
|
|
300
284
|
type ModuleContext<E extends EventMap = EventMap> = Context<E> & {
|
|
301
|
-
/** Binds the handler of an action
|
|
285
|
+
/** Binds the handler of an action; undone on unmount. */
|
|
302
286
|
handle<P, R>(handled: Handled<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
|
|
303
287
|
/** Also an action made without a handler: the handler answers it as it would one made with a handler. */
|
|
304
288
|
handle<A extends {
|
|
305
289
|
readonly type: string;
|
|
306
290
|
}>(action: A, fn: LooseHandler<A, E>): UnsubscribeCallback;
|
|
307
291
|
handle(type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
|
|
308
|
-
handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
|
|
309
292
|
/**
|
|
310
293
|
* Brings sub-modules and actions, in any order. A sub-module is mounted and
|
|
311
294
|
* unmounted with this one. An action is bound to the handler it carries, for
|
|
@@ -339,7 +322,7 @@ type Module<E extends EventMap = EventMap> = {
|
|
|
339
322
|
* ```ts
|
|
340
323
|
* export const userModule = createModule<Events>("user", (m) => {
|
|
341
324
|
* m.handle(getUser, fetchUser);
|
|
342
|
-
* m.handle(
|
|
325
|
+
* m.handle(sendMail, smtpSend);
|
|
343
326
|
* m.use(authModule);
|
|
344
327
|
*
|
|
345
328
|
* m.on(loginRequested, (user, ctx) => ctx.emit(userLoaded(user)));
|
|
@@ -435,4 +418,4 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
435
418
|
*/
|
|
436
419
|
export declare const modulePlugin: <E extends EventMap>(modules?: readonly Module<E>[]) => (bus: Bus<E> & ScopeSlots & WithApp) => ModuleApi<E>;
|
|
437
420
|
//#endregion
|
|
438
|
-
export type { ActionCreator,
|
|
421
|
+
export type { ActionCreator, EmitterApi, FreeActionCreator, Job, Module, ModuleApi, ModuleContext };
|
package/dist/notrace/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e,i as t,l as n,n as r,s as i}from"./context-_HWcdjZf.js";import{n as a,r as o}from"./bus-37nlVljc.js";import{n as s,t as c}from"./lifecycle-key-8iX8RUVD.js";import{t as l}from"./origin-key-BbWN3gmN.js";const u=(e,t)=>e(),d=Symbol.for(`nanoeffects.scope`),f=(e,t)=>(t&&Object.defineProperty(e,d,{value:t}),e),p=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),m=e=>Reflect.get(e,d),h=e=>{let t=Object.assign(n=>a(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,o=e=>r?e.length===2&&g(e[1]):typeof e[1]==`object`;function s(...i){let[s,c]=i;if(o(i)){if(!t)throw new _(`No handler for action "${e}"`);return t(s,c)}let u=r&&Reflect.apply(r,null,i),d=u?u.payload:s;return f(a(l,e,d),n)}let c=Object.assign(s,{type:e,match:i,toString:()=>e}),l=f(c,n);return l}
|
|
1
|
+
import{c as e,i as t,l as n,n as r,s as i}from"./context-_HWcdjZf.js";import{n as a,r as o}from"./bus-37nlVljc.js";import{n as s,t as c}from"./lifecycle-key-8iX8RUVD.js";import{t as l}from"./origin-key-BbWN3gmN.js";const u=(e,t)=>e(),d=Symbol.for(`nanoeffects.scope`),f=(e,t)=>(t&&Object.defineProperty(e,d,{value:t}),e),p=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),m=e=>Reflect.get(e,d),h=e=>{let t=Object.assign(n=>a(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,o=e=>r?e.length===2&&g(e[1]):typeof e[1]==`object`;function s(...i){let[s,c]=i;if(o(i)){if(!t)throw new _(`No handler for action "${e}"`);return t(s,c)}let u=r&&Reflect.apply(r,null,i),d=u?u.payload:s;return f(a(l,e,d),n)}let c=Object.assign(s,{type:e,match:i,toString:()=>e}),l=f(c,n);return l}const b=e=>typeof Reflect.get(e??{},`handle`)==`function`,x=()=>e=>{let t=e.actions;if(b(t))return t;let n=/* @__PURE__ */ new Map,r=(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 i(e,t){return r(o(e),t)}let a={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:i,bound:e=>n.has(e)?h(e):void 0};return e.actions=a,a};function S(e,t,n){let{handle:r,prepare:i,scope:a}=C(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 C=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}}},w=()=>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})},T=e=>typeof e==`string`?S(e):e,E=(e,t,n,r)=>{let i=m(t);e.emit(Reflect.apply(t,null,[n]),r&&i?p(r,i):r)},D=(e,t,r,a)=>{let o=e;o[i]=!0;try{E(e,t,r,a)}catch(e){n(`cancel listener`,e)}o[i]=!1},O=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof _)throw e;return Promise.reject(e)}},k=(t,r,i={},o)=>{let l=v(t,r),u=e=>T(i[e]??`${t}/${e}`),d=u(`pending`),p=u(`success`),m=u(`fail`),h=u(`cancel`),g=(t,r,i,a)=>{let s=i,l=`idle`,u=()=>{},f=()=>{l=`closed`,u()},g=e=>{l===`started`&&(f(),D(s,h,e,o))},_=()=>{l===`idle`&&(l=`started`,u=e(s,g),E(s,d,r,o))},v=e=>{l===`started`&&(f(),s.signal.aborted?D(s,h,s.signal.reason,o):`error`in e?E(s,m,e.error,o):E(s,p,e.value,o))};return Reflect.set(s,c,{start:_,cancel:g,done:v,drop:()=>{l===`idle`&&(l=`closed`)}}),O(()=>(a||_(),Reflect.apply(t,null,[r,i]))).then(e=>(v({value:e}),e),e=>{if(l===`idle`)try{_()}catch(e){n(`pending listener`,e)}v({error:e})})},_=e=>{let t=Reflect.get(e,s)===!0;return(n,r)=>g(e,n,r,t)},y=Reflect.get(r??{},s)===!0;function b(e,n){return typeof n==`object`?r?g(l,e,n,y):Reflect.apply(l,null,[e,n]):f(a(S,t,e),o)}let x=Object.assign(b,{type:t,match:l.match,wrap:_,pending:d,success:p,fail:m,cancel:h}),S=f(x,o);return S},A=e=>typeof e==`object`&&!!e,j=e=>{if(typeof e==`string`)return e;if(A(e)||typeof e==`function`){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},M=(e,t)=>{let[n,r]=e;return n===void 0||Object.is(j(n),j(t[0]))&&(r===void 0||Object.is(r,t[1]))},N=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,stack:void 0,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`&&M(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=u(()=>i(n,e.args),e.stack)}}}}},P=e=>({mount:(...t)=>{let[n]=t;if(t.length===1&&n!==void 0)return e.mount(n);let r=[],i=()=>{for(let e of r.splice(0).reverse())e()};try{for(let n of t)e.isMounted(n)||r.push(e.mount(n))}catch(e){throw i(),e}return i},unmount:(...t)=>{for(let n of[...t].reverse())e.unmount(n)},isMounted:e.isMounted});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)=>[t,e(n)],z=(e,t)=>{let n=e.on;return(...e)=>{let[r,i,...a]=e;return u(()=>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=(i=[])=>a=>{let o=a.actions??x()(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=n=>{let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map;return{mount:o=>{let l=r.get(o);if(l)return l;let u=i.get(o.name);u&&r.get(u)?.();let d=t(n,o.name),f=d.ctx,p=c(f),m=()=>{r.delete(o),i.get(o.name)===o&&i.delete(o.name),f.abort(),d.unlink()};r.set(o,m),i.set(o.name,o);let h=o.scope,g=I(a,h),_=(t,n)=>{let r=Reflect.apply(s.handle,null,R(g,t,n));return e(f,r),r},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=>r.get(e)?.(),isMounted:e=>r.has(e)}},l=N(a),u=a.emit,d=a.get,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:P(c(t.ctx))};try{e&&f.apps.mount(...i)}finally{l.replay()}return f},m=(...e)=>(p(),Reflect.apply(u,a,e));a.emit=m,a.get=((...e)=>(p(),Reflect.apply(d,a,e))),i.length>0&&queueMicrotask(()=>{try{p()}catch(e){n(`the modules of the app could not start`,e)}});let h={get actions(){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:F};return a.app=h,h};export{x as actionsPlugin,S as createAction,k as createJob,F as createModule,w as emitterPlugin,V as modulePlugin};
|
package/docs/actions.md
CHANGED
|
@@ -143,15 +143,21 @@ export const sendMail = createAction("mail/send");
|
|
|
143
143
|
```js
|
|
144
144
|
// bus.js
|
|
145
145
|
import { createBus } from "nanoeffects";
|
|
146
|
-
import { actionsPlugin
|
|
147
|
-
import { sendMail } from "./mail";
|
|
146
|
+
import { actionsPlugin } from "nanoeffects/actions";
|
|
148
147
|
|
|
149
148
|
export const bus = createBus();
|
|
150
149
|
|
|
151
|
-
// actionsPlugin — the registry of handlers of this bus
|
|
152
|
-
export const actions = bus.use(
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
// actionsPlugin — the registry of handlers of this bus
|
|
151
|
+
export const actions = bus.use(actionsPlugin());
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
// mail-handlers.js
|
|
156
|
+
import { actions } from "./bus";
|
|
157
|
+
import { sendMail } from "./mail";
|
|
158
|
+
|
|
159
|
+
// actions.handle — binds the work to the action on this bus
|
|
160
|
+
actions.handle(sendMail, smtpSend);
|
|
155
161
|
```
|
|
156
162
|
|
|
157
163
|
```js
|
|
@@ -165,7 +171,7 @@ unbind();
|
|
|
165
171
|
|
|
166
172
|
A handler bound in the registry makes `bus.emit(sendMail(mail))` run it and return its result, the same as for an action that carries its handler. The listeners of the type are told first. With no handler bound, `sendMail` is an event again.
|
|
167
173
|
|
|
168
|
-
The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the handler that the action carries. `bus.use(actionsPlugin())` sets `bus.actions`. A second `actionsPlugin`
|
|
174
|
+
The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the handler that the action carries. `bus.use(actionsPlugin())` sets `bus.actions`. A second `actionsPlugin` returns that registry. There is no `actions` option in `createBus`; assign a custom runner with `bus.actions = { run }`, and add `bound(type)` to it to answer for actions that have no handler of their own.
|
|
169
175
|
|
|
170
176
|
### The result type of a bound handler
|
|
171
177
|
|
package/docs/guide.md
CHANGED
|
@@ -377,7 +377,7 @@ Use strings for the plain case. Use objects when other code already builds actio
|
|
|
377
377
|
|
|
378
378
|
**Problem.** The handler of an action is in the file of the feature, but a test needs a fake, or another layer owns the work. If the handler sits in `createAction`, the event file is tied to the work.
|
|
379
379
|
|
|
380
|
-
**What to do.** Create the action without a handler: it is an event. Bind the real handler with `
|
|
380
|
+
**What to do.** Create the action without a handler: it is an event. Bind the real handler with `actions.handle` when the bus is made. A later `actions.handle` binds a newer handler that wins until you remove it.
|
|
381
381
|
|
|
382
382
|
```js
|
|
383
383
|
// user.js
|
|
@@ -389,16 +389,16 @@ export const getUser = createAction("user/get"); // no handler yet: an event
|
|
|
389
389
|
```js
|
|
390
390
|
// bus.js
|
|
391
391
|
import { createBus } from "nanoeffects";
|
|
392
|
-
import { actionsPlugin
|
|
392
|
+
import { actionsPlugin } from "nanoeffects/actions";
|
|
393
393
|
import { getUser } from "./user";
|
|
394
394
|
|
|
395
395
|
export const bus = createBus();
|
|
396
396
|
|
|
397
|
-
// setHandler — binds the real handler to the action
|
|
398
|
-
const realUser = setHandler(getUser, (id) => api.user(id));
|
|
399
|
-
|
|
400
397
|
// actionsPlugin — the registry of bound handlers of this bus
|
|
401
|
-
export const actions = bus.use(actionsPlugin(
|
|
398
|
+
export const actions = bus.use(actionsPlugin());
|
|
399
|
+
|
|
400
|
+
// actions.handle — binds the real handler to the action
|
|
401
|
+
actions.handle(getUser, (id) => api.user(id));
|
|
402
402
|
```
|
|
403
403
|
|
|
404
404
|
```js
|
package/docs/ru/actions.md
CHANGED
|
@@ -141,15 +141,21 @@ export const sendMail = createAction("mail/send");
|
|
|
141
141
|
```js
|
|
142
142
|
// bus.js
|
|
143
143
|
import { createBus } from "nanoeffects";
|
|
144
|
-
import { actionsPlugin
|
|
145
|
-
import { sendMail } from "./mail";
|
|
144
|
+
import { actionsPlugin } from "nanoeffects/actions";
|
|
146
145
|
|
|
147
146
|
export const bus = createBus();
|
|
148
147
|
|
|
149
|
-
// actionsPlugin — реестр обработчиков этой
|
|
150
|
-
export const actions = bus.use(
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
// actionsPlugin — реестр обработчиков этой шины
|
|
149
|
+
export const actions = bus.use(actionsPlugin());
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
// mail-handlers.js
|
|
154
|
+
import { actions } from "./bus";
|
|
155
|
+
import { sendMail } from "./mail";
|
|
156
|
+
|
|
157
|
+
// actions.handle — привязывает работу к action на этой шине
|
|
158
|
+
actions.handle(sendMail, smtpSend);
|
|
153
159
|
```
|
|
154
160
|
|
|
155
161
|
```js
|
|
@@ -163,7 +169,7 @@ unbind();
|
|
|
163
169
|
|
|
164
170
|
Обработчик, привязанный в реестре, заставляет `bus.emit(sendMail(mail))` запустить его и вернуть его результат, как и для action со своим обработчиком. Подписки на этот тип получают сообщение первыми. Если обработчик не привязан, `sendMail` снова просто событие.
|
|
165
171
|
|
|
166
|
-
Реестр живёт в `nanoeffects/actions`, поэтому шина без него этого кода не содержит. Без реестра `emit` запускает собственный обработчик action. `bus.use(actionsPlugin())` задаёт `bus.actions`. Второй `actionsPlugin`
|
|
172
|
+
Реестр живёт в `nanoeffects/actions`, поэтому шина без него этого кода не содержит. Без реестра `emit` запускает собственный обработчик action. `bus.use(actionsPlugin())` задаёт `bus.actions`. Второй `actionsPlugin` возвращает тот же реестр. В `createBus` нет параметра `actions`. Собственный исполнитель назначьте через `bus.actions = { run }`, а добавив ему `bound(type)`, вы сможете отвечать за actions без собственного обработчика.
|
|
167
173
|
|
|
168
174
|
### Тип результата привязанного обработчика
|
|
169
175
|
|
package/docs/ru/guide.md
CHANGED
|
@@ -376,7 +376,7 @@ bus.emit(double(21)); // 42
|
|
|
376
376
|
|
|
377
377
|
**Проблема.** Обработчик action лежит в файле фичи, а тесту нужна подделка, или работа принадлежит другому слою. Если положить обработчик в `createAction`, файл события окажется связан с работой.
|
|
378
378
|
|
|
379
|
-
**Что делать.** Создайте action без обработчика: это событие. Настоящий обработчик привяжите через `
|
|
379
|
+
**Что делать.** Создайте action без обработчика: это событие. Настоящий обработчик привяжите через `actions.handle` при создании шины. Следующий `actions.handle` привязывает новый обработчик, который главнее, пока вы его не снимете.
|
|
380
380
|
|
|
381
381
|
```js
|
|
382
382
|
// user.js
|
|
@@ -388,16 +388,16 @@ export const getUser = createAction("user/get"); // обработчика по
|
|
|
388
388
|
```js
|
|
389
389
|
// bus.js
|
|
390
390
|
import { createBus } from "nanoeffects";
|
|
391
|
-
import { actionsPlugin
|
|
391
|
+
import { actionsPlugin } from "nanoeffects/actions";
|
|
392
392
|
import { getUser } from "./user";
|
|
393
393
|
|
|
394
394
|
export const bus = createBus();
|
|
395
395
|
|
|
396
|
-
// setHandler — привязывает настоящий обработчик к action
|
|
397
|
-
const realUser = setHandler(getUser, (id) => api.user(id));
|
|
398
|
-
|
|
399
396
|
// actionsPlugin — реестр привязанных обработчиков этой шины
|
|
400
|
-
export const actions = bus.use(actionsPlugin(
|
|
397
|
+
export const actions = bus.use(actionsPlugin());
|
|
398
|
+
|
|
399
|
+
// actions.handle — привязывает настоящий обработчик к action
|
|
400
|
+
actions.handle(getUser, (id) => api.user(id));
|
|
401
401
|
```
|
|
402
402
|
|
|
403
403
|
```js
|
package/llms-full.txt
CHANGED
|
@@ -394,7 +394,7 @@ Use strings for the plain case. Use objects when other code already builds actio
|
|
|
394
394
|
|
|
395
395
|
**Problem.** The handler of an action is in the file of the feature, but a test needs a fake, or another layer owns the work. If the handler sits in `createAction`, the event file is tied to the work.
|
|
396
396
|
|
|
397
|
-
**What to do.** Create the action without a handler: it is an event. Bind the real handler with `
|
|
397
|
+
**What to do.** Create the action without a handler: it is an event. Bind the real handler with `actions.handle` when the bus is made. A later `actions.handle` binds a newer handler that wins until you remove it.
|
|
398
398
|
|
|
399
399
|
```js
|
|
400
400
|
// user.js
|
|
@@ -406,16 +406,16 @@ export const getUser = createAction("user/get"); // no handler yet: an event
|
|
|
406
406
|
```js
|
|
407
407
|
// bus.js
|
|
408
408
|
import { createBus } from "nanoeffects";
|
|
409
|
-
import { actionsPlugin
|
|
409
|
+
import { actionsPlugin } from "nanoeffects/actions";
|
|
410
410
|
import { getUser } from "./user";
|
|
411
411
|
|
|
412
412
|
export const bus = createBus();
|
|
413
413
|
|
|
414
|
-
// setHandler — binds the real handler to the action
|
|
415
|
-
const realUser = setHandler(getUser, (id) => api.user(id));
|
|
416
|
-
|
|
417
414
|
// actionsPlugin — the registry of bound handlers of this bus
|
|
418
|
-
export const actions = bus.use(actionsPlugin(
|
|
415
|
+
export const actions = bus.use(actionsPlugin());
|
|
416
|
+
|
|
417
|
+
// actions.handle — binds the real handler to the action
|
|
418
|
+
actions.handle(getUser, (id) => api.user(id));
|
|
419
419
|
```
|
|
420
420
|
|
|
421
421
|
```js
|
|
@@ -3049,15 +3049,21 @@ export const sendMail = createAction("mail/send");
|
|
|
3049
3049
|
```js
|
|
3050
3050
|
// bus.js
|
|
3051
3051
|
import { createBus } from "nanoeffects";
|
|
3052
|
-
import { actionsPlugin
|
|
3053
|
-
import { sendMail } from "./mail";
|
|
3052
|
+
import { actionsPlugin } from "nanoeffects/actions";
|
|
3054
3053
|
|
|
3055
3054
|
export const bus = createBus();
|
|
3056
3055
|
|
|
3057
|
-
// actionsPlugin — the registry of handlers of this bus
|
|
3058
|
-
export const actions = bus.use(
|
|
3059
|
-
|
|
3060
|
-
|
|
3056
|
+
// actionsPlugin — the registry of handlers of this bus
|
|
3057
|
+
export const actions = bus.use(actionsPlugin());
|
|
3058
|
+
```
|
|
3059
|
+
|
|
3060
|
+
```js
|
|
3061
|
+
// mail-handlers.js
|
|
3062
|
+
import { actions } from "./bus";
|
|
3063
|
+
import { sendMail } from "./mail";
|
|
3064
|
+
|
|
3065
|
+
// actions.handle — binds the work to the action on this bus
|
|
3066
|
+
actions.handle(sendMail, smtpSend);
|
|
3061
3067
|
```
|
|
3062
3068
|
|
|
3063
3069
|
```js
|
|
@@ -3071,7 +3077,7 @@ unbind();
|
|
|
3071
3077
|
|
|
3072
3078
|
A handler bound in the registry makes `bus.emit(sendMail(mail))` run it and return its result, the same as for an action that carries its handler. The listeners of the type are told first. With no handler bound, `sendMail` is an event again.
|
|
3073
3079
|
|
|
3074
|
-
The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the handler that the action carries. `bus.use(actionsPlugin())` sets `bus.actions`. A second `actionsPlugin`
|
|
3080
|
+
The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the handler that the action carries. `bus.use(actionsPlugin())` sets `bus.actions`. A second `actionsPlugin` returns that registry. There is no `actions` option in `createBus`; assign a custom runner with `bus.actions = { run }`, and add `bound(type)` to it to answer for actions that have no handler of their own.
|
|
3075
3081
|
|
|
3076
3082
|
### The result type of a bound handler
|
|
3077
3083
|
|