nanoeffects 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/{action-creator-BrZF6hXc.d.ts → action-creator-9e8jesRa.d.ts} +28 -12
  2. package/dist/actions.d.ts +12 -5
  3. package/dist/actions.js +1 -1
  4. package/dist/bus-Cdw5ajKF.js +1 -0
  5. package/dist/context-CJGW6OWj.js +1 -0
  6. package/dist/{context-plugin--HkNN1ha.d.ts → context-plugin-Xibukkbj.d.ts} +1 -1
  7. package/dist/{delay-CIHnOh_L.js → delay-BnBdGPh7.js} +1 -1
  8. package/dist/dev.d.ts +2 -2
  9. package/dist/effects.d.ts +2 -2
  10. package/dist/effects.js +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +1 -1
  13. package/dist/lifecycle-key-8iX8RUVD.js +1 -0
  14. package/dist/match.d.ts +1 -1
  15. package/dist/match.js +1 -1
  16. package/dist/{module-DqKegXHt.d.ts → module-BlkU3i6B.d.ts} +3 -3
  17. package/dist/notrace/{action-creator-DHAEgwuh.d.ts → action-creator-DQTwMBwP.d.ts} +27 -11
  18. package/dist/notrace/actions.d.ts +12 -5
  19. package/dist/notrace/actions.js +1 -1
  20. package/dist/notrace/bus-G2AdGObY.js +1 -0
  21. package/dist/notrace/{context-plugin-BapQkGwQ.d.ts → context-plugin-DlZCkcX9.d.ts} +1 -1
  22. package/dist/notrace/effects.d.ts +2 -2
  23. package/dist/notrace/index.d.ts +2 -2
  24. package/dist/notrace/index.js +1 -1
  25. package/dist/notrace/lifecycle-key-8iX8RUVD.js +1 -0
  26. package/dist/notrace/origin-key-BbWN3gmN.js +1 -0
  27. package/dist/notrace/origin-key-CxK0SkeG.d.ts +10 -0
  28. package/dist/notrace/{plugin-Cxadz6uU.d.ts → plugin-DOYHG78z.d.ts} +2 -2
  29. package/dist/notrace/policies.d.ts +1 -1
  30. package/dist/notrace/policies.js +1 -1
  31. package/dist/notrace/redux.d.ts +4 -3
  32. package/dist/notrace/redux.js +1 -1
  33. package/dist/observable.d.ts +1 -1
  34. package/dist/oop.d.ts +13 -7
  35. package/dist/oop.js +1 -1
  36. package/dist/origin-key-BbWN3gmN.js +1 -0
  37. package/dist/origin-key-CxK0SkeG.d.ts +10 -0
  38. package/dist/{plugin-DkXCvsmB.d.ts → plugin-DHkamZdH.d.ts} +2 -2
  39. package/dist/policies.d.ts +2 -2
  40. package/dist/policies.js +1 -1
  41. package/dist/react.d.ts +15 -8
  42. package/dist/react.js +1 -1
  43. package/dist/redux.d.ts +4 -3
  44. package/dist/redux.js +1 -1
  45. package/dist/scope.d.ts +2 -2
  46. package/dist/scope.js +1 -1
  47. package/dist/signal.d.ts +1 -1
  48. package/dist/signal.js +1 -1
  49. package/dist/trace.d.ts +30 -1
  50. package/dist/trace.js +1 -1
  51. package/dist/{types-KavbvHix.d.ts → types-ByPzv7tw.d.ts} +2 -2
  52. package/dist/{with-policy-DBV9dh2F.js → with-policy-CW50Edgk.js} +1 -1
  53. package/dist/{with-policy-CqUS-VWW.d.ts → with-policy-DpCvSZRk.d.ts} +1 -1
  54. package/docs/actions.md +10 -3
  55. package/docs/migrating-from-effector.md +1 -1
  56. package/docs/oop.md +2 -0
  57. package/docs/package.md +1 -1
  58. package/docs/plugins.md +16 -1
  59. package/docs/scope.md +1 -1
  60. package/docs/stores.md +5 -1
  61. package/docs/tracing.md +121 -1
  62. package/llms-full.txt +158 -10
  63. package/llms.txt +1 -1
  64. package/package.json +3 -3
  65. package/dist/bus-CkTHUAvq.js +0 -1
  66. package/dist/context-COgyNlrC.js +0 -1
  67. package/dist/notrace/bus-BmCTo8BG.js +0 -1
  68. package/dist/onAbort-BgMfspK4.js +0 -1
@@ -1,11 +1,12 @@
1
- import { R as EventMap, p as Bus } from "./action-creator-DHAEgwuh.js";
1
+ import { R as EventMap, p as Bus } from "./action-creator-DQTwMBwP.js";
2
+ import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
2
3
  //#region src/store/redux/middleware.d.ts
3
4
  /** What Redux passes through a middleware: an action is a plain object with a `type`. */
4
5
  type ReduxAction = {
5
6
  type: string;
6
7
  payload?: unknown;
7
8
  };
8
- type NanoeffectsMiddlewareOptions = {
9
+ type NanoeffectsMiddlewareOptions = OriginOptions & {
9
10
  /** Return `false` for the actions that the bus should not get. Default: all of them. */
10
11
  filter?: (action: ReduxAction) => boolean;
11
12
  /** Receives an error that `emit` throws, so `dispatch` does not. Default: the error is thrown. */
@@ -21,7 +22,7 @@ type ReduxStoreApi<State = unknown> = {
21
22
  }) => unknown;
22
23
  getState: () => State;
23
24
  };
24
- type EffectsMiddlewareOptions = Pick<NanoeffectsMiddlewareOptions, "filter">;
25
+ type EffectsMiddlewareOptions = Pick<NanoeffectsMiddlewareOptions, "filter" | "traceId" | "traceGroupId">;
25
26
  /**
26
27
  * Makes the Redux middleware that feeds a bus you already have: the actions of
27
28
  * the store reach its handlers, and every task gets `ctx.store`, the `dispatch`
@@ -1 +1 @@
1
- const e=e=>typeof e==`object`&&!!e&&`type`in e,t=(t,n={})=>{let r=t.emit,i=e=>{try{r(e)}catch(t){if(!n.onError)throw t;n.onError(t,e)}};return()=>t=>r=>{let a=t(r);return e(r)&&(n.filter?.(r)??!0)&&i(r),a}},n=(e,n={})=>{let r,i=/* @__PURE__ */ new WeakMap,a=e.addContext;e.addContext=e=>{a?.(e);let t=r;if(!t)return;let{type:n}=e;Object.assign(e,{store:{getState:t.getState,dispatch:e=>{typeof e==`object`&&e&&i.set(e,n);let r=e;return t.dispatch(r)}}})};let o=t(e,{filter:e=>i.get(e)!==e.type&&(n.filter?.(e)??!0),onError:(e,{type:t})=>{globalThis.console?.error(`[nanoeffects] handler of "${t}" failed`,e)}});return e=>(r=e,o())};export{n as createEffectsMiddleware};
1
+ import{t as e}from"./origin-key-BbWN3gmN.js";const t=e=>typeof e==`object`&&!!e&&`type`in e,n=(n,r={})=>{let i=n.emit,a=t=>{try{let a=Reflect.get(n,e);a?a(t,r):i(t)}catch(e){if(!r.onError)throw e;r.onError(e,t)}};return()=>e=>n=>{let i=e(n);return t(n)&&(r.filter?.(n)??!0)&&a(n),i}},r=(e,t={})=>{let r,i=/* @__PURE__ */ new WeakMap,a=e.addContext;e.addContext=e=>{a?.(e);let t=r;if(!t)return;let{type:n}=e;Object.assign(e,{store:{getState:t.getState,dispatch:e=>{typeof e==`object`&&e&&i.set(e,n);let r=e;return t.dispatch(r)}}})};let o=n(e,{...t.traceId===void 0?{}:{traceId:t.traceId},...t.traceGroupId===void 0?{}:{traceGroupId:t.traceGroupId},filter:e=>i.get(e)!==e.type&&(t.filter?.(e)??!0),onError:(e,{type:t})=>{globalThis.console?.error(`[nanoeffects] handler of "${t}" failed`,e)}});return e=>(r=e,o())};export{r as createEffectsMiddleware};
@@ -1,4 +1,4 @@
1
- import { B as EventMap, V as Keys, h as Bus } from "./action-creator-BrZF6hXc.js";
1
+ import { B as EventMap, V as Keys, h as Bus } from "./action-creator-9e8jesRa.js";
2
2
  //#region src/observable.d.ts
3
3
  declare global {
4
4
  interface SymbolConstructor {
package/dist/oop.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, _ as Command, h as Bus, t as ActionCreator } from "./action-creator-BrZF6hXc.js";
1
+ import { B as EventMap, H as ListenerOptions, _ as Command, h as Bus, t as ActionCreator } from "./action-creator-9e8jesRa.js";
2
2
  //#region src/oop/types.d.ts
3
3
  type Slots = "ctx" | "commands" | "trace" | "addContext" | "applyScope" | "fitScope" | "app" | "use";
4
4
  type AnyFunction = (...args: never[]) => unknown;
@@ -64,8 +64,10 @@ export declare function WithBus(): <C extends Constructor>(Base: C) => Extended<
64
64
  * Makes a method of a class from `extendBus` or `@WithBus` a handler of `event`
65
65
  * for each instance, with `this` bound to the instance, from the construction
66
66
  * until `dispose()`. `wrap` takes the bound handler and returns the one to
67
- * subscribe, such as `latest`. An event emitted inside the constructor of the
68
- * base class does not reach it: the subscription is made right after.
67
+ * subscribe, such as `latest`. `{ traceId }`, as the second or the third
68
+ * argument, names the method in the trace (see `tracePlugin`). An event emitted
69
+ * inside the constructor of the base class does not reach it: the
70
+ * subscription is made right after.
69
71
  *
70
72
  * @example
71
73
  * ```ts
@@ -75,11 +77,15 @@ export declare function WithBus(): <C extends Constructor>(Base: C) => Extended<
75
77
  * }
76
78
  * ```
77
79
  */
78
- export declare function On<P>(event: EventOf<P>, wrap?: Wrap): MethodDecorator<P>;
79
- export declare function On(event: string, wrap?: Wrap): MethodDecorator<never>;
80
+ export declare function On<P>(event: EventOf<P>, wrap?: Wrap, options?: ListenerOptions): MethodDecorator<P>;
81
+ export declare function On<P>(event: EventOf<P>, options: ListenerOptions): MethodDecorator<P>;
82
+ export declare function On(event: string, wrap?: Wrap, options?: ListenerOptions): MethodDecorator<never>;
83
+ export declare function On(event: string, options: ListenerOptions): MethodDecorator<never>;
80
84
  /** Like `On`, but the handler runs at most once for an instance. */
81
- export declare function Once<P>(event: EventOf<P>, wrap?: Wrap): MethodDecorator<P>;
82
- export declare function Once(event: string, wrap?: Wrap): MethodDecorator<never>;
85
+ export declare function Once<P>(event: EventOf<P>, wrap?: Wrap, options?: ListenerOptions): MethodDecorator<P>;
86
+ export declare function Once<P>(event: EventOf<P>, options: ListenerOptions): MethodDecorator<P>;
87
+ export declare function Once(event: string, wrap?: Wrap, options?: ListenerOptions): MethodDecorator<never>;
88
+ export declare function Once(event: string, options: ListenerOptions): MethodDecorator<never>;
83
89
  //#endregion
84
90
  //#region src/oop/extend.d.ts
85
91
  /**
package/dist/oop.js CHANGED
@@ -1 +1 @@
1
- import{r as e}from"./bus-CkTHUAvq.js";const t=/* @__PURE__ */ new WeakMap,n=e=>typeof e==`function`,r=e=>Reflect.ownKeys(e).flatMap(t=>{let r=Reflect.getOwnPropertyDescriptor(e,t)?.value;return n(r)?[{key:t,value:r}]:[]}),i=/* @__PURE__ */ new WeakMap,a=e=>{let n=i.get(e);if(n)return n;let a=/* @__PURE__ */ new Set,o=[];for(let n=e;n&&n!==Object.prototype;n=Reflect.getPrototypeOf(n))for(let{key:e,value:i}of r(n)){if(e===`constructor`||a.has(e))continue;a.add(e);let n=t.get(i);n&&o.push({key:e,method:i,spec:n})}return i.set(e,o),o},o=/* @__PURE__ */ new WeakMap,s=e=>typeof e==`object`&&!!e||typeof e==`function`,c=e=>{let t=o.get(e);if(t)return t;let n=/* @__PURE__ */ new Set;return o.set(e,n),n},l=e=>{if(typeof e==`string`)return e;if(s(e)){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},u=(e,t,n)=>Reflect.apply(Reflect.get(e,t),e,n),d=(e,t,n,r)=>{let[i,a,...o]=r,s=c(e),l={event:i,handler:a,off:()=>{}},d=u(t,n,[i,n===`once`?(e,t)=>(s.delete(l),typeof a==`function`?Reflect.apply(a,null,[e,t]):void 0):a,...o]),f=typeof d==`function`?()=>d():()=>{};return l.off=f,s.add(l),()=>{s.delete(l),f()}},f=(e,t,n)=>{let[r,i]=n,a=c(e);r===void 0&&a.clear();for(let e of[...a])Object.is(l(e.event),l(r))&&(i===void 0||e.handler===i)&&(a.delete(e),e.off());return u(t,`off`,n)},p=e=>{if(!s(e))return;let t=o.get(e);if(t){o.delete(e);for(let e of t)e.off()}},m=/* @__PURE__ */ new Set([`on`,`once`,`off`,`emit`,`ctx`,`commands`,`trace`,`addContext`,`applyScope`,`fitScope`,`app`,`use`]),h=Reflect.get(Symbol,`dispose`),g=e=>typeof e==`function`,_=(e,t,n,r)=>{n in t||Object.defineProperty(e,n,{configurable:!0,enumerable:!1,...r})},v=/* @__PURE__ */ new WeakMap,y=(e,t)=>{let n=v.get(e);if(n)return n;let r={on:(...n)=>d(e,t,`on`,n),once:(...n)=>d(e,t,`once`,n),off:(...n)=>f(e,t,n)};return v.set(e,r),r},b=(e,t)=>(...n)=>Reflect.apply(Reflect.get(e,t),e,n),x=(e,t)=>{for(let{method:n,spec:r}of a(Reflect.getPrototypeOf(e)??{})){let i=(t,r)=>Reflect.apply(n,e,[t,r]),a=r.wrap?r.wrap(i):i;d(e,t,r.once?`once`:`on`,[r.event,a])}},S=(e,t,n)=>{for(let r of[`on`,`once`,`off`])_(e,t,r,{get(){return y(this,n)[r]}});_(e,t,`emit`,{value:b(n,`emit`)});for(let[r,i]of Object.entries(n))typeof i==`function`&&!m.has(r)&&_(e,t,r,{value:b(n,r)});let r=function(){p(this)};_(e,t,`dispose`,{value:r}),h&&_(e,t,h,{value:r}),_(e,t,`bus`,{get:()=>n})},C=(e,t)=>{let n=function(...r){let i=Reflect.construct(t,r,new.target??n);return x(i,e),i};return Object.setPrototypeOf(n,t),n.prototype=Object.create(t.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),Object.defineProperty(n,"name",{value:t.name,configurable:!0}),`bus`in t||Object.defineProperty(n,"bus",{value:e,configurable:!0}),S(n.prototype,t.prototype,e),n};function w(t,n){if(g(t))return C(e(),t);if(!n)throw TypeError(`extendBus: pass the class to extend`);return C(t,n)}function T(e){return t=>e?w(e,t):w(t)}const E=e=>{let[t,n,r]=e;return typeof n==`string`||typeof n==`symbol`?Reflect.get(Object(r),`value`):t},D=(e,n,r)=>((...i)=>{let a=E(i);typeof a==`function`&&t.set(a,{event:e,wrap:n,once:r})});function O(e,t){return D(e,t,!1)}function k(e,t){return D(e,t,!0)}export{O as On,k as Once,T as WithBus,p as disposeBus,w as extendBus};
1
+ import{r as e}from"./bus-Cdw5ajKF.js";const t=/* @__PURE__ */ new WeakMap,n=e=>typeof e==`function`,r=e=>Reflect.ownKeys(e).flatMap(t=>{let r=Reflect.getOwnPropertyDescriptor(e,t)?.value;return n(r)?[{key:t,value:r}]:[]}),i=/* @__PURE__ */ new WeakMap,a=e=>{let n=i.get(e);if(n)return n;let a=/* @__PURE__ */ new Set,o=[];for(let n=e;n&&n!==Object.prototype;n=Reflect.getPrototypeOf(n))for(let{key:e,value:i}of r(n)){if(e===`constructor`||a.has(e))continue;a.add(e);let n=t.get(i);n&&o.push({key:e,method:i,spec:n})}return i.set(e,o),o},o=/* @__PURE__ */ new WeakMap,s=e=>typeof e==`object`&&!!e||typeof e==`function`,c=e=>{let t=o.get(e);if(t)return t;let n=/* @__PURE__ */ new Set;return o.set(e,n),n},l=e=>{if(typeof e==`string`)return e;if(s(e)){let t=Reflect.get(e,`type`);return typeof t==`string`?t:e}return e},u=(e,t,n)=>Reflect.apply(Reflect.get(e,t),e,n),d=(e,t,n,r)=>{let[i,a,...o]=r,s=c(e),l={event:i,handler:a,off:()=>{}},d=u(t,n,[i,n===`once`?(e,t)=>(s.delete(l),typeof a==`function`?Reflect.apply(a,null,[e,t]):void 0):a,...o]),f=typeof d==`function`?()=>d():()=>{};return l.off=f,s.add(l),()=>{s.delete(l),f()}},f=(e,t,n)=>{let[r,i]=n,a=c(e);r===void 0&&a.clear();for(let e of[...a])Object.is(l(e.event),l(r))&&(i===void 0||e.handler===i)&&(a.delete(e),e.off());return u(t,`off`,n)},p=e=>{if(!s(e))return;let t=o.get(e);if(t){o.delete(e);for(let e of t)e.off()}},m=/* @__PURE__ */ new Set([`on`,`once`,`off`,`emit`,`ctx`,`commands`,`trace`,`addContext`,`applyScope`,`fitScope`,`app`,`use`]),h=Reflect.get(Symbol,`dispose`),g=e=>typeof e==`function`,_=(e,t,n,r)=>{n in t||Object.defineProperty(e,n,{configurable:!0,enumerable:!1,...r})},v=/* @__PURE__ */ new WeakMap,y=(e,t)=>{let n=v.get(e);if(n)return n;let r={on:(...n)=>d(e,t,`on`,n),once:(...n)=>d(e,t,`once`,n),off:(...n)=>f(e,t,n)};return v.set(e,r),r},b=(e,t)=>(...n)=>Reflect.apply(Reflect.get(e,t),e,n),x=(e,t)=>{for(let{method:n,spec:r}of a(Reflect.getPrototypeOf(e)??{})){let i=(t,r)=>Reflect.apply(n,e,[t,r]),a=r.wrap?r.wrap(i):i,o=r.once?`once`:`on`;d(e,t,o,[r.event,a,r.options])}},S=(e,t,n)=>{for(let r of[`on`,`once`,`off`])_(e,t,r,{get(){return y(this,n)[r]}});_(e,t,`emit`,{value:b(n,`emit`)});for(let[r,i]of Object.entries(n))typeof i==`function`&&!m.has(r)&&_(e,t,r,{value:b(n,r)});let r=function(){p(this)};_(e,t,`dispose`,{value:r}),h&&_(e,t,h,{value:r}),_(e,t,`bus`,{get:()=>n})},C=(e,t)=>{let n=function(...r){let i=Reflect.construct(t,r,new.target??n);return x(i,e),i};return Object.setPrototypeOf(n,t),n.prototype=Object.create(t.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),Object.defineProperty(n,"name",{value:t.name,configurable:!0}),`bus`in t||Object.defineProperty(n,"bus",{value:e,configurable:!0}),S(n.prototype,t.prototype,e),n};function w(t,n){if(g(t))return C(e(),t);if(!n)throw TypeError(`extendBus: pass the class to extend`);return C(t,n)}function T(e){return t=>e?w(e,t):w(t)}const E=e=>{let[t,n,r]=e;return typeof n==`string`||typeof n==`symbol`?Reflect.get(Object(r),`value`):t},D=(e,n,r,i)=>((...a)=>{let o=E(a);if(typeof o==`function`){let a=typeof n==`function`?n:void 0,s=typeof n==`function`?r:n??r;t.set(o,{event:e,wrap:a,once:i,options:s})}});function O(e,t,n){return D(e,t,n,!1)}function k(e,t,n){return D(e,t,n,!0)}export{O as On,k as Once,T as WithBus,p as disposeBus,w as extendBus};
@@ -0,0 +1 @@
1
+ const e=Symbol.for(`nanoeffects.origin`);export{e as t};
@@ -0,0 +1,10 @@
1
+ //#region src/core/origin-key.d.ts
2
+ /** What an emitter tells the trace about itself. */
3
+ type OriginOptions = {
4
+ /** Names the subscription that emits; it is the `type` of the root task of the trace. */
5
+ traceId?: string;
6
+ /** Names the flow the emit belongs to; every task below it has it too. */
7
+ traceGroupId?: string;
8
+ };
9
+ //#endregion
10
+ export { OriginOptions as t };
@@ -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-BrZF6hXc.js";
2
- import { n as NoClash } from "./context-plugin--HkNN1ha.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-9e8jesRa.js";
2
+ import { n as NoClash } from "./context-plugin-Xibukkbj.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,5 +1,5 @@
1
- import { B as EventMap, W as Task } from "./action-creator-BrZF6hXc.js";
2
- import { n as withPolicy, t as PolicyFunction } from "./with-policy-CqUS-VWW.js";
1
+ import { B as EventMap, G as Task } from "./action-creator-9e8jesRa.js";
2
+ import { n as withPolicy, t as PolicyFunction } from "./with-policy-DpCvSZRk.js";
3
3
  //#region src/policies/keyed.d.ts
4
4
  /** Options shared by every policy that can keep its state per key. */
5
5
  type KeyOption<P> = {
package/dist/policies.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./onAbort-BgMfspK4.js";import{a as t,i as n,o as r,t as i}from"./context-COgyNlrC.js";import{n as a,r as o,t as s}from"./delay-CIHnOh_L.js";import{n as c,t as l}from"./promise-CSLpzwSR.js";import{t as u}from"./with-policy-DBV9dh2F.js";const d=()=>{},f=(e,t)=>{let n=/* @__PURE__ */ new Map;return(r,i)=>{let a=e(r),o=n.get(a);if(!o){let e=t(()=>{n.get(a)===e&&n.delete(a)});o=e,n.set(a,e)}return o(r,i)}},p=(e,t)=>e?f(e,t):t(d),m=(e,t={})=>p(t.key,t=>h(`latest`,(t,n,r)=>(r(),e(t,n)),t)),h=(t,a,o=d)=>{let s=null;return(l,u)=>{if(s?.abort(),u.signal.aborted)return;let{ctx:d,unlink:f}=n(u);s=d;let p=i(d,t),m=!1,h=()=>{d.signal.aborted||(m=!0,r(d,t,`start`))};e(d,()=>p(m?`cancel`:`drop`));let g=()=>{f(),s===d&&(s=null,o())},_=e=>{if(!d.signal.aborted)return p(m?`done`:`drop`),e};try{let e=a(l,d,h);return c(e)?Promise.resolve(e).then(_,e=>{if(!d.signal.aborted)throw p(`error`,e),e}).finally(g):(g(),_(e))}catch(e){if(g(),d.signal.aborted)return;throw p(`error`,e),e}}},g=(e,t,n={})=>p(n.key,r=>v(e,t,n,r)),_=(e,n,i,s)=>{let c=s!==d,l=null,u=null;return(d,f)=>{if(f.signal.aborted)return;let p=Date.now(),m=l===null||p-l>=e||p-l<0;return l=p,u!==null&&a(u),u=c?o(s,e):null,i&&m?Promise.resolve(t(f,`debounce`,()=>n(d,f))):(r(f,`debounce`,`drop`),Promise.resolve(void 0))}},v=(n,r,i,a)=>{let o=i.leading??!1,c=i.trailing??!0;if(!c)return _(n,r,o,a);let l=!1,u=0,d=h(`debounce`,([t,i],a,o)=>{let d=u;return e(a,()=>{u===d&&(l=!1)}),s(a,n).then(()=>{if(l=!1,c&&!i)return o(),r(t,a)})},a);return(e,n)=>{if(n.signal.aborted)return;let i=o&&!l;l=!0,u++;let a=d([e,i],n);return i?Promise.resolve(t(n,`debounce`,()=>r(e,n))):a}},y=(n,i={})=>p(i.key,i=>{let a=null;return(o,s)=>{if(s.signal.aborted)return;if(a){r(s,`leading`,`drop`);return}let c={};a=c;let u=()=>{d(),a===c&&(a=null,i())},d=e(s,u);try{return l(t(s,`leading`,()=>n(o,s)),u)}catch(e){throw u(),e}}}),b=(e,t={})=>p(t.key,n=>x(e,t,n)),x=(n,i,a)=>{let o=i.concurrency??1,s=i.limit??1/0,c=[],l=0,u=()=>{for(;l<o;){let e=c.shift();if(!e)return;e.start()}};return(f,p)=>new Promise((m,h)=>{if(p.signal.aborted){m(void 0);return}let g=d,_=()=>{g(),r(p,`queue`,`drop`),m(void 0)},v=()=>{if(g(),p.signal.aborted){_();return}l++;let r=!0,i=()=>{r&&(r=!1,l--,u(),l===0&&c.length===0&&a())},o=e(p,()=>{i(),m(void 0)}),s;try{s=Promise.resolve(t(p,`queue`,()=>n(f,p)))}catch(e){s=Promise.reject(e)}let d=()=>{o(),i()};s.then(e=>{d(),m(p.signal.aborted?void 0:e)},e=>{d(),p.signal.aborted?m(void 0):h(e)})};if(l<o){v();return}if(c.length>=s){if(i.overflow!==`drop-old`||s<=0){_();return}c.shift()?.drop()}let y={start:v,drop:_};c.push(y),g=e(p,()=>{let e=c.indexOf(y);e>=0&&c.splice(e,1),_()})})},S=(e,t,n={})=>p(n.key,r=>C(e,t,n,r)),C=(e,n,i,s)=>{let c=i.leading??!0,l=i.trailing??!0,u=s!==d,f=null,p=0,m=null,h=null,g=null,_=!1,v=t=>f===null||t-f>=e||t-f<0||t-p>=e,y=e=>r(e,`throttle`,`drop`),b=()=>{let e=g;g=null,e&&(y(e.ctx),e.resolve())},x=(e,t)=>(b(),new Promise((n,r)=>{g={payload:e,ctx:t,resolve:n,reject:r}})),S=(e,r,i)=>{b(),p=i,_=!0;try{return t(r,`throttle`,()=>n(e,r))}finally{_=!1}},C=()=>{u&&(h=o(w,e))},w=()=>{if(h=null,!u||m!==null||g)return;let t=Date.now(),n=Math.max(e-(t-(f??-1/0)),e-(t-p));n<=0?s():h=o(w,n)},T=e=>{m=null;let t=g;if(l&&t&&!t.ctx.signal.aborted){g=null;try{t.resolve(S(t.payload,t.ctx,e))}catch(e){t.reject(e)}}else b();w()},E=()=>{let t=Date.now();v(t)?T(t):m=o(E,Math.min(e-(t-(f??t)),e-(t-p)))},D=()=>{h!==null&&(a(h),h=null)},O=(e,t)=>{if(!l)y(t);else return x(e,t)},k=(e,t,n,r)=>{if(!r)y(t);else if(p=n,C(),!c)y(t);else return S(e,t,n)},A=(t,n,r,i)=>i&&m!==null?(a(m),m=o(E,e),S(t,n,r)):m===null&&(m=o(E,e),i&&(p=r,c))?S(t,n,r):O(t,n);return(e,t)=>{if(t.signal.aborted)return;let n=Date.now(),r=!_&&v(n);return f=n,D(),l?A(e,t,n,r):k(e,t,n,r)}};export{g as debounce,m as latest,y as leading,b as queue,S as throttle,u as withPolicy};
1
+ import{i as e,o as t,s as n,t as r,u as i}from"./context-CJGW6OWj.js";import{n as a,r as o,t as s}from"./delay-BnBdGPh7.js";import{n as c,t as l}from"./promise-CSLpzwSR.js";import{n as u,t as d}from"./lifecycle-key-8iX8RUVD.js";import{t as f}from"./with-policy-CW50Edgk.js";const p=e=>Reflect.get(e,d),m=e=>{p(e)?.start()},h=(e,n,r)=>(m(e),t(e,n,r)),g=(e,t)=>{n(e,t,`drop`),p(e)?.drop()},_=(e,t)=>{p(e)?.done(t)},v=(e,t,n)=>{let r=p(e);t?r?.cancel(n):r?.drop()},y=e=>(Reflect.set(e,u,!0),e),b=()=>{},x=(e,t)=>{let n=/* @__PURE__ */ new Map;return(r,i)=>{let a=e(r),o=n.get(a);if(!o){let e=t(()=>{n.get(a)===e&&n.delete(a)});o=e,n.set(a,e)}return o(r,i)}},S=(e,t)=>y(e?x(e,t):t(b)),C=(e,t={})=>S(t.key,t=>w(`latest`,(t,n,r)=>(r(),e(t,n)),t)),w=(t,a,o=b)=>{let s=null;return(l,u)=>{if(s?.abort(),u.signal.aborted)return;let{ctx:d,unlink:f}=e(u);s=d;let p=r(d,t),h=!1,g=()=>{d.signal.aborted||(h=!0,m(d),n(d,t,`start`))};i(d,e=>{v(d,h,e),p(h?`cancel`:`drop`)});let _=()=>{f(),s===d&&(s=null,o())},y=e=>{if(!d.signal.aborted)return p(h?`done`:`drop`),e};try{let e=a(l,d,g);return c(e)?Promise.resolve(e).then(y,e=>{if(!d.signal.aborted)throw p(`error`,e),e}).finally(_):(_(),y(e))}catch(e){if(_(),d.signal.aborted)return;throw p(`error`,e),e}}},T=(e,t,n={})=>S(n.key,r=>D(e,t,n,r)),E=(e,t,n,r)=>{let i=r!==b,s=null,c=null;return(l,u)=>{if(u.signal.aborted)return;let d=Date.now(),f=s===null||d-s>=e||d-s<0;return s=d,c!==null&&a(c),c=i?o(r,e):null,n&&f?Promise.resolve(h(u,`debounce`,()=>t(l,u))):(g(u,`debounce`),Promise.resolve(void 0))}},D=(e,t,n,r)=>{let a=n.leading??!1,o=n.trailing??!0;if(!o)return E(e,t,a,r);let c=!1,l=0,u=w(`debounce`,([n,r],a,u)=>{let d=l;return i(a,()=>{l===d&&(c=!1)}),s(a,e).then(()=>{if(c=!1,o&&!r)return u(),t(n,a)})},r);return(e,n)=>{if(n.signal.aborted)return;let r=a&&!c;c=!0,l++;let i=u([e,r],n);return r?Promise.resolve(h(n,`debounce`,()=>t(e,n))):i}},O=(e,t={})=>S(t.key,t=>{let n=null;return(r,a)=>{if(a.signal.aborted)return;if(n){g(a,`leading`);return}let o={};n=o;let s=()=>{c(),n===o&&(n=null,t())},c=i(a,s);try{return l(h(a,`leading`,()=>e(r,a)),s)}catch(e){throw s(),e}}}),k=(e,t={})=>S(t.key,n=>A(e,t,n)),A=(e,t,n)=>{let r=t.concurrency??1,a=t.limit??1/0,o=[],s=0,c=()=>{for(;s<r;){let e=o.shift();if(!e)return;e.start()}};return(l,u)=>new Promise((d,f)=>{if(u.signal.aborted){d(void 0);return}let p=b,m=()=>{p(),g(u,`queue`),d(void 0)},v=()=>{if(p(),u.signal.aborted){m();return}s++;let t=!0,r=()=>{t&&(t=!1,s--,c(),s===0&&o.length===0&&n())},a=i(u,()=>{r(),d(void 0)}),g;try{g=Promise.resolve(h(u,`queue`,()=>e(l,u)))}catch(e){g=Promise.reject(e)}let v=()=>{a(),r()};g.then(e=>{_(u,{value:e}),v(),d(u.signal.aborted?void 0:e)},e=>{_(u,{error:e}),v(),u.signal.aborted?d(void 0):f(e)})};if(s<r){v();return}if(o.length>=a){if(t.overflow!==`drop-old`||a<=0){m();return}o.shift()?.drop()}let y={start:v,drop:m};o.push(y),p=i(u,()=>{let e=o.indexOf(y);e>=0&&o.splice(e,1),m()})})},j=(e,t,n={})=>S(n.key,r=>M(e,t,n,r)),M=(e,t,n,r)=>{let i=n.leading??!0,s=n.trailing??!0,c=r!==b,l=null,u=0,d=null,f=null,p=null,m=!1,_=t=>l===null||t-l>=e||t-l<0||t-u>=e,v=e=>g(e,`throttle`),y=()=>{let e=p;p=null,e&&(v(e.ctx),e.resolve())},x=(e,t)=>(y(),new Promise((n,r)=>{p={payload:e,ctx:t,resolve:n,reject:r}})),S=(e,n,r)=>{y(),u=r,m=!0;try{return h(n,`throttle`,()=>t(e,n))}finally{m=!1}},C=()=>{c&&(f=o(w,e))},w=()=>{if(f=null,!c||d!==null||p)return;let t=Date.now(),n=Math.max(e-(t-(l??-1/0)),e-(t-u));n<=0?r():f=o(w,n)},T=e=>{d=null;let t=p;if(s&&t&&!t.ctx.signal.aborted){p=null;try{t.resolve(S(t.payload,t.ctx,e))}catch(e){t.reject(e)}}else y();w()},E=()=>{let t=Date.now();_(t)?T(t):d=o(E,Math.min(e-(t-(l??t)),e-(t-u)))},D=()=>{f!==null&&(a(f),f=null)},O=(e,t)=>{if(!s)v(t);else return x(e,t)},k=(e,t,n,r)=>{if(!r)v(t);else if(u=n,C(),!i)v(t);else return S(e,t,n)},A=(t,n,r,s)=>s&&d!==null?(a(d),d=o(E,e),S(t,n,r)):d===null&&(d=o(E,e),s&&(u=r,i))?S(t,n,r):O(t,n);return(e,t)=>{if(t.signal.aborted)return;let n=Date.now(),r=!m&&_(n);return l=n,D(),s?A(e,t,n,r):k(e,t,n,r)}};export{T as debounce,C as latest,O as leading,k as queue,j as throttle,f as withPolicy};
package/dist/react.d.ts CHANGED
@@ -1,9 +1,15 @@
1
- import { B as EventMap, T as EventKey, W as Task, h as Bus, r as FreeActionCreator, t as ActionCreator } from "./action-creator-BrZF6hXc.js";
2
- import { t as PolicyFunction } from "./with-policy-CqUS-VWW.js";
1
+ import { B as EventMap, G as Task, T as EventKey, h as Bus, r as FreeActionCreator, t as ActionCreator } from "./action-creator-9e8jesRa.js";
2
+ import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
3
+ import { t as PolicyFunction } from "./with-policy-DpCvSZRk.js";
3
4
  //#region src/react/plugin.d.ts
4
- /** What `reactPlugin` adds to the bus. */
5
5
  /** Tells whether two values are the same: `Object.is` unless the caller gives another. */
6
6
  type Equals<V> = (previous: V, next: V) => boolean;
7
+ /** The third argument of `useEmitter`. */
8
+ type UseEmitterOptions<V> = OriginOptions & {
9
+ /** Tells whether a new value is the same as the previous one; `Object.is` when absent. */
10
+ equals?: Equals<V>;
11
+ };
12
+ /** What `reactPlugin` adds to the bus. */
7
13
  type ReactApi<E extends EventMap> = {
8
14
  /**
9
15
  * A React hook: after a render commits with a `value` that differs from the
@@ -13,18 +19,19 @@ type ReactApi<E extends EventMap> = {
13
19
  * `export const { useEmitter } = bus` gives a free hook.
14
20
  */
15
21
  useEmitter: {
16
- <K extends EventKey<E>>(event: K, value: E[K], equals?: Equals<E[K]>): void;
22
+ <K extends EventKey<E>>(event: K, value: E[K], options?: UseEmitterOptions<E[K]>): void;
17
23
  /** A creator without a payload type: the payload comes from the event map. */
18
- <K extends EventKey<E>>(creator: FreeActionCreator<K>, value: E[K], equals?: Equals<E[K]>): void;
19
- <P, T extends string>(creator: ActionCreator<P, T>, value: P, equals?: Equals<P>): void;
24
+ <K extends EventKey<E>>(creator: FreeActionCreator<K>, value: E[K], options?: UseEmitterOptions<E[K]>): void;
25
+ <P, T extends string>(creator: ActionCreator<P, T>, value: P, options?: UseEmitterOptions<P>): void;
20
26
  };
21
27
  };
22
28
  /**
23
29
  * Adds `bus.useEmitter(event, value)`: a hook that tells the bus when a value of
24
30
  * a component changes, without the traps of `useEffect(() => emit(value), [value])`
25
31
  * (an emit on mount, two on mount under StrictMode, a new `emit` re-running it).
26
- * Pass `equals` as the third argument for values that `Object.is` tells apart
27
- * although they are the same, such as a new object with the same fields.
32
+ * Pass `{ equals }` as the third argument for values that `Object.is` tells apart
33
+ * although they are the same, such as a new object with the same fields. `{ traceId }`
34
+ * names the subscription and `{ traceGroupId }` the flow in the trace of the bus.
28
35
  * Needs `react` 18 or newer. Returns the same bus.
29
36
  *
30
37
  * @example
package/dist/react.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./with-policy-DBV9dh2F.js";import{useEffect as t,useMemo as n,useRef as r}from"react";const i=()=>e=>{function n(n,i,a){let o=r(i),s=r({event:n,equals:a});t(()=>{s.current={event:n,equals:a}}),t(()=>{let{event:t,equals:n}=s.current,r=n??Object.is,a=o.current;if(r(a,i))return;o.current=i;let c=typeof t==`function`?t(i):{type:t,payload:i};e.emit(c)},[i])}return Object.assign(e,{useEmitter:n})},a=(i,a,o=[])=>{let s=r(i);t(()=>{s.current=i});let c=n(()=>e(a((e,t)=>s.current(e,t))),o);return t(()=>()=>c.cancel(),[c]),c};export{i as reactPlugin,a as usePolicy};
1
+ import{t as e}from"./with-policy-CW50Edgk.js";import{t}from"./origin-key-BbWN3gmN.js";import{useEffect as n,useMemo as r,useRef as i}from"react";const a=()=>e=>{function r(r,a,o){let s=i(a),c=i({event:r,options:o});n(()=>{c.current={event:r,options:o}}),n(()=>{let{event:n,options:r}=c.current,i=r?.equals??Object.is,o=s.current;if(i(o,a))return;s.current=a;let l=typeof n==`function`?n(a):{type:n,payload:a},u=Reflect.get(e,t);u?u(l,r??{}):e.emit(l)},[a])}return Object.assign(e,{useEmitter:r})},o=(t,a,o=[])=>{let s=i(t);n(()=>{s.current=t});let c=r(()=>e(a((e,t)=>s.current(e,t))),o);return n(()=>()=>c.cancel(),[c]),c};export{a as reactPlugin,o as usePolicy};
package/dist/redux.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- import { B as EventMap, h as Bus } from "./action-creator-BrZF6hXc.js";
1
+ import { B as EventMap, h as Bus } from "./action-creator-9e8jesRa.js";
2
+ import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
2
3
  //#region src/store/redux/middleware.d.ts
3
4
  /** What Redux passes through a middleware: an action is a plain object with a `type`. */
4
5
  type ReduxAction = {
5
6
  type: string;
6
7
  payload?: unknown;
7
8
  };
8
- type NanoeffectsMiddlewareOptions = {
9
+ type NanoeffectsMiddlewareOptions = OriginOptions & {
9
10
  /** Return `false` for the actions that the bus should not get. Default: all of them. */
10
11
  filter?: (action: ReduxAction) => boolean;
11
12
  /** Receives an error that `emit` throws, so `dispatch` does not. Default: the error is thrown. */
@@ -21,7 +22,7 @@ type ReduxStoreApi<State = unknown> = {
21
22
  }) => unknown;
22
23
  getState: () => State;
23
24
  };
24
- type EffectsMiddlewareOptions = Pick<NanoeffectsMiddlewareOptions, "filter">;
25
+ type EffectsMiddlewareOptions = Pick<NanoeffectsMiddlewareOptions, "filter" | "traceId" | "traceGroupId">;
25
26
  /**
26
27
  * Makes the Redux middleware that feeds a bus you already have: the actions of
27
28
  * the store reach its handlers, and every task gets `ctx.store`, the `dispatch`
package/dist/redux.js CHANGED
@@ -1 +1 @@
1
- const e=e=>typeof e==`object`&&!!e&&`type`in e,t=(t,n={})=>{let r=t.emit,i=e=>{try{r(e)}catch(t){if(!n.onError)throw t;n.onError(t,e)}};return()=>t=>r=>{let a=t(r);return e(r)&&(n.filter?.(r)??!0)&&i(r),a}},n=(e,n={})=>{let r,i=/* @__PURE__ */ new WeakMap,a=e.addContext;e.addContext=e=>{a?.(e);let t=r;if(!t)return;let{type:n}=e;Object.assign(e,{store:{getState:t.getState,dispatch:e=>{typeof e==`object`&&e&&i.set(e,n);let r=e;return t.dispatch(r)}}})};let o=t(e,{filter:e=>i.get(e)!==e.type&&(n.filter?.(e)??!0),onError:(e,{type:t})=>{globalThis.console?.error(`[nanoeffects] handler of "${t}" failed`,e)}});return e=>(r=e,o())};export{n as createEffectsMiddleware};
1
+ import{t as e}from"./origin-key-BbWN3gmN.js";const t=e=>typeof e==`object`&&!!e&&`type`in e,n=(n,r={})=>{let i=n.emit,a=t=>{try{let a=Reflect.get(n,e);a?a(t,r):i(t)}catch(e){if(!r.onError)throw e;r.onError(e,t)}};return()=>e=>n=>{let i=e(n);return t(n)&&(r.filter?.(n)??!0)&&a(n),i}},r=(e,t={})=>{let r,i=/* @__PURE__ */ new WeakMap,a=e.addContext;e.addContext=e=>{a?.(e);let t=r;if(!t)return;let{type:n}=e;Object.assign(e,{store:{getState:t.getState,dispatch:e=>{typeof e==`object`&&e&&i.set(e,n);let r=e;return t.dispatch(r)}}})};let o=n(e,{...t.traceId===void 0?{}:{traceId:t.traceId},...t.traceGroupId===void 0?{}:{traceGroupId:t.traceGroupId},filter:e=>i.get(e)!==e.type&&(t.filter?.(e)??!0),onError:(e,{type:t})=>{globalThis.console?.error(`[nanoeffects] handler of "${t}" failed`,e)}});return e=>(r=e,o())};export{r as createEffectsMiddleware};
package/dist/scope.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as EventMap, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect } from "./action-creator-BrZF6hXc.js";
2
- import { n as ScopeSlots, r as Scoped } from "./types-KavbvHix.js";
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-9e8jesRa.js";
2
+ import { n as ScopeSlots, r as Scoped } from "./types-ByPzv7tw.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
package/dist/scope.js CHANGED
@@ -1 +1 @@
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 p=e.emit;return e.emit=(e,...i)=>{let a=y(e,i);if(!(a||t))return p(e,...i);let o=t,s=n,c=r;t=a,n=g(e),r=_(e,i);try{return p(e,...i)}finally{t=o,n=s,r=c}},e};export{n as effect,b 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`,`traceGroupId`]),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 p=e.emit;return e.emit=(e,...i)=>{let a=y(e,i);if(!(a||t))return p(e,...i);let o=t,s=n,c=r;t=a,n=g(e),r=_(e,i);try{return p(e,...i)}finally{t=o,n=s,r=c}},e};export{n as effect,b as scopePlugin};
package/dist/signal.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, C as Emit, E as EventLike, a as EmitScope, c as WithScope, h as Bus, v as CommandCall } from "./action-creator-BrZF6hXc.js";
1
+ import { B as EventMap, C as Emit, E as EventLike, a as EmitScope, c as WithScope, h as Bus, v as CommandCall } from "./action-creator-9e8jesRa.js";
2
2
  //#region src/signal/plugin.d.ts
3
3
  /** What the second argument of `emit` takes besides the fields of a scope. */
4
4
  type SignalOption = {
package/dist/signal.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./onAbort-BgMfspK4.js";import{n as t}from"./promise-CSLpzwSR.js";import{n}from"./bus-CkTHUAvq.js";const r=e=>typeof e==`object`&&!!e,i=(e,t)=>{let n=t[0];if(!r(n)||!r(e)&&typeof e!=`function`)return;let i=Reflect.get(n,`signal`);return i instanceof AbortSignal?i:void 0},a=(r,i,a,o)=>{let s=e({signal:r},e=>{for(let{target:t}of i)t.abort(e)});return t(o)?o.then(s,s):n(a)&&s(),o},o=()=>e=>{let t,n,r=e.addContext;e.addContext=e=>{r?.(e),t?.push({target:e}),n?.aborted&&e.abort(n.reason)};let o=e.emit;return e.emit=(e,...r)=>{let s=i(e,r);if(!(s||t))return o(e,...r);let c=t,l=n,u=[];t=s?u:void 0,n=s;let d;try{d=o(e,...r)}finally{t=c,n=l}return s?a(s,u,e,d):d},e};export{o as signalPlugin};
1
+ import{u as e}from"./context-CJGW6OWj.js";import{n as t}from"./promise-CSLpzwSR.js";import{n}from"./bus-Cdw5ajKF.js";const r=e=>typeof e==`object`&&!!e,i=(e,t)=>{let n=t[0];if(!r(n)||!r(e)&&typeof e!=`function`)return;let i=Reflect.get(n,`signal`);return i instanceof AbortSignal?i:void 0},a=(r,i,a,o)=>{let s=e({signal:r},e=>{for(let{target:t}of i)t.abort(e)});return t(o)?o.then(s,s):n(a)&&s(),o},o=()=>e=>{let t,n,r=e.addContext;e.addContext=e=>{r?.(e),t?.push({target:e}),n?.aborted&&e.abort(n.reason)};let o=e.emit;return e.emit=(e,...r)=>{let s=i(e,r);if(!(s||t))return o(e,...r);let c=t,l=n,u=[];t=s?u:void 0,n=s;let d;try{d=o(e,...r)}finally{t=c,n=l}return s?a(s,u,e,d):d},e};export{o as signalPlugin};
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-BrZF6hXc.js";
1
+ import { B as EventMap, J as TraceLogger, K as Trace, L as Tracer, h as Bus, q as TraceEvent } from "./action-creator-9e8jesRa.js";
2
2
  //#region src/trace/console.d.ts
3
3
  type ConsoleLoggerOptions = {
4
4
  /** Defaults to `globalThis.console`. */
@@ -48,6 +48,34 @@ type PerformanceLoggerOptions = {
48
48
  */
49
49
  export declare const performanceLogger: (options?: PerformanceLoggerOptions) => TraceLogger;
50
50
  //#endregion
51
+ //#region src/trace/plugin.d.ts
52
+ /**
53
+ * Names and groups what the trace shows, on a bus that has a tracer:
54
+ *
55
+ * - `traceGroupId` in the scope of a command, a job or an action goes to every
56
+ * task that the call starts;
57
+ * - `useEmitter` and `bus.emitter` emit from a root task named by their
58
+ * `traceId`, so a chain says which subscription began it;
59
+ * - `bus.on(event, handler, { traceId })` and `once` name a listener: each call of
60
+ * it is a task with that `traceId`, and what it emits is below it.
61
+ *
62
+ * The plugin does nothing on a bus without a tracer, and without it all of these
63
+ * options are ignored, so a project can leave it out, or use it only in
64
+ * development, and nothing else changes. Returns the same bus.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import { createBus } from "nanoeffects";
69
+ * import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
70
+ *
71
+ * const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
72
+ *
73
+ * // a listener that you can find in the log
74
+ * bus.on("name/changed", saveName, { traceId: "saveNotifier" });
75
+ * ```
76
+ */
77
+ export declare const tracePlugin: () => <E extends EventMap>(bus: Bus<E>) => Bus<E>;
78
+ //#endregion
51
79
  //#region src/trace/recorder.d.ts
52
80
  type Recorder<E = Record<string, unknown>> = TraceLogger<E> & {
53
81
  /** Every event received so far, in order. */
@@ -105,6 +133,7 @@ type ReduxDevtoolsState = {
105
133
  running: Record<number, {
106
134
  type: string;
107
135
  effect: string;
136
+ traceGroupId?: string;
108
137
  }>;
109
138
  };
110
139
  /**
package/dist/trace.js CHANGED
@@ -1 +1 @@
1
- import{n as e,t}from"./onAbort-BgMfspK4.js";import{n}from"./promise-CSLpzwSR.js";const r=(e={})=>{let t=e.console??globalThis.console,n=e.prefix??`[nanoeffects]`,r=/* @__PURE__ */ new Map;return({id:e,parent:i,type:a,effect:o,phase:s,payload:c,error:l})=>{r.has(e)||r.set(e,typeof i==`number`?(r.get(i)??0)+1:0);let u=` `.repeat(r.get(e)??0);s!==`start`&&r.delete(e);let d=`${n} ${u}${a} · ${o} ${s}`;s===`error`?t?.error(d,l):t?.debug(d,c)}},i=(e={})=>{let t=e.performance??globalThis.performance,n=/* @__PURE__ */ new Map;return({id:e,parent:r,type:i,effect:a,phase:o,error:s})=>{if(!t)return;if(o===`start`){n.set(e,t.now());return}let c=`${i} · ${a} (${o})`,l={id:e,parent:r,phase:o,...o===`error`&&{error:String(s)}},u=n.get(e);n.delete(e),typeof u==`number`?t.measure(c,{start:u,end:t.now(),detail:l}):t.mark(c,{detail:l})}},a=()=>{let e=[];return Object.assign(t=>{e.push(t)},{events:e,clear:()=>{e.length=0}})},o=()=>{},s=(e={})=>{let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;if(!t)return o;let{starts:n=!1,depth:r=1,maxAge:i}=e,a=t.connect({name:e.name??`nanoeffects`,...i!==void 0&&{maxAge:i}}),s=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map,l=()=>({running:Object.fromEntries(s)});return a.init(l()),({error:e,...t})=>{let i=(t.parent===void 0?-1:c.get(t.parent)??-1)+1,o=i<=r;t.phase===`start`?(c.set(t.id,i),o&&s.set(t.id,{type:t.type,effect:t.effect})):(c.delete(t.id),s.delete(t.id)),o&&(t.phase!==`start`||n)&&a.send({...t,type:`${t.type}/${t.effect}/${t.phase}`,event:t.type,...t.phase===`error`&&{error:e instanceof Error?e.message:e}},l())}},c=t=>{let n=typeof t==`function`?[t]:[...t??[]];return n.length===0?null:t=>{for(let r of n)try{r(t)}catch(t){e(`trace logger failed`,t)}}},l=()=>{},u=(e,r={})=>{let i=c(e),{filter:a,sample:o=1}=r,s=0,u=e=>typeof o==`function`?o({type:e.type,payload:e.payload}):Math.random()<o,d={task:(e,t)=>({id:++s,parent:t?.id,type:e.type,payload:e.payload,sampled:t?.sampled??u(e)}),report:(e,t,n,r)=>{if(!e.sampled)return;let o={id:e.id,type:e.type,effect:t,phase:n,payload:e.payload};typeof e.parent==`number`&&(o.parent=e.parent),n===`error`&&(o.error=r),(!a||a(o))&&i?.(o)},settler:(e,t)=>{if(!e.sampled)return l;let n=!1;return(r,i)=>{n||(n=!0,d.report(e,t,r,i))}},run:(e,r,i,a)=>{if(!e.sampled)return a();let o=d.settler(e,r);d.report(e,r,`start`),t({signal:i},()=>o(`cancel`));let s;try{s=a()}catch(e){throw o(`error`,e),e}return n(s)?Promise.resolve(s).then(e=>(o(`done`),e),e=>{throw o(`error`,e),e}):(o(`done`),s)}};return d};export{r as consoleLogger,a as createTraceRecorder,u as createTracer,i as performanceLogger,s as reduxDevtools};
1
+ import{a as e,c as t,d as n,n as r,o as i,s as a,u as o}from"./context-CJGW6OWj.js";import{n as s}from"./promise-CSLpzwSR.js";import{t as c}from"./origin-key-BbWN3gmN.js";import{t as l}from"./scope-key-B7l7mUS5.js";const u=(e={})=>{let t=e.console??globalThis.console,n=e.prefix??`[nanoeffects]`,r=/* @__PURE__ */ new Map;return({id:e,parentId:i,type:a,effect:o,phase:s,payload:c,error:l,traceGroupId:u})=>{r.has(e)||r.set(e,typeof i==`number`?(r.get(i)??0)+1:0);let d=` `.repeat(r.get(e)??0);s!==`start`&&r.delete(e);let f=u===void 0?``:` [${u}]`,p=`${n}${f} ${d}${a} · ${o} ${s}`;s===`error`?t?.error(p,l):t?.debug(p,c)}},d=(e={})=>{let t=e.performance??globalThis.performance,n=/* @__PURE__ */ new Map;return({id:e,parentId:r,rootId:i,traceGroupId:a,type:o,effect:s,phase:c,error:l})=>{if(!t)return;if(c===`start`){n.set(e,t.now());return}let u=`${o} · ${s} (${c})`,d={id:e,parentId:r,...i!==void 0&&{rootId:i},...a!==void 0&&{traceGroupId:a},phase:c,...c===`error`&&{error:String(l)}},f=n.get(e);n.delete(e),typeof f==`number`?t.measure(u,{start:f,end:t.now(),detail:d}):t.mark(u,{detail:d})}};let f;const p=()=>f,m=(e,t)=>{let n=f;f=e;try{return t()}finally{f=n}},h=(e,t,n={})=>{let{trace:i}=e;if(!i){e.emit(t);return}let{traceId:o,traceGroupId:s}=n,{ctx:c,unlink:l}=r({event:{type:o??t.type,payload:t,...s===void 0?{}:{traceGroupId:s}},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e.ctx,addContext:e.addContext},tracer:i});a(c,`run`,`start`);try{c.emit(t),a(c,`run`,`done`)}catch(e){throw a(c,`run`,`error`,e),e}finally{l()}},g=e=>typeof e==`string`?e:Reflect.get(Object(e),`type`),_=()=>n=>{if(!(t&&n.trace))return n;let r=n.emit;n.emit=(e,...t)=>m(e,()=>r(e,...t)),Reflect.set(n,c,(e,t)=>{h(n,e,t)});let a=/* @__PURE__ */ new WeakMap,o=(e,t,n)=>{let r=a.get(t)??/* @__PURE__ */ new Map,i=g(e);r.set(i,[...r.get(i)??[],n]),a.set(t,r)},s=(e,t)=>(a.get(t)?.get(g(e)))?.pop(),l=(t,n)=>(r,a)=>(e(a,n),i(a,`run`,()=>t(r,a))),u=(e,t)=>(n,r,i)=>{let a=Reflect.get(Object(i),`traceId`);if(typeof a!=`string`)return e(n,r);let c=t?(e,t)=>(s(n,r),l(r,a)(e,t)):l(r,a);o(n,r,c);let u=e(n,c);return()=>{u()}},d=n.off,f=(e,t)=>{t===void 0?d(e):d(e,s(e,t)??t)},p=n.on,_=n.once;return n.on=u(p,!1),n.once=u(_,!0),n.off=f,n},v=()=>{let e=[];return Object.assign(t=>{e.push(t)},{events:e,clear:()=>{e.length=0}})},y=()=>{},b=(e={})=>{let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;if(!t)return y;let{starts:n=!1,depth:r=1,maxAge:i}=e,a=t.connect({name:e.name??`nanoeffects`,...i!==void 0&&{maxAge:i}}),o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Map,c=()=>({running:Object.fromEntries(o)});return a.init(c()),({error:e,...t})=>{let i=(t.parentId===void 0?-1:s.get(t.parentId)??-1)+1,l=i<=r;t.phase===`start`?(s.set(t.id,i),l&&o.set(t.id,{type:t.type,effect:t.effect,...t.traceGroupId!==void 0&&{traceGroupId:t.traceGroupId}})):(s.delete(t.id),o.delete(t.id)),l&&(t.phase!==`start`||n)&&a.send({...t,type:`${t.type}/${t.effect}/${t.phase}`,event:t.type,...t.phase===`error`&&{error:e instanceof Error?e.message:e}},c())}},x=e=>{let t=typeof e==`function`?[e]:[...e??[]];return t.length===0?null:e=>{for(let r of t)try{r(e)}catch(e){n(`trace logger failed`,e)}}},S=()=>{},C=e=>{if(typeof e.traceGroupId==`string`)return e.traceGroupId;let t=p();if(typeof t!=`object`&&typeof t!=`function`||t===null)return;let n=Reflect.get(t,l),r=typeof n==`object`&&n?Reflect.get(n,`traceGroupId`):void 0;return typeof r==`string`?r:void 0},w=(e,t={})=>{let n=x(e),{filter:r,sample:i=1}=t,a=0,c=e=>typeof i==`function`?i({type:e.type,payload:e.payload}):Math.random()<i,l={task:(e,t)=>({id:++a,root:t?.root??a,parent:t?.id,type:e.type,payload:e.payload,sampled:t?.sampled??c(e),group:C(e)??t?.group}),rename:(e,t)=>{e.type=t},report:(e,t,i,a)=>{if(!e.sampled)return;let o={id:e.id,type:e.type,effect:t,phase:i,payload:e.payload};typeof e.parent==`number`&&(o.parentId=e.parent),e.root!==e.id&&(o.rootId=e.root),e.group!==void 0&&(o.traceGroupId=e.group),i===`error`&&(o.error=a),(!r||r(o))&&n?.(o)},settler:(e,t)=>{if(!e.sampled)return S;let n=!1;return(r,i)=>{n||(n=!0,l.report(e,t,r,i))}},run:(e,t,n,r)=>{if(!e.sampled)return r();let i=l.settler(e,t);l.report(e,t,`start`),o({signal:n},()=>i(`cancel`));let a;try{a=r()}catch(e){throw i(`error`,e),e}return s(a)?Promise.resolve(a).then(e=>(i(`done`),e),e=>{throw i(`error`,e),e}):(i(`done`),a)}};return l};export{u as consoleLogger,v as createTraceRecorder,w as createTracer,d as performanceLogger,b as reduxDevtools,_ as tracePlugin};
@@ -1,5 +1,5 @@
1
- import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-BrZF6hXc.js";
2
- import { t as BoundOf } from "./plugin-DkXCvsmB.js";
1
+ import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-9e8jesRa.js";
2
+ import { t as BoundOf } from "./plugin-DHkamZdH.js";
3
3
  //#region src/scope/types.d.ts
4
4
  /**
5
5
  * The slots `scopePlugin` fills for `modulePlugin`. They are not on `Bus`: a bus
@@ -1 +1 @@
1
- import{n as e}from"./context-COgyNlrC.js";const t=Reflect.get(Symbol,`dispose`),n=n=>{let r=new AbortController,i=async(...t)=>{let i=t[0],{ctx:a,unlink:o}=e({event:{type:`withPolicy`,payload:i},parent:{signal:r.signal}});try{return await n(i,a)}finally{o()}},a=e=>{r.abort(e),r=new AbortController},o=Object.assign(i,{cancel:a});return t&&Reflect.set(o,t,()=>a()),o};export{n as t};
1
+ import{n as e}from"./context-CJGW6OWj.js";const t=Reflect.get(Symbol,`dispose`),n=n=>{let r=new AbortController,i=async(...t)=>{let i=t[0],{ctx:a,unlink:o}=e({event:{type:`withPolicy`,payload:i},parent:{signal:r.signal}});try{return await n(i,a)}finally{o()}},a=e=>{r.abort(e),r=new AbortController},o=Object.assign(i,{cancel:a});return t&&Reflect.set(o,t,()=>a()),o};export{n as t};
@@ -1,4 +1,4 @@
1
- import { B as EventMap, H as PayloadOf, W as Task } from "./action-creator-BrZF6hXc.js";
1
+ import { B as EventMap, G as Task, U as PayloadOf } from "./action-creator-9e8jesRa.js";
2
2
  //#region src/policies/with-policy.d.ts
3
3
  /** What `withPolicy` returns: the task as a plain function that can be cancelled. */
4
4
  type PolicyFunction<P, R> = {
package/docs/actions.md CHANGED
@@ -118,7 +118,7 @@ The registry lives in `nanoeffects/actions`, so a bus without one carries none o
118
118
  - When the task is aborted before the handler settles, emits `loadUser.cancel(reason)` instead of `success` or `fail`. After `pending`, exactly one of the three follows.
119
119
  - `pending` / `success` / `fail` / `cancel` are a string (job makes an action creator) or an existing action creator. Missing ones become `<type>/pending`, `<type>/success`, `<type>/fail` or `<type>/cancel`.
120
120
  - `bus.emit(loadUser(id))` returns a promise of the result. Handler errors are always handled: they go to `fail` and the promise resolves with `undefined`. For a result that must reject, use a command.
121
- - Outcome actions are emitted as the task that ran the job (also for a handler bound with `commands.handle`). `success` and `fail` are not sent after abort; `cancel` is, once the handler settles.
121
+ - Outcome actions are emitted as the task that ran the job (also for a handler bound with `commands.handle`). `success` and `fail` are not sent after abort; `cancel` is, at the moment of the abort.
122
122
  - A missing handler is not an outcome: `emit` throws `No handler for command "..."` and nothing is sent.
123
123
  - Calling the job only builds `{ type, payload }`; the handler runs on `emit`.
124
124
  - The outcomes are events like any other: with `onUnhandled` set on the bus, it is called for a `pending` or a `cancel` that nobody listens to.
@@ -185,9 +185,16 @@ import { loadUser } from "./user";
185
185
  bus.emit(loadUser("1"));
186
186
  ```
187
187
 
188
- `cancel` is sent when the handler settles, not at the moment of the abort. A handler that stops on `ctx.signal` (as `fetch` does) settles at once. A handler that ignores the signal sends `cancel` when it finishes, and one that never finishes sends nothing.
188
+ `cancel` is sent at the moment of the abort, with the abort reason. A handler that ignores the signal and finishes later sends nothing more, so the flag is already `false`.
189
189
 
190
- **Limit: policies.** A policy such as `latest`, `leading`, `throttle` or `queue` wraps the handler, inside the job. The job does not know what the policy decides, so `pending` is sent for every call. A call that the policy drops, and a run that `latest` replaces, then close with `success(undefined)`, not with `cancel`. A flag set by `pending` is exact for a job without a policy, and for an abort of the task or of its parent. With a policy, count the calls or use the result of `emit`.
190
+ **With a policy.** A policy such as `latest`, `leading`, `debounce`, `throttle` or `queue` around the handler tells the job what it does with each call:
191
+
192
+ - `pending` is sent when the handler really starts: at once for `leading`, after the wait for `debounce`, when its turn comes for `queue`.
193
+ - A call that the policy drops before it starts sends nothing, not even `pending`. A second click that `leading` ignores does not clear the flag of the first.
194
+ - A run that `latest` replaces sends `cancel` before the next call sends `pending`.
195
+ - Every `pending` is followed by exactly one of `success`, `fail` and `cancel`, and nothing is sent without `pending`.
196
+
197
+ A flag is exact when calls do not overlap, or when `latest` replaces them. If several runs can go at once (`queue` with `concurrency` above 1, or a job without a policy called many times), keep a counter: add one on `pending` and take one off on `success`, `fail` and `cancel`. A policy that you write yourself, or that you hide in your own function, does not report to the job; `pending` is then sent at the call, as for a job with no policy.
191
198
 
192
199
  ## Mount domains with modules
193
200
 
@@ -318,7 +318,7 @@ bus.on(
318
318
  ## What is different
319
319
 
320
320
  - **No state.** There is no `createStore`, `combine`, `restore`, `$store.map`. The bus holds listeners only. Take a store such as Zustand for state ([Stores](stores.md)).
321
- - **`pending` is an action, not a store.** `createJob` sends `pending` when the handler starts, and `success`, `fail` or `cancel` when it ends; you keep the flag in your store ([Show a loading state](actions.md#show-a-loading-state-with-a-job)). With a policy such as `latest` around the handler the flag is not exact yet, which that section explains. A running task is also visible through [Tracing](tracing.md).
321
+ - **`pending` is an action, not a store.** `createJob` sends `pending` when the handler starts, and `success`, `fail` or `cancel` when it ends; you keep the flag in your store ([Show a loading state](actions.md#show-a-loading-state-with-a-job)). Policies report to the job, so the flag also follows `latest`, `leading`, `debounce`, `throttle` and `queue`; that section says when to count instead. A running task is also visible through [Tracing](tracing.md).
322
322
  - **Cancellation is the default tool.** An Effector effect runs every call to the end. Here `latest` and `ctx.signal` stop what is no longer needed.
323
323
  - **Events are matched by name, not by identity.** An event is a string or a `createAction` creator, not a unit that you import and call. Two modules agree on the name.
324
324
  - **No graph.** `sample` and `forward` build a static graph. A bus subscribes handlers at runtime and the call order is the order of `on`.
package/docs/oop.md CHANGED
@@ -194,6 +194,8 @@ Things to know:
194
194
  - On a class that `extendBus` did not make, a decorator does nothing.
195
195
  - Both the decorators of the language and `experimentalDecorators` work. Your compiler has to support the one you use: TypeScript 5 or newer for the first, and the `experimentalDecorators` option for the second.
196
196
 
197
+ Name a method for the [trace](tracing.md#name-a-listener) with `{ traceId }`, as the second argument or, after a wrapper, the third: `@On(queryChanged, { traceId: "searcher" })` or `@On(queryChanged, latest, { traceId: "searcher" })`. It needs `tracePlugin` on the bus and is ignored without it.
198
+
197
199
  ## Decorate the class with `@WithBus`
198
200
 
199
201
  `@WithBus(bus)` does what `extendBus(bus, Base)` does, as a class decorator. `@WithBus()` makes a bus for the class.
package/docs/package.md CHANGED
@@ -16,7 +16,7 @@ Imports are split so you only load what you use. `createBus` stays small; the re
16
16
  | `nanoeffects/observable` | `createObserve` |
17
17
  | `nanoeffects/react` | `reactPlugin`: adds the `useEmitter` hook to a bus (needs React 18 or newer) |
18
18
  | `nanoeffects/dev` | `devPlugin` (no-op in production) |
19
- | `nanoeffects/trace` | `createTracer` and the loggers |
19
+ | `nanoeffects/trace` | `createTracer`, the loggers and `tracePlugin` (names for the trace) |
20
20
 
21
21
  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.
22
22
 
package/docs/plugins.md CHANGED
@@ -95,6 +95,7 @@ Add one of these when you meet its need. None of them is required.
95
95
  | [`signalPlugin`](#tie-an-emit-to-an-outside-signal-signalplugin) | `emit(call, { signal })` that aborts its tasks when an outside signal aborts. | `nanoeffects/signal` | The same bus, typed with the new `emit` |
96
96
  | [`matchPlugin`](#listen-to-a-group-of-events-matchplugin) | `on` that listens to a group of events which a function picks. | `nanoeffects/match` | The same bus, typed with the new overloads |
97
97
  | [`emitterPlugin`](#hand-the-bus-to-a-state-manager-emitterplugin) | A callback that emits an action, to give to a state manager. | `nanoeffects/actions` | The same bus, with `emitter` |
98
+ | [`tracePlugin`](#name-the-origin-and-the-flow-of-a-trace-traceplugin) | `traceId` and `traceGroupId` for a trace: the root of an emitter, a flow, a named listener. | `nanoeffects/trace` | The same bus |
98
99
  | [`reactPlugin`](#tell-the-bus-when-a-react-value-changes-reactplugin) | A React hook that emits when a value of a component changes. | `nanoeffects/react` | The same bus, with `useEmitter` |
99
100
  | [`devPlugin`](#find-leaks-while-you-develop-devplugin) | Warnings about listeners that pile up, and messages about modules, while you develop. | `nanoeffects/dev` | A function that disconnects it |
100
101
 
@@ -138,6 +139,20 @@ bus.emit(getUser("1"), { signal: controller.signal }); // runs the command
138
139
  controller.abort(); // ctx.signal of the handler aborts with it
139
140
  ```
140
141
 
142
+ ### Name the origin and the flow of a trace: `tracePlugin`
143
+
144
+ With a tracer on the bus, the plugin lets a trace say which subscription began a chain (`traceId` of `useEmitter` and `bus.emitter`), follow one flow by a `traceGroupId`, and show which listener ran (`bus.on(event, handler, { traceId })`). Without the plugin the options are ignored. More in [Tracing](tracing.md#show-which-subscription-began-a-chain).
145
+
146
+ ```js
147
+ import { createBus } from "nanoeffects";
148
+ import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
149
+
150
+ // tracePlugin — names the roots, groups and listeners of the trace
151
+ const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
152
+
153
+ bus.on("name/changed", saveName, { traceId: "saveNotifier" }); // a listener you can find in the log
154
+ ```
155
+
141
156
  ### Listen to a group of events: `matchPlugin`
142
157
 
143
158
  `on`, `once` and `off` take a function that picks the events, such as every `user/*` event. A type guard narrows the payload. The subscription listens to every event, so for a few known events `on(creator, handler)` is cheaper. More in the [Guide](guide.md#pick-events-with-a-function).
@@ -174,7 +189,7 @@ $name.listen(bus.emitter(nameChanged)); // every change of $name emits nameChang
174
189
 
175
190
  ### Tell the bus when a React value changes: `reactPlugin`
176
191
 
177
- `useEmitter(event, value)` emits after a render commits with a new value. It does not emit on mount, and not twice under StrictMode. It needs React 18 or newer. More in [Stores](stores.md#react-sharing-a-value-across-components).
192
+ `useEmitter(event, value, options?)` emits after a render commits with a new value. It does not emit on mount, and not twice under StrictMode. The options are `{ equals, traceId, traceGroupId }`; the last two name the emit in the [trace](tracing.md#show-which-subscription-began-a-chain). It needs React 18 or newer. More in [Stores](stores.md#react-sharing-a-value-across-components).
178
193
 
179
194
  ```jsx
180
195
  import { useState } from "react";
package/docs/scope.md CHANGED
@@ -189,7 +189,7 @@ When several places set the same field, the strongest one wins:
189
189
  | 3 | the scope of the module that owns the handler |
190
190
  | 4 (weakest) | the bus: `createBus({ context })`, `contextPlugin`, `effectsPlugin` |
191
191
 
192
- The fields of the library (`signal`, `abort`, `emit`, `on`, `off`, `run`, `type`, `payload`) are never replaced. A child task (`ctx.run`, an attempt of `retry`) has the fields of its parent.
192
+ The fields of the library (`signal`, `abort`, `emit`, `on`, `off`, `run`, `type`, `payload`) are never replaced. The name `traceGroupId` is reserved: the tracer reads it, and it is not put on `ctx` ([Follow one flow](tracing.md#follow-one-flow-with-a-tracegroupid)). A child task (`ctx.run`, an attempt of `retry`) has the fields of its parent.
193
193
 
194
194
  ## Keep a generic effect generic
195
195
 
package/docs/stores.md CHANGED
@@ -83,6 +83,10 @@ String event names (`"user/requested"`) work the same. Use your usual RTK action
83
83
 
84
84
  The only option is `filter`, which keeps rejected actions away from the bus. Options of the bus (`onError`, `context`, `trace`) go to `createBus`. Use one bus for one store. Values that are not plain actions (thunks) go through untouched. Nothing here depends on the `redux` package at runtime.
85
85
 
86
+ ### Show which dispatch began a chain
87
+
88
+ With a tracer and `tracePlugin` on the bus, each dispatched action reaches the bus from a root task of the trace. Name it with `traceId`, and put the flow in a group with `traceGroupId`: `createEffectsMiddleware(bus, { traceId: "store", traceGroupId: "checkout" })`. Without a `traceId` the root has the type of the action. Without the plugin the options are ignored. More in [Tracing](tracing.md#show-which-subscription-began-a-chain).
89
+
86
90
  ### Type `ctx.store` (optional)
87
91
 
88
92
  Only if you want TypeScript to know `store` on handlers. **Skip this in JavaScript** — the middleware already puts `store` on the task at runtime. These lines do not connect the store across the app.
@@ -461,7 +465,7 @@ export function Results() {
461
465
  Two limits to know:
462
466
 
463
467
  - **All consumers re-render** on any change of the context value. Split it into two contexts (`query` and `items`) if that is slow.
464
- - **A new object counts as a change.** For a value that is rebuilt with the same content, pass a third argument that says when two values are the same: `useEmitter(profileChanged, profile, (previous, next) => previous.id === next.id)`.
468
+ - **A new object counts as a change.** For a value that is rebuilt with the same content, pass `equals` in the options, the third argument, to say when two values are the same: `useEmitter(profileChanged, profile, { equals: (previous, next) => previous.id === next.id })`. The same object takes `traceId` and `traceGroupId`, which name the subscription and the flow in the [trace](tracing.md#show-which-subscription-began-a-chain).
465
469
 
466
470
  `useEmitter` emits after the render, not at the moment of `setQuery`. To emit on every call, even with the same value, call an emitter from the event handler (`bus.emitter(action)`, see [Zustand](#zustand)) instead.
467
471