nanoeffects 0.6.0 → 0.7.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-Bd4TBZgl.d.ts} +5 -5
- package/dist/actions.d.ts +6 -6
- package/dist/actions.js +1 -1
- package/dist/{context-plugin-1pYmSQEG.d.ts → context-plugin-BVNKqkIx.d.ts} +1 -1
- package/dist/dev.d.ts +2 -2
- package/dist/effects.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/{module-BlF2KSgy.d.ts → module-DZ5ngUrM.d.ts} +3 -3
- package/dist/notrace/{action-creator-DKG_sIia.d.ts → action-creator-D-y_-xRS.d.ts} +5 -5
- package/dist/notrace/actions.d.ts +9 -9
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/{context-plugin-Dt1IgVo8.d.ts → context-plugin-DDfsaErA.d.ts} +1 -1
- package/dist/notrace/effects.d.ts +2 -2
- package/dist/notrace/index.d.ts +2 -2
- package/dist/notrace/{plugin-B0AXKusc.d.ts → plugin-CDAngHwg.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/{plugin-CCYlC0XS.d.ts → plugin-DQNPBcxx.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 +5 -5
- package/dist/scope.js +1 -1
- package/dist/trace.d.ts +1 -1
- package/dist/{types-mLaAx-EZ.d.ts → types-BJ0fzV1p.d.ts} +5 -5
- package/docs/README.md +1 -0
- package/docs/actions.md +1 -1
- package/docs/package.md +2 -0
- package/docs/plugins.md +108 -0
- package/docs/scope.md +16 -14
- package/llms-full.txt +131 -15
- package/llms.txt +1 -0
- package/package.json +1 -1
|
@@ -227,7 +227,7 @@ interface Bus<E extends EventMap> {
|
|
|
227
227
|
/** Puts the fields of a module's scope on the context of one of its tasks; `scopePlugin` sets it. Without it a module's scope is ignored. */
|
|
228
228
|
applyScope?: ((ctx: object, scope: Scope) => void) | undefined;
|
|
229
229
|
/** Wraps a handler so it runs with the fields of a module's scope; `scopePlugin` sets it. */
|
|
230
|
-
fitScope?: ((scope: Scope, fn:
|
|
230
|
+
fitScope?: ((scope: Scope, fn: unknown) => unknown) | undefined;
|
|
231
231
|
}
|
|
232
232
|
type BusOptions<E extends EventMap> = {
|
|
233
233
|
/** Tracing of every task phase; make it with `createTracer` from `nanoeffects/trace`. */
|
|
@@ -305,17 +305,17 @@ type WithScope<F> = {
|
|
|
305
305
|
type Carrying<X, F> = X & ([unknown] extends [F] ? unknown : WithScope<F>);
|
|
306
306
|
/**
|
|
307
307
|
* What a task gets on top of the context of its bus, for one place: every key
|
|
308
|
-
* is a context value and goes on `ctx` as it is, except
|
|
308
|
+
* is a context value and goes on `ctx` as it is, except `$effects`, whose
|
|
309
309
|
* functions (each takes `{ signal }` first) are bound to the task. Made work by
|
|
310
310
|
* `scopePlugin` from `nanoeffects/scope`; without it a scope is ignored.
|
|
311
311
|
*
|
|
312
312
|
* @example
|
|
313
313
|
* ```ts
|
|
314
|
-
* const scope = { cache, effects: { race } }; // ctx.cache, and ctx.race bound to the task
|
|
314
|
+
* const scope = { cache, $effects: { race } }; // ctx.cache, and ctx.race bound to the task
|
|
315
315
|
* ```
|
|
316
316
|
*/
|
|
317
317
|
type Scope = {
|
|
318
|
-
readonly effects?: Effects;
|
|
318
|
+
readonly $effects?: Effects;
|
|
319
319
|
readonly [field: string]: unknown;
|
|
320
320
|
};
|
|
321
321
|
/**
|
|
@@ -325,7 +325,7 @@ type Scope = {
|
|
|
325
325
|
* an error.
|
|
326
326
|
*/
|
|
327
327
|
type EmitScope<E extends EventMap, F = unknown> = ([unknown] extends [ContextOf<E> & F] ? unknown : Partial<ContextOf<E> & F>) & {
|
|
328
|
-
effects?: Partial<Effects>;
|
|
328
|
+
$effects?: Partial<Effects>;
|
|
329
329
|
};
|
|
330
330
|
/** `A` with the fields of `B` replacing those of the same name, not intersected with them. */
|
|
331
331
|
type Merge<A, B> = Omit<A, keyof B> & B;
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { B as EventMap, H as PayloadOf, T as EventKey, W as Task, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator } from "./action-creator-
|
|
2
|
-
import { n as Scoped, t as FieldsOfScope } from "./types-
|
|
3
|
-
import { a as modulePlugin, c as MadeCommand, d as createCommand, i as createModule, l as bindHandler, n as ModuleApi, o as Binding, r as ModuleContext, s as CarryingCalls, t as Module, u as commandsPlugin } from "./module-
|
|
1
|
+
import { B as EventMap, H as PayloadOf, T as EventKey, W as Task, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator } from "./action-creator-Bd4TBZgl.js";
|
|
2
|
+
import { n as Scoped, t as FieldsOfScope } from "./types-BJ0fzV1p.js";
|
|
3
|
+
import { a as modulePlugin, c as MadeCommand, d as createCommand, i as createModule, l as bindHandler, n as ModuleApi, o as Binding, r as ModuleContext, s as CarryingCalls, t as Module, u as commandsPlugin } from "./module-DZ5ngUrM.js";
|
|
4
4
|
//#region src/actions/action.d.ts
|
|
5
5
|
type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCreator<T, F> & WithScope<F> : ActionCreator<P, T, PayloadOf<P>, F> & WithScope<F>;
|
|
6
6
|
/**
|
|
@@ -15,7 +15,7 @@ type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCrea
|
|
|
15
15
|
* generic: `createAction<User, "user/loaded">("user/loaded")`.
|
|
16
16
|
* - With `prepare` the arguments of the creator can differ from the payload,
|
|
17
17
|
* and both types are inferred.
|
|
18
|
-
* - A scope as the last argument, `{ cache, effects: { race } }`, gives the
|
|
18
|
+
* - A scope as the last argument, `{ cache, $effects: { race } }`, gives the
|
|
19
19
|
* listeners of its calls those context fields; it works on a bus that uses `scopePlugin`
|
|
20
20
|
* from `nanoeffects/scope` and is ignored on any other.
|
|
21
21
|
*
|
|
@@ -102,7 +102,7 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCom
|
|
|
102
102
|
* effect. It returns the command, with the two actions on it: `job.success` and
|
|
103
103
|
* `job.fail`.
|
|
104
104
|
*
|
|
105
|
-
* A scope as the last argument, `{ cache, effects: { race } }`, gives the handler
|
|
105
|
+
* A scope as the last argument, `{ cache, $effects: { race } }`, gives the handler
|
|
106
106
|
* and the listeners of the outcome those context fields; see `scopePlugin`.
|
|
107
107
|
*
|
|
108
108
|
* `success` and `fail` are a type string, from which the job makes an action
|
|
@@ -135,7 +135,7 @@ export declare const createJob: <P = void, R = void, T extends string = string,
|
|
|
135
135
|
/** Gets the error when the handler fails: a type to make an action creator of, or an action creator. */
|
|
136
136
|
fail?: F;
|
|
137
137
|
},
|
|
138
|
-
/** Context values and
|
|
138
|
+
/** Context values and `$effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
|
|
139
139
|
scope?: X) => CarryingCalls<Job<P, R, T, S, F>, P, Promise<Awaited<R> | undefined>, FieldsOfScope<E, X>>;
|
|
140
140
|
//#endregion
|
|
141
141
|
export { type ActionCreator, type Binding, type EmitterApi, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, commandsPlugin, createCommand, createModule, modulePlugin };
|
package/dist/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-CK0ZomWt.js";import{t as r}from"./event-0RIBGYqS.js";import{r as i}from"./bus-BAs9x7IQ.js";import{t as a}from"./scope-key-B7l7mUS5.js";const o=(e,t)=>(t&&Object.defineProperty(e,a,{value:t}),e);function
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-CK0ZomWt.js";import{t as r}from"./event-0RIBGYqS.js";import{r as i}from"./bus-BAs9x7IQ.js";import{t as a}from"./scope-key-B7l7mUS5.js";const o=(e,t)=>(t&&Object.defineProperty(e,a,{value:t}),e),s=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),c=e=>Reflect.get(e,a);function l(e,t,n){let r=typeof t==`function`?t:void 0,i=typeof t==`function`?n:t;return Object.assign(o((...t)=>o({type:e,payload:r?r(...t).payload:t[0]},i),i),{type:e,match:t=>t.type===e,toString:()=>e})}var u=class extends Error{};function d(e,t,n){let r=t=>t.type===e;function a(r,a){if(typeof a==`object`){if(!t)throw new u(`No handler for command "${e}"`);return t(r,a)}return o(i(c,e,r),n)}let s=Object.assign(a,{type:e,match:r}),c=o(s,n);return c}const f=(e,t)=>[e,t],p=(e=[])=>t=>{let n=/* @__PURE__ */ new Map,i=(e,t)=>{let r={fn:t};return n.set(e,[...n.get(e)??[],r]),()=>{let t=(n.get(e)??[]).filter(e=>e!==r);t.length>0?n.set(e,t):n.delete(e)}};function a(e,t){if(t)return i(r(e),t);let n=e.map(([e,t])=>i(r(e),t));return()=>{for(let e of n)e()}}a(e);let o={run:(e,t,r)=>{let i=(n.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,r])},handle:a};return t.commands=o,o},m=()=>e=>{function t(t){return(...n)=>{let r=t(...n);e.emit(r)}}return Object.assign(e,{emitter:t})},h=e=>typeof e==`string`?l(e):e,g=(e,t,n,r)=>{let i=c(t);e.emit(Reflect.apply(t,null,[n]),r&&i?s(r,i):r)},_=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof u)throw e;return Promise.reject(e)}},v=(e,t,n={},r)=>{let a=d(e,t),s=h(n.success??`${e}/success`),c=h(n.fail??`${e}/fail`),l=(e,t,n)=>{let i=n;return _(()=>Reflect.apply(e,null,[t,n])).then(e=>(g(i,s,e,r),e),e=>{g(i,c,e,r)})},u=e=>(t,n)=>l(e,t,n);function f(t,n){return typeof n==`object`?l(a,t,n):o(i(m,e,t),r)}let p=Object.assign(f,{type:e,match:a.match,wrap:u,success:s,fail:c}),m=o(p,r);return m};function y(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const b=(e,t)=>n=>t?e.fitScope?.(t,n)??n:n,x=(e,t,n)=>{n&&e.applyScope?.(t,n)},S=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],C=(e,t)=>{let n=e.on;return(...e)=>{let[r,i]=e;return Reflect.apply(n,null,[r,t(i)])}},w=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},T=(r=[])=>i=>{let a=i.commands??p()(i),o={handle:(...e)=>{if(!a.handle)throw Error("modulePlugin: the `commands` of the bus is not a registry from `commandsPlugin()`");return Reflect.apply(a.handle,null,e)}},s=n({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),c=n=>{let r=/* @__PURE__ */ new Map,a=/* @__PURE__ */ new Map;return{mount:s=>{let l=r.get(s);if(l)return l;let u=a.get(s.name);u&&r.get(u)?.();let d=t(n,s.name),f=d.ctx,p=c(f),m=()=>{r.delete(s),a.get(s.name)===s&&a.delete(s.name),f.abort(),d.unlink()};r.set(s,m),a.set(s.name,s);let h=s.scope,g=b(i,h),_=(t,n)=>{let r=Reflect.apply(o.handle,null,S(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=w(f),T=C(f,g);try{x(i,f,h),s.setup(Object.assign(f,{handle:_,use:v,spawn:y,on:T}))}catch(e){throw m(),e}return m},unmount:e=>r.get(e)?.(),isMounted:e=>r.has(e)}},l=c(s.ctx);for(let e of r)l.mount(e);return{commands:a,mount:e=>(s.ctx.signal.throwIfAborted(),l.mount(e)),unmount:l.unmount,isMounted:l.isMounted,dispose:()=>s.ctx.abort(),createModule:y}};export{f as bindHandler,p as commandsPlugin,l as createAction,d as createCommand,v as createJob,y as createModule,m as emitterPlugin,T as modulePlugin};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as EventMap, N as WithContext, h as Bus, x as ContextOf } from "./action-creator-
|
|
1
|
+
import { B as EventMap, N as WithContext, h as Bus, x as ContextOf } from "./action-creator-Bd4TBZgl.js";
|
|
2
2
|
//#region src/core/context-plugin.d.ts
|
|
3
3
|
/** What a function that makes the fields of a task gets to see. */
|
|
4
4
|
type ContextTask = {
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as EventMap, J as UnsubscribeCallback, h as Bus } from "./action-creator-
|
|
2
|
-
import { n as ModuleApi } from "./module-
|
|
1
|
+
import { B as EventMap, J as UnsubscribeCallback, h as Bus } from "./action-creator-Bd4TBZgl.js";
|
|
2
|
+
import { n as ModuleApi } from "./module-DZ5ngUrM.js";
|
|
3
3
|
//#region src/dev/types.d.ts
|
|
4
4
|
/** The parts of what `modulePlugin` returned that the diagnostics report. */
|
|
5
5
|
type ReportedApp<E extends EventMap> = Pick<ModuleApi<E>, "mount" | "unmount" | "isMounted">;
|
package/dist/effects.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as EventMap, W as Task, u 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 { B as EventMap, W as Task, u as Effect } from "./action-creator-Bd4TBZgl.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-DQNPBcxx.js";
|
|
3
3
|
//#region src/utils/onAbort.d.ts
|
|
4
4
|
export declare const onAbort: (ctx: {
|
|
5
5
|
signal?: AbortSignal | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as HandlerRef, B as EventMap, C as Emit, D as EventPayload, E as EventLike, F as commandCall, G as Trace, H as PayloadOf, I as createBus, J as UnsubscribeCallback, K as TraceEvent, L as Tracer, M as On, N as WithContext, O as EventsOf, P as WithType, R as anyEventType, S as ContextOption, T as EventKey, U as Phase, V as Keys, W as Task, _ as Command, b as Context, g as BusOptions, h as Bus, i as WithPayload, j as Off, k as Handler, m as AnyEvent, p as Action, q as TraceLogger, v as CommandCall, w as Emittable, x as ContextOf, y as Commands, z as EffectName } from "./action-creator-
|
|
2
|
-
import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-
|
|
1
|
+
import { A as HandlerRef, B as EventMap, C as Emit, D as EventPayload, E as EventLike, F as commandCall, G as Trace, H as PayloadOf, I as createBus, J as UnsubscribeCallback, K as TraceEvent, L as Tracer, M as On, N as WithContext, O as EventsOf, P as WithType, R as anyEventType, S as ContextOption, T as EventKey, U as Phase, V as Keys, W as Task, _ as Command, b as Context, g as BusOptions, h as Bus, i as WithPayload, j as Off, k as Handler, m as AnyEvent, p as Action, q as TraceLogger, v as CommandCall, w as Emittable, x as ContextOf, y as Commands, z as EffectName } from "./action-creator-Bd4TBZgl.js";
|
|
2
|
+
import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-BVNKqkIx.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 { B as EventMap, H as PayloadOf, J as UnsubscribeCallback, W as Task, _ as Command, b as Context, c as WithScope, h as Bus, o as Scope, v as CommandCall } from "./action-creator-
|
|
2
|
-
import { n as Scoped, t as FieldsOfScope } from "./types-
|
|
1
|
+
import { B as EventMap, H as PayloadOf, J as UnsubscribeCallback, W as Task, _ as Command, b as Context, c as WithScope, h as Bus, o as Scope, v as CommandCall } from "./action-creator-Bd4TBZgl.js";
|
|
2
|
+
import { n as Scoped, t as FieldsOfScope } from "./types-BJ0fzV1p.js";
|
|
3
3
|
//#region src/actions/command.d.ts
|
|
4
4
|
/** What `createCommand` returns: a command that also tells whether an action is of its type. */
|
|
5
5
|
type MadeCommand<P, R, T extends string> = Command<P, R> & {
|
|
@@ -33,7 +33,7 @@ type ScopedCommand<P, R, T extends string, F> = ((payload: P) => CommandCall<P,
|
|
|
33
33
|
* `command.match(action)` tells whether an action has the type of the command,
|
|
34
34
|
* as it does for an action creator.
|
|
35
35
|
*
|
|
36
|
-
* A scope as the third argument, `{ cache, effects: { race } }`, puts those fields on
|
|
36
|
+
* A scope as the third argument, `{ cache, $effects: { race } }`, puts those fields on
|
|
37
37
|
* the context of the handler and of the listeners of the command; the handler
|
|
38
38
|
* sees them typed. It works on a bus that uses `scopePlugin` from
|
|
39
39
|
* `nanoeffects/scope` and is ignored on any other.
|
|
@@ -227,7 +227,7 @@ interface Bus<E extends EventMap> {
|
|
|
227
227
|
/** Puts the fields of a module's scope on the context of one of its tasks; `scopePlugin` sets it. Without it a module's scope is ignored. */
|
|
228
228
|
applyScope?: ((ctx: object, scope: Scope) => void) | undefined;
|
|
229
229
|
/** Wraps a handler so it runs with the fields of a module's scope; `scopePlugin` sets it. */
|
|
230
|
-
fitScope?: ((scope: Scope, fn:
|
|
230
|
+
fitScope?: ((scope: Scope, fn: unknown) => unknown) | undefined;
|
|
231
231
|
}
|
|
232
232
|
type BusOptions<E extends EventMap> = {
|
|
233
233
|
/** Tracing of every task phase; make it with `createTracer` from `nanoeffects/trace`. */
|
|
@@ -305,17 +305,17 @@ type WithScope<F> = {
|
|
|
305
305
|
type Carrying<X, F> = X & ([unknown] extends [F] ? unknown : WithScope<F>);
|
|
306
306
|
/**
|
|
307
307
|
* What a task gets on top of the context of its bus, for one place: every key
|
|
308
|
-
* is a context value and goes on `ctx` as it is, except
|
|
308
|
+
* is a context value and goes on `ctx` as it is, except `$effects`, whose
|
|
309
309
|
* functions (each takes `{ signal }` first) are bound to the task. Made work by
|
|
310
310
|
* `scopePlugin` from `nanoeffects/scope`; without it a scope is ignored.
|
|
311
311
|
*
|
|
312
312
|
* @example
|
|
313
313
|
* ```ts
|
|
314
|
-
* const scope = { cache, effects: { race } }; // ctx.cache, and ctx.race bound to the task
|
|
314
|
+
* const scope = { cache, $effects: { race } }; // ctx.cache, and ctx.race bound to the task
|
|
315
315
|
* ```
|
|
316
316
|
*/
|
|
317
317
|
type Scope = {
|
|
318
|
-
readonly effects?: Effects;
|
|
318
|
+
readonly $effects?: Effects;
|
|
319
319
|
readonly [field: string]: unknown;
|
|
320
320
|
};
|
|
321
321
|
/**
|
|
@@ -325,7 +325,7 @@ type Scope = {
|
|
|
325
325
|
* an error.
|
|
326
326
|
*/
|
|
327
327
|
type EmitScope<E extends EventMap, F = unknown> = ([unknown] extends [ContextOf<E> & F] ? unknown : Partial<ContextOf<E> & F>) & {
|
|
328
|
-
effects?: Partial<Effects>;
|
|
328
|
+
$effects?: Partial<Effects>;
|
|
329
329
|
};
|
|
330
330
|
/** `A` with the fields of `B` replacing those of the same name, not intersected with them. */
|
|
331
331
|
type Merge<A, B> = Omit<A, keyof B> & B;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { B as PayloadOf, C as EventKey, H as Task, K as UnsubscribeCallback, R as EventMap, a as Scope, g as CommandCall, h as Command, n as Free, o as WithFields, p as Bus, r as FreeActionCreator, s as WithScope, t as ActionCreator, u as Effects, v as Context } from "./action-creator-
|
|
2
|
-
import { t as BoundOf } from "./plugin-
|
|
1
|
+
import { B as PayloadOf, C as EventKey, H as Task, K as UnsubscribeCallback, R as EventMap, a as Scope, g as CommandCall, h as Command, n as Free, o as WithFields, p as Bus, r as FreeActionCreator, s as WithScope, t as ActionCreator, u as Effects, v as Context } from "./action-creator-D-y_-xRS.js";
|
|
2
|
+
import { t as BoundOf } from "./plugin-CDAngHwg.js";
|
|
3
3
|
//#region src/scope/types.d.ts
|
|
4
4
|
type BoundEffects<E extends EventMap, S extends Scope> = S extends {
|
|
5
|
-
effects: infer H extends Effects;
|
|
5
|
+
$effects: infer H extends Effects;
|
|
6
6
|
} ? BoundOf<H, E> : unknown;
|
|
7
|
-
/** The fields a scope gives a task: its values as they are and its
|
|
8
|
-
type FieldsOfScope<E extends EventMap, S extends Scope> = [keyof S] extends [never] ? unknown : Omit<S, "effects"> & BoundEffects<E, S>;
|
|
7
|
+
/** The fields a scope gives a task: its values as they are and its `$effects` bound; `unknown` for an empty scope. */
|
|
8
|
+
type FieldsOfScope<E extends EventMap, S extends Scope> = [keyof S] extends [never] ? unknown : Omit<S, "$effects"> & BoundEffects<E, S>;
|
|
9
9
|
/**
|
|
10
10
|
* The event map `E` with the fields of a scope on top of its context fields.
|
|
11
11
|
* A field the scope sets replaces the one of the bus in the type too.
|
|
@@ -26,7 +26,7 @@ type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCrea
|
|
|
26
26
|
* generic: `createAction<User, "user/loaded">("user/loaded")`.
|
|
27
27
|
* - With `prepare` the arguments of the creator can differ from the payload,
|
|
28
28
|
* and both types are inferred.
|
|
29
|
-
* - A scope as the last argument, `{ cache, effects: { race } }`, gives the
|
|
29
|
+
* - A scope as the last argument, `{ cache, $effects: { race } }`, gives the
|
|
30
30
|
* listeners of its calls those context fields; it works on a bus that uses `scopePlugin`
|
|
31
31
|
* from `nanoeffects/scope` and is ignored on any other.
|
|
32
32
|
*
|
|
@@ -87,7 +87,7 @@ type ScopedCommand<P, R, T extends string, F> = ((payload: P) => CommandCall<P,
|
|
|
87
87
|
* `command.match(action)` tells whether an action has the type of the command,
|
|
88
88
|
* as it does for an action creator.
|
|
89
89
|
*
|
|
90
|
-
* A scope as the third argument, `{ cache, effects: { race } }`, puts those fields on
|
|
90
|
+
* A scope as the third argument, `{ cache, $effects: { race } }`, puts those fields on
|
|
91
91
|
* the context of the handler and of the listeners of the command; the handler
|
|
92
92
|
* sees them typed. It works on a bus that uses `scopePlugin` from
|
|
93
93
|
* `nanoeffects/scope` and is ignored on any other.
|
|
@@ -208,7 +208,7 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCom
|
|
|
208
208
|
* effect. It returns the command, with the two actions on it: `job.success` and
|
|
209
209
|
* `job.fail`.
|
|
210
210
|
*
|
|
211
|
-
* A scope as the last argument, `{ cache, effects: { race } }`, gives the handler
|
|
211
|
+
* A scope as the last argument, `{ cache, $effects: { race } }`, gives the handler
|
|
212
212
|
* and the listeners of the outcome those context fields; see `scopePlugin`.
|
|
213
213
|
*
|
|
214
214
|
* `success` and `fail` are a type string, from which the job makes an action
|
|
@@ -241,7 +241,7 @@ export declare const createJob: <P = void, R = void, T extends string = string,
|
|
|
241
241
|
/** Gets the error when the handler fails: a type to make an action creator of, or an action creator. */
|
|
242
242
|
fail?: F;
|
|
243
243
|
},
|
|
244
|
-
/** Context values and
|
|
244
|
+
/** Context values and `$effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
|
|
245
245
|
scope?: X) => CarryingCalls<Job<P, R, T, S, F>, P, Promise<Awaited<R> | undefined>, FieldsOfScope<E, X>>;
|
|
246
246
|
//#endregion
|
|
247
247
|
//#region src/actions/module.d.ts
|
package/dist/notrace/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e,n as t,s as n}from"./context-CctVmHLC.js";import{a as r,r as i}from"./bus-Bdk0YyFQ.js";const a=Symbol.for(`nanoeffects.scope`),o=(e,t)=>(t&&Object.defineProperty(e,a,{value:t}),e);function
|
|
1
|
+
import{i as e,n as t,s as n}from"./context-CctVmHLC.js";import{a as r,r as i}from"./bus-Bdk0YyFQ.js";const a=Symbol.for(`nanoeffects.scope`),o=(e,t)=>(t&&Object.defineProperty(e,a,{value:t}),e),s=(e,t)=>({...e,...t,$effects:{...e.$effects,...t.$effects}}),c=e=>Reflect.get(e,a);function l(e,t,n){let r=typeof t==`function`?t:void 0,i=typeof t==`function`?n:t;return Object.assign(o((...t)=>o({type:e,payload:r?r(...t).payload:t[0]},i),i),{type:e,match:t=>t.type===e,toString:()=>e})}var u=class extends Error{};function d(e,t,n){let r=t=>t.type===e;function a(r,a){if(typeof a==`object`){if(!t)throw new u(`No handler for command "${e}"`);return t(r,a)}return o(i(c,e,r),n)}let s=Object.assign(a,{type:e,match:r}),c=o(s,n);return c}const f=(e,t)=>[e,t],p=(e=[])=>t=>{let n=/* @__PURE__ */ new Map,i=(e,t)=>{let r={fn:t};return n.set(e,[...n.get(e)??[],r]),()=>{let t=(n.get(e)??[]).filter(e=>e!==r);t.length>0?n.set(e,t):n.delete(e)}};function a(e,t){if(t)return i(r(e),t);let n=e.map(([e,t])=>i(r(e),t));return()=>{for(let e of n)e()}}a(e);let o={run:(e,t,r)=>{let i=(n.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,r])},handle:a};return t.commands=o,o},m=()=>e=>{function t(t){return(...n)=>{let r=t(...n);e.emit(r)}}return Object.assign(e,{emitter:t})},h=e=>typeof e==`string`?l(e):e,g=(e,t,n,r)=>{let i=c(t);e.emit(Reflect.apply(t,null,[n]),r&&i?s(r,i):r)},_=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof u)throw e;return Promise.reject(e)}},v=(e,t,n={},r)=>{let a=d(e,t),s=h(n.success??`${e}/success`),c=h(n.fail??`${e}/fail`),l=(e,t,n)=>{let i=n;return _(()=>Reflect.apply(e,null,[t,n])).then(e=>(g(i,s,e,r),e),e=>{g(i,c,e,r)})},u=e=>(t,n)=>l(e,t,n);function f(t,n){return typeof n==`object`?l(a,t,n):o(i(m,e,t),r)}let p=Object.assign(f,{type:e,match:a.match,wrap:u,success:s,fail:c}),m=o(p,r);return m};function y(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const b=(e,t)=>n=>t?e.fitScope?.(t,n)??n:n,x=(e,t,n)=>{n&&e.applyScope?.(t,n)},S=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],C=(e,t)=>{let n=e.on;return(...e)=>{let[r,i]=e;return Reflect.apply(n,null,[r,t(i)])}},w=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},T=(r=[])=>i=>{let a=i.commands??p()(i),o={handle:(...e)=>{if(!a.handle)throw Error("modulePlugin: the `commands` of the bus is not a registry from `commandsPlugin()`");return Reflect.apply(a.handle,null,e)}},s=t({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),c=t=>{let r=/* @__PURE__ */ new Map,a=/* @__PURE__ */ new Map;return{mount:s=>{let l=r.get(s);if(l)return l;let u=a.get(s.name);u&&r.get(u)?.();let d=e(t,s.name),f=d.ctx,p=c(f),m=()=>{r.delete(s),a.get(s.name)===s&&a.delete(s.name),f.abort(),d.unlink()};r.set(s,m),a.set(s.name,s);let h=s.scope,g=b(i,h),_=(e,t)=>{let r=Reflect.apply(o.handle,null,S(g,e,t));return n(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=w(f),T=C(f,g);try{x(i,f,h),s.setup(Object.assign(f,{handle:_,use:v,spawn:y,on:T}))}catch(e){throw m(),e}return m},unmount:e=>r.get(e)?.(),isMounted:e=>r.has(e)}},l=c(s.ctx);for(let e of r)l.mount(e);return{commands:a,mount:e=>(s.ctx.signal.throwIfAborted(),l.mount(e)),unmount:l.unmount,isMounted:l.isMounted,dispose:()=>s.ctx.abort(),createModule:y}};export{f as bindHandler,p as commandsPlugin,l as createAction,d as createCommand,v as createJob,y as createModule,m as emitterPlugin,T as modulePlugin};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as EventMap, j as WithContext, p as Bus, y as ContextOf } from "./action-creator-
|
|
1
|
+
import { R as EventMap, j as WithContext, p as Bus, y as ContextOf } from "./action-creator-D-y_-xRS.js";
|
|
2
2
|
//#region src/core/context-plugin.d.ts
|
|
3
3
|
/** What a function that makes the fields of a task gets to see. */
|
|
4
4
|
type ContextTask = {
|
|
@@ -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-D-y_-xRS.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-CDAngHwg.js";
|
|
3
3
|
//#region src/utils/onAbort.d.ts
|
|
4
4
|
export declare const onAbort: (ctx: {
|
|
5
5
|
signal?: AbortSignal | undefined;
|
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-D-y_-xRS.js";
|
|
2
|
+
import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-DDfsaErA.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-D-y_-xRS.js";
|
|
2
|
+
import { n as NoClash } from "./context-plugin-DDfsaErA.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-D-y_-xRS.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-D-y_-xRS.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
|
@@ -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-Bd4TBZgl.js";
|
|
2
|
+
import { n as NoClash } from "./context-plugin-BVNKqkIx.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-Bd4TBZgl.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-Bd4TBZgl.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-Bd4TBZgl.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-Bd4TBZgl.js";
|
|
2
|
+
import { n as Scoped } from "./types-BJ0fzV1p.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,12 +23,12 @@ 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.
|
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-Bd4TBZgl.js";
|
|
2
2
|
//#region src/trace/console.d.ts
|
|
3
3
|
type ConsoleLoggerOptions = {
|
|
4
4
|
/** Defaults to `globalThis.console`. */
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-
|
|
2
|
-
import { t as BoundOf } from "./plugin-
|
|
1
|
+
import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-Bd4TBZgl.js";
|
|
2
|
+
import { t as BoundOf } from "./plugin-DQNPBcxx.js";
|
|
3
3
|
//#region src/scope/types.d.ts
|
|
4
4
|
type BoundEffects<E extends EventMap, S extends Scope> = S extends {
|
|
5
|
-
effects: infer H extends Effects;
|
|
5
|
+
$effects: infer H extends Effects;
|
|
6
6
|
} ? BoundOf<H, E> : unknown;
|
|
7
|
-
/** The fields a scope gives a task: its values as they are and its
|
|
8
|
-
type FieldsOfScope<E extends EventMap, S extends Scope> = [keyof S] extends [never] ? unknown : Omit<S, "effects"> & BoundEffects<E, S>;
|
|
7
|
+
/** The fields a scope gives a task: its values as they are and its `$effects` bound; `unknown` for an empty scope. */
|
|
8
|
+
type FieldsOfScope<E extends EventMap, S extends Scope> = [keyof S] extends [never] ? unknown : Omit<S, "$effects"> & BoundEffects<E, S>;
|
|
9
9
|
/**
|
|
10
10
|
* The event map `E` with the fields of a scope on top of its context fields.
|
|
11
11
|
* A field the scope sets replaces the one of the bus in the type too.
|
package/docs/README.md
CHANGED
|
@@ -30,6 +30,7 @@ Code for every guide step: [`examples/guide.ts`](../examples/guide.ts) (type-che
|
|
|
30
30
|
- [TypeScript in many files](typescript.md) — keep the types of the bus in commands and modules
|
|
31
31
|
- [Errors and unheard events](errors.md)
|
|
32
32
|
- [Package entries](package.md) — which import holds what
|
|
33
|
+
- [Plugins](plugins.md) — every plugin of a bus, what it returns, and the order to use them in
|
|
33
34
|
- [Tracing](tracing.md) — loggers, Redux DevTools, `notrace`
|
|
34
35
|
- [Observable](observable.md) — events as TC39 Observables
|
|
35
36
|
- [Hot reload and dev diagnostics](hmr.md)
|
package/docs/actions.md
CHANGED
|
@@ -155,7 +155,7 @@ app.dispose(); // unmounts every module
|
|
|
155
155
|
|
|
156
156
|
## Give a command, an action or a module its own fields
|
|
157
157
|
|
|
158
|
-
A command can need its own client, a module its own logger. Pass a scope, a plain object whose keys go on `ctx` (
|
|
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`:
|
|
159
159
|
|
|
160
160
|
```js
|
|
161
161
|
import { createBus } from "nanoeffects";
|
package/docs/package.md
CHANGED
|
@@ -15,6 +15,8 @@ Imports are split so you only load what you use. `createBus` stays small; the re
|
|
|
15
15
|
| `nanoeffects/dev` | `devPlugin` (no-op in production) |
|
|
16
16
|
| `nanoeffects/trace` | `createTracer` and the loggers |
|
|
17
17
|
|
|
18
|
+
The `...Plugin` names above are used with `bus.use`. [Plugins](plugins.md) lists all of them, what each returns, and the order to use them in.
|
|
19
|
+
|
|
18
20
|
## Give the docs to an AI assistant
|
|
19
21
|
|
|
20
22
|
The package ships two plain-text files for assistants that write code with it:
|
package/docs/plugins.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Choose the plugins of a bus
|
|
2
|
+
|
|
3
|
+
Optional. A plugin adds one thing to a bus: fields on `ctx`, a method, a registry of handlers, diagnostics. Read this page when you want to know which plugins exist, what each one returns, and in what order to use them. Skip it for a small bus: `createBus()` alone is enough.
|
|
4
|
+
|
|
5
|
+
## Use a plugin
|
|
6
|
+
|
|
7
|
+
A plugin is a function of the bus. `bus.use(plugin)` calls it with the bus and returns whatever the plugin returns.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { createBus } from "nanoeffects";
|
|
11
|
+
|
|
12
|
+
// your own plugin: a function of the bus that returns what you want back
|
|
13
|
+
const countPlugin = (bus) => {
|
|
14
|
+
let count = 0;
|
|
15
|
+
const unbind = bus.on("*", () => {
|
|
16
|
+
count += 1;
|
|
17
|
+
});
|
|
18
|
+
return { count: () => count, stop: unbind };
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const bus = createBus();
|
|
22
|
+
// use — calls countPlugin(bus) and returns its result
|
|
23
|
+
const counter = bus.use(countPlugin);
|
|
24
|
+
|
|
25
|
+
bus.emit("greet", "Ada");
|
|
26
|
+
console.log(counter.count()); // 1
|
|
27
|
+
counter.stop();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The bus keeps no list of plugins. A plugin that was used cannot be taken out by the bus; it returns its own `stop` or `disconnect` when it needs one.
|
|
31
|
+
|
|
32
|
+
## See every plugin
|
|
33
|
+
|
|
34
|
+
| Plugin | Import | It adds | `bus.use` returns | Page |
|
|
35
|
+
| ------ | ------ | ------- | ----------------- | ---- |
|
|
36
|
+
| `contextPlugin` | `nanoeffects` | Fields on `ctx` of every task | The same bus, typed with the fields | [Context fields](context.md) |
|
|
37
|
+
| `effectsPlugin` | `nanoeffects/effects` | `race`, `all`, `take`, `delay` and your own effects on `ctx`, bound to the task | The same bus, typed with the effects | [Effects](effects.md#put-selected-effects-on-ctx) |
|
|
38
|
+
| `scopePlugin` | `nanoeffects/scope` | Fields and effects for one command, action, job, module or `emit` | The same bus | [Scopes](scope.md) |
|
|
39
|
+
| `commandsPlugin` | `nanoeffects/actions` | The registry of command handlers, `bus.commands` | The registry `{ run, handle }` | [Actions](actions.md#describe-events-commands-and-jobs) |
|
|
40
|
+
| `modulePlugin` | `nanoeffects/actions` | Modules that mount and unmount as one unit | The app `{ mount, unmount, isMounted, dispose, commands, createModule }` | [Modules](actions.md#mount-domains-with-modules) |
|
|
41
|
+
| `emitterPlugin` | `nanoeffects/actions` | `bus.emitter(creator)`, a callback that emits an action | The same bus, with `emitter` | [Stores](stores.md#emit-from-a-state-manager) |
|
|
42
|
+
| `reactPlugin` | `nanoeffects/react` | `bus.useEmitter(event, value)`, a React hook | The same bus, with `useEmitter` | [React and stores](stores.md#react-sharing-a-value-across-components) |
|
|
43
|
+
| `devPlugin` | `nanoeffects/dev` | Warnings about listeners that pile up, and messages about modules | A function that disconnects it | [Hot reload](hmr.md#report-leaks-with-devplugin) |
|
|
44
|
+
|
|
45
|
+
"The same bus" means you can keep writing `bus` and ignore the result. Keep the result of `contextPlugin` and `effectsPlugin` in TypeScript, though: the type of the bus with its new fields is the type they return (`const bus = createBus().use(contextPlugin({ store }))`).
|
|
46
|
+
|
|
47
|
+
## Put them together
|
|
48
|
+
|
|
49
|
+
One bus with every plugin that does not need React. The order is explained in the next section.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { contextPlugin, createBus } from "nanoeffects";
|
|
53
|
+
import {
|
|
54
|
+
commandsPlugin,
|
|
55
|
+
emitterPlugin,
|
|
56
|
+
modulePlugin,
|
|
57
|
+
} from "nanoeffects/actions";
|
|
58
|
+
import { devPlugin } from "nanoeffects/dev";
|
|
59
|
+
import { all, effectsPlugin, race } from "nanoeffects/effects";
|
|
60
|
+
import { scopePlugin } from "nanoeffects/scope";
|
|
61
|
+
|
|
62
|
+
const bus = createBus()
|
|
63
|
+
.use(contextPlugin({ store })) // ctx.store on every task
|
|
64
|
+
.use(effectsPlugin({ race, all })) // ctx.race and ctx.all, bound to the task
|
|
65
|
+
.use(scopePlugin()) // scopes for commands, actions, jobs, modules and emit
|
|
66
|
+
.use(emitterPlugin()); // bus.emitter(creator)
|
|
67
|
+
|
|
68
|
+
// commandsPlugin — the registry of command handlers; keep it to bind handlers later
|
|
69
|
+
const commands = bus.use(commandsPlugin());
|
|
70
|
+
|
|
71
|
+
// modulePlugin — mounts userModule now; keep app to mount and unmount later
|
|
72
|
+
const app = bus.use(modulePlugin([userModule]));
|
|
73
|
+
|
|
74
|
+
// devPlugin — reports leaks and modules while you develop; keep disconnect to remove it
|
|
75
|
+
const disconnect = bus.use(devPlugin({ app }));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Follow the order
|
|
79
|
+
|
|
80
|
+
Most plugins do not care about each other. These pairs do:
|
|
81
|
+
|
|
82
|
+
| Rule | Why |
|
|
83
|
+
| ---- | --- |
|
|
84
|
+
| `scopePlugin` after `effectsPlugin` | `effectsPlugin` assigns its effects on `ctx`, so one used later would replace an effect with the same name that a scope put there. |
|
|
85
|
+
| `scopePlugin` before `modulePlugin` | `modulePlugin` mounts the modules when it is used, and a module with a scope needs the plugin by then. |
|
|
86
|
+
| `commandsPlugin` before `modulePlugin`, if you bind handlers yourself | `modulePlugin` uses the registry that exists. Without one it makes its own, which is `app.commands`. |
|
|
87
|
+
| `modulePlugin` before `devPlugin({ app })` | `devPlugin` reports the modules of the `app` that `modulePlugin` returned. |
|
|
88
|
+
| `emitterPlugin` and `reactPlugin` anywhere | They only call `bus.emit`, which is read when you call them. |
|
|
89
|
+
|
|
90
|
+
Two more things the checker enforces. A name that is already a context field of the bus is a type error when `contextPlugin` or `effectsPlugin` adds it again. `contextPlugin` does not replace a field that is on `ctx` already, so a scope wins over it in any order.
|
|
91
|
+
|
|
92
|
+
## Choose by need
|
|
93
|
+
|
|
94
|
+
- **A store, a client or a logger on every handler.** `contextPlugin`. Or `createBus({ context })` when you do not need the types from the value.
|
|
95
|
+
- **`race`, `all` or your own effect without passing `ctx`.** `effectsPlugin`.
|
|
96
|
+
- **Fields for one command, or a fake for one test call.** `scopePlugin`.
|
|
97
|
+
- **A request with a result, or handlers that a test can replace.** `commandsPlugin`. Add `modulePlugin` when the domain needs a mount and an unmount.
|
|
98
|
+
- **A callback for a state manager, such as `$name.listen(...)`.** `emitterPlugin`.
|
|
99
|
+
- **Tell the bus when a value of a React component changes.** `reactPlugin`.
|
|
100
|
+
- **Find listeners that were never unbound, or see modules reload.** `devPlugin`. In a production build `nanoeffects/dev` is empty, so the call can stay.
|
|
101
|
+
|
|
102
|
+
## See what is not a plugin
|
|
103
|
+
|
|
104
|
+
These connect the bus to something, but are not used with `bus.use`:
|
|
105
|
+
|
|
106
|
+
- `createEffectsMiddleware(bus, { filter })` from `nanoeffects/redux` is a Redux middleware for a bus that exists. See [Stores](stores.md#wire-redux-toolkit-to-a-bus).
|
|
107
|
+
- `createBus({ trace })` takes a tracer from `nanoeffects/trace`. See [Tracing](tracing.md).
|
|
108
|
+
- `createObserve(bus)` from `nanoeffects/observable` turns an event into an Observable. See [Observable](observable.md).
|
package/docs/scope.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Optional, for domain logic. Use it when one command needs its own client, one module its own logger, or one test its own fake, and a field on the whole bus ([context fields](context.md)) is too wide.
|
|
4
4
|
|
|
5
|
-
A **scope** is a plain object. Every key goes on `ctx` as it is (`cache`, `api`, `logger`), except one:
|
|
5
|
+
A **scope** is a plain object. Every key goes on `ctx` as it is (`cache`, `api`, `logger`), except one: `$effects`. Its functions take `{ signal }` as the first parameter, like `retry(ctx, fn)`, and go on `ctx` bound to the task, so you call `ctx.fetchJson(url)` instead of `fetchJson(ctx, url)`.
|
|
6
6
|
|
|
7
7
|
You give a scope to the thing that uses it. A call can replace it. Nothing else changes: handlers still get `(payload, ctx)`.
|
|
8
8
|
|
|
@@ -47,7 +47,7 @@ The listeners of `"user/get"` get the same fields when the command is emitted, b
|
|
|
47
47
|
|
|
48
48
|
## Bind an effect to the task
|
|
49
49
|
|
|
50
|
-
An effect is a function with `{ signal }` as its first parameter. In
|
|
50
|
+
An effect is a function with `{ signal }` as its first parameter. In `$effects` it is bound to the task, so it stops with the task.
|
|
51
51
|
|
|
52
52
|
```js
|
|
53
53
|
import { createBus } from "nanoeffects";
|
|
@@ -69,16 +69,16 @@ const loadFeed = createCommand(
|
|
|
69
69
|
feed: () => fetchJson(url),
|
|
70
70
|
timeout: () => delay(5000),
|
|
71
71
|
}),
|
|
72
|
-
// effects — built-in and your own functions share one object
|
|
73
|
-
{ effects: { fetchJson, race, delay } },
|
|
72
|
+
// $effects — built-in and your own functions share one object
|
|
73
|
+
{ $effects: { fetchJson, race, delay } },
|
|
74
74
|
);
|
|
75
75
|
|
|
76
76
|
const result = await bus.emit(loadFeed("/feed"));
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
- Put in
|
|
80
|
-
- A function under any other key is copied as it is; a function in
|
|
81
|
-
-
|
|
79
|
+
- Put in `$effects` only functions that take `ctx` first. A plain function (a formatter, a logger) belongs in `context`.
|
|
80
|
+
- A function under any other key is copied as it is; a function in `$effects` is bound.
|
|
81
|
+
- `$effects` is the one name with a meaning. A field called `effects` is an ordinary field: `{ effects: { retries: 3 } }` puts that object on `ctx`, and nothing in it is bound.
|
|
82
82
|
|
|
83
83
|
## Replace a field for one call
|
|
84
84
|
|
|
@@ -106,7 +106,7 @@ Rules for the second argument:
|
|
|
106
106
|
|
|
107
107
|
- It works with an action or a command call (`emit(getUser("1"), scope)`, `ctx.emit(loaded(user), scope)`).
|
|
108
108
|
- It does not work with the string form: in `emit("user/get", payload)` the second argument is the payload.
|
|
109
|
-
- It can replace
|
|
109
|
+
- It can replace `$effects` too: `{ $effects: { delay: instantDelay } }`.
|
|
110
110
|
- Fields of the library (`signal`, `emit`, …) in it are ignored.
|
|
111
111
|
|
|
112
112
|
## Give an action, a job or a module its own fields
|
|
@@ -137,6 +137,8 @@ const auditModule = createModule(
|
|
|
137
137
|
);
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
+
If the outcome action (`success` or `fail`) has a scope of its own, its listeners see the job's fields with the action's own fields over them; the handler of the job sees only the job's.
|
|
141
|
+
|
|
140
142
|
A module scope does not leak: a listener registered outside the module does not see it.
|
|
141
143
|
|
|
142
144
|
## Which value wins
|
|
@@ -154,7 +156,7 @@ The fields of the library (`signal`, `abort`, `emit`, `on`, `off`, `run`, `type`
|
|
|
154
156
|
|
|
155
157
|
## Keep a generic effect generic
|
|
156
158
|
|
|
157
|
-
In
|
|
159
|
+
In `$effects` a function loses its first parameter, and TypeScript cannot keep a generic through that step: `<T>(items: T[]) => T` becomes `(items: unknown[]) => unknown`. JavaScript code is not affected. If you want the exact type, use one of two ways.
|
|
158
160
|
|
|
159
161
|
`effect(fn)`: mark the function where you declare it, then put it in the scope like any other value. No annotation, so a JavaScript editor shows the exact hint too.
|
|
160
162
|
|
|
@@ -166,13 +168,13 @@ import { effect } from "nanoeffects/scope";
|
|
|
166
168
|
const first = effect((ctx, items) => items[0]);
|
|
167
169
|
|
|
168
170
|
const pick = createCommand("pick", (items, { first }) => first(items), {
|
|
169
|
-
first, // a plain key of the scope, not an entry of effects
|
|
171
|
+
first, // a plain key of the scope, not an entry of `$effects`
|
|
170
172
|
});
|
|
171
173
|
```
|
|
172
174
|
|
|
173
175
|
Call `effect` where you declare the function, as above, and not inline in the scope: TypeScript does not infer the scope from a generic call written there.
|
|
174
176
|
|
|
175
|
-
`Effect<F>` (TypeScript): declare how the effect looks **after** it is bound, and keep it in
|
|
177
|
+
`Effect<F>` (TypeScript): declare how the effect looks **after** it is bound, and keep it in `$effects`.
|
|
176
178
|
|
|
177
179
|
```ts
|
|
178
180
|
import { createCommand } from "nanoeffects/actions";
|
|
@@ -187,7 +189,7 @@ const first: Effect<First> = (ctx, items) => items[0];
|
|
|
187
189
|
const pick = createCommand(
|
|
188
190
|
"pick",
|
|
189
191
|
(id: string, { first }) => first([id]), // string
|
|
190
|
-
{ effects: { first } },
|
|
192
|
+
{ $effects: { first } },
|
|
191
193
|
);
|
|
192
194
|
```
|
|
193
195
|
|
|
@@ -222,5 +224,5 @@ const getUser = createCommand(
|
|
|
222
224
|
- No plugin, no scope: see [Turn scopes on](#turn-scopes-on).
|
|
223
225
|
- The fields of the bus are not in the handler of a command made with `createCommand(…, scope)` unless you type `ctx` yourself; the fields of the scope are.
|
|
224
226
|
- The string form of `emit` takes no scope.
|
|
225
|
-
- `effect(fn)` works only as a value of a scope, not as an entry of
|
|
226
|
-
- A scope on a command, action, job or module is typed without the events of a bus, so `take` in its
|
|
227
|
+
- `effect(fn)` works only as a value of a scope, not as an entry of `$effects`.
|
|
228
|
+
- A scope on a command, action, job or module is typed without the events of a bus, so `take` in its `$effects` takes any event name.
|
package/llms-full.txt
CHANGED
|
@@ -396,6 +396,8 @@ Imports are split so you only load what you use. `createBus` stays small; the re
|
|
|
396
396
|
| `nanoeffects/dev` | `devPlugin` (no-op in production) |
|
|
397
397
|
| `nanoeffects/trace` | `createTracer` and the loggers |
|
|
398
398
|
|
|
399
|
+
The `...Plugin` names above are used with `bus.use`. [Plugins](plugins.md) lists all of them, what each returns, and the order to use them in.
|
|
400
|
+
|
|
399
401
|
## Give the docs to an AI assistant
|
|
400
402
|
|
|
401
403
|
The package ships two plain-text files for assistants that write code with it:
|
|
@@ -405,6 +407,118 @@ The package ships two plain-text files for assistants that write code with it:
|
|
|
405
407
|
|
|
406
408
|
Find them in `node_modules/nanoeffects/`, or load `llms-full.txt` from a CDN such as `https://unpkg.com/nanoeffects/llms-full.txt`. Both match the installed version.
|
|
407
409
|
|
|
410
|
+
---
|
|
411
|
+
Source: docs/plugins.md
|
|
412
|
+
|
|
413
|
+
# Choose the plugins of a bus
|
|
414
|
+
|
|
415
|
+
Optional. A plugin adds one thing to a bus: fields on `ctx`, a method, a registry of handlers, diagnostics. Read this page when you want to know which plugins exist, what each one returns, and in what order to use them. Skip it for a small bus: `createBus()` alone is enough.
|
|
416
|
+
|
|
417
|
+
## Use a plugin
|
|
418
|
+
|
|
419
|
+
A plugin is a function of the bus. `bus.use(plugin)` calls it with the bus and returns whatever the plugin returns.
|
|
420
|
+
|
|
421
|
+
```js
|
|
422
|
+
import { createBus } from "nanoeffects";
|
|
423
|
+
|
|
424
|
+
// your own plugin: a function of the bus that returns what you want back
|
|
425
|
+
const countPlugin = (bus) => {
|
|
426
|
+
let count = 0;
|
|
427
|
+
const unbind = bus.on("*", () => {
|
|
428
|
+
count += 1;
|
|
429
|
+
});
|
|
430
|
+
return { count: () => count, stop: unbind };
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const bus = createBus();
|
|
434
|
+
// use — calls countPlugin(bus) and returns its result
|
|
435
|
+
const counter = bus.use(countPlugin);
|
|
436
|
+
|
|
437
|
+
bus.emit("greet", "Ada");
|
|
438
|
+
console.log(counter.count()); // 1
|
|
439
|
+
counter.stop();
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
The bus keeps no list of plugins. A plugin that was used cannot be taken out by the bus; it returns its own `stop` or `disconnect` when it needs one.
|
|
443
|
+
|
|
444
|
+
## See every plugin
|
|
445
|
+
|
|
446
|
+
| Plugin | Import | It adds | `bus.use` returns | Page |
|
|
447
|
+
| ------ | ------ | ------- | ----------------- | ---- |
|
|
448
|
+
| `contextPlugin` | `nanoeffects` | Fields on `ctx` of every task | The same bus, typed with the fields | [Context fields](context.md) |
|
|
449
|
+
| `effectsPlugin` | `nanoeffects/effects` | `race`, `all`, `take`, `delay` and your own effects on `ctx`, bound to the task | The same bus, typed with the effects | [Effects](effects.md#put-selected-effects-on-ctx) |
|
|
450
|
+
| `scopePlugin` | `nanoeffects/scope` | Fields and effects for one command, action, job, module or `emit` | The same bus | [Scopes](scope.md) |
|
|
451
|
+
| `commandsPlugin` | `nanoeffects/actions` | The registry of command handlers, `bus.commands` | The registry `{ run, handle }` | [Actions](actions.md#describe-events-commands-and-jobs) |
|
|
452
|
+
| `modulePlugin` | `nanoeffects/actions` | Modules that mount and unmount as one unit | The app `{ mount, unmount, isMounted, dispose, commands, createModule }` | [Modules](actions.md#mount-domains-with-modules) |
|
|
453
|
+
| `emitterPlugin` | `nanoeffects/actions` | `bus.emitter(creator)`, a callback that emits an action | The same bus, with `emitter` | [Stores](stores.md#emit-from-a-state-manager) |
|
|
454
|
+
| `reactPlugin` | `nanoeffects/react` | `bus.useEmitter(event, value)`, a React hook | The same bus, with `useEmitter` | [React and stores](stores.md#react-sharing-a-value-across-components) |
|
|
455
|
+
| `devPlugin` | `nanoeffects/dev` | Warnings about listeners that pile up, and messages about modules | A function that disconnects it | [Hot reload](hmr.md#report-leaks-with-devplugin) |
|
|
456
|
+
|
|
457
|
+
"The same bus" means you can keep writing `bus` and ignore the result. Keep the result of `contextPlugin` and `effectsPlugin` in TypeScript, though: the type of the bus with its new fields is the type they return (`const bus = createBus().use(contextPlugin({ store }))`).
|
|
458
|
+
|
|
459
|
+
## Put them together
|
|
460
|
+
|
|
461
|
+
One bus with every plugin that does not need React. The order is explained in the next section.
|
|
462
|
+
|
|
463
|
+
```js
|
|
464
|
+
import { contextPlugin, createBus } from "nanoeffects";
|
|
465
|
+
import {
|
|
466
|
+
commandsPlugin,
|
|
467
|
+
emitterPlugin,
|
|
468
|
+
modulePlugin,
|
|
469
|
+
} from "nanoeffects/actions";
|
|
470
|
+
import { devPlugin } from "nanoeffects/dev";
|
|
471
|
+
import { all, effectsPlugin, race } from "nanoeffects/effects";
|
|
472
|
+
import { scopePlugin } from "nanoeffects/scope";
|
|
473
|
+
|
|
474
|
+
const bus = createBus()
|
|
475
|
+
.use(contextPlugin({ store })) // ctx.store on every task
|
|
476
|
+
.use(effectsPlugin({ race, all })) // ctx.race and ctx.all, bound to the task
|
|
477
|
+
.use(scopePlugin()) // scopes for commands, actions, jobs, modules and emit
|
|
478
|
+
.use(emitterPlugin()); // bus.emitter(creator)
|
|
479
|
+
|
|
480
|
+
// commandsPlugin — the registry of command handlers; keep it to bind handlers later
|
|
481
|
+
const commands = bus.use(commandsPlugin());
|
|
482
|
+
|
|
483
|
+
// modulePlugin — mounts userModule now; keep app to mount and unmount later
|
|
484
|
+
const app = bus.use(modulePlugin([userModule]));
|
|
485
|
+
|
|
486
|
+
// devPlugin — reports leaks and modules while you develop; keep disconnect to remove it
|
|
487
|
+
const disconnect = bus.use(devPlugin({ app }));
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## Follow the order
|
|
491
|
+
|
|
492
|
+
Most plugins do not care about each other. These pairs do:
|
|
493
|
+
|
|
494
|
+
| Rule | Why |
|
|
495
|
+
| ---- | --- |
|
|
496
|
+
| `scopePlugin` after `effectsPlugin` | `effectsPlugin` assigns its effects on `ctx`, so one used later would replace an effect with the same name that a scope put there. |
|
|
497
|
+
| `scopePlugin` before `modulePlugin` | `modulePlugin` mounts the modules when it is used, and a module with a scope needs the plugin by then. |
|
|
498
|
+
| `commandsPlugin` before `modulePlugin`, if you bind handlers yourself | `modulePlugin` uses the registry that exists. Without one it makes its own, which is `app.commands`. |
|
|
499
|
+
| `modulePlugin` before `devPlugin({ app })` | `devPlugin` reports the modules of the `app` that `modulePlugin` returned. |
|
|
500
|
+
| `emitterPlugin` and `reactPlugin` anywhere | They only call `bus.emit`, which is read when you call them. |
|
|
501
|
+
|
|
502
|
+
Two more things the checker enforces. A name that is already a context field of the bus is a type error when `contextPlugin` or `effectsPlugin` adds it again. `contextPlugin` does not replace a field that is on `ctx` already, so a scope wins over it in any order.
|
|
503
|
+
|
|
504
|
+
## Choose by need
|
|
505
|
+
|
|
506
|
+
- **A store, a client or a logger on every handler.** `contextPlugin`. Or `createBus({ context })` when you do not need the types from the value.
|
|
507
|
+
- **`race`, `all` or your own effect without passing `ctx`.** `effectsPlugin`.
|
|
508
|
+
- **Fields for one command, or a fake for one test call.** `scopePlugin`.
|
|
509
|
+
- **A request with a result, or handlers that a test can replace.** `commandsPlugin`. Add `modulePlugin` when the domain needs a mount and an unmount.
|
|
510
|
+
- **A callback for a state manager, such as `$name.listen(...)`.** `emitterPlugin`.
|
|
511
|
+
- **Tell the bus when a value of a React component changes.** `reactPlugin`.
|
|
512
|
+
- **Find listeners that were never unbound, or see modules reload.** `devPlugin`. In a production build `nanoeffects/dev` is empty, so the call can stay.
|
|
513
|
+
|
|
514
|
+
## See what is not a plugin
|
|
515
|
+
|
|
516
|
+
These connect the bus to something, but are not used with `bus.use`:
|
|
517
|
+
|
|
518
|
+
- `createEffectsMiddleware(bus, { filter })` from `nanoeffects/redux` is a Redux middleware for a bus that exists. See [Stores](stores.md#wire-redux-toolkit-to-a-bus).
|
|
519
|
+
- `createBus({ trace })` takes a tracer from `nanoeffects/trace`. See [Tracing](tracing.md).
|
|
520
|
+
- `createObserve(bus)` from `nanoeffects/observable` turns an event into an Observable. See [Observable](observable.md).
|
|
521
|
+
|
|
408
522
|
---
|
|
409
523
|
Source: docs/policies.md
|
|
410
524
|
|
|
@@ -1023,7 +1137,7 @@ app.dispose(); // unmounts every module
|
|
|
1023
1137
|
|
|
1024
1138
|
## Give a command, an action or a module its own fields
|
|
1025
1139
|
|
|
1026
|
-
A command can need its own client, a module its own logger. Pass a scope, a plain object whose keys go on `ctx` (
|
|
1140
|
+
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`:
|
|
1027
1141
|
|
|
1028
1142
|
```js
|
|
1029
1143
|
import { createBus } from "nanoeffects";
|
|
@@ -1790,7 +1904,7 @@ Source: docs/scope.md
|
|
|
1790
1904
|
|
|
1791
1905
|
Optional, for domain logic. Use it when one command needs its own client, one module its own logger, or one test its own fake, and a field on the whole bus ([context fields](context.md)) is too wide.
|
|
1792
1906
|
|
|
1793
|
-
A **scope** is a plain object. Every key goes on `ctx` as it is (`cache`, `api`, `logger`), except one:
|
|
1907
|
+
A **scope** is a plain object. Every key goes on `ctx` as it is (`cache`, `api`, `logger`), except one: `$effects`. Its functions take `{ signal }` as the first parameter, like `retry(ctx, fn)`, and go on `ctx` bound to the task, so you call `ctx.fetchJson(url)` instead of `fetchJson(ctx, url)`.
|
|
1794
1908
|
|
|
1795
1909
|
You give a scope to the thing that uses it. A call can replace it. Nothing else changes: handlers still get `(payload, ctx)`.
|
|
1796
1910
|
|
|
@@ -1835,7 +1949,7 @@ The listeners of `"user/get"` get the same fields when the command is emitted, b
|
|
|
1835
1949
|
|
|
1836
1950
|
## Bind an effect to the task
|
|
1837
1951
|
|
|
1838
|
-
An effect is a function with `{ signal }` as its first parameter. In
|
|
1952
|
+
An effect is a function with `{ signal }` as its first parameter. In `$effects` it is bound to the task, so it stops with the task.
|
|
1839
1953
|
|
|
1840
1954
|
```js
|
|
1841
1955
|
import { createBus } from "nanoeffects";
|
|
@@ -1857,16 +1971,16 @@ const loadFeed = createCommand(
|
|
|
1857
1971
|
feed: () => fetchJson(url),
|
|
1858
1972
|
timeout: () => delay(5000),
|
|
1859
1973
|
}),
|
|
1860
|
-
// effects — built-in and your own functions share one object
|
|
1861
|
-
{ effects: { fetchJson, race, delay } },
|
|
1974
|
+
// $effects — built-in and your own functions share one object
|
|
1975
|
+
{ $effects: { fetchJson, race, delay } },
|
|
1862
1976
|
);
|
|
1863
1977
|
|
|
1864
1978
|
const result = await bus.emit(loadFeed("/feed"));
|
|
1865
1979
|
```
|
|
1866
1980
|
|
|
1867
|
-
- Put in
|
|
1868
|
-
- A function under any other key is copied as it is; a function in
|
|
1869
|
-
-
|
|
1981
|
+
- Put in `$effects` only functions that take `ctx` first. A plain function (a formatter, a logger) belongs in `context`.
|
|
1982
|
+
- A function under any other key is copied as it is; a function in `$effects` is bound.
|
|
1983
|
+
- `$effects` is the one name with a meaning. A field called `effects` is an ordinary field: `{ effects: { retries: 3 } }` puts that object on `ctx`, and nothing in it is bound.
|
|
1870
1984
|
|
|
1871
1985
|
## Replace a field for one call
|
|
1872
1986
|
|
|
@@ -1894,7 +2008,7 @@ Rules for the second argument:
|
|
|
1894
2008
|
|
|
1895
2009
|
- It works with an action or a command call (`emit(getUser("1"), scope)`, `ctx.emit(loaded(user), scope)`).
|
|
1896
2010
|
- It does not work with the string form: in `emit("user/get", payload)` the second argument is the payload.
|
|
1897
|
-
- It can replace
|
|
2011
|
+
- It can replace `$effects` too: `{ $effects: { delay: instantDelay } }`.
|
|
1898
2012
|
- Fields of the library (`signal`, `emit`, …) in it are ignored.
|
|
1899
2013
|
|
|
1900
2014
|
## Give an action, a job or a module its own fields
|
|
@@ -1925,6 +2039,8 @@ const auditModule = createModule(
|
|
|
1925
2039
|
);
|
|
1926
2040
|
```
|
|
1927
2041
|
|
|
2042
|
+
If the outcome action (`success` or `fail`) has a scope of its own, its listeners see the job's fields with the action's own fields over them; the handler of the job sees only the job's.
|
|
2043
|
+
|
|
1928
2044
|
A module scope does not leak: a listener registered outside the module does not see it.
|
|
1929
2045
|
|
|
1930
2046
|
## Which value wins
|
|
@@ -1942,7 +2058,7 @@ The fields of the library (`signal`, `abort`, `emit`, `on`, `off`, `run`, `type`
|
|
|
1942
2058
|
|
|
1943
2059
|
## Keep a generic effect generic
|
|
1944
2060
|
|
|
1945
|
-
In
|
|
2061
|
+
In `$effects` a function loses its first parameter, and TypeScript cannot keep a generic through that step: `<T>(items: T[]) => T` becomes `(items: unknown[]) => unknown`. JavaScript code is not affected. If you want the exact type, use one of two ways.
|
|
1946
2062
|
|
|
1947
2063
|
`effect(fn)`: mark the function where you declare it, then put it in the scope like any other value. No annotation, so a JavaScript editor shows the exact hint too.
|
|
1948
2064
|
|
|
@@ -1954,13 +2070,13 @@ import { effect } from "nanoeffects/scope";
|
|
|
1954
2070
|
const first = effect((ctx, items) => items[0]);
|
|
1955
2071
|
|
|
1956
2072
|
const pick = createCommand("pick", (items, { first }) => first(items), {
|
|
1957
|
-
first, // a plain key of the scope, not an entry of effects
|
|
2073
|
+
first, // a plain key of the scope, not an entry of `$effects`
|
|
1958
2074
|
});
|
|
1959
2075
|
```
|
|
1960
2076
|
|
|
1961
2077
|
Call `effect` where you declare the function, as above, and not inline in the scope: TypeScript does not infer the scope from a generic call written there.
|
|
1962
2078
|
|
|
1963
|
-
`Effect<F>` (TypeScript): declare how the effect looks **after** it is bound, and keep it in
|
|
2079
|
+
`Effect<F>` (TypeScript): declare how the effect looks **after** it is bound, and keep it in `$effects`.
|
|
1964
2080
|
|
|
1965
2081
|
```ts
|
|
1966
2082
|
import { createCommand } from "nanoeffects/actions";
|
|
@@ -1975,7 +2091,7 @@ const first: Effect<First> = (ctx, items) => items[0];
|
|
|
1975
2091
|
const pick = createCommand(
|
|
1976
2092
|
"pick",
|
|
1977
2093
|
(id: string, { first }) => first([id]), // string
|
|
1978
|
-
{ effects: { first } },
|
|
2094
|
+
{ $effects: { first } },
|
|
1979
2095
|
);
|
|
1980
2096
|
```
|
|
1981
2097
|
|
|
@@ -2010,8 +2126,8 @@ const getUser = createCommand(
|
|
|
2010
2126
|
- No plugin, no scope: see [Turn scopes on](#turn-scopes-on).
|
|
2011
2127
|
- The fields of the bus are not in the handler of a command made with `createCommand(…, scope)` unless you type `ctx` yourself; the fields of the scope are.
|
|
2012
2128
|
- The string form of `emit` takes no scope.
|
|
2013
|
-
- `effect(fn)` works only as a value of a scope, not as an entry of
|
|
2014
|
-
- A scope on a command, action, job or module is typed without the events of a bus, so `take` in its
|
|
2129
|
+
- `effect(fn)` works only as a value of a scope, not as an entry of `$effects`.
|
|
2130
|
+
- A scope on a command, action, job or module is typed without the events of a bus, so `take` in its `$effects` takes any event name.
|
|
2015
2131
|
|
|
2016
2132
|
---
|
|
2017
2133
|
Source: docs/typescript.md
|
package/llms.txt
CHANGED
|
@@ -16,6 +16,7 @@ Rules that generated code often gets wrong:
|
|
|
16
16
|
|
|
17
17
|
- [Guide](docs/guide.md): bus, handler context, policies, effects, commands and modules, step by step
|
|
18
18
|
- [Package entries](docs/package.md): which import holds what
|
|
19
|
+
- [Plugins](docs/plugins.md): every plugin of a bus, what `bus.use` returns, and the order to use them in
|
|
19
20
|
|
|
20
21
|
## Async UI and unreliable networks
|
|
21
22
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nanoeffects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Typed event bus where every handler is a task with an AbortSignal, plus policies (latest, debounce, throttle, queue), effects (retry, timeout, poll) and tracing. About 1.3 kB gzip.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"event-bus",
|