nanoeffects 0.33.0 → 0.35.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 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
- `pnpm add nanoeffects` and `yarn add nanoeffects` work the same. 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.
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 CarryingCalls, d as actionsPlugin, f as setHandler, l as MadeHandled, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as ScopedHandled } from "./module-DPKjzfRk.js";
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 Binding, type EmitterApi, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, actionsPlugin, createModule, modulePlugin, setHandler };
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}function S(e,t){return[e,t]}const C=e=>typeof Reflect.get(e??{},`handle`)==`function`,w=(e=[])=>t=>{let n=t.actions;if(C(n))return n.handle(e),n;let r=/* @__PURE__ */ new Map,i=(e,t)=>{let n={fn:t};return r.set(e,[...r.get(e)??[],n]),()=>{let t=(r.get(e)??[]).filter(e=>e!==n);t.length>0?r.set(e,t):r.delete(e)}};function a(e,t){if(t)return i(s(e),t);let n=e.map(([e,t])=>i(s(e),t));return()=>{for(let e of n)e()}}a(e);let o={run:(e,t,n)=>{let i=(r.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,n])},handle:a,bound:e=>r.has(e)?_(e):void 0};return t.actions=o,o};function T(e,t,n){let{handle:r,prepare:i,scope:a}=E(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 E=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}}},D=()=>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})},O=e=>typeof e==`string`?T(e):e,k=(e,t,n,r)=>{let i=g(t);e.emit(Reflect.apply(t,null,[n]),r&&i?h(r,i):r)},A=(e,t,r,i)=>{let a=e;a[o]=!0;try{k(e,t,r,i)}catch(e){n(`cancel listener`,e)}a[o]=!1},j=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof y)throw e;return Promise.reject(e)}},M=(e,t,i={},a)=>{let o=b(e,t),s=t=>O(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(),A(s,h,e,a))},_=()=>{c===`idle`&&(c=`started`,l=r(s,g),k(s,d,t,a))},v=e=>{c===`started`&&(m(),s.signal.aborted?A(s,h,s.signal.reason,a):`error`in e?k(s,p,e.error,a):k(s,f,e.value,a))};return Reflect.set(s,u,{start:_,cancel:g,done:v,drop:()=>{c===`idle`&&(c=`closed`)}}),j(()=>(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},N=e=>typeof e==`object`&&!!e,P=e=>{if(typeof e==`string`)return e;if(N(e)||typeof e==`function`){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},F=(e,t)=>{let[n,r]=e;return n===void 0||Object.is(P(n),P(t[0]))&&(r===void 0||Object.is(r,t[1]))},I=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`&&F(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 L(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const R=(e,t)=>n=>t?e.fitScope?.(t,n)??n:n,z=(e,t,n)=>{n&&e.applyScope?.(t,n)},B=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],V=(t,n)=>{let r=t.on;return(...t)=>{let[i,a,...o]=t;return e(()=>Reflect.apply(r,null,[i,n(a),...o]))}},H=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},U=(e=[])=>t=>{let o=t.actions??w()(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=R(t,h),_=(e,t)=>{let n=Reflect.apply(s.handle,null,B(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=H(f),b=V(f,g);try{z(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=I(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:L};return t.app=g,g};export{w as actionsPlugin,T as createAction,M as createJob,L as createModule,D as emitterPlugin,U as modulePlugin,S as setHandler};
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-DPKjzfRk.js";
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(bindings))` makes the registry, sets it as the
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 more later and returns the
59
- * function that unbinds. A second `actionsPlugin` on the same bus binds its
60
- * list in the registry that is there and returns it, so the order of the
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([setHandler(sendMail, smtpSend)]));
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>(bindings?: readonly Binding<E>[]) => (bus: Bus<E>) => Registry$1<E>;
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, or a list of them; undone on unmount. */
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([setHandler(sendMail, smtpSend)]);
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, CarryingCalls as c, actionsPlugin as d, setHandler as f, WithApp as i, MadeHandled as l, ModuleApi as n, modulePlugin as o, ModuleContext as r, Binding as s, Module as t, ScopedHandled as u };
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(bindings))` makes the registry, sets it as the
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 more later and returns the
82
- * function that unbinds. A second `actionsPlugin` on the same bus binds its
83
- * list in the registry that is there and returns it, so the order of the
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([setHandler(sendMail, smtpSend)]));
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>(bindings?: readonly Binding<E>[]) => (bus: Bus<E>) => Registry$1<E>;
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, or a list of them; undone on unmount. */
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([setHandler(sendMail, smtpSend)]);
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, Binding, EmitterApi, FreeActionCreator, Job, Module, ModuleApi, ModuleContext };
421
+ export type { ActionCreator, EmitterApi, FreeActionCreator, Job, Module, ModuleApi, ModuleContext };
@@ -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}function b(e,t){return[e,t]}const x=e=>typeof Reflect.get(e??{},`handle`)==`function`,S=(e=[])=>t=>{let n=t.actions;if(x(n))return n.handle(e),n;let r=/* @__PURE__ */ new Map,i=(e,t)=>{let n={fn:t};return r.set(e,[...r.get(e)??[],n]),()=>{let t=(r.get(e)??[]).filter(e=>e!==n);t.length>0?r.set(e,t):r.delete(e)}};function a(e,t){if(t)return i(o(e),t);let n=e.map(([e,t])=>i(o(e),t));return()=>{for(let e of n)e()}}a(e);let s={run:(e,t,n)=>{let i=(r.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,n])},handle:a,bound:e=>r.has(e)?h(e):void 0};return t.actions=s,s};function C(e,t,n){let{handle:r,prepare:i,scope:a}=w(typeof t==`function`?n:t),o=typeof t==`function`?t:r;return o?y(e,o,a,i):Object.assign(f((...t)=>{let n=i&&Reflect.apply(i,null,t);return f({type:e,payload:n?n.payload:t[0]},a)},a),{type:e,match:t=>t.type===e,toString:()=>e})}const w=e=>{if(!e)return{};let{handle:t,prepare:n,...r}=e,i=typeof t==`function`,a=typeof n==`function`;if(!i&&!a)return{scope:e};let o=Object.keys(r).length>0?r:void 0;return{...i&&{handle:t},...a&&{prepare:n},...o&&{scope:o}}},T=()=>e=>{function t(t,n){return(...r)=>{let i=t(...r),a=Reflect.get(e,l);a?a(i,n):e.emit(i)}}return Object.assign(e,{emitter:t})},E=e=>typeof e==`string`?C(e):e,D=(e,t,n,r)=>{let i=m(t);e.emit(Reflect.apply(t,null,[n]),r&&i?p(r,i):r)},O=(e,t,r,a)=>{let o=e;o[i]=!0;try{D(e,t,r,a)}catch(e){n(`cancel listener`,e)}o[i]=!1},k=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof _)throw e;return Promise.reject(e)}},A=(t,r,i={},o)=>{let l=v(t,r),u=e=>E(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(),O(s,h,e,o))},_=()=>{l===`idle`&&(l=`started`,u=e(s,g),D(s,d,r,o))},v=e=>{l===`started`&&(f(),s.signal.aborted?O(s,h,s.signal.reason,o):`error`in e?D(s,m,e.error,o):D(s,p,e.value,o))};return Reflect.set(s,c,{start:_,cancel:g,done:v,drop:()=>{l===`idle`&&(l=`closed`)}}),k(()=>(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},j=e=>typeof e==`object`&&!!e,M=e=>{if(typeof e==`string`)return e;if(j(e)||typeof e==`function`){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},N=(e,t)=>{let[n,r]=e;return n===void 0||Object.is(M(n),M(t[0]))&&(r===void 0||Object.is(r,t[1]))},P=e=>{let t={on:e.on,once:e.once,off:e.off},n=[],r=!0,i=(t,n)=>Reflect.apply(t,e,n),a=(e,t)=>(...a)=>{if(!r)return i(t,a);let o={kind:e,args:a,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`&&N(t,e.args)&&(e.dropped=!0);n.push({kind:`off`,args:t,dropped:!1})},s=a(`on`,t.on),c=a(`once`,t.once),l=o(t.off);return e.on=s,e.once=c,e.off=l,{release:()=>{r=!1,e.on===s&&(e.on=t.on),e.once===c&&(e.once=t.once),e.off===l&&(e.off=t.off)},replay:()=>{for(let e of n.splice(0))if(!e.dropped){if(e.kind===`off`)i(t.off,e.args);else{let n=e.kind===`on`?t.on:t.once;e.live=u(()=>i(n,e.args),e.stack)}}}}},F=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 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)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],B=(e,t)=>{let n=e.on;return(...e)=>{let[r,i,...a]=e;return u(()=>Reflect.apply(n,null,[r,t(i),...a]))}},V=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},H=(i=[])=>a=>{let o=a.actions??S()(a),s={handle:(...e)=>{if(!o.handle)throw Error("modulePlugin: the `actions` of the bus is not a registry from `actionsPlugin()`");return Reflect.apply(o.handle,null,e)}},c=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=L(a,h),_=(t,n)=>{let r=Reflect.apply(s.handle,null,z(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=V(f),b=B(f,g);try{R(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=P(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:F(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:I};return a.app=h,h};export{S as actionsPlugin,C as createAction,A as createJob,I as createModule,T as emitterPlugin,H as modulePlugin,b as setHandler};
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/dist/signal.d.ts CHANGED
@@ -1,24 +1,25 @@
1
- import { A as HandledCall, C as Emit, E as EventLike, H as EventMap, s as EmitScope, u as WithScope, v as Bus } from "./action-creator-DprJV-pN.js";
1
+ import { A as HandledCall, C as Emit, E as EventLike, H as EventMap, I as WithSignal, s as EmitScope, u as WithScope, v as Bus } from "./action-creator-DprJV-pN.js";
2
2
  import { n as FromSignal } from "./context-types-BiTMp_lW.js";
3
3
  //#region src/signal/fetch.d.ts
4
4
  /**
5
- * A `fetch` that carries a given signal, so the request stops when the signal aborts and the caller never passes
6
- * it. Give it to `fromSignal` and every task gets a `ctx.fetch` made from its own signal. A call that has a
7
- * `signal` of its own stops with whichever aborts first.
5
+ * A `fetch` that stops with its task: an effect, so `ctx` is the first argument and the request carries
6
+ * `ctx.signal`. The caller never passes the signal. A call that has a `signal` of its own in `init` stops with
7
+ * whichever aborts first. Outside a task pass `{ signal }` of the source, or `{}` in plain JavaScript.
8
+ *
9
+ * With `effectsPlugin({ fetch: signalFetch })` every task has a `ctx.fetch(input, init)` bound to itself.
8
10
  *
9
11
  * @example
10
12
  * ```ts
11
- * const bus = createBus().use(
12
- * contextPlugin({ fetch: fromSignal(signalFetch) }),
13
- * );
14
- *
15
13
  * bus.on("search", latest(async (query, ctx) => {
16
- * const response = await ctx.fetch(`/api?q=${query}`); // stops when the policy cancels the handler
14
+ * const response = await signalFetch(ctx, `/api?q=${query}`); // stops when the policy cancels the handler
17
15
  * ctx.emit("results", await response.json());
18
16
  * }));
17
+ *
18
+ * const withFetch = createBus().use(effectsPlugin({ fetch: signalFetch }));
19
+ * withFetch.on("search", latest(async (query, { fetch }) => fetch(`/api?q=${query}`)));
19
20
  * ```
20
21
  */
21
- export declare const signalFetch: (signal: AbortSignal) => typeof fetch;
22
+ export declare const signalFetch: (ctx: WithSignal, input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
22
23
  //#endregion
23
24
  //#region src/signal/from-signal.d.ts
24
25
  /**
package/dist/signal.js CHANGED
@@ -1 +1 @@
1
- import{i as e}from"./caller-DULg_ZYV.js";import{t}from"./onAbort-BgMfspK4.js";import{n}from"./promise-CSLpzwSR.js";import{n as r}from"./bus-Cmk4U1hQ.js";import{t as i}from"./signal-field-key-CWT-3OXC.js";const a=e=>(t,n)=>{let r=n?.signal,i=r?AbortSignal.any([e,r]):e;return fetch(t,{...n,signal:i})},o=e=>({[i]:e}),s=(n,r)=>{let i=(e,i,a)=>{let o=a?.signal;if(!o)return n(e,i,a);let s=n(e,r?(e,t)=>(c(),i(e,t)):i,a),c=t({signal:o},s);return()=>{c(),s()}};return(t,n,r)=>e(()=>i(t,n,r))},c=e=>typeof e==`object`&&!!e,l=(e,t)=>{let n=t[0];if(!c(n)||!c(e)&&typeof e!=`function`)return;let r=Reflect.get(n,`signal`);return r instanceof AbortSignal?r:void 0},u=(e,i,a,o)=>{let s=t({signal:e},e=>{for(let{target:t}of i)t.abort(e)});return n(o)?o.then(s,s):r(a)&&s(),o},d=()=>e=>{let t,n,r=e.addContext;e.addContext=e=>{r?.(e),t?.push({target:e}),n?.aborted&&e.abort(n.reason)};let i=e.emit;e.emit=(e,...r)=>{let a=l(e,r);if(!(a||t))return i(e,...r);let o=t,s=n,c=[];t=a?c:void 0,n=a;let d;try{d=i(e,...r)}finally{t=o,n=s}return a?u(a,c,e,d):d};let a=e.on,o=e.once;return e.on=s(a,!1),e.once=s(o,!0),e};export{o as fromSignal,a as signalFetch,d as signalPlugin};
1
+ import{i as e}from"./caller-DULg_ZYV.js";import{t}from"./onAbort-BgMfspK4.js";import{n}from"./promise-CSLpzwSR.js";import{n as r}from"./bus-Cmk4U1hQ.js";import{t as i}from"./signal-field-key-CWT-3OXC.js";const a=(e,t,n)=>{let r=n?.signal,i=r&&e.signal?AbortSignal.any([e.signal,r]):r??e.signal;return fetch(t,{...n,signal:i})},o=e=>({[i]:e}),s=(n,r)=>{let i=(e,i,a)=>{let o=a?.signal;if(!o)return n(e,i,a);let s=n(e,r?(e,t)=>(c(),i(e,t)):i,a),c=t({signal:o},s);return()=>{c(),s()}};return(t,n,r)=>e(()=>i(t,n,r))},c=e=>typeof e==`object`&&!!e,l=(e,t)=>{let n=t[0];if(!c(n)||!c(e)&&typeof e!=`function`)return;let r=Reflect.get(n,`signal`);return r instanceof AbortSignal?r:void 0},u=(e,i,a,o)=>{let s=t({signal:e},e=>{for(let{target:t}of i)t.abort(e)});return n(o)?o.then(s,s):r(a)&&s(),o},d=()=>e=>{let t,n,r=e.addContext;e.addContext=e=>{r?.(e),t?.push({target:e}),n?.aborted&&e.abort(n.reason)};let i=e.emit;e.emit=(e,...r)=>{let a=l(e,r);if(!(a||t))return i(e,...r);let o=t,s=n,c=[];t=a?c:void 0,n=a;let d;try{d=i(e,...r)}finally{t=o,n=s}return a?u(a,c,e,d):d};let a=e.on,o=e.once;return e.on=s(a,!1),e.once=s(o,!0),e};export{o as fromSignal,a as signalFetch,d as signalPlugin};
package/docs/README.md CHANGED
@@ -3,10 +3,11 @@
3
3
  nanoeffects moves business logic and side effects out of UI components into plain `async` functions. Policies (`latest`, `debounce`, `queue`) and effects (`retry`, `timeout`, `poll`) handle races, retries and cancellation.
4
4
 
5
5
  ```js
6
+ // search.js
6
7
  import { createBus } from "nanoeffects";
7
8
  import { latest } from "nanoeffects/policies";
8
9
 
9
- const bus = createBus(); // event bus
10
+ export const bus = createBus(); // event bus
10
11
 
11
12
  // The search logic lives outside the component; latest aborts the previous request
12
13
  bus.on(
@@ -22,7 +23,33 @@ bus.on("found", (items) => render(items));
22
23
  bus.emit("search", "nano");
23
24
  ```
24
25
 
25
- The same example with a UI framework: [React](react.md), [Vue](vue.md), [Svelte](svelte.md). Ready projects: [Start a project from a template](starter.md).
26
+ In a component, the same bus needs no extra code. This is React; the handler stays in `search.js`:
27
+
28
+ ```jsx
29
+ // SearchBox.jsx
30
+ import { useEffect, useState } from "react";
31
+ import { bus } from "./search";
32
+
33
+ export function SearchBox() {
34
+ const [items, setItems] = useState([]);
35
+
36
+ // on returns the unbind function; returning it removes the listener on unmount
37
+ useEffect(() => bus.on("found", setItems), []);
38
+
39
+ return (
40
+ <>
41
+ <input onChange={(event) => bus.emit("search", event.target.value)} />
42
+ <ul>
43
+ {items.map((item) => (
44
+ <li key={item.id}>{item.title}</li>
45
+ ))}
46
+ </ul>
47
+ </>
48
+ );
49
+ }
50
+ ```
51
+
52
+ The same example step by step: [React](react.md), [Vue](vue.md), [Svelte](svelte.md). Ready projects: [Start a project from a template](starter.md).
26
53
 
27
54
  ## Install
28
55
 
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, setHandler } from "nanoeffects/actions";
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; setHandler pairs an action with its work
152
- export const actions = bus.use(
153
- actionsPlugin([setHandler(sendMail, smtpSend)]),
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` binds its list in that registry and returns it. 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.
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
 
@@ -47,7 +47,7 @@ bus.on("results", (items) => render(items));
47
47
 
48
48
  What to replace: `api.search` is your own request function. It must accept `{ signal }` and give it to `fetch`. Without it, `latest` still ignores the old result, but the request keeps running.
49
49
 
50
- To make the mistake impossible, bind the signal once: `contextPlugin({ fetch: fromSignal(signalFetch) })` gives every handler a `ctx.fetch` that stops with the handler ([how](pass-the-signal.md#bind-the-signal-once)). To find the requests that already forget it, use `devPlugin` while you develop, and `getLostSignalsCount(bus)` in a test ([how](pass-the-signal.md#count-lost-signals-in-a-test)).
50
+ To make the mistake impossible, bind the signal once: `signalFetch(ctx, url)` is a `fetch` that stops with the handler, and `effectsPlugin({ fetch: signalFetch })` gives every handler a `ctx.fetch` of it ([how](pass-the-signal.md#bind-the-signal-once)). To find the requests that already forget it, use `devPlugin` while you develop, and `getLostSignalsCount(bus)` in a test ([how](pass-the-signal.md#count-lost-signals-in-a-test)).
51
51
 
52
52
  Result: `emit("search", "ab")` followed by `emit("search", "abc")` shows only the results for `abc`, and the request for `ab` is cancelled.
53
53
 
package/docs/context.md CHANGED
@@ -99,7 +99,7 @@ bus.on("user/open", async (id, ctx) => {
99
99
  });
100
100
  ```
101
101
 
102
- - `signalFetch` from the same entry is a `fetch` bound to a signal, so `fetch: fromSignal(signalFetch)` gives every handler a `ctx.fetch` that stops with the task. [Bind the signal once](pass-the-signal.md#bind-the-signal-once)
102
+ - For `fetch` you need no `fromSignal`: `signalFetch(ctx, url)` from the same entry stops with the task, and `effectsPlugin({ fetch: signalFetch })` gives every handler a `ctx.fetch`. [Bind the signal once](pass-the-signal.md#bind-the-signal-once)
103
103
  - `make` runs at the first read, so a handler that never uses `ctx.http` pays nothing for it.
104
104
  - A task that `ctx.run` starts builds its own value from its own signal, so aborting the child stops only the child's requests.
105
105
  - The field is not copied by `{ ...ctx }` and `Object.keys(ctx)` does not list it. Read `ctx.http` first if you copy a context.
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 `setHandler` when the bus is made. Later, `actions.handle` binds a newer handler that wins until you remove it.
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, setHandler } from "nanoeffects/actions";
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([realUser]));
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/package.md CHANGED
@@ -21,7 +21,7 @@ Each row is optional. The "when" column says what need brings you to it.
21
21
  | `nanoeffects/actions` | `createAction`, `createJob`, `createModule`, `modulePlugin`, `actionsPlugin` | `emit` must return a result, or domain logic leaves the UI |
22
22
  | `nanoeffects/match` | `matchPlugin`: `on`, `once` and `off` that take a function which picks the events | `on` must take a function that picks events |
23
23
  | `nanoeffects/context` | `contextPlugin`: your own fields (a store, a client) on `ctx` of every task, typed from the value | a store or client must be on every task |
24
- | `nanoeffects/signal` | `signalPlugin`: `emit(call, { signal })` ties the tasks of an emit to an outside `AbortSignal`; `fromSignal` and `signalFetch`: a client or a `fetch` made from the signal of each task | an outside `AbortSignal` (request, React Query) must stop tasks, or a handler must not pass the signal by hand |
24
+ | `nanoeffects/signal` | `signalPlugin`: `emit(call, { signal })` ties the tasks of an emit to an outside `AbortSignal`; `signalFetch(ctx, url)`: a `fetch` that stops with its task; `fromSignal`: a client made from the signal of each task | an outside `AbortSignal` (request, React Query) must stop tasks, or a handler must not pass the signal by hand |
25
25
  | `nanoeffects/oop` | `Emitter`, `extendBus`, `WithBus`, `On`, `Once`, `disposeBus`: the methods of a bus on a class, with the subscriptions of each instance ended by `dispose()` | you write classes, not functions |
26
26
  | `nanoeffects/scope` | `scopePlugin`, `effect`: fields for one action, job, module or `emit` (the types `Scope`, `Effect`) | one action or module needs its own fields |
27
27
  | `nanoeffects/redux` | `createEffectsMiddleware` | you already use Redux |
@@ -22,25 +22,36 @@ A policy such as `latest` cancels an older call by aborting its `ctx.signal`. Th
22
22
 
23
23
  ## Bind the signal once
24
24
 
25
- The surest fix is a handler with no signal in it. Make the client from the signal of the task, once, and the handler calls `ctx.fetch` and cannot forget anything. `signalFetch` is a `fetch` bound to a signal; `fromSignal` builds it for each task.
25
+ The surest fix is a handler that never writes a signal. `signalFetch` is a `fetch` that takes `ctx` first, like `retry(ctx, ...)`: the request carries `ctx.signal`, so there is nothing to forget.
26
26
 
27
27
  ```js
28
- // bus.js
29
- import { createBus } from "nanoeffects";
30
- import { contextPlugin } from "nanoeffects/context";
31
- import { fromSignal, signalFetch } from "nanoeffects/signal";
32
- import axios from "axios";
28
+ // search-handlers.js
29
+ import { latest } from "nanoeffects/policies";
30
+ import { signalFetch } from "nanoeffects/signal";
31
+ import { bus } from "./bus";
33
32
 
34
- // fromSignal — ctx.fetch is made from the signal of the task that reads it
35
- export const bus = createBus().use(
36
- contextPlugin({
37
- fetch: fromSignal(signalFetch),
38
- // your own client too: it is made from the signal of each task
39
- http: fromSignal((signal) => axios.create({ signal })),
33
+ bus.on(
34
+ "search",
35
+ latest(async (query, ctx) => {
36
+ // signalFetch — fetch with ctx.signal: it stops when the policy cancels the handler
37
+ const response = await signalFetch(ctx, `/api?q=${query}`);
38
+ ctx.emit("results", await response.json());
40
39
  }),
41
40
  );
42
41
  ```
43
42
 
43
+ To get `ctx.fetch` in every handler, put the effect on the context once. `effectsPlugin` binds it to each task, so the handler drops the `ctx` argument:
44
+
45
+ ```js
46
+ // bus.js
47
+ import { createBus } from "nanoeffects";
48
+ import { effectsPlugin } from "nanoeffects/effects";
49
+ import { signalFetch } from "nanoeffects/signal";
50
+
51
+ // effectsPlugin — ctx.fetch is signalFetch bound to the task that reads it
52
+ export const bus = createBus().use(effectsPlugin({ fetch: signalFetch }));
53
+ ```
54
+
44
55
  ```js
45
56
  // search-handlers.js
46
57
  import { latest } from "nanoeffects/policies";
@@ -48,10 +59,27 @@ import { bus } from "./bus";
48
59
 
49
60
  bus.on(
50
61
  "search",
51
- latest(async (query, ctx) => {
52
- // no signal here: ctx.fetch stops when the policy cancels the handler
53
- const response = await ctx.fetch(`/api?q=${query}`);
54
- ctx.emit("results", await response.json());
62
+ latest(async (query, { fetch, emit }) => {
63
+ // no signal and no ctx argument: fetch stops when the policy cancels the handler
64
+ const response = await fetch(`/api?q=${query}`);
65
+ emit("results", await response.json());
66
+ }),
67
+ );
68
+ ```
69
+
70
+ A client that is not an effect, such as an `axios` instance, is made from the signal of each task with `fromSignal`:
71
+
72
+ ```js
73
+ // bus.js
74
+ import { createBus } from "nanoeffects";
75
+ import { contextPlugin } from "nanoeffects/context";
76
+ import { fromSignal } from "nanoeffects/signal";
77
+ import axios from "axios";
78
+
79
+ // fromSignal — ctx.http is made from the signal of the task that reads it
80
+ export const bus = createBus().use(
81
+ contextPlugin({
82
+ http: fromSignal((signal) => axios.create({ signal })),
55
83
  }),
56
84
  );
57
85
  ```
@@ -123,7 +151,7 @@ Each row is a situation where the signal is easy to lose. The right column says
123
151
  | A function takes the signal and never uses it: `search(query, options) { return fetch(url); }` | Pass the signal on inside it. Only type information finds this in the linter; a test finds it for sure | types | |
124
152
  | The signal is wired by hand: `signal.addEventListener("abort", ...)` | Nothing, the handler is not checked step by step | accepted | |
125
153
  | `bus.emit` in a handler | Use `ctx.emit`: only it is dropped after the handler was cancelled | yes | |
126
- | A request that keeps running after the cancel, on Node | Pass the signal, or use `ctx.fetch` | | yes |
154
+ | A request that keeps running after the cancel, on Node | Pass the signal, or use `signalFetch(ctx, url)` | | yes |
127
155
  | Results emitted after the cancel | The handler ignored the signal. Pass it, or check `ctx.signal.aborted` | | yes |
128
156
  | `ctx.on` after the cancel | The same | | yes |
129
157
 
package/docs/policies.md CHANGED
@@ -24,7 +24,7 @@ bus.on(
24
24
  );
25
25
  ```
26
26
 
27
- Pass `signal` to `fetch` (and the like) so the request cancels. To not pass it at all, bind it once: with `ctx.fetch` the request stops with the handler and there is nothing to forget ([Keep the signal through a handler](pass-the-signal.md#bind-the-signal-once)).
27
+ Pass `signal` to `fetch` (and the like) so the request cancels. To not pass it at all, bind it once: with `signalFetch(ctx, url)` the request stops with the handler and there is nothing to forget ([Keep the signal through a handler](pass-the-signal.md#bind-the-signal-once)).
28
28
 
29
29
  ## Choose a policy
30
30