nanoeffects 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -24
- package/dist/{action-creator-D_c223C7.d.ts → action-creator-jeEqIKgK.d.ts} +73 -32
- package/dist/actions.d.ts +18 -18
- package/dist/actions.js +1 -1
- package/dist/bus-0uYn_kRj.js +1 -0
- package/dist/context-DtUnzC-1.js +1 -0
- package/dist/context-types-Bme1mzkn.d.ts +31 -0
- package/dist/context.d.ts +8 -3
- package/dist/context.js +1 -1
- package/dist/{delay-18LJ8jSk.js → delay-5OhJZZQ0.js} +1 -1
- package/dist/dev.d.ts +2 -2
- package/dist/dev.js +1 -1
- package/dist/effects.d.ts +2 -2
- package/dist/effects.js +1 -1
- package/dist/event-DVcW0mME.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/match.d.ts +3 -3
- package/dist/match.js +1 -1
- package/dist/{module-WX0Gngnt.d.ts → module-BWMW83qk.d.ts} +38 -38
- package/dist/notrace/{action-creator-Bux7Zzsl.d.ts → action-creator-xpQ_I2h8.d.ts} +73 -32
- package/dist/notrace/actions.d.ts +52 -52
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/bus-37nlVljc.js +1 -0
- package/dist/notrace/effects.d.ts +2 -2
- package/dist/notrace/index.d.ts +2 -2
- package/dist/notrace/index.js +1 -1
- package/dist/notrace/{plugin-CATXOxtj.d.ts → plugin-6LhNx0th.d.ts} +1 -1
- package/dist/notrace/policies.d.ts +1 -1
- package/dist/notrace/policies.js +1 -1
- package/dist/notrace/redux.d.ts +1 -1
- package/dist/observable.d.ts +43 -1
- package/dist/observable.js +1 -1
- package/dist/onAbort-BgMfspK4.js +1 -0
- package/dist/oop.d.ts +3 -3
- package/dist/oop.js +1 -1
- package/dist/{plugin-vvkmIIz2.d.ts → plugin-BaErevLy.d.ts} +2 -2
- package/dist/policies.d.ts +2 -2
- package/dist/policies.js +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/redux.d.ts +1 -1
- package/dist/scope.d.ts +9 -9
- package/dist/signal-field-key-CWT-3OXC.js +1 -0
- package/dist/signal.d.ts +34 -6
- package/dist/signal.js +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/trace.d.ts +20 -5
- package/dist/trace.js +1 -1
- package/dist/{types-Bf-LX_tE.d.ts → types-D5xsR37j.d.ts} +2 -2
- package/dist/vue.d.ts +1 -1
- package/dist/{with-policy-CPjx0sLm.d.ts → with-policy-BZVRBUOm.d.ts} +1 -1
- package/dist/{with-policy-D-SQRkPh.js → with-policy-C6BIiPIi.js} +1 -1
- package/docs/README.md +14 -1
- package/docs/actions.md +28 -2
- package/docs/chat-example.md +16 -4
- package/docs/chat-widget.md +135 -0
- package/docs/context.md +33 -1
- package/docs/effects.md +20 -9
- package/docs/guide.md +21 -3
- package/docs/migrating-from-effector.md +11 -0
- package/docs/observable.md +43 -0
- package/docs/oop.md +18 -1
- package/docs/package.md +22 -13
- package/docs/plugins.md +69 -11
- package/docs/query-libraries.md +273 -5
- package/docs/react.md +12 -2
- package/docs/scope.md +14 -2
- package/docs/semantics.md +10 -0
- package/docs/server.md +3 -3
- package/docs/stability.md +2 -2
- package/docs/starter.md +3 -3
- package/docs/tracing.md +77 -9
- package/docs/typescript.md +1 -1
- package/llms-full.txt +816 -72
- package/llms.txt +8 -7
- package/package.json +6 -6
- package/skills/nanoeffects/SKILL.md +2 -2
- package/dist/bus-DQDHUN5e.js +0 -1
- package/dist/context-UcmrAIhP.js +0 -1
- package/dist/context-types-nGtzhjxD.d.ts +0 -14
- package/dist/event-0RIBGYqS.js +0 -1
- package/dist/notrace/bus-CGi2lrd5.js +0 -1
- package/dist/notrace/promise-CSLpzwSR.js +0 -1
package/llms.txt
CHANGED
|
@@ -10,11 +10,11 @@ Rules that generated code often gets wrong:
|
|
|
10
10
|
- Effects take `ctx` as the first argument: `retry(ctx, fn, options)`, `timeout(ctx, ms, fn)`, `take(ctx, "event")`.
|
|
11
11
|
- Policies wrap a handler: `bus.on("search", latest(async (query, ctx) => { ... }))`.
|
|
12
12
|
- Each part is imported from its own path: `nanoeffects` (`createBus`), `nanoeffects/policies`, `nanoeffects/effects`, `nanoeffects/actions`, `nanoeffects/redux`, `nanoeffects/observable`, `nanoeffects/react` (`reactPlugin`, adds `bus.useEmitter` and `bus.useEventState`; `usePolicy`; needs React), `nanoeffects/svelte` (`sveltePlugin`, adds `bus.eventStore`; needs Svelte), `nanoeffects/vue` (`vuePlugin`, adds `bus.watchEmitter` and `bus.useEventRef`; needs Vue), `nanoeffects/trace` (`createTracer`, loggers, `tracePlugin`), `nanoeffects/dev`.
|
|
13
|
-
- `bus.emit(
|
|
13
|
+
- `bus.emit(action(payload))` runs the action's handler and returns its result. Plain events return nothing.
|
|
14
14
|
|
|
15
15
|
## Start here
|
|
16
16
|
|
|
17
|
-
- [Guide](docs/guide.md): bus, handler context, policies, effects,
|
|
17
|
+
- [Guide](docs/guide.md): bus, handler context, policies, effects, actions and modules, step by step
|
|
18
18
|
- [Start a project from a template](docs/starter.md): `npm create nanoeffects` makes a Vite project (`vanilla`, `react`, `vue-ts`, `svelte-ts` and others, in JavaScript or TypeScript) with a bus, `debounce` and `latest`
|
|
19
19
|
- [Why nanoeffects exists](docs/motivation.md): logic and side effects outside UI components as plain async functions, no dependencies and no global state for SDKs and Module Federation, and why not redux-saga, RxJS, Redux or Effector
|
|
20
20
|
- [Package entries](docs/package.md): which import holds what
|
|
@@ -32,21 +32,22 @@ Rules that generated code often gets wrong:
|
|
|
32
32
|
|
|
33
33
|
## Domain logic and stores
|
|
34
34
|
|
|
35
|
-
- [Actions, jobs and modules](docs/actions.md): `createAction
|
|
35
|
+
- [Actions, jobs and modules](docs/actions.md): `createAction` (with or without a handler), `createJob`, `createModule`
|
|
36
36
|
- [Connect Redux, Zustand, MobX, or Pinia](docs/stores.md): the Redux middleware and callbacks of other stores
|
|
37
|
-
- [Use nanoeffects with React Query and
|
|
37
|
+
- [Use nanoeffects with React Query, SWR and RTK Query](docs/query-libraries.md): call an action from `queryFn` or the SWR fetcher; `signalPlugin` passes the abort of the library to the task; `fromSignal` clients, a live stream into the cache with `events`, refetch on a bus event
|
|
38
38
|
- [Stop work on a server when the client leaves](docs/server.md): one `AbortController` for each request or socket, `signalPlugin`, a clean shutdown, and a queue worker; Fastify examples
|
|
39
39
|
- [Build a sign-up form in React](docs/react.md): a form with a username check, a submit that cannot fire twice, and retry; `useEventState` shows an event as state, `usePolicy` keeps a debounced handler between renders
|
|
40
40
|
- [Build a chat with entities, features, modules and a small server](docs/chat-example.md): rooms, a history under `latest`, a send under `queue` and `retry`, a search with `allSettled` and `timeout`, new messages polled with `poll`
|
|
41
|
+
- [Build a chat widget that listens only while it is open](docs/chat-widget.md): a bot widget for a site; opening mounts a module (handlers, polling, Escape), closing unmounts it and aborts running requests
|
|
41
42
|
- [Use nanoeffects in a Vue app](docs/vue.md): `vuePlugin` adds `watchEmitter` (a value of a component to the bus) and `useEventRef` (an event as a ref)
|
|
42
43
|
- [Use nanoeffects in a Svelte app](docs/svelte.md): `sveltePlugin` adds `eventStore` (an event as a Svelte store)
|
|
43
44
|
- [Context fields](docs/context.md): put a store or client on every task
|
|
44
|
-
- [Scopes](docs/scope.md): fields for one
|
|
45
|
+
- [Scopes](docs/scope.md): fields for one action, job, module or `emit`, and a way to replace them in a test
|
|
45
46
|
- [Use the bus from a class](docs/oop.md): `Emitter`, `extendBus`, `@WithBus`, `@On`, `@Once`, and `dispose()` for the subscriptions of an instance
|
|
46
47
|
|
|
47
48
|
## TypeScript
|
|
48
49
|
|
|
49
|
-
- [TypeScript in many files](docs/typescript.md): keep the types of the bus in
|
|
50
|
+
- [TypeScript in many files](docs/typescript.md): keep the types of the bus in actions and modules
|
|
50
51
|
|
|
51
52
|
## Coming from another library
|
|
52
53
|
|
|
@@ -59,5 +60,5 @@ Rules that generated code often gets wrong:
|
|
|
59
60
|
|
|
60
61
|
- [Semantics](docs/semantics.md): exact rules of emit, abort and ordering
|
|
61
62
|
- [Trace tasks](docs/tracing.md): loggers, Redux DevTools, the `notrace` build
|
|
62
|
-
- [Turn events into Observables](docs/observable.md): `createObserve`, `Subject`
|
|
63
|
+
- [Turn events into Observables](docs/observable.md): `createObserve`, `Subject`, `events` (an event as a `for await` loop)
|
|
63
64
|
- [Hot reload and development diagnostics](docs/hmr.md): `devPlugin`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nanoeffects",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.29.0",
|
|
4
|
+
"description": "Business logic and side effects outside UI components, as plain async functions. Tiny, no dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"event-bus",
|
|
7
7
|
"event-emitter",
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
{
|
|
227
227
|
"name": "actions (all)",
|
|
228
228
|
"path": "dist/actions.js",
|
|
229
|
-
"limit": "3.
|
|
229
|
+
"limit": "3.31 kB"
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
232
|
"name": "match (plugin)",
|
|
@@ -236,12 +236,12 @@
|
|
|
236
236
|
{
|
|
237
237
|
"name": "signal (plugin)",
|
|
238
238
|
"path": "dist/signal.js",
|
|
239
|
-
"limit": "0.
|
|
239
|
+
"limit": "0.7 kB"
|
|
240
240
|
},
|
|
241
241
|
{
|
|
242
242
|
"name": "context (plugin)",
|
|
243
243
|
"path": "dist/context.js",
|
|
244
|
-
"limit": "0.
|
|
244
|
+
"limit": "0.4 kB"
|
|
245
245
|
},
|
|
246
246
|
{
|
|
247
247
|
"name": "oop (class entry)",
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
{
|
|
262
262
|
"name": "observable",
|
|
263
263
|
"path": "dist/observable.js",
|
|
264
|
-
"limit": "0.
|
|
264
|
+
"limit": "0.85 kB"
|
|
265
265
|
},
|
|
266
266
|
{
|
|
267
267
|
"name": "react",
|
|
@@ -26,7 +26,7 @@ Do not write the setup by hand. `npm create nanoeffects@latest my-app -- --templ
|
|
|
26
26
|
|
|
27
27
|
- `vanilla`, `react`: JavaScript, one search handler with `debounce` and `latest`.
|
|
28
28
|
- `vanilla-ts`, `react-ts`: the same apps in TypeScript.
|
|
29
|
-
- `domains-ts`: React and TypeScript with the logic split by domain (
|
|
29
|
+
- `domains-ts`: React and TypeScript with the logic split by domain (actions, a job, modules). Pick it for an app with several features.
|
|
30
30
|
|
|
31
31
|
In an existing project, start from `docs/cancel-stale-requests.md` (races) or `docs/guide.md`.
|
|
32
32
|
|
|
@@ -38,7 +38,7 @@ In an existing project, start from `docs/cancel-stale-requests.md` (races) or `d
|
|
|
38
38
|
- Effects take `ctx` first: `retry(ctx, fn, options)`, `timeout(ctx, ms, fn)`, `take(ctx, "event")`. The wrapper form is `retry(fn, options)`.
|
|
39
39
|
- Policies wrap a handler: `bus.on("search", latest(async (query, ctx) => { ... }))`.
|
|
40
40
|
- Import each part from its path: `nanoeffects`, `nanoeffects/policies`, `nanoeffects/effects`, `nanoeffects/actions`, `nanoeffects/redux`, `nanoeffects/react`.
|
|
41
|
-
- `bus.emit(
|
|
41
|
+
- `bus.emit(action(payload))` returns the action's result. Plain events return nothing.
|
|
42
42
|
- The bus keeps no values: an event emitted before a listener exists is lost. `useEventState(event, initial)` (`nanoeffects/react`) is state of one component, not a store. Share a value through a Context provider at the root or a state manager.
|
|
43
43
|
|
|
44
44
|
## Where to read more
|
package/dist/bus-DQDHUN5e.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as e,n as t,o as n,r}from"./context-UcmrAIhP.js";import{n as i}from"./promise-CSLpzwSR.js";import{n as a,t as o}from"./event-0RIBGYqS.js";const s=()=>{let e={},t={},n=n=>(t[n]=void 0,e[n]??=/* @__PURE__ */ new Map,e[n]),r=(e,t)=>{let r=n(e),i=r.get(t)??0;i>1?r.set(t,i-1):t?r.delete(t):r.clear()};return{get:n=>(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]),add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>{r(e,t)}},remove:r,clear:()=>{e={},t={}}}},c=Symbol(),l=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,c,{value:e,enumerable:!1}),r},u=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&c in e)return e[c]},d=(e,t,n)=>Reflect.apply(e,null,[t,n]),f=(c={})=>{let l=s(),f=(e,t)=>l.add(o(e),t),p=(e,t)=>{let n=f(e,(e,r)=>(n(),t(e,r)));return n},m=(e,t)=>{e===void 0?l.clear():l.remove(o(e),t)},h=(n,a)=>{let{onError:o}=c,{ctx:s}=t({event:a,bus:g,tracer:c.trace,parent:{ref:r()}});if(!o){n(a.payload,s);return}let l=t=>{try{o(t,a)}catch(t){e(`onError failed`,t)}};try{let e=n(a.payload,s);i(e)&&Promise.resolve(e).catch(l)}catch(e){l(e)}},g={on:f,once:p,off:m,emit:(e,i)=>{let s=o(e),f=a(e,i),p={type:s,payload:f},m=u(e);m||=g.actions?.bound?.(s);let _=[...l.get(s),...l.get(`*`)];_.length===0&&!m&&c.onUnhandled?.(p);for(let e of _)h(e,p);if(m){let{ctx:e}=t({event:p,bus:g,tracer:c.trace,parent:{ref:r()}}),i=g.actions?.run??d;return n(e,`command`,()=>i(m,f,e))}},use:e=>e(g),ctx:c.context,trace:c.trace};return g};export{f as n,l as r,u as t};
|
package/dist/context-UcmrAIhP.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=(e,t)=>{globalThis.console?.error(`[nanoeffects] ${e}`,t)},t=()=>{},n=/* @__PURE__ */ new WeakMap,r=t=>{let r=n.get(t);if(r)return r;let i=/* @__PURE__ */ new Set;return n.set(t,i),t.addEventListener(`abort`,()=>{let n=[...i];i.clear();for(let{fn:r}of n)try{r(t.reason)}catch(t){e(`abort callback failed`,t)}},{once:!0}),i},i=(e,n)=>{let{signal:i}=e;if(!i)return t;if(i.aborted)return n(i.reason),t;let a=r(i),o={fn:n};return a.add(o),()=>{a.delete(o)}},a=e=>{let t=new AbortController;return{controller:t,unlink:i({signal:e},e=>t.abort(e))}},o=Symbol(),s=typeof __NANOEFFECTS_TRACE__>`u`||__NANOEFFECTS_TRACE__,c=/* @__PURE__ */ new WeakMap;let l;const u=()=>l,d=()=>{},f=(e,t,n,r)=>{let i=s&&c.get(e);i&&i.tracer.report(i.ref,t,n,r)},p=(e,t)=>{let n=s&&c.get(e);n&&n.tracer.rename?.(n.ref,t)},m=(e,t)=>{let n=s&&c.get(e);return n?n.tracer.settler(n.ref,t):d},h=(e,t,n)=>{let r=s&&c.get(e);return r?r.tracer.run(r.ref,t,e.signal,n):n()},g=Symbol(`nanoeffects.addContext`),_=(e,t)=>typeof e==`function`?e(t):e,v=({event:e,bus:t,tracer:n,parent:r})=>{let{controller:u,unlink:f}=a(r?.signal),{signal:p}=u,m=s&&n?{tracer:n,ref:n.task(e,r?.ref)}:void 0,y=(...e)=>{if(p.aborted&&!S[o])return;let n=t?.emit,r=l;l=m?.ref;try{return n?.(...e)}finally{l=r}},b=(...e)=>{if(p.aborted)return d;let n=t?.on,r=n?.(...e);if(!r)return d;let a=i({signal:p},r);return()=>{a(),r()}},x=async(r,...i)=>{p.throwIfAborted();let a=i[0],o=v({event:{type:e.type,payload:a},bus:t,tracer:n,parent:{signal:p,ref:m?.ref}});try{return await h(o.ctx,`run`,()=>r(a,o.ctx))}finally{o.unlink()}},S={..._(t?.ctx,e),type:e.type,payload:e.payload,signal:p,abort:e=>u.abort(e),emit:y,on:b,off:t?.off??d,run:x,[g]:t?.addContext};return t?.addContext?.(S),m&&c.set(S,m),{ctx:S,unlink:f}},y=(e,t)=>{let n=s?c.get(e):void 0;return v({event:{type:t??e.type??``,payload:e.payload},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e,addContext:Reflect.get(e,g)},tracer:n?.tracer,parent:{signal:e.signal,ref:n?.ref}})};export{p as a,s as c,e as d,y as i,o as l,v as n,h as o,u as r,f as s,m as t,i as u};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { C as ContextOf, H as EventMap } from "./action-creator-D_c223C7.js";
|
|
2
|
-
//#region src/core/context-types.d.ts
|
|
3
|
-
/** What a function that makes the fields of a task gets to see. */
|
|
4
|
-
type ContextTask = {
|
|
5
|
-
type: string;
|
|
6
|
-
payload?: unknown;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* `unknown` when none of the `fields` is already a context field of the bus,
|
|
10
|
-
* else `never`: the call does not compile. Used as `bus: Bus<E> & NoClash<…>`.
|
|
11
|
-
*/
|
|
12
|
-
type NoClash<Fields extends object, E extends EventMap> = [keyof Fields & keyof ContextOf<E>] extends [never] ? unknown : never;
|
|
13
|
-
//#endregion
|
|
14
|
-
export { NoClash as n, ContextTask as t };
|
package/dist/event-0RIBGYqS.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=e=>typeof e==`string`?e:e.type,t=(e,t)=>typeof e==`object`?e.payload:t;export{t as n,e as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,l as t,n,r}from"./context-_HWcdjZf.js";import{n as i}from"./promise-CSLpzwSR.js";const a=e=>typeof e==`string`?e:e.type,o=(e,t)=>typeof e==`object`?e.payload:t,s=()=>{let e={},t={},n=n=>(t[n]=void 0,e[n]??=/* @__PURE__ */ new Map,e[n]),r=(e,t)=>{let r=n(e),i=r.get(t)??0;i>1?r.set(t,i-1):t?r.delete(t):r.clear()};return{get:n=>(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]),add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>{r(e,t)}},remove:r,clear:()=>{e={},t={}}}},c=Symbol(),l=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,c,{value:e,enumerable:!1}),r},u=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&c in e)return e[c]},d=(e,t,n)=>Reflect.apply(e,null,[t,n]),f=(c={})=>{let l=s(),f=(e,t)=>l.add(a(e),t),p=(e,t)=>{let n=f(e,(e,r)=>(n(),t(e,r)));return n},m=(e,t)=>{e===void 0?l.clear():l.remove(a(e),t)},h=(e,a)=>{let{onError:o}=c,{ctx:s}=n({event:a,bus:g,tracer:c.trace,parent:{ref:r()}});if(!o){e(a.payload,s);return}let l=e=>{try{o(e,a)}catch(e){t(`onError failed`,e)}};try{let t=e(a.payload,s);i(t)&&Promise.resolve(t).catch(l)}catch(e){l(e)}},g={on:f,once:p,off:m,emit:(t,i)=>{let s=a(t),f=o(t,i),p={type:s,payload:f},m=u(t);m||=g.actions?.bound?.(s);let _=[...l.get(s),...l.get(`*`)];_.length===0&&!m&&c.onUnhandled?.(p);for(let e of _)h(e,p);if(m){let{ctx:t}=n({event:p,bus:g,tracer:c.trace,parent:{ref:r()}}),i=g.actions?.run??d;return e(t,`command`,()=>i(m,f,t))}},use:e=>e(g),ctx:c.context,trace:c.trace};return g};export{l as n,a as r,f as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=e=>typeof e!=`object`||!e?!1:`then`in e&&typeof e.then==`function`,t=(t,n)=>e(t)?Promise.resolve(t).finally(n):(n(),t);export{e as n,t};
|