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.
- package/dist/{action-creator-c292nK7v.d.ts → action-creator-BrZF6hXc.d.ts} +4 -8
- package/dist/actions.d.ts +6 -6
- package/dist/actions.js +1 -1
- package/dist/{context-plugin-1pYmSQEG.d.ts → context-plugin--HkNN1ha.d.ts} +1 -1
- package/dist/dev.d.ts +9 -8
- package/dist/dev.js +1 -1
- package/dist/effects.d.ts +2 -2
- package/dist/effects.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/match.d.ts +50 -0
- package/dist/match.js +1 -0
- package/dist/{module-BlF2KSgy.d.ts → module-DqKegXHt.d.ts} +44 -19
- package/dist/notrace/{action-creator-DKG_sIia.d.ts → action-creator-DHAEgwuh.d.ts} +4 -8
- package/dist/notrace/actions.d.ts +59 -24
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/{context-plugin-Dt1IgVo8.d.ts → context-plugin-BapQkGwQ.d.ts} +1 -1
- package/dist/notrace/effects.d.ts +2 -2
- package/dist/notrace/effects.js +1 -1
- package/dist/notrace/index.d.ts +2 -2
- package/dist/notrace/{plugin-B0AXKusc.d.ts → plugin-Cxadz6uU.d.ts} +2 -2
- package/dist/notrace/policies.d.ts +1 -1
- package/dist/notrace/redux.d.ts +1 -1
- package/dist/observable.d.ts +1 -1
- package/dist/oop.d.ts +131 -0
- package/dist/oop.js +1 -0
- package/dist/{plugin-CCYlC0XS.d.ts → plugin-DkXCvsmB.d.ts} +2 -2
- package/dist/policies.d.ts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/redux.d.ts +1 -1
- package/dist/scope.d.ts +8 -9
- package/dist/scope.js +1 -1
- package/dist/trace.d.ts +1 -1
- package/dist/types-KavbvHix.d.ts +25 -0
- package/docs/README.md +2 -0
- package/docs/actions.md +80 -36
- package/docs/guide.md +142 -18
- package/docs/hmr.md +6 -5
- package/docs/migrating-from-effector.md +49 -16
- package/docs/migrating-from-rxjs.md +17 -6
- package/docs/oop.md +286 -0
- package/docs/package.md +4 -0
- package/docs/plugins.md +284 -0
- package/docs/react.md +17 -5
- package/docs/scope.md +76 -36
- package/docs/stores.md +263 -109
- package/llms-full.txt +1232 -231
- package/llms.txt +2 -0
- package/package.json +20 -2
- 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-
|
|
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-
|
|
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;
|
package/dist/notrace/effects.js
CHANGED
|
@@ -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};
|
package/dist/notrace/index.d.ts
CHANGED
|
@@ -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-
|
|
2
|
-
import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-
|
|
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-
|
|
2
|
-
import { n as NoClash } from "./context-plugin-
|
|
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-
|
|
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> = {
|
package/dist/notrace/redux.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as EventMap, p as Bus } from "./action-creator-
|
|
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 = {
|
package/dist/observable.d.ts
CHANGED
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-
|
|
2
|
-
import { n as NoClash } from "./context-plugin
|
|
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
|
package/dist/policies.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as EventMap, W as Task } from "./action-creator-
|
|
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-
|
|
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-
|
|
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-
|
|
2
|
-
import { n as Scoped } from "./types-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
39
|
-
*
|
|
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
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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` (
|
|
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(
|
|
170
|
-
|
|
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" }) };
|