nanoeffects 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/{action-creator-c292nK7v.d.ts → action-creator-BrZF6hXc.d.ts} +4 -8
  2. package/dist/actions.d.ts +6 -6
  3. package/dist/actions.js +1 -1
  4. package/dist/{context-plugin-1pYmSQEG.d.ts → context-plugin--HkNN1ha.d.ts} +1 -1
  5. package/dist/dev.d.ts +9 -8
  6. package/dist/dev.js +1 -1
  7. package/dist/effects.d.ts +2 -2
  8. package/dist/effects.js +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/match.d.ts +50 -0
  11. package/dist/match.js +1 -0
  12. package/dist/{module-BlF2KSgy.d.ts → module-DqKegXHt.d.ts} +44 -19
  13. package/dist/notrace/{action-creator-DKG_sIia.d.ts → action-creator-DHAEgwuh.d.ts} +4 -8
  14. package/dist/notrace/actions.d.ts +59 -24
  15. package/dist/notrace/actions.js +1 -1
  16. package/dist/notrace/{context-plugin-Dt1IgVo8.d.ts → context-plugin-BapQkGwQ.d.ts} +1 -1
  17. package/dist/notrace/effects.d.ts +2 -2
  18. package/dist/notrace/effects.js +1 -1
  19. package/dist/notrace/index.d.ts +2 -2
  20. package/dist/notrace/{plugin-B0AXKusc.d.ts → plugin-Cxadz6uU.d.ts} +2 -2
  21. package/dist/notrace/policies.d.ts +1 -1
  22. package/dist/notrace/redux.d.ts +1 -1
  23. package/dist/observable.d.ts +1 -1
  24. package/dist/oop.d.ts +131 -0
  25. package/dist/oop.js +1 -0
  26. package/dist/{plugin-CCYlC0XS.d.ts → plugin-DkXCvsmB.d.ts} +2 -2
  27. package/dist/policies.d.ts +1 -1
  28. package/dist/react.d.ts +1 -1
  29. package/dist/redux.d.ts +1 -1
  30. package/dist/scope.d.ts +8 -9
  31. package/dist/scope.js +1 -1
  32. package/dist/trace.d.ts +1 -1
  33. package/dist/types-KavbvHix.d.ts +25 -0
  34. package/docs/README.md +2 -0
  35. package/docs/actions.md +80 -36
  36. package/docs/guide.md +142 -18
  37. package/docs/hmr.md +6 -5
  38. package/docs/migrating-from-effector.md +49 -16
  39. package/docs/migrating-from-rxjs.md +17 -6
  40. package/docs/oop.md +286 -0
  41. package/docs/package.md +4 -0
  42. package/docs/plugins.md +284 -0
  43. package/docs/react.md +17 -5
  44. package/docs/scope.md +76 -36
  45. package/docs/stores.md +263 -109
  46. package/llms-full.txt +1232 -231
  47. package/llms.txt +2 -0
  48. package/package.json +20 -2
  49. package/dist/types-mLaAx-EZ.d.ts +0 -15
@@ -1,5 +1,5 @@
1
- import { H as Task, R as EventMap, l as Effect } from "./action-creator-DKG_sIia.js";
2
- import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-B0AXKusc.js";
1
+ import { H as Task, R as EventMap, l as Effect } from "./action-creator-DHAEgwuh.js";
2
+ import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-Cxadz6uU.js";
3
3
  //#region src/utils/onAbort.d.ts
4
4
  export declare const onAbort: (ctx: {
5
5
  signal?: AbortSignal | undefined;
@@ -1 +1 @@
1
- import{a as e,c as t,i as n,o as r,s as i,t as a}from"./context-CctVmHLC.js";import{n as o,r as s,t as c}from"./delay-B_W1tnkS.js";const l=(e,t)=>t?.signal?new Promise((n,r)=>{let a=i(t,r);e.then(n,r).finally(a)}):e,u=async(e,t,o,s)=>{t?.signal?.throwIfAborted();let{ctx:c,unlink:u}=n(t??{}),d=a(c,e);r(c,e,`start`),i(c,()=>d(`cancel`));try{let e=await l(o(c),t);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(t,r)=>{let{ctx:i,unlink:a}=n(t);try{return await e(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(t,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=n(t),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(t=>t(e(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=e=>e>0?e:0,x=(e={})=>{let t=b(e.base??100),n=e.factor??2,r=b(e.max??1/0),i=e.jitter??!1;return e=>{let a=b(Math.min(r,t*n**(e-1)));return i?Math.random()*a||0:a}},S=(e,n)=>e.signal.aborted?Promise.reject(e.signal.reason):new Promise((r,a)=>{let o=[],s=`pending`,c=e=>{try{e()}catch(e){t(`cancel cleanup failed`,e)}},l=i(e,e=>{s=`cancelled`;for(let e of o.reverse())c(e);a(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(r),reject:u(a),onCancel:d})}catch(e){u(a)(e)}}),C=e=>t=>{let n=t.addContext,r=Object.entries(e);return t.addContext=e=>{n?.(e);for(let[t,n]of r)Object.assign(e,{[t]:(...t)=>n(e,...t)})},t},w=(e,t,n,r)=>{if(e.signal.aborted)return Promise.resolve();let a=r.leading??!1,c=r.waitForFn??!1;return u(`poll`,e,e=>new Promise((r,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof n==`function`?n(e):n)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let n=u++;d(e,e=>t(e,n)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};i(e,p),a?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function T(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>w(n,p(r,t),e,i)}let i=t;return w(e,(e,t)=>i(t),n,r)}function E(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const D=(e,t,n)=>typeof e==`function`?e(t,n):e,O=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),k=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=O(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),!(n<r)||!a(t,n))throw t;let o=D(i,n,t);o>0&&await c(e,o)}});function A(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>k(r,p(n,e),t??{})}let r=t;return k(e,(e,t)=>r(t),n)}function j(e,t,n={}){return u(`take`,e,r=>new Promise((a,o)=>{let{predicate:s}=n,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)};c.push(e.on(t,e=>{(!s||s(e))&&(l(),a(e))})),c.push(i(r,u)),c.push(i({signal:n.signal},u))}))}const M=(t,r,i)=>u(`timeout`,t,async t=>{let a=n(t),c=s(()=>a.ctx.abort(new DOMException(`Timed out after ${r}ms`,`TimeoutError`)),r);try{return await l(Promise.resolve(e(a.ctx,`run`,()=>i(a.ctx))),a.ctx)}finally{o(c),a.unlink()}});function N(e,t,n){if(typeof e==`number`){let n=t;return(t,r)=>M(r,e,p(n,t))}let r=t,i=n;return M(e,r,e=>i({signal:e.signal,abort:e.abort}))}export{_ as all,y as allSettled,x as backoff,S as cancellable,c as delay,C as effectsPlugin,i as onAbort,T as poll,E as race,A as retry,j as take,N as timeout};
1
+ import{a as e,c as t,i as n,o as r,s as i,t as a}from"./context-CctVmHLC.js";import{n as o,r as s,t as c}from"./delay-B_W1tnkS.js";const l=(e,t)=>t?.signal?new Promise((n,r)=>{let a=i(t,r);e.then(n,r).finally(a)}):e,u=async(e,t,o,s)=>{t?.signal?.throwIfAborted();let{ctx:c,unlink:u}=n(t??{}),d=a(c,e);r(c,e,`start`),i(c,()=>d(`cancel`));try{let e=await l(o(c),t);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(t,r)=>{let{ctx:i,unlink:a}=n(t);try{return await e(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(t,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=n(t),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(t=>t(e(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=e=>e>0?e:0,x=(e={})=>{let t=b(e.base??100),n=e.factor??2,r=b(e.max??1/0),i=e.jitter??!1;return e=>{let a=b(Math.min(r,t*n**(e-1)));return i?Math.random()*a||0:a}},S=(e,n)=>e.signal.aborted?Promise.reject(e.signal.reason):new Promise((r,a)=>{let o=[],s=`pending`,c=e=>{try{e()}catch(e){t(`cancel cleanup failed`,e)}},l=i(e,e=>{s=`cancelled`;for(let e of o.reverse())c(e);a(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(r),reject:u(a),onCancel:d})}catch(e){u(a)(e)}}),C=e=>t=>{let n=t.addContext,r=Object.entries(e);return t.addContext=e=>{n?.(e);for(let[t,n]of r)t in e||Object.assign(e,{[t]:(...t)=>n(e,...t)})},t},w=(e,t,n,r)=>{if(e.signal.aborted)return Promise.resolve();let a=r.leading??!1,c=r.waitForFn??!1;return u(`poll`,e,e=>new Promise((r,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof n==`function`?n(e):n)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let n=u++;d(e,e=>t(e,n)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};i(e,p),a?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function T(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>w(n,p(r,t),e,i)}let i=t;return w(e,(e,t)=>i(t),n,r)}function E(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const D=(e,t,n)=>typeof e==`function`?e(t,n):e,O=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),k=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=O(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),!(n<r)||!a(t,n))throw t;let o=D(i,n,t);o>0&&await c(e,o)}});function A(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>k(r,p(n,e),t??{})}let r=t;return k(e,(e,t)=>r(t),n)}function j(e,t,n={}){return u(`take`,e,r=>new Promise((a,o)=>{let{predicate:s}=n,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)};c.push(e.on(t,e=>{(!s||s(e))&&(l(),a(e))})),c.push(i(r,u)),c.push(i({signal:n.signal},u))}))}const M=(t,r,i)=>u(`timeout`,t,async t=>{let a=n(t),c=s(()=>a.ctx.abort(new DOMException(`Timed out after ${r}ms`,`TimeoutError`)),r);try{return await l(Promise.resolve(e(a.ctx,`run`,()=>i(a.ctx))),a.ctx)}finally{o(c),a.unlink()}});function N(e,t,n){if(typeof e==`number`){let n=t;return(t,r)=>M(r,e,p(n,t))}let r=t,i=n;return M(e,r,e=>i({signal:e.signal,abort:e.abort}))}export{_ as all,y as allSettled,x as backoff,S as cancellable,c as delay,C as effectsPlugin,i as onAbort,T as poll,E as race,A as retry,j as take,N as timeout};
@@ -1,3 +1,3 @@
1
- import { A as On, B as PayloadOf, C as EventKey, D as Handler, E as EventsOf, F as Tracer, G as TraceLogger, H as Task, I as anyEventType, K as UnsubscribeCallback, L as EffectName, M as WithType, N as commandCall, O as HandlerRef, P as createBus, R as EventMap, S as Emittable, T as EventPayload, U as Trace, V as Phase, W as TraceEvent, _ as Commands, b as ContextOption, d as Action, f as AnyEvent, g as CommandCall, h as Command, i as WithPayload, j as WithContext, k as Off, m as BusOptions, p as Bus, v as Context, w as EventLike, x as Emit, y as ContextOf, z as Keys } from "./action-creator-DKG_sIia.js";
2
- import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-Dt1IgVo8.js";
1
+ import { A as On, B as PayloadOf, C as EventKey, D as Handler, E as EventsOf, F as Tracer, G as TraceLogger, H as Task, I as anyEventType, K as UnsubscribeCallback, L as EffectName, M as WithType, N as commandCall, O as HandlerRef, P as createBus, R as EventMap, S as Emittable, T as EventPayload, U as Trace, V as Phase, W as TraceEvent, _ as Commands, b as ContextOption, d as Action, f as AnyEvent, g as CommandCall, h as Command, i as WithPayload, j as WithContext, k as Off, m as BusOptions, p as Bus, v as Context, w as EventLike, x as Emit, y as ContextOf, z as Keys } from "./action-creator-DHAEgwuh.js";
2
+ import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-BapQkGwQ.js";
3
3
  export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type CommandCall, type Commands, type Context, type ContextOf, type ContextOption, type ContextTask, type EffectName, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type EventsOf, type Handler, type HandlerRef, type Keys, type NoClash, type Off, type On, type PayloadOf, type Phase, type Task, type Trace, type TraceEvent, type TraceLogger, type Tracer, type UnsubscribeCallback, type WithContext, type WithPayload, type WithType, anyEventType, commandCall, contextPlugin, createBus };
@@ -1,5 +1,5 @@
1
- import { R as EventMap, c as BoundForm, j as WithContext, p as Bus, u as Effects, v as Context, z as Keys } from "./action-creator-DKG_sIia.js";
2
- import { n as NoClash } from "./context-plugin-Dt1IgVo8.js";
1
+ import { R as EventMap, c as BoundForm, j as WithContext, p as Bus, u as Effects, v as Context, z as Keys } from "./action-creator-DHAEgwuh.js";
2
+ import { n as NoClash } from "./context-plugin-BapQkGwQ.js";
3
3
  //#region src/utils/delay.d.ts
4
4
  /**
5
5
  * Resolves after `ms`, or rejects early if `ctx.signal` aborts. `ms` can be a
@@ -1,4 +1,4 @@
1
- import { H as Task, R as EventMap } from "./action-creator-DKG_sIia.js";
1
+ import { H as Task, R as EventMap } from "./action-creator-DHAEgwuh.js";
2
2
  //#region src/policies/keyed.d.ts
3
3
  /** Options shared by every policy that can keep its state per key. */
4
4
  type KeyOption<P> = {
@@ -1,4 +1,4 @@
1
- import { R as EventMap, p as Bus } from "./action-creator-DKG_sIia.js";
1
+ import { R as EventMap, p as Bus } from "./action-creator-DHAEgwuh.js";
2
2
  //#region src/store/redux/middleware.d.ts
3
3
  /** What Redux passes through a middleware: an action is a plain object with a `type`. */
4
4
  type ReduxAction = {
@@ -1,4 +1,4 @@
1
- import { B as EventMap, V as Keys, h as Bus } from "./action-creator-c292nK7v.js";
1
+ import { B as EventMap, V as Keys, h as Bus } from "./action-creator-BrZF6hXc.js";
2
2
  //#region src/observable.d.ts
3
3
  declare global {
4
4
  interface SymbolConstructor {
package/dist/oop.d.ts ADDED
@@ -0,0 +1,131 @@
1
+ import { B as EventMap, _ as Command, h as Bus, t as ActionCreator } from "./action-creator-BrZF6hXc.js";
2
+ //#region src/oop/types.d.ts
3
+ type Slots = "ctx" | "commands" | "trace" | "addContext" | "applyScope" | "fitScope" | "app" | "use";
4
+ type AnyFunction = (...args: never[]) => unknown;
5
+ /**
6
+ * The methods an instance of a class from `extendBus` gets: the function
7
+ * members of the bus (`on`, `once`, `off`, `emit`, and those of the plugins used
8
+ * before `extendBus`), `dispose`, and the bus itself as `bus`. A member of the
9
+ * class with the same name is kept and not replaced.
10
+ */
11
+ type BusMethods<B> = { [K in keyof B as K extends Slots ? never : B[K] extends AnyFunction ? K : never]: B[K]; } & {
12
+ /** Removes every subscription made through `on` and `once` of this instance. */
13
+ dispose(): void;
14
+ [Symbol.dispose](): void;
15
+ readonly bus: B;
16
+ };
17
+ /** Any class that `extendBus` can extend. */
18
+ type Constructor = new (...args: never[]) => object;
19
+ /**
20
+ * The class `extendBus` returns: it takes the arguments of `C`, its instances
21
+ * are instances of `C` with the methods of `B` that `C` does not have, and its
22
+ * static members are those of `C` plus the bus.
23
+ */
24
+ type Extended<C extends Constructor, B> = {
25
+ new (...args: ConstructorParameters<C>): InstanceType<C> & Omit<BusMethods<B>, keyof InstanceType<C>>;
26
+ readonly bus: B;
27
+ readonly prototype: InstanceType<C>;
28
+ } & Omit<C, "prototype">;
29
+ /** A wrapper for the handler of `@On` and `@Once`, such as `latest`. */
30
+ type Wrap = (handler: (payload: unknown, ctx: unknown) => unknown) => unknown;
31
+ type Handler<P> = (payload: P, ctx: never) => unknown;
32
+ /**
33
+ * What `@On(event)` and `@Once(event)` return: a method decorator of the
34
+ * language and of `experimentalDecorators`. The payload of the method is
35
+ * checked against the event when the event is an action creator or a command.
36
+ */
37
+ interface MethodDecorator<P> {
38
+ (value: Handler<P>, context: ClassMethodDecoratorContext): void;
39
+ (target: object, key: string | symbol, descriptor: TypedPropertyDescriptor<Handler<P>>): void;
40
+ }
41
+ /** The events a decorator takes. */
42
+ type EventOf<P> = ActionCreator<P> | Command<P, unknown>;
43
+ //#endregion
44
+ //#region src/oop/decorators.d.ts
45
+ /**
46
+ * A class decorator that does what `extendBus` does: `@WithBus(bus) class A {}`
47
+ * replaces `A` by `extendBus(bus, A)`, and `@WithBus() class A {}` by
48
+ * `extendBus(A)`. It works for the decorators of the language and for
49
+ * `experimentalDecorators`. TypeScript does not change the type of a decorated
50
+ * class, so declare what it gets next to it:
51
+ * `interface A extends BusMethods<typeof bus> {}`.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * interface SearchService extends BusMethods<typeof bus> {}
56
+ *
57
+ * @WithBus(bus)
58
+ * class SearchService {}
59
+ * ```
60
+ */
61
+ export declare function WithBus<B extends object>(bus: B): <C extends Constructor>(Base: C) => Extended<C, B>;
62
+ export declare function WithBus(): <C extends Constructor>(Base: C) => Extended<C, Bus<EventMap>>;
63
+ /**
64
+ * Makes a method of a class from `extendBus` or `@WithBus` a handler of `event`
65
+ * for each instance, with `this` bound to the instance, from the construction
66
+ * until `dispose()`. `wrap` takes the bound handler and returns the one to
67
+ * subscribe, such as `latest`. An event emitted inside the constructor of the
68
+ * base class does not reach it: the subscription is made right after.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * class SearchServiceBase {
73
+ * @On(queryChanged, latest) // a new query aborts the one before it
74
+ * async search(query: string, { signal }: Context) {}
75
+ * }
76
+ * ```
77
+ */
78
+ export declare function On<P>(event: EventOf<P>, wrap?: Wrap): MethodDecorator<P>;
79
+ export declare function On(event: string, wrap?: Wrap): MethodDecorator<never>;
80
+ /** Like `On`, but the handler runs at most once for an instance. */
81
+ export declare function Once<P>(event: EventOf<P>, wrap?: Wrap): MethodDecorator<P>;
82
+ export declare function Once(event: string, wrap?: Wrap): MethodDecorator<never>;
83
+ //#endregion
84
+ //#region src/oop/extend.d.ts
85
+ /**
86
+ * Makes a class from `Base` whose instances have the methods of a bus, with the
87
+ * subscriptions made through an instance ended by `dispose()`. `new Extended(…)`
88
+ * runs the constructor of `Base`; the class has the name of `Base`, and its
89
+ * instances are instances of `Base`. The members of `Base` win: a method called
90
+ * `emit` or `dispose` in `Base` is kept, and `disposeBus(this)` ends the
91
+ * subscriptions from there.
92
+ *
93
+ * Without a bus, `extendBus(Base)` makes one for the class (`Extended.bus`),
94
+ * shared by its instances. The methods of the plugins used on `bus` before this
95
+ * call are on the instances too; the ones of plugins used later are not.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * class SearchServiceBase {
100
+ * constructor(private api: Api) {
101
+ * this.on("query/changed", (query) => this.load(query)); // ends with dispose()
102
+ * }
103
+ * load(query: string) {}
104
+ * }
105
+ *
106
+ * export const SearchService = extendBus(bus, SearchServiceBase);
107
+ * const service = new SearchService(api);
108
+ * service.dispose(); // its subscriptions are gone
109
+ * ```
110
+ */
111
+ export declare function extendBus<C extends Constructor>(Base: C): Extended<C, Bus<EventMap>>;
112
+ export declare function extendBus<B extends object, C extends Constructor>(bus: B, Base: C): Extended<C, B>;
113
+ //#endregion
114
+ //#region src/oop/subscriptions.d.ts
115
+ /**
116
+ * Removes every subscription an instance made through `on` and `once`, and does
117
+ * nothing for any other value. A class with a `dispose` of its own calls it from
118
+ * there.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * class SearchService {
123
+ * dispose() {
124
+ * disposeBus(this); // the subscriptions of this instance are removed
125
+ * }
126
+ * }
127
+ * ```
128
+ */
129
+ export declare const disposeBus: (instance: unknown) => void;
130
+ //#endregion
131
+ export type { BusMethods, Extended, MethodDecorator };
package/dist/oop.js ADDED
@@ -0,0 +1 @@
1
+ import{n as e}from"./bus-BAs9x7IQ.js";const t=/* @__PURE__ */ new WeakMap,n=e=>typeof e==`function`,r=e=>Reflect.ownKeys(e).flatMap(t=>{let r=Reflect.getOwnPropertyDescriptor(e,t)?.value;return n(r)?[{key:t,value:r}]:[]}),i=/* @__PURE__ */ new WeakMap,a=e=>{let n=i.get(e);if(n)return n;let a=/* @__PURE__ */ new Set,o=[];for(let n=e;n&&n!==Object.prototype;n=Reflect.getPrototypeOf(n))for(let{key:e,value:i}of r(n)){if(e===`constructor`||a.has(e))continue;a.add(e);let n=t.get(i);n&&o.push({key:e,method:i,spec:n})}return i.set(e,o),o},o=/* @__PURE__ */ new WeakMap,s=e=>typeof e==`object`&&!!e||typeof e==`function`,c=e=>{let t=o.get(e);if(t)return t;let n=/* @__PURE__ */ new Set;return o.set(e,n),n},l=e=>{if(typeof e==`string`)return e;if(s(e)){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},u=(e,t,n)=>Reflect.apply(Reflect.get(e,t),e,n),d=(e,t,n,r)=>{let[i,a,...o]=r,s=c(e),l={event:i,handler:a,off:()=>{}},d=u(t,n,[i,n===`once`?(e,t)=>(s.delete(l),typeof a==`function`?Reflect.apply(a,null,[e,t]):void 0):a,...o]),f=typeof d==`function`?()=>d():()=>{};return l.off=f,s.add(l),()=>{s.delete(l),f()}},f=(e,t,n)=>{let[r,i]=n,a=c(e);r===void 0&&a.clear();for(let e of[...a])Object.is(l(e.event),l(r))&&(i===void 0||e.handler===i)&&(a.delete(e),e.off());return u(t,`off`,n)},p=e=>{if(!s(e))return;let t=o.get(e);if(t){o.delete(e);for(let e of t)e.off()}},m=/* @__PURE__ */ new Set([`on`,`once`,`off`,`emit`,`ctx`,`commands`,`trace`,`addContext`,`applyScope`,`fitScope`,`app`,`use`]),h=Reflect.get(Symbol,`dispose`),g=e=>typeof e==`function`,_=(e,t,n,r)=>{n in t||Object.defineProperty(e,n,{configurable:!0,enumerable:!1,...r})},v=/* @__PURE__ */ new WeakMap,y=(e,t)=>{let n=v.get(e);if(n)return n;let r={on:(...n)=>d(e,t,`on`,n),once:(...n)=>d(e,t,`once`,n),off:(...n)=>f(e,t,n)};return v.set(e,r),r},b=(e,t)=>(...n)=>Reflect.apply(Reflect.get(e,t),e,n),x=(e,t)=>{for(let{method:n,spec:r}of a(Reflect.getPrototypeOf(e)??{})){let i=(t,r)=>Reflect.apply(n,e,[t,r]),a=r.wrap?r.wrap(i):i;d(e,t,r.once?`once`:`on`,[r.event,a])}},S=(e,t,n)=>{for(let r of[`on`,`once`,`off`])_(e,t,r,{get(){return y(this,n)[r]}});_(e,t,`emit`,{value:b(n,`emit`)});for(let[r,i]of Object.entries(n))typeof i==`function`&&!m.has(r)&&_(e,t,r,{value:b(n,r)});let r=function(){p(this)};_(e,t,`dispose`,{value:r}),h&&_(e,t,h,{value:r}),_(e,t,`bus`,{get:()=>n})},C=(e,t)=>{let n=function(...r){let i=Reflect.construct(t,r,new.target??n);return x(i,e),i};return Object.setPrototypeOf(n,t),n.prototype=Object.create(t.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),Object.defineProperty(n,"name",{value:t.name,configurable:!0}),`bus`in t||Object.defineProperty(n,"bus",{value:e,configurable:!0}),S(n.prototype,t.prototype,e),n};function w(t,n){if(g(t))return C(e(),t);if(!n)throw TypeError(`extendBus: pass the class to extend`);return C(t,n)}function T(e){return t=>e?w(e,t):w(t)}const E=e=>{let[t,n,r]=e;return typeof n==`string`||typeof n==`symbol`?Reflect.get(Object(r),`value`):t},D=(e,n,r)=>((...i)=>{let a=E(i);typeof a==`function`&&t.set(a,{event:e,wrap:n,once:r})});function O(e,t){return D(e,t,!1)}function k(e,t){return D(e,t,!0)}export{O as On,k as Once,T as WithBus,p as disposeBus,w as extendBus};
@@ -1,5 +1,5 @@
1
- import { B as EventMap, N as WithContext, V as Keys, b as Context, d as Effects, h as Bus, l as BoundForm } from "./action-creator-c292nK7v.js";
2
- import { n as NoClash } from "./context-plugin-1pYmSQEG.js";
1
+ import { B as EventMap, N as WithContext, V as Keys, b as Context, d as Effects, h as Bus, l as BoundForm } from "./action-creator-BrZF6hXc.js";
2
+ import { n as NoClash } from "./context-plugin--HkNN1ha.js";
3
3
  //#region src/utils/delay.d.ts
4
4
  /**
5
5
  * Resolves after `ms`, or rejects early if `ctx.signal` aborts. `ms` can be a
@@ -1,4 +1,4 @@
1
- import { B as EventMap, W as Task } from "./action-creator-c292nK7v.js";
1
+ import { B as EventMap, W as Task } from "./action-creator-BrZF6hXc.js";
2
2
  //#region src/policies/keyed.d.ts
3
3
  /** Options shared by every policy that can keep its state per key. */
4
4
  type KeyOption<P> = {
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, T as EventKey, h as Bus, r as FreeActionCreator, t as ActionCreator } from "./action-creator-c292nK7v.js";
1
+ import { B as EventMap, T as EventKey, h as Bus, r as FreeActionCreator, t as ActionCreator } from "./action-creator-BrZF6hXc.js";
2
2
  //#region src/react/plugin.d.ts
3
3
  /** What `reactPlugin` adds to the bus. */
4
4
  /** Tells whether two values are the same: `Object.is` unless the caller gives another. */
package/dist/redux.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, h as Bus } from "./action-creator-c292nK7v.js";
1
+ import { B as EventMap, h as Bus } from "./action-creator-BrZF6hXc.js";
2
2
  //#region src/store/redux/middleware.d.ts
3
3
  /** What Redux passes through a middleware: an action is a plain object with a `type`. */
4
4
  type ReduxAction = {
package/dist/scope.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { B as EventMap, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect } from "./action-creator-c292nK7v.js";
2
- import { n as Scoped } from "./types-mLaAx-EZ.js";
1
+ import { B as EventMap, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect } from "./action-creator-BrZF6hXc.js";
2
+ import { n as ScopeSlots, r as Scoped } from "./types-KavbvHix.js";
3
3
  //#region src/scope/effect.d.ts
4
4
  /**
5
5
  * Marks a function whose first parameter is `{ signal }` as an effect, so that
6
6
  * as a value of a scope it is bound to the task: `ctx.first(items)`
7
7
  * calls `first(ctx, items)`. The type is the function without that parameter,
8
- * generics included, which `effects` cannot do: no annotation is needed, and
8
+ * generics included, which `$effects` cannot do: no annotation is needed, and
9
9
  * JavaScript editors show the exact hint.
10
10
  *
11
11
  * Only `scopePlugin` binds it; the function itself is returned unchanged.
@@ -23,21 +23,20 @@ import { n as Scoped } from "./types-mLaAx-EZ.js";
23
23
  * );
24
24
  * ```
25
25
  */
26
- export declare const effect: <A extends unknown[], R>(fn: (ctx: Signaled, ...args: A) => R) => ((...args: A) => R);
26
+ export declare function effect<A extends unknown[], R>(fn: (ctx: Signaled, ...args: A) => R): (...args: A) => R;
27
27
  //#endregion
28
28
  //#region src/scope/plugin.d.ts
29
29
  /**
30
30
  * Switches scopes on for a bus: the scope given to a command, a job, an
31
- * action or a module (an object whose keys go on `ctx`, with `effects` bound to
31
+ * action or a module (an object whose keys go on `ctx`, with `$effects` bound to
32
32
  * the task), and the second argument of `emit`, put fields on
33
33
  * the context of the tasks they are for. A stronger layer replaces a field of
34
34
  * the same name: `emit`, then action / command / job, then module, then the bus.
35
35
  * Fields of the library (`signal`, `emit`, `run`, …) are never replaced. Returns
36
36
  * the same bus.
37
37
  *
38
- * Use it after `effectsPlugin`, which assigns its effects over those of a scope
39
- * when it comes later, and before `modulePlugin`, which mounts its modules when
40
- * it is used. Without this plugin a scope is ignored.
38
+ * It works in any order with the other plugins. Without this plugin a scope is
39
+ * ignored.
41
40
  *
42
41
  * @example
43
42
  * ```ts
@@ -53,6 +52,6 @@ export declare const effect: <A extends unknown[], R>(fn: (ctx: Signaled, ...arg
53
52
  * bus.emit(getUser("1"), { http: fakeHttp }); // this call only
54
53
  * ```
55
54
  */
56
- export declare const scopePlugin: () => <E extends EventMap>(bus: Bus<E>) => Bus<E>;
55
+ export declare const scopePlugin: () => <E extends EventMap>(bus: Bus<E> & ScopeSlots) => Bus<E> & ScopeSlots;
57
56
  //#endregion
58
57
  export type { Effect, Effects, EmitScope, Scope, Scoped, Signaled };
package/dist/scope.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./scope-key-B7l7mUS5.js";const t=/* @__PURE__ */ new WeakSet,n=e=>(t.add(e),e),r=/* @__PURE__ */ new Set([`signal`,`abort`,`emit`,`on`,`off`,`run`,`type`,`payload`]),i=Symbol(`nanoeffects.scope.entry`),a=[],o=/* @__PURE__ */ new WeakSet,s=(e,n)=>{if(!n)return;for(let i of Object.keys(n)){let a=n[i];if(i!==`effects`&&!r.has(i)){let n=a;e[i]=typeof a==`function`&&t.has(a)?c(e,n):a}}let i=n.effects??{};for(let t of Object.keys(i)){let n=i[t];r.has(t)||(e[t]=c(e,n))}},c=(e,t)=>(...n)=>t(e,...n),l=(e,t)=>{s(e,t.module);for(let n of t.call)s(e,n)},u=t=>{if(typeof t==`object`&&t||typeof t==`function`)return Reflect.get(t,e)},d=e=>typeof e==`object`&&!!e,f=(e,t)=>{let n=u(e),r=d(e)?t[0]:void 0;if(!(n||r))return;let i=n&&r?[n,r]:[n??r];return{type:typeof e==`string`?e:e.type,payload:d(e)?e.payload:t[0],layers:i}},p=()=>e=>{let t,n=e=>{let n=e.run;if(o.has(n))return;let a=(a,...o)=>{let s=t;t=void 0;try{return n((t,n)=>{let o=e[i];return o&&r(n,o.call,o.module),a(t,n)},...o)}finally{t=s}};o.add(a),e.run=a},r=(e,t,r)=>{let a={call:t,module:r};e[i]=a,l(e,a),n(e)},s=e.addContext;e.addContext=e=>{s?.(e);let n=e;t&&n.type===t.type&&Object.is(n.payload,t.payload)&&r(n,t.layers,void 0)},e.applyScope=(e,t)=>{let n=e,o=n[i]?.call??a;r(n,o,t)},e.fitScope=(t,n)=>(r,i)=>(e.applyScope?.(i,t),Reflect.apply(n,null,[r,i]));let c=e.emit;return e.emit=(e,...n)=>{let r=f(e,n);if(!(r||t))return c(e,...n);let i=t;t=r;try{return d(e)?c(e):c(e,...n)}finally{t=i}},e};export{n as effect,p as scopePlugin};
1
+ import{t as e}from"./scope-key-B7l7mUS5.js";const t=/* @__PURE__ */ new WeakSet;function n(e){return t.add(e),e}const r=/* @__PURE__ */ new Set([`signal`,`abort`,`emit`,`on`,`off`,`run`,`type`,`payload`]),i=[],a=Symbol(`nanoeffects.scope.call`),o=Symbol(`nanoeffects.scope.module`),s=Symbol(`nanoeffects.scope.watched`),c=e=>e,l=e=>typeof e==`function`&&t.has(e),u=(e,t)=>(...n)=>Reflect.apply(t,null,[e,...n]),d=(e,t)=>{if(!t)return;for(let n in t)if(n!==`$effects`&&!r.has(n)){let r=t[n];e[n]=l(r)?u(e,r):r}let n=t.$effects;if(n)for(let t in n){let i=n[t];i&&!r.has(t)&&(e[t]=u(e,i))}},f=(e,t,n)=>{d(e,n);for(let n of t)d(e,n)},p=e=>typeof e==`object`&&!!e,m=e=>p(e),h=e=>(p(e)||typeof e==`function`)&&`type`in e,g=e=>h(e)?e.type:e,_=(e,t)=>p(e)&&h(e)?e.payload:t[0],v=t=>p(t)||typeof t==`function`?Reflect.get(t,e):void 0,y=(e,t)=>{let n=v(e),r=p(e)&&m(t[0])?t[0]:void 0;if(n&&r)return[n,r];let i=n??r;return i?[i]:void 0},b=()=>e=>{let t,n,r,l=e=>{let n=e.run;if(n[s])return;let r=(r,...i)=>{let s=t;t=void 0;try{return n((t,n)=>{let i=e[a];return i&&u(n,i,e[o]),r(t,n)},...i)}finally{t=s}};r[s]=!0,e.run=r},u=(e,t,n)=>{e[a]=t,n&&(e[o]=n),f(e,t,n),l(e)},d=e.addContext;e.addContext=e=>{d?.(e);let i=c(e);t&&i.type===n&&Object.is(i.payload,r)&&u(i,t,void 0)},e.applyScope=(e,t)=>{let n=c(e);u(n,n[a]??i,t)},e.fitScope=(t,n)=>typeof n==`function`?(r,i)=>(e.applyScope?.(i,t),Reflect.apply(n,null,[r,i])):n;let m=e.emit;return e.emit=(e,...i)=>{let a=y(e,i);if(!(a||t))return m(e,...i);let o=t,s=n,c=r;t=a,n=g(e),r=_(e,i);try{return p(e)?m(e):m(e,...i)}finally{t=o,n=s,r=c}},e};export{n as effect,b as scopePlugin};
package/dist/trace.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as Trace, K as TraceEvent, L as Tracer, q as TraceLogger } from "./action-creator-c292nK7v.js";
1
+ import { G as Trace, K as TraceEvent, L as Tracer, q as TraceLogger } from "./action-creator-BrZF6hXc.js";
2
2
  //#region src/trace/console.d.ts
3
3
  type ConsoleLoggerOptions = {
4
4
  /** Defaults to `globalThis.console`. */
@@ -0,0 +1,25 @@
1
+ import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-BrZF6hXc.js";
2
+ import { t as BoundOf } from "./plugin-DkXCvsmB.js";
3
+ //#region src/scope/types.d.ts
4
+ /**
5
+ * The slots `scopePlugin` fills for `modulePlugin`. They are not on `Bus`: a bus
6
+ * has them in its type once the plugin returned it.
7
+ */
8
+ interface ScopeSlots {
9
+ /** Puts the fields of a module's scope on the context of one of its tasks. Without it a module's scope is ignored. */
10
+ applyScope?: ((ctx: object, scope: Scope) => void) | undefined;
11
+ /** Wraps a handler so it runs with the fields of a module's scope. */
12
+ fitScope?: ((scope: Scope, fn: unknown) => unknown) | undefined;
13
+ }
14
+ type BoundEffects<E extends EventMap, S extends Scope> = S extends {
15
+ $effects: infer H extends Effects;
16
+ } ? BoundOf<H, E> : unknown;
17
+ /** The fields a scope gives a task: its values as they are and its `$effects` bound; `unknown` for an empty scope. */
18
+ type FieldsOfScope<E extends EventMap, S extends Scope> = [keyof S] extends [never] ? unknown : Omit<S, "$effects"> & BoundEffects<E, S>;
19
+ /**
20
+ * The event map `E` with the fields of a scope on top of its context fields.
21
+ * A field the scope sets replaces the one of the bus in the type too.
22
+ */
23
+ type Scoped<E extends EventMap, S extends Scope> = WithFields<E, FieldsOfScope<E, S>>;
24
+ //#endregion
25
+ export { ScopeSlots as n, Scoped as r, FieldsOfScope as t };
package/docs/README.md CHANGED
@@ -27,9 +27,11 @@ Code for every guide step: [`examples/guide.ts`](../examples/guide.ts) (type-che
27
27
  - [Actions, commands, jobs and modules](actions.md)
28
28
  - [Sign-up form in React](react.md) — one form: races, double submit, retry
29
29
  - [Context fields](context.md) — store or client on every task
30
+ - [Use the bus from a class](oop.md) — `extendBus`, `@WithBus`, `@On`, and `dispose()` for the subscriptions of an instance
30
31
  - [TypeScript in many files](typescript.md) — keep the types of the bus in commands and modules
31
32
  - [Errors and unheard events](errors.md)
32
33
  - [Package entries](package.md) — which import holds what
34
+ - [Plugins](plugins.md) — every plugin of a bus, what it returns, and the order to use them in
33
35
  - [Tracing](tracing.md) — loggers, Redux DevTools, `notrace`
34
36
  - [Observable](observable.md) — events as TC39 Observables
35
37
  - [Hot reload and dev diagnostics](hmr.md)
package/docs/actions.md CHANGED
@@ -8,14 +8,28 @@ Recipe without a UI framework: [`examples/modules.ts`](../examples/modules.ts).
8
8
 
9
9
  ## Return a value from `emit` with a command
10
10
 
11
+ The bus lives in one file and its events in another, so any module can import both without a cycle.
12
+
11
13
  ```js
14
+ // bus.js
12
15
  import { createBus } from "nanoeffects";
13
- import { createCommand } from "nanoeffects/actions";
14
16
 
15
- const bus = createBus();
17
+ // createBus — one bus for the app
18
+ export const bus = createBus();
19
+ ```
20
+
21
+ ```js
22
+ // math.js
23
+ import { createCommand } from "nanoeffects/actions";
16
24
 
17
25
  // createCommand — emit runs the handler and returns its result
18
- const double = createCommand("math/double", (value) => value * 2);
26
+ export const double = createCommand("math/double", (value) => value * 2);
27
+ ```
28
+
29
+ ```js
30
+ import { bus } from "./bus";
31
+ import { double } from "./math";
32
+
19
33
  bus.emit(double(21)); // 42
20
34
  ```
21
35
 
@@ -24,27 +38,30 @@ bus.emit(double(21)); // 42
24
38
  `nanoeffects/actions` has the pieces to name what a bus does, apart from the code that does it.
25
39
 
26
40
  ```js
41
+ // user.js
27
42
  import { createAction, createCommand, createJob } from "nanoeffects/actions";
28
43
 
29
44
  // createAction — event with a payload (like RTK createAction)
30
- const userLoaded = createAction("user/loaded");
31
- const loadFailed = createAction("user/failed");
45
+ export const userLoaded = createAction("user/loaded");
46
+ export const loadFailed = createAction("user/failed");
32
47
 
33
48
  // createCommand with a handler — emit returns what the handler returns
34
- const getUser = createCommand("user/get", (id, { signal }) => {
49
+ export const getUser = createCommand("user/get", (id, { signal }) => {
35
50
  return fetchUser(id, { signal });
36
51
  });
37
52
 
38
53
  // createCommand without a handler — a contract; register the work elsewhere
39
- const sendMail = createCommand("mail/send");
54
+ export const sendMail = createCommand("mail/send");
40
55
 
41
56
  // createJob — command that also emits success / fail actions
42
- const loadUser = createJob("user/load", fetchUser, {
57
+ export const loadUser = createJob("user/load", fetchUser, {
43
58
  success: userLoaded,
44
59
  fail: "user/failed",
45
60
  });
46
61
  ```
47
62
 
63
+ These are the events of the user domain, in a file of their own (`user.js`). The bus is made in another file (`bus.js` below), and neither imports the other.
64
+
48
65
  ### Actions
49
66
 
50
67
  `createAction(type)` returns a creator. Without generics it keeps the literal type; the bus checks the payload against its event map. Without an event map, give the payload type: `createAction<User>("user/loaded")`.
@@ -65,22 +82,22 @@ Calling a command builds `{ type, payload }` like `createAction`. `bus.emit(comm
65
82
  **Where handlers are registered.** The command may carry a handler. The bus can also take handlers from a registry (`commandsPlugin`). A handler bound there wins over the command’s own. Each bus (each test) has its own registry.
66
83
 
67
84
  ```js
85
+ // bus.js
68
86
  import { createBus } from "nanoeffects";
69
- import {
70
- bindHandler,
71
- commandsPlugin,
72
- createCommand,
73
- } from "nanoeffects/actions";
74
-
75
- const sendMail = createCommand("mail/send");
76
- const getUser = createCommand("user/get");
87
+ import { bindHandler, commandsPlugin } from "nanoeffects/actions";
88
+ import { sendMail } from "./user";
77
89
 
78
- const bus = createBus();
90
+ export const bus = createBus();
79
91
 
80
92
  // commandsPlugin — registry of handlers for this bus
81
- const commands = bus.use(
93
+ export const commands = bus.use(
82
94
  commandsPlugin([bindHandler(sendMail, smtpSend)]),
83
95
  );
96
+ ```
97
+
98
+ ```js
99
+ import { commands } from "./bus";
100
+ import { getUser } from "./user";
84
101
 
85
102
  // later, e.g. in a test — latest binding wins
86
103
  const unbind = commands.handle(getUser, async () => fakeUser);
@@ -89,7 +106,7 @@ unbind();
89
106
 
90
107
  A command with no handler anywhere throws `No handler for command "..."` from `emit` (the call itself only builds the action).
91
108
 
92
- The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the command’s own handler. `bus.use(commandsPlugin())` sets `bus.commands`. A second plugin on the same bus replaces it. There is no `commands` option in `createBus`; assign a custom runner with `bus.commands = { run }`.
109
+ The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the command’s own handler. `bus.use(commandsPlugin())` sets `bus.commands`. A second `commandsPlugin` binds its list in that registry and returns it. There is no `commands` option in `createBus`; assign a custom runner with `bus.commands = { run }`.
93
110
 
94
111
  ### Jobs
95
112
 
@@ -112,18 +129,20 @@ The registry lives in `nanoeffects/actions`, so a bus without one carries none o
112
129
  `bus.use(modulePlugin(modules))` assembles an app from modules and returns `mount`, `unmount`, `isMounted`, `dispose`, `commands`, and `createModule`: the same as `createModule` below, with the context fields of the bus typed (see [TypeScript](typescript.md)).
113
130
 
114
131
  ```js
115
- import { createBus } from "nanoeffects";
116
- import {
117
- createCommand,
118
- createModule,
119
- modulePlugin,
120
- } from "nanoeffects/actions";
132
+ // user.js
133
+ import { createCommand } from "nanoeffects/actions";
121
134
 
122
- const getUser = createCommand("user/get");
123
- const saveUser = createCommand("user/save", persistUser);
135
+ export const getUser = createCommand("user/get");
136
+ export const saveUser = createCommand("user/save", persistUser);
137
+ ```
138
+
139
+ ```js
140
+ // user-module.js
141
+ import { createModule } from "nanoeffects/actions";
142
+ import { getUser, saveUser } from "./user";
124
143
 
125
144
  // createModule — one domain; unmount undoes everything below
126
- const userModule = createModule("user", (m) => {
145
+ export const userModule = createModule("user", (m) => {
127
146
  m.handle(getUser, fetchUser);
128
147
  m.use(saveUser, authModule);
129
148
  m.on("login", (user, ctx) => {
@@ -133,10 +152,22 @@ const userModule = createModule("user", (m) => {
133
152
  /* background work; ends when the module unmounts */
134
153
  });
135
154
  });
155
+ ```
156
+
157
+ ```js
158
+ // bus.js
159
+ import { createBus } from "nanoeffects";
160
+ import { modulePlugin } from "nanoeffects/actions";
161
+ import { userModule } from "./user-module";
136
162
 
137
- const bus = createBus();
163
+ export const bus = createBus();
138
164
  // modulePlugin — mount modules on the bus
139
- const app = bus.use(modulePlugin([userModule]));
165
+ export const app = bus.use(modulePlugin([userModule]));
166
+ ```
167
+
168
+ ```js
169
+ import { app } from "./bus";
170
+ import { getUser } from "./user";
140
171
 
141
172
  const unmount = app.mount(orderModule); // later, e.g. after a lazy import
142
173
  app.commands.handle(getUser, fakeUser); // in a test
@@ -147,6 +178,7 @@ app.dispose(); // unmounts every module
147
178
  - **A module is known by its name.** Mounting a module whose name is taken replaces the mounted one (see [Hot reload](hmr.md)).
148
179
  - **Registry.** If `bus.use(commandsPlugin())` ran first, that registry is used; otherwise the module plugin attaches its own. Either way it is `bus.commands` and `app.commands`.
149
180
  - A disposed app cannot mount modules.
181
+ - **The given modules are mounted on the first use** of the app or the bus (the first `bus.emit`, or a call of `mount`, `unmount`, `isMounted`, `dispose`, or the read of `commands`), so the plugins you use after `modulePlugin` still reach them. The `bus.on` calls made in between are held and made right after, so the listeners of a module stay ahead of them. See [using plugins in any order](plugins.md#use-them-in-any-order).
150
182
  - Module context = task context plus `handle`, `use`, and `spawn`:
151
183
  - `handle` — give a command a handler (contract, or override).
152
184
  - `use` — bring sub-modules and commands that carry a handler; the returned function undoes that call.
@@ -155,20 +187,32 @@ app.dispose(); // unmounts every module
155
187
 
156
188
  ## Give a command, an action or a module its own fields
157
189
 
158
- A command can need its own client, a module its own logger. Pass a scope, a plain object whose keys go on `ctx` (`effects` binds functions to the task), as the last argument of `createCommand`, `createAction`, `createModule` and `createJob`, and turn it on with `scopePlugin`:
190
+ A command can need its own client, a module its own logger. Pass a scope, a plain object whose keys go on `ctx` (`$effects` binds functions to the task), as the last argument of `createCommand`, `createAction`, `createModule` and `createJob`, and turn it on with `scopePlugin`:
159
191
 
160
192
  ```js
193
+ // bus.js
161
194
  import { createBus } from "nanoeffects";
162
- import { createCommand } from "nanoeffects/actions";
163
195
  import { scopePlugin } from "nanoeffects/scope";
164
196
 
165
197
  // scopePlugin — makes the scope of a command work on this bus
166
- const bus = createBus().use(scopePlugin());
198
+ export const bus = createBus().use(scopePlugin());
199
+ ```
200
+
201
+ ```js
202
+ // user.js
203
+ import { createCommand } from "nanoeffects/actions";
167
204
 
168
205
  // the third argument — fields of this command only
169
- const getUser = createCommand("user/get", (id, { api }) => api.getUser(id), {
170
- api,
171
- });
206
+ export const getUser = createCommand(
207
+ "user/get",
208
+ (id, { api }) => api.getUser(id),
209
+ { api },
210
+ );
211
+ ```
212
+
213
+ ```js
214
+ import { bus } from "./bus";
215
+ import { getUser } from "./user";
172
216
 
173
217
  // a test replaces them for one call
174
218
  const fakeApi = { getUser: async (id) => ({ id, name: "test" }) };