nanoeffects 0.10.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 (63) 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/match.d.ts +1 -1
  14. package/dist/match.js +1 -1
  15. package/dist/{module-DqKegXHt.d.ts → module-BlkU3i6B.d.ts} +3 -3
  16. package/dist/notrace/{action-creator-DHAEgwuh.d.ts → action-creator-DQTwMBwP.d.ts} +27 -11
  17. package/dist/notrace/actions.d.ts +12 -5
  18. package/dist/notrace/actions.js +1 -1
  19. package/dist/notrace/bus-G2AdGObY.js +1 -0
  20. package/dist/notrace/{context-plugin-BapQkGwQ.d.ts → context-plugin-DlZCkcX9.d.ts} +1 -1
  21. package/dist/notrace/effects.d.ts +2 -2
  22. package/dist/notrace/index.d.ts +2 -2
  23. package/dist/notrace/index.js +1 -1
  24. package/dist/notrace/origin-key-BbWN3gmN.js +1 -0
  25. package/dist/notrace/origin-key-CxK0SkeG.d.ts +10 -0
  26. package/dist/notrace/{plugin-Cxadz6uU.d.ts → plugin-DOYHG78z.d.ts} +2 -2
  27. package/dist/notrace/policies.d.ts +1 -1
  28. package/dist/notrace/redux.d.ts +4 -3
  29. package/dist/notrace/redux.js +1 -1
  30. package/dist/observable.d.ts +1 -1
  31. package/dist/oop.d.ts +13 -7
  32. package/dist/oop.js +1 -1
  33. package/dist/origin-key-BbWN3gmN.js +1 -0
  34. package/dist/origin-key-CxK0SkeG.d.ts +10 -0
  35. package/dist/{plugin-DkXCvsmB.d.ts → plugin-DHkamZdH.d.ts} +2 -2
  36. package/dist/policies.d.ts +2 -2
  37. package/dist/policies.js +1 -1
  38. package/dist/react.d.ts +15 -8
  39. package/dist/react.js +1 -1
  40. package/dist/redux.d.ts +4 -3
  41. package/dist/redux.js +1 -1
  42. package/dist/scope.d.ts +2 -2
  43. package/dist/scope.js +1 -1
  44. package/dist/signal.d.ts +1 -1
  45. package/dist/signal.js +1 -1
  46. package/dist/trace.d.ts +30 -1
  47. package/dist/trace.js +1 -1
  48. package/dist/{types-KavbvHix.d.ts → types-ByPzv7tw.d.ts} +2 -2
  49. package/dist/{with-policy-DBV9dh2F.js → with-policy-CW50Edgk.js} +1 -1
  50. package/dist/{with-policy-CqUS-VWW.d.ts → with-policy-DpCvSZRk.d.ts} +1 -1
  51. package/docs/oop.md +2 -0
  52. package/docs/package.md +1 -1
  53. package/docs/plugins.md +16 -1
  54. package/docs/scope.md +1 -1
  55. package/docs/stores.md +5 -1
  56. package/docs/tracing.md +121 -1
  57. package/llms-full.txt +147 -6
  58. package/llms.txt +1 -1
  59. package/package.json +3 -3
  60. package/dist/bus-CkTHUAvq.js +0 -1
  61. package/dist/context-COgyNlrC.js +0 -1
  62. package/dist/notrace/bus-BmCTo8BG.js +0 -1
  63. package/dist/onAbort-BgMfspK4.js +0 -1
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{n as u,t as d}from"./lifecycle-key-8iX8RUVD.js";import{t as f}from"./with-policy-DBV9dh2F.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)=>{r(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}=n(u);s=d;let p=i(d,t),h=!1,g=()=>{d.signal.aborted||(h=!0,m(d),r(d,t,`start`))};e(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=(t,n,r,i)=>{let a=r.leading??!1,o=r.trailing??!0;if(!o)return E(t,n,a,i);let c=!1,l=0,u=w(`debounce`,([r,i],a,u)=>{let d=l;return e(a,()=>{l===d&&(c=!1)}),s(a,t).then(()=>{if(c=!1,o&&!i)return u(),n(r,a)})},i);return(e,t)=>{if(t.signal.aborted)return;let r=a&&!c;c=!0,l++;let i=u([e,r],t);return r?Promise.resolve(h(t,`debounce`,()=>n(e,t))):i}},O=(t,n={})=>S(n.key,n=>{let r=null;return(i,a)=>{if(a.signal.aborted)return;if(r){g(a,`leading`);return}let o={};r=o;let s=()=>{c(),r===o&&(r=null,n())},c=e(a,s);try{return l(h(a,`leading`,()=>t(i,a)),s)}catch(e){throw s(),e}}}),k=(e,t={})=>S(t.key,n=>A(e,t,n)),A=(t,n,r)=>{let i=n.concurrency??1,a=n.limit??1/0,o=[],s=0,c=()=>{for(;s<i;){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 n=!0,i=()=>{n&&(n=!1,s--,c(),s===0&&o.length===0&&r())},a=e(u,()=>{i(),d(void 0)}),g;try{g=Promise.resolve(h(u,`queue`,()=>t(l,u)))}catch(e){g=Promise.reject(e)}let v=()=>{a(),i()};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<i){v();return}if(o.length>=a){if(n.overflow!==`drop-old`||a<=0){m();return}o.shift()?.drop()}let y={start:v,drop:m};o.push(y),p=e(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};
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/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
 
package/docs/tracing.md CHANGED
@@ -21,12 +21,14 @@ Each `TraceEvent` has:
21
21
  | Field | Meaning |
22
22
  | ----- | ------- |
23
23
  | `id` | Unique task id within the bus |
24
- | `parent` | Id of the task that started this one (absent for handler tasks) |
24
+ | `parentId` | Id of the task that started this one (absent for handler tasks) |
25
+ | `rootId` | Id of the root task of the chain; absent on the root itself, and it is the same through a listener that reports nothing |
25
26
  | `type` | Event type the task works on |
26
27
  | `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, … |
27
28
  | `phase` | `start`, `done`, `error`, `cancel`, or `drop` |
28
29
  | `payload` | Event payload |
29
30
  | `error` | Thrown value, only for `error` |
31
+ | `traceGroupId` | The flow of the task, when it or a task above has one |
30
32
 
31
33
  Phases:
32
34
 
@@ -51,6 +53,124 @@ bus.on("load", async (id, ctx) => {
51
53
  });
52
54
  ```
53
55
 
56
+ ## Show which subscription began a chain
57
+
58
+ **Problem.** `useEmitter` and `bus.emitter` put an event on the bus from outside any task, such as a render or the callback of a store. In the trace that event has no sign of where it came from. With many such subscriptions it is hard to tell which one started a chain of commands and retries.
59
+
60
+ **When.** The bus has a tracer and the project has several `useEmitter` or `bus.emitter` calls. The names come from `tracePlugin`: use it on the bus, or the `traceId` is ignored.
61
+
62
+ Give each one a `traceId`. When the bus has a tracer, the emit is made from a root task with that `traceId` as its `type` and `run` as its effect. What the event starts is below it. Without a tracer nothing changes.
63
+
64
+ ```js
65
+ import { createBus } from "nanoeffects";
66
+ import { createAction, emitterPlugin } from "nanoeffects/actions";
67
+ import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
68
+
69
+ // createTracer: reports every task phase to the console
70
+ const bus = createBus({ trace: createTracer(consoleLogger()) })
71
+ .use(tracePlugin()) // tracePlugin: makes the root task that traceId names
72
+ .use(emitterPlugin());
73
+
74
+ const cartChanged = createAction("cart/changed");
75
+
76
+ // emitter: a callback for a store; traceId names it in the trace
77
+ const emitCartChanged = bus.emitter(cartChanged, { traceId: "cartStore" });
78
+ ```
79
+
80
+ In a component, the same option goes in the options of the hook: `useEmitter(nameChanged, name, { traceId: "Profile" })`. In the console:
81
+
82
+ ```
83
+ [nanoeffects] Profile · run start
84
+ [nanoeffects] user/save · command start
85
+ [nanoeffects] user/save · command done
86
+ [nanoeffects] Profile · run done
87
+ ```
88
+
89
+ A `traceId` is a string you choose and keep stable: the name of the component or of the store. Without one the root has the type of the event.
90
+
91
+ Two things to know:
92
+
93
+ - **`sample` sees the `traceId`.** The root of an emitter is a task like any other, so a `sample` function gets `{ type, payload }` with the `traceId` as the `type` and the emitted action as the `payload`. A function that matches on the type of the event, such as `event.type === "search"`, does not pick a root that has another `traceId`; match the `traceId`, or the `payload.type`.
94
+ - **Inside a handler, emit with `ctx.emit`.** `ctx.emit` keeps the chain: what it starts is below the handler. A plain `bus.emit` inside a handler starts a new root with no parent and no `rootId`, so the trace loses the link.
95
+
96
+ Every event of a task below another one has `rootId`, the id of the root of the chain, also when a listener, which reports nothing, stands in between. `parentId` is the task directly above, which can be such a listener.
97
+
98
+ ## Follow one flow with a traceGroupId
99
+
100
+ **Problem.** A flow such as a checkout has several roots (a click, a timer, a store) and many branches. Parent ids give a tree, but you want to filter the log by the flow.
101
+
102
+ **When.** One business flow runs through several commands, and you read the log by flow.
103
+
104
+ Give the flow a `traceGroupId` in the scope of the command, the job or the action that starts it. Every task that it starts through `ctx.emit` or `ctx.run` has the same `traceGroupId`, and so does every trace event of those tasks. A task that declares its own `traceGroupId` uses it for itself and for what it starts.
105
+
106
+ ```js
107
+ import { createBus } from "nanoeffects";
108
+ import { createCommand } from "nanoeffects/actions";
109
+ import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
110
+
111
+ // tracePlugin: reads traceGroupId from the scope of a call
112
+ const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
113
+
114
+ // the scope names the flow; traceGroupId is for the tracer, not a field of ctx
115
+ const pay = createCommand("checkout/pay", async () => {}, {
116
+ traceGroupId: "checkout",
117
+ });
118
+ ```
119
+
120
+ ```
121
+ [nanoeffects] [checkout] checkout/pay · command start
122
+ ```
123
+
124
+ The emitters take it too: `useEmitter(cartChanged, cart, { traceId: "Cart", traceGroupId: "checkout" })`. `traceGroupId` works without `scopePlugin`, and needs `tracePlugin`.
125
+
126
+ ## Name a listener
127
+
128
+ **Problem.** The log shows what started a chain and what it started, but a listener of an event reports nothing, so you cannot tell which of several listeners ran.
129
+
130
+ **When.** An event has more than one listener and you read the trace to see which one did the work.
131
+
132
+ Give the listener a `traceId` when you register it. The plugin makes each call of it a task with that name, and what it emits with `ctx.emit` is below it. `off` works as before. It also works in a module (`m.on(event, handler, { traceId })`) and on an instance made by `extendBus`.
133
+
134
+ ```js
135
+ import { createBus } from "nanoeffects";
136
+ import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
137
+
138
+ const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
139
+
140
+ // traceId: the name of this listener in the trace
141
+ bus.on("name/changed", saveName, { traceId: "saveNotifier" });
142
+ bus.once("name/changed", (name) => console.log(name), { traceId: "auditLog" });
143
+ ```
144
+
145
+ ```
146
+ [nanoeffects] saveNotifier · run start
147
+ [nanoeffects] user/save · command start
148
+ [nanoeffects] saveNotifier · run done
149
+ ```
150
+
151
+ ## Keep the chain: `ctx.emit`, not `bus.emit`
152
+
153
+ Inside a handler, emit with `ctx.emit`. It keeps the chain: what it starts is below the handler, and its events have the `rootId` of the chain. A plain `bus.emit` inside a handler starts a new root with no `parentId` and no `rootId`, so the trace loses the link. The bus cannot warn about it, because it does not know which handler is running.
154
+
155
+ The names work wherever an event or a listener is made:
156
+
157
+ | Where | How to name it |
158
+ | ----- | -------------- |
159
+ | `useEmitter` and `bus.emitter` | `{ traceId, traceGroupId }` in the options |
160
+ | A Redux dispatch | `createEffectsMiddleware(bus, { traceId, traceGroupId })` |
161
+ | A listener, `bus.on` and `bus.once` | `{ traceId }` as the third argument |
162
+ | A listener of a module or of an instance of `extendBus` | the same third argument of its `on` and `once` |
163
+ | A method with `@On` or `@Once` | `{ traceId }` in the decorator |
164
+ | A command, a job or an action | `traceGroupId` in its scope |
165
+
166
+ ## Turn the names off
167
+
168
+ `tracePlugin` is optional, and none of it changes how your code runs:
169
+
170
+ - **Without the plugin**, or on a bus without a tracer, `traceId` and `traceGroupId` are ignored. Handlers run as they are, with the same results, errors and rejections, and `onError` sees the same errors.
171
+ - **The types are the same** with and without the plugin, so you can use it only in development: `if (isDev) bus.use(tracePlugin())` compiles in both builds.
172
+ - **A `traceGroupId` in a scope** is never put on `ctx`.
173
+
54
174
  ## Use built-in loggers
55
175
 
56
176
  A logger is `(event) => void`. `createTracer` takes one logger or an array. A logger that throws is reported to `console.error` and never breaks the task or the other loggers.