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.
@@ -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: (payload: never, ctx: never) => unknown) => unknown) | undefined;
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 `effects`, whose
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-c292nK7v.js";
2
- import { n as Scoped, t as FieldsOfScope } from "./types-mLaAx-EZ.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-BlF2KSgy.js";
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 `effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
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 s(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 c=class extends Error{};function l(e,t,n){let r=t=>t.type===e;function a(r,a){if(typeof a==`object`){if(!t)throw new c(`No handler for command "${e}"`);return t(r,a)}return o(i(l,e,r),n)}let s=Object.assign(a,{type:e,match:r}),l=o(s,n);return l}const u=(e,t)=>[e,t],d=(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},f=()=>e=>{function t(t){return(...n)=>{let r=t(...n);e.emit(r)}}return Object.assign(e,{emitter:t})},p=e=>typeof e==`string`?s(e):e,m=(e,t,n,r)=>{let i=e.emit;i(Reflect.apply(t,null,[n]),r)},h=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof c)throw e;return Promise.reject(e)}},g=(e,t,n={},r)=>{let a=l(e,t),s=p(n.success??`${e}/success`),c=p(n.fail??`${e}/fail`),u=(e,t,n)=>{let i=n;return h(()=>Reflect.apply(e,null,[t,n])).then(e=>(m(i,s,e,r),e),e=>{m(i,c,e,r)})},d=e=>(t,n)=>u(e,t,n);function f(t,n){return typeof n==`object`?u(a,t,n):o(i(_,e,t),r)}let g=Object.assign(f,{type:e,match:a.match,wrap:d,success:s,fail:c}),_=o(g,r);return _};function _(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const v=(e,t)=>n=>t&&typeof n==`function`?e.fitScope?.(t,n)??n:n,y=(e,t,n)=>{n&&e.applyScope?.(t,n)},b=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],x=(e,t)=>{let n=e.on;return(...e)=>{let[r,i]=e;return Reflect.apply(n,null,[r,t(i)])}},S=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},C=(r=[])=>i=>{let a=i.commands??d()(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=v(i,h),_=(t,n)=>{let r=Reflect.apply(o.handle,null,b(g,t,n));return e(f,r),r},C=(...e)=>{let t=e.map(e=>typeof e==`function`?_(e,e):p.mount(e));return()=>{for(let e of t)e()}},w=S(f),T=x(f,g);try{y(i,f,h),s.setup(Object.assign(f,{handle:_,use:C,spawn:w,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:_}};export{u as bindHandler,d as commandsPlugin,s as createAction,l as createCommand,g as createJob,_ as createModule,f as emitterPlugin,C as modulePlugin};
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-c292nK7v.js";
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-c292nK7v.js";
2
- import { n as ModuleApi } from "./module-BlF2KSgy.js";
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-c292nK7v.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-CCYlC0XS.js";
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-c292nK7v.js";
2
- import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-1pYmSQEG.js";
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-c292nK7v.js";
2
- import { n as Scoped, t as FieldsOfScope } from "./types-mLaAx-EZ.js";
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: (payload: never, ctx: never) => unknown) => unknown) | undefined;
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 `effects`, whose
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-DKG_sIia.js";
2
- import { t as BoundOf } from "./plugin-B0AXKusc.js";
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 `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>;
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 `effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
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
@@ -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 s(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 c=class extends Error{};function l(e,t,n){let r=t=>t.type===e;function a(r,a){if(typeof a==`object`){if(!t)throw new c(`No handler for command "${e}"`);return t(r,a)}return o(i(l,e,r),n)}let s=Object.assign(a,{type:e,match:r}),l=o(s,n);return l}const u=(e,t)=>[e,t],d=(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},f=()=>e=>{function t(t){return(...n)=>{let r=t(...n);e.emit(r)}}return Object.assign(e,{emitter:t})},p=e=>typeof e==`string`?s(e):e,m=(e,t,n,r)=>{let i=e.emit;i(Reflect.apply(t,null,[n]),r)},h=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof c)throw e;return Promise.reject(e)}},g=(e,t,n={},r)=>{let a=l(e,t),s=p(n.success??`${e}/success`),c=p(n.fail??`${e}/fail`),u=(e,t,n)=>{let i=n;return h(()=>Reflect.apply(e,null,[t,n])).then(e=>(m(i,s,e,r),e),e=>{m(i,c,e,r)})},d=e=>(t,n)=>u(e,t,n);function f(t,n){return typeof n==`object`?u(a,t,n):o(i(_,e,t),r)}let g=Object.assign(f,{type:e,match:a.match,wrap:d,success:s,fail:c}),_=o(g,r);return _};function _(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const v=(e,t)=>n=>t&&typeof n==`function`?e.fitScope?.(t,n)??n:n,y=(e,t,n)=>{n&&e.applyScope?.(t,n)},b=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],x=(e,t)=>{let n=e.on;return(...e)=>{let[r,i]=e;return Reflect.apply(n,null,[r,t(i)])}},S=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},C=(r=[])=>i=>{let a=i.commands??d()(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=v(i,h),_=(e,t)=>{let r=Reflect.apply(o.handle,null,b(g,e,t));return n(f,r),r},C=(...e)=>{let t=e.map(e=>typeof e==`function`?_(e,e):p.mount(e));return()=>{for(let e of t)e()}},w=S(f),T=x(f,g);try{y(i,f,h),s.setup(Object.assign(f,{handle:_,use:C,spawn:w,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:_}};export{u as bindHandler,d as commandsPlugin,s as createAction,l as createCommand,g as createJob,_ as createModule,f as emitterPlugin,C as modulePlugin};
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-DKG_sIia.js";
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-DKG_sIia.js";
2
- import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-B0AXKusc.js";
1
+ import { H as Task, R as EventMap, l as Effect } from "./action-creator-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;
@@ -1,3 +1,3 @@
1
- import { A as On, B as PayloadOf, C as EventKey, D as Handler, E as EventsOf, F as Tracer, G as TraceLogger, H as Task, I as anyEventType, K as UnsubscribeCallback, L as EffectName, M as WithType, N as commandCall, O as HandlerRef, P as createBus, R as EventMap, S as Emittable, T as EventPayload, U as Trace, V as Phase, W as TraceEvent, _ as Commands, b as ContextOption, d as Action, f as AnyEvent, g as CommandCall, h as Command, i as WithPayload, j as WithContext, k as Off, m as BusOptions, p as Bus, v as Context, w as EventLike, x as Emit, y as ContextOf, z as Keys } from "./action-creator-DKG_sIia.js";
2
- import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-Dt1IgVo8.js";
1
+ import { A as On, B as PayloadOf, C as EventKey, D as Handler, E as EventsOf, F as Tracer, G as TraceLogger, H as Task, I as anyEventType, K as UnsubscribeCallback, L as EffectName, M as WithType, N as commandCall, O as HandlerRef, P as createBus, R as EventMap, S as Emittable, T as EventPayload, U as Trace, V as Phase, W as TraceEvent, _ as Commands, b as ContextOption, d as Action, f as AnyEvent, g as CommandCall, h as Command, i as WithPayload, j as WithContext, k as Off, m as BusOptions, p as Bus, v as Context, w as EventLike, x as Emit, y as ContextOf, z as Keys } from "./action-creator-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-DKG_sIia.js";
2
- import { n as NoClash } from "./context-plugin-Dt1IgVo8.js";
1
+ import { R as EventMap, c as BoundForm, j as WithContext, p as Bus, u as Effects, v as Context, z as Keys } from "./action-creator-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-DKG_sIia.js";
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> = {
@@ -1,4 +1,4 @@
1
- import { R as EventMap, p as Bus } from "./action-creator-DKG_sIia.js";
1
+ import { R as EventMap, p as Bus } from "./action-creator-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 = {
@@ -1,4 +1,4 @@
1
- import { B as EventMap, V as Keys, h as Bus } from "./action-creator-c292nK7v.js";
1
+ import { B as EventMap, V as Keys, h as Bus } from "./action-creator-Bd4TBZgl.js";
2
2
  //#region src/observable.d.ts
3
3
  declare global {
4
4
  interface SymbolConstructor {
@@ -1,5 +1,5 @@
1
- import { B as EventMap, N as WithContext, V as Keys, b as Context, d as Effects, h as Bus, l as BoundForm } from "./action-creator-c292nK7v.js";
2
- import { n as NoClash } from "./context-plugin-1pYmSQEG.js";
1
+ import { B as EventMap, N as WithContext, V as Keys, b as Context, d as Effects, h as Bus, l as BoundForm } from "./action-creator-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
@@ -1,4 +1,4 @@
1
- import { B as EventMap, W as Task } from "./action-creator-c292nK7v.js";
1
+ import { B as EventMap, W as Task } from "./action-creator-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-c292nK7v.js";
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-c292nK7v.js";
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-c292nK7v.js";
2
- import { n as Scoped } from "./types-mLaAx-EZ.js";
1
+ import { B as EventMap, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect } from "./action-creator-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 `effects` cannot do: no annotation is needed, and
8
+ * generics included, which `$effects` cannot do: no annotation is needed, and
9
9
  * JavaScript editors show the exact hint.
10
10
  *
11
11
  * Only `scopePlugin` binds it; the function itself is returned unchanged.
@@ -23,12 +23,12 @@ import { n as Scoped } from "./types-mLaAx-EZ.js";
23
23
  * );
24
24
  * ```
25
25
  */
26
- export declare const effect: <A extends unknown[], R>(fn: (ctx: Signaled, ...args: A) => R) => ((...args: A) => R);
26
+ export declare function effect<A extends unknown[], R>(fn: (ctx: Signaled, ...args: A) => R): (...args: A) => R;
27
27
  //#endregion
28
28
  //#region src/scope/plugin.d.ts
29
29
  /**
30
30
  * Switches scopes on for a bus: the scope given to a command, a job, an
31
- * action or a module (an object whose keys go on `ctx`, with `effects` bound to
31
+ * action or a module (an object whose keys go on `ctx`, with `$effects` bound to
32
32
  * the task), and the second argument of `emit`, put fields on
33
33
  * the context of the tasks they are for. A stronger layer replaces a field of
34
34
  * the same name: `emit`, then action / command / job, then module, then the bus.
package/dist/scope.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./scope-key-B7l7mUS5.js";const t=/* @__PURE__ */ new WeakSet,n=e=>(t.add(e),e),r=/* @__PURE__ */ new Set([`signal`,`abort`,`emit`,`on`,`off`,`run`,`type`,`payload`]),i=Symbol(`nanoeffects.scope.entry`),a=[],o=/* @__PURE__ */ new WeakSet,s=(e,n)=>{if(!n)return;for(let i of Object.keys(n)){let a=n[i];if(i!==`effects`&&!r.has(i)){let n=a;e[i]=typeof a==`function`&&t.has(a)?c(e,n):a}}let i=n.effects??{};for(let t of Object.keys(i)){let n=i[t];r.has(t)||(e[t]=c(e,n))}},c=(e,t)=>(...n)=>t(e,...n),l=(e,t)=>{s(e,t.module);for(let n of t.call)s(e,n)},u=t=>{if(typeof t==`object`&&t||typeof t==`function`)return Reflect.get(t,e)},d=e=>typeof e==`object`&&!!e,f=(e,t)=>{let n=u(e),r=d(e)?t[0]:void 0;if(!(n||r))return;let i=n&&r?[n,r]:[n??r];return{type:typeof e==`string`?e:e.type,payload:d(e)?e.payload:t[0],layers:i}},p=()=>e=>{let t,n=e=>{let n=e.run;if(o.has(n))return;let a=(a,...o)=>{let s=t;t=void 0;try{return n((t,n)=>{let o=e[i];return o&&r(n,o.call,o.module),a(t,n)},...o)}finally{t=s}};o.add(a),e.run=a},r=(e,t,r)=>{let a={call:t,module:r};e[i]=a,l(e,a),n(e)},s=e.addContext;e.addContext=e=>{s?.(e);let n=e;t&&n.type===t.type&&Object.is(n.payload,t.payload)&&r(n,t.layers,void 0)},e.applyScope=(e,t)=>{let n=e,o=n[i]?.call??a;r(n,o,t)},e.fitScope=(t,n)=>(r,i)=>(e.applyScope?.(i,t),Reflect.apply(n,null,[r,i]));let c=e.emit;return e.emit=(e,...n)=>{let r=f(e,n);if(!(r||t))return c(e,...n);let i=t;t=r;try{return d(e)?c(e):c(e,...n)}finally{t=i}},e};export{n as effect,p as scopePlugin};
1
+ import{t as e}from"./scope-key-B7l7mUS5.js";const t=/* @__PURE__ */ new WeakSet;function n(e){return t.add(e),e}const r=/* @__PURE__ */ new Set([`signal`,`abort`,`emit`,`on`,`off`,`run`,`type`,`payload`]),i=[],a=Symbol(`nanoeffects.scope.call`),o=Symbol(`nanoeffects.scope.module`),s=Symbol(`nanoeffects.scope.watched`),c=e=>e,l=e=>typeof e==`function`&&t.has(e),u=(e,t)=>(...n)=>Reflect.apply(t,null,[e,...n]),d=(e,t)=>{if(!t)return;for(let n in t)if(n!==`$effects`&&!r.has(n)){let r=t[n];e[n]=l(r)?u(e,r):r}let n=t.$effects;if(n)for(let t in n){let i=n[t];i&&!r.has(t)&&(e[t]=u(e,i))}},f=(e,t,n)=>{d(e,n);for(let n of t)d(e,n)},p=e=>typeof e==`object`&&!!e,m=e=>p(e),h=e=>(p(e)||typeof e==`function`)&&`type`in e,g=e=>h(e)?e.type:e,_=(e,t)=>p(e)&&h(e)?e.payload:t[0],v=t=>p(t)||typeof t==`function`?Reflect.get(t,e):void 0,y=(e,t)=>{let n=v(e),r=p(e)&&m(t[0])?t[0]:void 0;if(n&&r)return[n,r];let i=n??r;return i?[i]:void 0},b=()=>e=>{let t,n,r,l=e=>{let n=e.run;if(n[s])return;let r=(r,...i)=>{let s=t;t=void 0;try{return n((t,n)=>{let i=e[a];return i&&u(n,i,e[o]),r(t,n)},...i)}finally{t=s}};r[s]=!0,e.run=r},u=(e,t,n)=>{e[a]=t,n&&(e[o]=n),f(e,t,n),l(e)},d=e.addContext;e.addContext=e=>{d?.(e);let i=c(e);t&&i.type===n&&Object.is(i.payload,r)&&u(i,t,void 0)},e.applyScope=(e,t)=>{let n=c(e);u(n,n[a]??i,t)},e.fitScope=(t,n)=>typeof n==`function`?(r,i)=>(e.applyScope?.(i,t),Reflect.apply(n,null,[r,i])):n;let m=e.emit;return e.emit=(e,...i)=>{let a=y(e,i);if(!(a||t))return m(e,...i);let o=t,s=n,c=r;t=a,n=g(e),r=_(e,i);try{return p(e)?m(e):m(e,...i)}finally{t=o,n=s,r=c}},e};export{n as effect,b as scopePlugin};
package/dist/trace.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as Trace, K as TraceEvent, L as Tracer, q as TraceLogger } from "./action-creator-c292nK7v.js";
1
+ import { G as Trace, K as TraceEvent, L as Tracer, q as TraceLogger } from "./action-creator-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-c292nK7v.js";
2
- import { t as BoundOf } from "./plugin-CCYlC0XS.js";
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 `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>;
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` (`effects` binds functions to the task), as the last argument of `createCommand`, `createAction`, `createModule` and `createJob`, and turn it on with `scopePlugin`:
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:
@@ -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: `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)`.
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 `effects` it is bound to the task, so it stops with the task.
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 `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 a field of the scope cannot have.
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 `effects` too: `{ effects: { delay: instantDelay } }`.
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 `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.
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 `effects`.
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 `effects`.
226
- - 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.
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` (`effects` binds functions to the task), as the last argument of `createCommand`, `createAction`, `createModule` and `createJob`, and turn it on with `scopePlugin`:
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: `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)`.
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 `effects` it is bound to the task, so it stops with the task.
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 `effects` only functions that take `ctx` first. A plain function (a formatter, a logger) belongs in `context`.
1868
- - A function under any other key is copied as it is; a function in `effects` is bound.
1869
- - `effects` is the one name a field of the scope cannot have.
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 `effects` too: `{ effects: { delay: instantDelay } }`.
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 `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.
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 `effects`.
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 `effects`.
2014
- - 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.
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.6.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",