nanoeffects 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +2 -2
- package/dist/dev.d.ts +1 -1
- package/dist/effects.d.ts +1 -1
- package/dist/effects.js +1 -1
- package/dist/{module-B0KVDNjY.d.ts → module-1sUEXIcD.d.ts} +1 -1
- package/dist/notrace/actions.d.ts +1 -1
- package/dist/notrace/effects.d.ts +1 -1
- package/dist/notrace/effects.js +1 -1
- package/dist/notrace/{plugin-C6KA4dX-.d.ts → plugin-ByDuI8Vq.d.ts} +20 -3
- package/dist/{plugin-D9UTzGRG.d.ts → plugin-BiGv84Xu.d.ts} +20 -3
- package/dist/scope.d.ts +1 -1
- package/dist/svelte.d.ts +46 -0
- package/dist/svelte.js +1 -0
- package/dist/{types-CbdJKC2z.d.ts → types-DC-aLJit.d.ts} +1 -1
- package/docs/README.md +2 -0
- package/docs/effects.md +8 -1
- package/docs/migrating-from-saga.md +3 -1
- package/docs/package.md +1 -0
- package/docs/starter.md +2 -2
- package/docs/stores.md +1 -1
- package/docs/svelte.md +81 -0
- package/llms-full.txt +101 -6
- package/llms.txt +2 -1
- package/package.json +18 -1
package/dist/actions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { H as PayloadOf, T as EventKey, W as Task, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
-
import { r as Scoped, t as FieldsOfScope } from "./types-
|
|
3
|
-
import { a as createModule, c as CarryingCalls, d as commandsPlugin, f as createCommand, l as MadeCommand, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as bindHandler } from "./module-
|
|
2
|
+
import { r as Scoped, t as FieldsOfScope } from "./types-DC-aLJit.js";
|
|
3
|
+
import { a as createModule, c as CarryingCalls, d as commandsPlugin, f as createCommand, l as MadeCommand, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as bindHandler } from "./module-1sUEXIcD.js";
|
|
4
4
|
import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
|
|
5
5
|
//#region src/actions/action.d.ts
|
|
6
6
|
type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCreator<T, F> & WithScope<F> : ActionCreator<P, T, PayloadOf<P>, F> & WithScope<F>;
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { J as UnsubscribeCallback, h as Bus, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
-
import { i as WithApp, n as ModuleApi } from "./module-
|
|
2
|
+
import { i as WithApp, n as ModuleApi } from "./module-1sUEXIcD.js";
|
|
3
3
|
//#region src/dev/types.d.ts
|
|
4
4
|
/** The parts of what `modulePlugin` returned that the diagnostics report. */
|
|
5
5
|
type ReportedApp<E extends EventMap> = Pick<ModuleApi<E>, "mount" | "unmount" | "isMounted">;
|
package/dist/effects.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { P as WithSignal, W as Task, u as Effect, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
-
import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-
|
|
2
|
+
import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-BiGv84Xu.js";
|
|
3
3
|
//#region src/utils/onAbort.d.ts
|
|
4
4
|
export declare const onAbort: (ctx: {
|
|
5
5
|
signal?: AbortSignal | undefined;
|
package/dist/effects.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as e,i as t,o as n,s as r,t as i,u as a}from"./context-CJGW6OWj.js";import{n as o,r as s,t as c}from"./delay-BnBdGPh7.js";const l=(e,t)=>t?.signal?new Promise((n,r)=>{let i=a(t,r);e.then(n,r).finally(i)}):e,u=async(e,n,o,s)=>{n?.signal?.throwIfAborted();let{ctx:c,unlink:u}=t(n??{}),d=i(c,e);r(c,e,`start`),a(c,()=>d(`cancel`));try{let e=await l(o(c),n);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(e,r)=>{let{ctx:i,unlink:a}=t(e);try{return await n(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(e,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=t(e),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(e=>e(n(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=e=>e>0?e:0,x=(e={})=>{let t=b(e.base??100),n=e.factor??2,r=b(e.max??1/0),i=e.jitter??!1;return e=>{let a=b(Math.min(r,t*n**(e-1)));return i?Math.random()*a||0:a}},S=(t,n)=>t.signal.aborted?Promise.reject(t.signal.reason):new Promise((r,i)=>{let o=[],s=`pending`,c=t=>{try{t()}catch(t){e(`cancel cleanup failed`,t)}},l=a(t,e=>{s=`cancelled`;for(let e of o.reverse())c(e);i(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(r),reject:u(i),onCancel:d})}catch(e){u(i)(e)}}),C=e=>t=>{let n=t.addContext,r=Object.entries(e);return t.addContext=e=>{n?.(e);for(let[t,n]of r)t in e||Object.assign(e,{[t]:(...t)=>n(e,...t)})},t},w=(e,t,n,r)=>{if(e.signal.aborted)return Promise.resolve();let i=r.leading??!1,c=r.waitForFn??!1;return u(`poll`,e,e=>new Promise((r,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof n==`function`?n(e):n)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let n=u++;d(e,e=>t(e,n)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};a(e,p),i?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function T(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>w(n,p(r,t),e,i)}let i=t;return w(e,(e,t)=>i(t),n,r)}function E(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const D=(e,t,n)=>typeof e==`function`?e(t,n):e,O=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),k=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=O(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),!(n<r)||!a(t,n))throw t;let o=D(i,n,t);o>0&&await c(e,o)}});function A(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>k(r,p(n,e),t??{})}let r=t;return k(e,(e,t)=>r(t),n)}function j(e,t,n={}){return u(`take`,e,r=>new Promise((i,o)=>{let{predicate:s}=n,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)};c.push(e.on(
|
|
1
|
+
import{d as e,i as t,o as n,s as r,t as i,u as a}from"./context-CJGW6OWj.js";import{n as o,r as s,t as c}from"./delay-BnBdGPh7.js";const l=(e,t)=>t?.signal?new Promise((n,r)=>{let i=a(t,r);e.then(n,r).finally(i)}):e,u=async(e,n,o,s)=>{n?.signal?.throwIfAborted();let{ctx:c,unlink:u}=t(n??{}),d=i(c,e);r(c,e,`start`),a(c,()=>d(`cancel`));try{let e=await l(o(c),n);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(e,r)=>{let{ctx:i,unlink:a}=t(e);try{return await n(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(e,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=t(e),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(e=>e(n(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=e=>e>0?e:0,x=(e={})=>{let t=b(e.base??100),n=e.factor??2,r=b(e.max??1/0),i=e.jitter??!1;return e=>{let a=b(Math.min(r,t*n**(e-1)));return i?Math.random()*a||0:a}},S=(t,n)=>t.signal.aborted?Promise.reject(t.signal.reason):new Promise((r,i)=>{let o=[],s=`pending`,c=t=>{try{t()}catch(t){e(`cancel cleanup failed`,t)}},l=a(t,e=>{s=`cancelled`;for(let e of o.reverse())c(e);i(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(r),reject:u(i),onCancel:d})}catch(e){u(i)(e)}}),C=e=>t=>{let n=t.addContext,r=Object.entries(e);return t.addContext=e=>{n?.(e);for(let[t,n]of r)t in e||Object.assign(e,{[t]:(...t)=>n(e,...t)})},t},w=(e,t,n,r)=>{if(e.signal.aborted)return Promise.resolve();let i=r.leading??!1,c=r.waitForFn??!1;return u(`poll`,e,e=>new Promise((r,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof n==`function`?n(e):n)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let n=u++;d(e,e=>t(e,n)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};a(e,p),i?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function T(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>w(n,p(r,t),e,i)}let i=t;return w(e,(e,t)=>i(t),n,r)}function E(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const D=(e,t,n)=>typeof e==`function`?e(t,n):e,O=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),k=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=O(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),!(n<r)||!a(t,n))throw t;let o=D(i,n,t);o>0&&await c(e,o)}});function A(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>k(r,p(n,e),t??{})}let r=t;return k(e,(e,t)=>r(t),n)}function j(e,t,n={}){return u(`take`,e,r=>new Promise((i,o)=>{let{predicate:s}=n,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)},d=t;c.push(e.on(d,e=>{(!s||s(e))&&(l(),i(e))})),c.push(a(r,u)),c.push(a({signal:n.signal},u))}))}const M=(e,r,i)=>u(`timeout`,e,async e=>{let a=t(e),c=s(()=>a.ctx.abort(new DOMException(`Timed out after ${r}ms`,`TimeoutError`)),r);try{return await l(Promise.resolve(n(a.ctx,`run`,()=>i(a.ctx))),a.ctx)}finally{o(c),a.unlink()}});function N(e,t,n){if(typeof e==`number`){let n=t;return(t,r)=>M(r,e,p(n,t))}let r=t,i=n;return M(e,r,e=>i({signal:e.signal,abort:e.abort}))}export{_ as all,y as allSettled,x as backoff,S as cancellable,c as delay,C as effectsPlugin,a as onAbort,T as poll,E as race,A as retry,j as take,N as timeout};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { H as PayloadOf, J as UnsubscribeCallback, W as Task, _ as Command, b as Context, c as WithScope, h as Bus, o as Scope, v as CommandCall, y as Commands, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
-
import { n as ScopeSlots, r as Scoped, t as FieldsOfScope } from "./types-
|
|
2
|
+
import { n as ScopeSlots, r as Scoped, t as FieldsOfScope } from "./types-DC-aLJit.js";
|
|
3
3
|
//#region src/actions/command.d.ts
|
|
4
4
|
/** What `createCommand` returns: a command that also tells whether an action is of its type. */
|
|
5
5
|
type MadeCommand<P, R, T extends string> = Command<P, R> & {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { C as EventKey, F as EventMap, L as PayloadOf, R as Task, _ as Commands, a as Scope, g as CommandCall, h as Command, n as Free, o as WithFields, p as Bus, r as FreeActionCreator, s as WithScope, t as ActionCreator, u as Effects, v as Context, z as UnsubscribeCallback } from "./action-creator-D2CotugT.js";
|
|
2
|
-
import { t as BoundOf } from "./plugin-
|
|
2
|
+
import { t as BoundOf } from "./plugin-ByDuI8Vq.js";
|
|
3
3
|
import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
|
|
4
4
|
//#region src/scope/types.d.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { F as EventMap, M as WithSignal, R as Task, l as Effect } from "./action-creator-D2CotugT.js";
|
|
2
|
-
import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-
|
|
2
|
+
import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-ByDuI8Vq.js";
|
|
3
3
|
//#region src/utils/onAbort.d.ts
|
|
4
4
|
export declare const onAbort: (ctx: {
|
|
5
5
|
signal?: AbortSignal | undefined;
|
package/dist/notrace/effects.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,c as t,i as n,l as r,o as i,t as a}from"./context-yYU3_9lq.js";import{n as o,r as s,t as c}from"./delay-BosJKFjg.js";const l=(e,n)=>n?.signal?new Promise((r,i)=>{let a=t(n,i);e.then(r,i).finally(a)}):e,u=async(e,r,o,s)=>{r?.signal?.throwIfAborted();let{ctx:c,unlink:u}=n(r??{}),d=a(c,e);i(c,e,`start`),t(c,()=>d(`cancel`));try{let e=await l(o(c),r);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(t,r)=>{let{ctx:i,unlink:a}=n(t);try{return await e(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(t,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=n(t),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(t=>t(e(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=e=>e>0?e:0,x=(e={})=>{let t=b(e.base??100),n=e.factor??2,r=b(e.max??1/0),i=e.jitter??!1;return e=>{let a=b(Math.min(r,t*n**(e-1)));return i?Math.random()*a||0:a}},S=(e,n)=>e.signal.aborted?Promise.reject(e.signal.reason):new Promise((i,a)=>{let o=[],s=`pending`,c=e=>{try{e()}catch(e){r(`cancel cleanup failed`,e)}},l=t(e,e=>{s=`cancelled`;for(let e of o.reverse())c(e);a(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(i),reject:u(a),onCancel:d})}catch(e){u(a)(e)}}),C=e=>t=>{let n=t.addContext,r=Object.entries(e);return t.addContext=e=>{n?.(e);for(let[t,n]of r)t in e||Object.assign(e,{[t]:(...t)=>n(e,...t)})},t},w=(e,n,r,i)=>{if(e.signal.aborted)return Promise.resolve();let a=i.leading??!1,c=i.waitForFn??!1;return u(`poll`,e,e=>new Promise((i,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof r==`function`?r(e):r)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let t=u++;d(e,e=>n(e,t)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};t(e,p),a?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function T(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>w(n,p(r,t),e,i)}let i=t;return w(e,(e,t)=>i(t),n,r)}function E(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const D=(e,t,n)=>typeof e==`function`?e(t,n):e,O=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),k=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=O(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),!(n<r)||!a(t,n))throw t;let o=D(i,n,t);o>0&&await c(e,o)}});function A(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>k(r,p(n,e),t??{})}let r=t;return k(e,(e,t)=>r(t),n)}function j(e,n,r={}){return u(`take`,e,i=>new Promise((a,o)=>{let{predicate:s}=r,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)};c.push(e.on(
|
|
1
|
+
import{a as e,c as t,i as n,l as r,o as i,t as a}from"./context-yYU3_9lq.js";import{n as o,r as s,t as c}from"./delay-BosJKFjg.js";const l=(e,n)=>n?.signal?new Promise((r,i)=>{let a=t(n,i);e.then(r,i).finally(a)}):e,u=async(e,r,o,s)=>{r?.signal?.throwIfAborted();let{ctx:c,unlink:u}=n(r??{}),d=a(c,e);i(c,e,`start`),t(c,()=>d(`cancel`));try{let e=await l(o(c),r);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(t,r)=>{let{ctx:i,unlink:a}=n(t);try{return await e(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(t,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=n(t),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(t=>t(e(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=e=>e>0?e:0,x=(e={})=>{let t=b(e.base??100),n=e.factor??2,r=b(e.max??1/0),i=e.jitter??!1;return e=>{let a=b(Math.min(r,t*n**(e-1)));return i?Math.random()*a||0:a}},S=(e,n)=>e.signal.aborted?Promise.reject(e.signal.reason):new Promise((i,a)=>{let o=[],s=`pending`,c=e=>{try{e()}catch(e){r(`cancel cleanup failed`,e)}},l=t(e,e=>{s=`cancelled`;for(let e of o.reverse())c(e);a(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(i),reject:u(a),onCancel:d})}catch(e){u(a)(e)}}),C=e=>t=>{let n=t.addContext,r=Object.entries(e);return t.addContext=e=>{n?.(e);for(let[t,n]of r)t in e||Object.assign(e,{[t]:(...t)=>n(e,...t)})},t},w=(e,n,r,i)=>{if(e.signal.aborted)return Promise.resolve();let a=i.leading??!1,c=i.waitForFn??!1;return u(`poll`,e,e=>new Promise((i,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof r==`function`?r(e):r)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let t=u++;d(e,e=>n(e,t)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};t(e,p),a?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function T(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>w(n,p(r,t),e,i)}let i=t;return w(e,(e,t)=>i(t),n,r)}function E(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const D=(e,t,n)=>typeof e==`function`?e(t,n):e,O=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),k=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=O(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),!(n<r)||!a(t,n))throw t;let o=D(i,n,t);o>0&&await c(e,o)}});function A(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>k(r,p(n,e),t??{})}let r=t;return k(e,(e,t)=>r(t),n)}function j(e,n,r={}){return u(`take`,e,i=>new Promise((a,o)=>{let{predicate:s}=r,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)},d=n;c.push(e.on(d,e=>{(!s||s(e))&&(l(),a(e))})),c.push(t(i,u)),c.push(t({signal:r.signal},u))}))}const M=(t,r,i)=>u(`timeout`,t,async t=>{let a=n(t),c=s(()=>a.ctx.abort(new DOMException(`Timed out after ${r}ms`,`TimeoutError`)),r);try{return await l(Promise.resolve(e(a.ctx,`run`,()=>i(a.ctx))),a.ctx)}finally{o(c),a.unlink()}});function N(e,t,n){if(typeof e==`number`){let n=t;return(t,r)=>M(r,e,p(n,t))}let r=t,i=n;return M(e,r,e=>i({signal:e.signal,abort:e.abort}))}export{_ as all,y as allSettled,x as backoff,S as cancellable,c as delay,C as effectsPlugin,t as onAbort,T as poll,E as race,A as retry,j as take,N as timeout};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as EventMap, I as Keys, c as BoundForm, j as WithContext, p as Bus, u as Effects, v as Context, y as ContextOf } from "./action-creator-D2CotugT.js";
|
|
1
|
+
import { F as EventMap, I as Keys, c as BoundForm, j as WithContext, p as Bus, r as FreeActionCreator, t as ActionCreator, u as Effects, v as Context, y as ContextOf } from "./action-creator-D2CotugT.js";
|
|
2
2
|
//#region src/core/context-types.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* `unknown` when none of the `fields` is already a context field of the bus,
|
|
@@ -124,7 +124,8 @@ type TakeOptions<P> = {
|
|
|
124
124
|
signal?: AbortSignal | undefined;
|
|
125
125
|
};
|
|
126
126
|
/**
|
|
127
|
-
* Waits for the next `event` on the bus and resolves with its payload.
|
|
127
|
+
* Waits for the next `event` on the bus and resolves with its payload. `event` is the name
|
|
128
|
+
* of an event or an action creator, as for `bus.on`. It sees
|
|
128
129
|
* only events emitted after the call, takes only one, and unsubscribes when it
|
|
129
130
|
* is done. If `ctx` (or `options.signal`) aborts first, it unsubscribes and
|
|
130
131
|
* rejects with the abort reason.
|
|
@@ -141,6 +142,9 @@ type TakeOptions<P> = {
|
|
|
141
142
|
* take(ctx, "saved", { signal, predicate: (id) => id === draftId }),
|
|
142
143
|
* );
|
|
143
144
|
*
|
|
145
|
+
* // an action creator works as the name does, and gives its payload type
|
|
146
|
+
* const stopped = await take(ctx, stopRequested);
|
|
147
|
+
*
|
|
144
148
|
* // a type guard narrows the result: `failed` is `{ ok: false }`
|
|
145
149
|
* const failed = await take(ctx, "result", {
|
|
146
150
|
* predicate: (result): result is { ok: false } => !result.ok,
|
|
@@ -151,6 +155,14 @@ declare function take<E extends EventMap, K extends Keys<E>, T extends E[K]>(ctx
|
|
|
151
155
|
predicate: (payload: E[K]) => payload is T;
|
|
152
156
|
}): Promise<T>;
|
|
153
157
|
declare function take<E extends EventMap, K extends Keys<E>>(ctx: Context<E>, event: K, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
158
|
+
declare function take<E extends EventMap, K extends Keys<E>, T extends E[K]>(ctx: Context<E>, creator: FreeActionCreator<K>, options: TakeOptions<E[K]> & {
|
|
159
|
+
predicate: (payload: E[K]) => payload is T;
|
|
160
|
+
}): Promise<T>;
|
|
161
|
+
declare function take<E extends EventMap, K extends Keys<E>>(ctx: Context<E>, creator: FreeActionCreator<K>, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
162
|
+
declare function take<E extends EventMap, P, T extends P>(ctx: Context<E>, creator: ActionCreator<P>, options: TakeOptions<P> & {
|
|
163
|
+
predicate: (payload: P) => payload is T;
|
|
164
|
+
}): Promise<T>;
|
|
165
|
+
declare function take<E extends EventMap, P>(ctx: Context<E>, creator: ActionCreator<P>, options?: TakeOptions<P>): Promise<P>;
|
|
154
166
|
//#endregion
|
|
155
167
|
//#region src/effects/plugin.d.ts
|
|
156
168
|
/** The effects that `effectsPlugin` can put on `ctx`. */
|
|
@@ -177,7 +189,12 @@ type Bound<E extends EventMap> = {
|
|
|
177
189
|
<T extends Results>(effects: T): Promise<{ [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
|
|
178
190
|
<const T extends List>(effects: T): Promise<{ -readonly [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
|
|
179
191
|
};
|
|
180
|
-
take:
|
|
192
|
+
take: {
|
|
193
|
+
<K extends Keys<E>>(event: K, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
194
|
+
/** A creator without a payload type: the payload comes from the event map. */
|
|
195
|
+
<K extends Keys<E>>(creator: FreeActionCreator<K>, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
196
|
+
<P>(creator: ActionCreator<P>, options?: TakeOptions<P>): Promise<P>;
|
|
197
|
+
};
|
|
181
198
|
delay: (ms: number | (() => number)) => Promise<void>;
|
|
182
199
|
};
|
|
183
200
|
/** Any function whose first parameter takes `{ signal }` can go on the context, not only the built-in ones. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as Keys, N as WithContext, b as Context, d as Effects, h as Bus, l as BoundForm, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
1
|
+
import { B as Keys, N as WithContext, b as Context, d as Effects, h as Bus, l as BoundForm, r as FreeActionCreator, t as ActionCreator, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
2
|
import { n as NoClash } from "./context-types-BKIH5a8s.js";
|
|
3
3
|
//#region src/utils/delay.d.ts
|
|
4
4
|
/**
|
|
@@ -118,7 +118,8 @@ type TakeOptions<P> = {
|
|
|
118
118
|
signal?: AbortSignal | undefined;
|
|
119
119
|
};
|
|
120
120
|
/**
|
|
121
|
-
* Waits for the next `event` on the bus and resolves with its payload.
|
|
121
|
+
* Waits for the next `event` on the bus and resolves with its payload. `event` is the name
|
|
122
|
+
* of an event or an action creator, as for `bus.on`. It sees
|
|
122
123
|
* only events emitted after the call, takes only one, and unsubscribes when it
|
|
123
124
|
* is done. If `ctx` (or `options.signal`) aborts first, it unsubscribes and
|
|
124
125
|
* rejects with the abort reason.
|
|
@@ -135,6 +136,9 @@ type TakeOptions<P> = {
|
|
|
135
136
|
* take(ctx, "saved", { signal, predicate: (id) => id === draftId }),
|
|
136
137
|
* );
|
|
137
138
|
*
|
|
139
|
+
* // an action creator works as the name does, and gives its payload type
|
|
140
|
+
* const stopped = await take(ctx, stopRequested);
|
|
141
|
+
*
|
|
138
142
|
* // a type guard narrows the result: `failed` is `{ ok: false }`
|
|
139
143
|
* const failed = await take(ctx, "result", {
|
|
140
144
|
* predicate: (result): result is { ok: false } => !result.ok,
|
|
@@ -145,6 +149,14 @@ declare function take<E extends EventMap, K extends Keys<E>, T extends E[K]>(ctx
|
|
|
145
149
|
predicate: (payload: E[K]) => payload is T;
|
|
146
150
|
}): Promise<T>;
|
|
147
151
|
declare function take<E extends EventMap, K extends Keys<E>>(ctx: Context<E>, event: K, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
152
|
+
declare function take<E extends EventMap, K extends Keys<E>, T extends E[K]>(ctx: Context<E>, creator: FreeActionCreator<K>, options: TakeOptions<E[K]> & {
|
|
153
|
+
predicate: (payload: E[K]) => payload is T;
|
|
154
|
+
}): Promise<T>;
|
|
155
|
+
declare function take<E extends EventMap, K extends Keys<E>>(ctx: Context<E>, creator: FreeActionCreator<K>, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
156
|
+
declare function take<E extends EventMap, P, T extends P>(ctx: Context<E>, creator: ActionCreator<P>, options: TakeOptions<P> & {
|
|
157
|
+
predicate: (payload: P) => payload is T;
|
|
158
|
+
}): Promise<T>;
|
|
159
|
+
declare function take<E extends EventMap, P>(ctx: Context<E>, creator: ActionCreator<P>, options?: TakeOptions<P>): Promise<P>;
|
|
148
160
|
//#endregion
|
|
149
161
|
//#region src/effects/plugin.d.ts
|
|
150
162
|
/** The effects that `effectsPlugin` can put on `ctx`. */
|
|
@@ -171,7 +183,12 @@ type Bound<E extends EventMap> = {
|
|
|
171
183
|
<T extends Results>(effects: T): Promise<{ [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
|
|
172
184
|
<const T extends List>(effects: T): Promise<{ -readonly [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
|
|
173
185
|
};
|
|
174
|
-
take:
|
|
186
|
+
take: {
|
|
187
|
+
<K extends Keys<E>>(event: K, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
188
|
+
/** A creator without a payload type: the payload comes from the event map. */
|
|
189
|
+
<K extends Keys<E>>(creator: FreeActionCreator<K>, options?: TakeOptions<E[K]>): Promise<E[K]>;
|
|
190
|
+
<P>(creator: ActionCreator<P>, options?: TakeOptions<P>): Promise<P>;
|
|
191
|
+
};
|
|
175
192
|
delay: (ms: number | (() => number)) => Promise<void>;
|
|
176
193
|
};
|
|
177
194
|
/** Any function whose first parameter takes `{ signal }` can go on the context, not only the built-in ones. */
|
package/dist/scope.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { P as WithSignal, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
-
import { n as ScopeSlots, r as Scoped } from "./types-
|
|
2
|
+
import { n as ScopeSlots, r as Scoped } from "./types-DC-aLJit.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/svelte.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { T as EventKey, h as Bus, r as FreeActionCreator, t as ActionCreator, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
+
import { t as StateOf } from "./state-of-C19H6BoV.js";
|
|
3
|
+
import { Readable } from "svelte/store";
|
|
4
|
+
//#region src/svelte/plugin.d.ts
|
|
5
|
+
/** What `sveltePlugin` adds to the bus. */
|
|
6
|
+
type SvelteApi<E extends EventMap> = {
|
|
7
|
+
/**
|
|
8
|
+
* A readable Svelte store: `initial` at first, then the payload of every emit of `event`.
|
|
9
|
+
* The bus listens only while something subscribes (the first subscriber starts it, the last
|
|
10
|
+
* one stops it), and the bus keeps no values, so an event emitted while nothing reads the
|
|
11
|
+
* store is lost. It is state of the components that read it, not a store of the app. A
|
|
12
|
+
* function payload is stored, not called. The method does not read `this`, so
|
|
13
|
+
* `export const { eventStore } = bus` gives a free function.
|
|
14
|
+
*/
|
|
15
|
+
eventStore: {
|
|
16
|
+
<K extends EventKey<E>, S>(event: K, initial: StateOf<E[K], S>): Readable<StateOf<E[K], S>>;
|
|
17
|
+
/** A creator without a payload type: the payload comes from the event map. */
|
|
18
|
+
<K extends EventKey<E>, S>(creator: FreeActionCreator<K>, initial: StateOf<E[K], S>): Readable<StateOf<E[K], S>>;
|
|
19
|
+
<P, T extends string>(creator: ActionCreator<P, T>, initial: P): Readable<P>;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Adds `bus.eventStore(event, initial)`: an event of the bus as a Svelte store, so `$found` in
|
|
24
|
+
* a component shows the last payload and Svelte subscribes and unsubscribes for you. The store
|
|
25
|
+
* is not a store of the app: it keeps the last payload only while something reads it, and an
|
|
26
|
+
* event that came before is lost. Keep a value that must outlive the component in a store of
|
|
27
|
+
* your own. Needs `svelte` 5 or newer. Returns the same bus.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { createBus } from "nanoeffects";
|
|
32
|
+
* import { createAction } from "nanoeffects/actions";
|
|
33
|
+
* import { sveltePlugin } from "nanoeffects/svelte";
|
|
34
|
+
*
|
|
35
|
+
* export const bus = createBus().use(sveltePlugin());
|
|
36
|
+
* export const { eventStore } = bus; // a free function
|
|
37
|
+
*
|
|
38
|
+
* const searchLoaded = createAction<string[]>("search/loaded");
|
|
39
|
+
*
|
|
40
|
+
* // in a component: `$found` is string[], set by every searchLoaded
|
|
41
|
+
* const found = eventStore(searchLoaded, []);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare const sveltePlugin: () => <E extends EventMap>(bus: Bus<E>) => Bus<E> & SvelteApi<E>;
|
|
45
|
+
//#endregion
|
|
46
|
+
export type { SvelteApi };
|
package/dist/svelte.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readable as e}from"svelte/store";const t=()=>t=>{function n(n,r){let i=n;return e(r,e=>t.on(i,e))}return Object.assign(t,{eventStore:n})};export{t as sveltePlugin};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as Effects, o as Scope, s as WithFields, z as EventMap } from "./action-creator-Bpa8BUS9.js";
|
|
2
|
-
import { t as BoundOf } from "./plugin-
|
|
2
|
+
import { t as BoundOf } from "./plugin-BiGv84Xu.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
|
package/docs/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Code for every guide step: [`examples/guide.ts`](../examples/guide.ts) (type-che
|
|
|
19
19
|
| Already on Redux / Zustand / MobX / Pinia | [Stores](stores.md) |
|
|
20
20
|
| A React form with async checks | [Build a sign-up form in React](react.md) |
|
|
21
21
|
| A Vue app | [Use nanoeffects in a Vue app](vue.md) |
|
|
22
|
+
| A Svelte app | [Use nanoeffects in a Svelte app](svelte.md) |
|
|
22
23
|
| Old responses overwrite new ones | [Cancel stale requests](cancel-stale-requests.md) |
|
|
23
24
|
| Debounced input that must also cancel the old request | [Debounce and abort](debounce-and-abort.md) |
|
|
24
25
|
| A second click sends a second request | [Prevent a double submit](prevent-double-submit.md) |
|
|
@@ -34,6 +35,7 @@ Code for every guide step: [`examples/guide.ts`](../examples/guide.ts) (type-che
|
|
|
34
35
|
- [Actions, commands, jobs and modules](actions.md)
|
|
35
36
|
- [Sign-up form in React](react.md) — one form: races, double submit, retry
|
|
36
37
|
- [nanoeffects in a Vue app](vue.md) — `watchEmitter` and `useEventRef`
|
|
38
|
+
- [nanoeffects in a Svelte app](svelte.md) — `eventStore`
|
|
37
39
|
- [Context fields](context.md) — store or client on every task
|
|
38
40
|
- [Use the bus from a class](oop.md) — `extendBus`, `@WithBus`, `@On`, and `dispose()` for the subscriptions of an instance
|
|
39
41
|
- [TypeScript in many files](typescript.md) — keep the types of the bus in commands and modules
|
package/docs/effects.md
CHANGED
|
@@ -244,18 +244,24 @@ bus.on("watch", (_, ctx) => {
|
|
|
244
244
|
- Under `latest`, subscriptions of a superseded run end with that run.
|
|
245
245
|
- `ctx.off(event)` clears every handler of the event on the bus, not only the task’s.
|
|
246
246
|
|
|
247
|
-
`take(ctx, event, options?)` waits for the next `event` and resolves with its payload
|
|
247
|
+
`take(ctx, event, options?)` waits for the next `event` and resolves with its payload. `event` is the name of an event or an action creator, as for `bus.on`:
|
|
248
248
|
|
|
249
249
|
```js
|
|
250
250
|
import { createBus } from "nanoeffects";
|
|
251
|
+
import { createAction } from "nanoeffects/actions";
|
|
251
252
|
import { take, timeout } from "nanoeffects/effects";
|
|
252
253
|
|
|
253
254
|
const bus = createBus();
|
|
255
|
+
const stopRequested = createAction("stop"); // an event with a name; calling it builds the action
|
|
254
256
|
|
|
255
257
|
bus.on("login", async (user, ctx) => {
|
|
256
258
|
const reason = await take(ctx, "logout"); // resolves on the next logout
|
|
257
259
|
cleanup(user, reason);
|
|
258
260
|
|
|
261
|
+
// an action creator works the same, and its payload type is the type of the result
|
|
262
|
+
const stopped = await take(ctx, stopRequested);
|
|
263
|
+
console.log(stopped);
|
|
264
|
+
|
|
259
265
|
// wait for a matching event, but not longer than 5 s
|
|
260
266
|
const id = await timeout(ctx, 5000, ({ signal }) =>
|
|
261
267
|
take(ctx, "saved", { signal, predicate: (savedId) => savedId === draft.id }),
|
|
@@ -264,6 +270,7 @@ bus.on("login", async (user, ctx) => {
|
|
|
264
270
|
```
|
|
265
271
|
|
|
266
272
|
- Sees only events emitted after the call, takes one, and unsubscribes.
|
|
273
|
+
- With an action creator the result has the payload type of the creator. A creator made without a payload type (`createAction("saved")`) takes it from the event map of the bus.
|
|
267
274
|
- `options.predicate` skips payloads it rejects. In TypeScript, a type guard (`(payload): payload is Admin`) narrows the type of the result.
|
|
268
275
|
- `options.signal` stops the wait (how a `race` or `timeout` starter cancels it).
|
|
269
276
|
- On abort of the task or of `options.signal`, unsubscribes and rejects with the abort reason.
|
|
@@ -14,7 +14,7 @@ Read this page if you know redux-saga and want to find the same job in nanoeffec
|
|
|
14
14
|
| `fork(fn, x)` | `ctx.run(task, x)` without `await` |
|
|
15
15
|
| `spawn(fn, x)` | `ctx.emit(event, x)` or `bus.emit(event, x)` |
|
|
16
16
|
| `put(action)` | `ctx.emit(action)` |
|
|
17
|
-
| `take(pattern)` | [`take(ctx, event, { predicate })`](effects.md) |
|
|
17
|
+
| `take(pattern)` | [`take(ctx, event, { predicate })`](effects.md), where `event` is a name or an action creator |
|
|
18
18
|
| `select` | a [context field](context.md), such as `store` |
|
|
19
19
|
| `all`, `race` | [`all`, `race`](effects.md) |
|
|
20
20
|
| `retry`, `delay` | [`retry`, `delay`](effects.md) |
|
|
@@ -62,6 +62,8 @@ bus.on(
|
|
|
62
62
|
|
|
63
63
|
Tasks are plain `async` functions. `ctx.signal` replaces `cancel`: pass it to `fetch` or to your client, and the request stops.
|
|
64
64
|
|
|
65
|
+
To see the counter, `takeLatest` with `delay`, a cancel and a failing call running, open the [counter example](../examples/counter): it has a page without a store and a page with Redux Toolkit.
|
|
66
|
+
|
|
65
67
|
## Start a task without waiting
|
|
66
68
|
|
|
67
69
|
`fork` and `spawn` differ in what happens when the caller is aborted. Pick by that.
|
package/docs/package.md
CHANGED
|
@@ -16,6 +16,7 @@ Imports are split so you only load what you use. `createBus` stays small; the re
|
|
|
16
16
|
| `nanoeffects/redux` | `createEffectsMiddleware` |
|
|
17
17
|
| `nanoeffects/observable` | `createObserve`, `Subject` |
|
|
18
18
|
| `nanoeffects/react` | `reactPlugin`: adds the `useEmitter` hook to a bus (needs React 18 or newer) |
|
|
19
|
+
| `nanoeffects/svelte` | `sveltePlugin`: adds `eventStore` to a bus (needs Svelte 5 or newer) |
|
|
19
20
|
| `nanoeffects/vue` | `vuePlugin`: adds `watchEmitter` and `useEventRef` to a bus (needs Vue 3.3 or newer) |
|
|
20
21
|
| `nanoeffects/dev` | `devPlugin` (no-op in production) |
|
|
21
22
|
| `nanoeffects/trace` | `createTracer`, the loggers and `tracePlugin` (names for the trace) |
|
package/docs/starter.md
CHANGED
|
@@ -48,7 +48,7 @@ Pass `--template` to skip the question. With npm, put `--` before the option.
|
|
|
48
48
|
| `react-ts` | The `react` app in TypeScript | You build a React app in TypeScript |
|
|
49
49
|
| `domains-ts` | Commands, a job and modules, with the logic split into domain folders and React as a thin layer | You build an app with more than one feature and want the logic off the UI |
|
|
50
50
|
| `vue-ts` | The `react-ts` app in Vue 3, with `watchEmitter` and `useEventRef` from `nanoeffects/vue` | You build a Vue 3 app |
|
|
51
|
-
| `svelte-ts` | The `react-ts` app in Svelte 5, with
|
|
51
|
+
| `svelte-ts` | The `react-ts` app in Svelte 5, with `eventStore` from `nanoeffects/svelte` | You build a Svelte app |
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
npm create nanoeffects@latest my-app -- --template react
|
|
@@ -74,7 +74,7 @@ npm create nanoeffects@latest my-app -- --template vue-ts
|
|
|
74
74
|
|
|
75
75
|
### Start with Svelte
|
|
76
76
|
|
|
77
|
-
`svelte-ts` is the search app of `react-ts` in Svelte 5.
|
|
77
|
+
`svelte-ts` is the search app of `react-ts` in Svelte 5. Its bus uses `sveltePlugin` from `nanoeffects/svelte`: `eventStore` makes a Svelte store from an event. In a component, `$found` reads it, and Svelte subscribes and unsubscribes for you. The query goes to the bus from the `oninput` handler, so nothing is emitted when the component mounts. The store listens only while something reads it, and is [not a store of the app](svelte.md#it-is-not-a-store-of-the-app): a value that several components share belongs in a store of your own.
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
npm create nanoeffects@latest my-app -- --template svelte-ts
|
package/docs/stores.md
CHANGED
|
@@ -25,7 +25,7 @@ Redux’s `dispatch` → reducers updates **state**. It does not give you first-
|
|
|
25
25
|
|
|
26
26
|
`createEffectsMiddleware(bus)` does the same job: after the reducer runs, the action also hits the nanoeffects bus you pass to it. Keep RTK for state; use policies and effects for async.
|
|
27
27
|
|
|
28
|
-
Skip this section if you are not already on Redux. The recipe as a file, type-checked with the project: [`examples/redux.ts`](../examples/redux.ts).
|
|
28
|
+
Skip this section if you are not already on Redux. The recipe as a file, type-checked with the project: [`examples/redux.ts`](../examples/redux.ts). To run it, open the [counter example](../examples/counter): `redux.html` is a counter with Redux Toolkit and this middleware.
|
|
29
29
|
|
|
30
30
|
```js
|
|
31
31
|
// bus.js
|
package/docs/svelte.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Use nanoeffects in a Svelte app
|
|
2
|
+
|
|
3
|
+
Use this page when your app is in Svelte 5 and a component has to show the result of an event. `sveltePlugin` gives you one function for that, `eventStore`. To see it in a running project, start from the `svelte-ts` template in [Start a project from a template](starter.md#start-with-svelte).
|
|
4
|
+
|
|
5
|
+
You need Svelte 5 or newer and `nanoeffects` installed. If you do not know what the bus is, read the [Guide](guide.md) first.
|
|
6
|
+
|
|
7
|
+
## Add the plugin to the bus
|
|
8
|
+
|
|
9
|
+
**Problem.** A Svelte component reads a value with `$store`. An event of the bus is not a store, so without help the component writes its own `readable` and its own `bus.on`, and has to remember to stop listening. The same few lines are copied into every component.
|
|
10
|
+
|
|
11
|
+
**When.** A component shows the payload of an event, such as the result of a search.
|
|
12
|
+
|
|
13
|
+
`sveltePlugin()` adds `eventStore` to the bus. Take it out of the bus once, in the file that makes it, and import it where you need it:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// bus.js
|
|
17
|
+
import { createBus } from "nanoeffects";
|
|
18
|
+
import { sveltePlugin } from "nanoeffects/svelte";
|
|
19
|
+
|
|
20
|
+
// createBus — the event bus, created once outside Svelte
|
|
21
|
+
// sveltePlugin — adds eventStore to the bus
|
|
22
|
+
export const bus = createBus().use(sveltePlugin());
|
|
23
|
+
export const { eventStore } = bus; // a free function: it does not read `this`
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Show the result of an event
|
|
27
|
+
|
|
28
|
+
<!-- docs-check: continue -->
|
|
29
|
+
|
|
30
|
+
`eventStore(event, initial)` returns a Svelte store. Its value is `initial` at first and the payload of every `event` after that. In a component, `$found` reads it, and Svelte subscribes and unsubscribes for you. A payload that is a function is stored, not called.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// search.js
|
|
34
|
+
import { createAction } from "nanoeffects/actions";
|
|
35
|
+
|
|
36
|
+
// createAction — an event with a name; calling it builds { type, payload }
|
|
37
|
+
export const searchLoaded = createAction("search/loaded");
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
<!-- docs-check: skip - a .svelte file -->
|
|
41
|
+
```svelte
|
|
42
|
+
<script>
|
|
43
|
+
import { eventStore } from "./bus";
|
|
44
|
+
import { searchLoaded } from "./search";
|
|
45
|
+
|
|
46
|
+
// eventStore — like a store of [], but searchLoaded sets the value
|
|
47
|
+
const found = eventStore(searchLoaded, []);
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<ul>
|
|
51
|
+
{#each $found as title (title)}
|
|
52
|
+
<li>{title}</li>
|
|
53
|
+
{/each}
|
|
54
|
+
</ul>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
In TypeScript the type of the store comes from the event: with `createAction<string[]>("search/loaded")` it is `Readable<string[]>`, and for an event with no payload type it comes from `initial`.
|
|
58
|
+
|
|
59
|
+
To send a value to the bus, call `bus.emit` from the handler of the event, for example `oninput`. Nothing is emitted when the component mounts, so no plugin function is needed for that direction.
|
|
60
|
+
|
|
61
|
+
### It is not a store of the app
|
|
62
|
+
|
|
63
|
+
The bus listens only while something reads the store: the first `$found` starts it, and the last one stops it. The bus keeps no values, so an event that came while nothing read the store is lost, and a component that mounts later starts from `initial`. To keep a value for components that come later, keep it in a store of your own and write to it from a handler of the event:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
// found-store.js
|
|
67
|
+
import { writable } from "svelte/store";
|
|
68
|
+
import { bus } from "./bus";
|
|
69
|
+
import { searchLoaded } from "./search";
|
|
70
|
+
|
|
71
|
+
// writable — a Svelte store that you keep for the whole app
|
|
72
|
+
export const foundTitles = writable([]);
|
|
73
|
+
|
|
74
|
+
// bus.on — copy every searchLoaded into the store, whether or not a component is open
|
|
75
|
+
bus.on(searchLoaded, (titles) => foundTitles.set(titles));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Where to go next
|
|
79
|
+
|
|
80
|
+
- The same job for React: [Build a sign-up form in React](react.md); for Vue: [Use nanoeffects in a Vue app](vue.md).
|
|
81
|
+
- A policy in a component (a debounced save): in the script of a component make it once with [`withPolicy`](policies.md#use-a-policy-without-a-bus); Svelte runs the script once, so it needs no hook.
|
package/llms-full.txt
CHANGED
|
@@ -9,7 +9,7 @@ Rules that generated code often gets wrong:
|
|
|
9
9
|
- A handler gets the payload and a task context: `(payload, ctx)`. The context has `signal`, `emit`, `on`, `run`. Pass `ctx.signal` to `fetch`.
|
|
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
|
-
- 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/vue` (`vuePlugin`, adds `bus.watchEmitter` and `bus.useEventRef`; needs Vue), `nanoeffects/trace` (`createTracer`, loggers, `tracePlugin`), `nanoeffects/dev`.
|
|
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
13
|
- `bus.emit(command(payload))` runs the command's handler and returns its result. Plain events return nothing.
|
|
14
14
|
|
|
15
15
|
---
|
|
@@ -553,7 +553,7 @@ Pass `--template` to skip the question. With npm, put `--` before the option.
|
|
|
553
553
|
| `react-ts` | The `react` app in TypeScript | You build a React app in TypeScript |
|
|
554
554
|
| `domains-ts` | Commands, a job and modules, with the logic split into domain folders and React as a thin layer | You build an app with more than one feature and want the logic off the UI |
|
|
555
555
|
| `vue-ts` | The `react-ts` app in Vue 3, with `watchEmitter` and `useEventRef` from `nanoeffects/vue` | You build a Vue 3 app |
|
|
556
|
-
| `svelte-ts` | The `react-ts` app in Svelte 5, with
|
|
556
|
+
| `svelte-ts` | The `react-ts` app in Svelte 5, with `eventStore` from `nanoeffects/svelte` | You build a Svelte app |
|
|
557
557
|
|
|
558
558
|
```bash
|
|
559
559
|
npm create nanoeffects@latest my-app -- --template react
|
|
@@ -579,7 +579,7 @@ npm create nanoeffects@latest my-app -- --template vue-ts
|
|
|
579
579
|
|
|
580
580
|
### Start with Svelte
|
|
581
581
|
|
|
582
|
-
`svelte-ts` is the search app of `react-ts` in Svelte 5.
|
|
582
|
+
`svelte-ts` is the search app of `react-ts` in Svelte 5. Its bus uses `sveltePlugin` from `nanoeffects/svelte`: `eventStore` makes a Svelte store from an event. In a component, `$found` reads it, and Svelte subscribes and unsubscribes for you. The query goes to the bus from the `oninput` handler, so nothing is emitted when the component mounts. The store listens only while something reads it, and is [not a store of the app](svelte.md#it-is-not-a-store-of-the-app): a value that several components share belongs in a store of your own.
|
|
583
583
|
|
|
584
584
|
```bash
|
|
585
585
|
npm create nanoeffects@latest my-app -- --template svelte-ts
|
|
@@ -692,6 +692,7 @@ Imports are split so you only load what you use. `createBus` stays small; the re
|
|
|
692
692
|
| `nanoeffects/redux` | `createEffectsMiddleware` |
|
|
693
693
|
| `nanoeffects/observable` | `createObserve`, `Subject` |
|
|
694
694
|
| `nanoeffects/react` | `reactPlugin`: adds the `useEmitter` hook to a bus (needs React 18 or newer) |
|
|
695
|
+
| `nanoeffects/svelte` | `sveltePlugin`: adds `eventStore` to a bus (needs Svelte 5 or newer) |
|
|
695
696
|
| `nanoeffects/vue` | `vuePlugin`: adds `watchEmitter` and `useEventRef` to a bus (needs Vue 3.3 or newer) |
|
|
696
697
|
| `nanoeffects/dev` | `devPlugin` (no-op in production) |
|
|
697
698
|
| `nanoeffects/trace` | `createTracer`, the loggers and `tracePlugin` (names for the trace) |
|
|
@@ -1827,18 +1828,24 @@ bus.on("watch", (_, ctx) => {
|
|
|
1827
1828
|
- Under `latest`, subscriptions of a superseded run end with that run.
|
|
1828
1829
|
- `ctx.off(event)` clears every handler of the event on the bus, not only the task’s.
|
|
1829
1830
|
|
|
1830
|
-
`take(ctx, event, options?)` waits for the next `event` and resolves with its payload
|
|
1831
|
+
`take(ctx, event, options?)` waits for the next `event` and resolves with its payload. `event` is the name of an event or an action creator, as for `bus.on`:
|
|
1831
1832
|
|
|
1832
1833
|
```js
|
|
1833
1834
|
import { createBus } from "nanoeffects";
|
|
1835
|
+
import { createAction } from "nanoeffects/actions";
|
|
1834
1836
|
import { take, timeout } from "nanoeffects/effects";
|
|
1835
1837
|
|
|
1836
1838
|
const bus = createBus();
|
|
1839
|
+
const stopRequested = createAction("stop"); // an event with a name; calling it builds the action
|
|
1837
1840
|
|
|
1838
1841
|
bus.on("login", async (user, ctx) => {
|
|
1839
1842
|
const reason = await take(ctx, "logout"); // resolves on the next logout
|
|
1840
1843
|
cleanup(user, reason);
|
|
1841
1844
|
|
|
1845
|
+
// an action creator works the same, and its payload type is the type of the result
|
|
1846
|
+
const stopped = await take(ctx, stopRequested);
|
|
1847
|
+
console.log(stopped);
|
|
1848
|
+
|
|
1842
1849
|
// wait for a matching event, but not longer than 5 s
|
|
1843
1850
|
const id = await timeout(ctx, 5000, ({ signal }) =>
|
|
1844
1851
|
take(ctx, "saved", { signal, predicate: (savedId) => savedId === draft.id }),
|
|
@@ -1847,6 +1854,7 @@ bus.on("login", async (user, ctx) => {
|
|
|
1847
1854
|
```
|
|
1848
1855
|
|
|
1849
1856
|
- Sees only events emitted after the call, takes one, and unsubscribes.
|
|
1857
|
+
- With an action creator the result has the payload type of the creator. A creator made without a payload type (`createAction("saved")`) takes it from the event map of the bus.
|
|
1850
1858
|
- `options.predicate` skips payloads it rejects. In TypeScript, a type guard (`(payload): payload is Admin`) narrows the type of the result.
|
|
1851
1859
|
- `options.signal` stops the wait (how a `race` or `timeout` starter cancels it).
|
|
1852
1860
|
- On abort of the task or of `options.signal`, unsubscribes and rejects with the abort reason.
|
|
@@ -2287,7 +2295,7 @@ Redux’s `dispatch` → reducers updates **state**. It does not give you first-
|
|
|
2287
2295
|
|
|
2288
2296
|
`createEffectsMiddleware(bus)` does the same job: after the reducer runs, the action also hits the nanoeffects bus you pass to it. Keep RTK for state; use policies and effects for async.
|
|
2289
2297
|
|
|
2290
|
-
Skip this section if you are not already on Redux. The recipe as a file, type-checked with the project: [`examples/redux.ts`](../examples/redux.ts).
|
|
2298
|
+
Skip this section if you are not already on Redux. The recipe as a file, type-checked with the project: [`examples/redux.ts`](../examples/redux.ts). To run it, open the [counter example](../examples/counter): `redux.html` is a counter with Redux Toolkit and this middleware.
|
|
2291
2299
|
|
|
2292
2300
|
```js
|
|
2293
2301
|
// bus.js
|
|
@@ -3695,6 +3703,91 @@ In TypeScript the type of the `ref` comes from the event: with `createAction<str
|
|
|
3695
3703
|
- A policy in a component (a debounced save): in `<script setup>` make it once with [`withPolicy`](policies.md#use-a-policy-without-a-bus); Vue runs the setup once, so it needs no hook.
|
|
3696
3704
|
- Keep values in Pinia: [Connect Redux, Zustand, MobX, or Pinia](stores.md).
|
|
3697
3705
|
|
|
3706
|
+
---
|
|
3707
|
+
Source: docs/svelte.md
|
|
3708
|
+
|
|
3709
|
+
# Use nanoeffects in a Svelte app
|
|
3710
|
+
|
|
3711
|
+
Use this page when your app is in Svelte 5 and a component has to show the result of an event. `sveltePlugin` gives you one function for that, `eventStore`. To see it in a running project, start from the `svelte-ts` template in [Start a project from a template](starter.md#start-with-svelte).
|
|
3712
|
+
|
|
3713
|
+
You need Svelte 5 or newer and `nanoeffects` installed. If you do not know what the bus is, read the [Guide](guide.md) first.
|
|
3714
|
+
|
|
3715
|
+
## Add the plugin to the bus
|
|
3716
|
+
|
|
3717
|
+
**Problem.** A Svelte component reads a value with `$store`. An event of the bus is not a store, so without help the component writes its own `readable` and its own `bus.on`, and has to remember to stop listening. The same few lines are copied into every component.
|
|
3718
|
+
|
|
3719
|
+
**When.** A component shows the payload of an event, such as the result of a search.
|
|
3720
|
+
|
|
3721
|
+
`sveltePlugin()` adds `eventStore` to the bus. Take it out of the bus once, in the file that makes it, and import it where you need it:
|
|
3722
|
+
|
|
3723
|
+
```js
|
|
3724
|
+
// bus.js
|
|
3725
|
+
import { createBus } from "nanoeffects";
|
|
3726
|
+
import { sveltePlugin } from "nanoeffects/svelte";
|
|
3727
|
+
|
|
3728
|
+
// createBus — the event bus, created once outside Svelte
|
|
3729
|
+
// sveltePlugin — adds eventStore to the bus
|
|
3730
|
+
export const bus = createBus().use(sveltePlugin());
|
|
3731
|
+
export const { eventStore } = bus; // a free function: it does not read `this`
|
|
3732
|
+
```
|
|
3733
|
+
|
|
3734
|
+
## Show the result of an event
|
|
3735
|
+
|
|
3736
|
+
<!-- docs-check: continue -->
|
|
3737
|
+
|
|
3738
|
+
`eventStore(event, initial)` returns a Svelte store. Its value is `initial` at first and the payload of every `event` after that. In a component, `$found` reads it, and Svelte subscribes and unsubscribes for you. A payload that is a function is stored, not called.
|
|
3739
|
+
|
|
3740
|
+
```js
|
|
3741
|
+
// search.js
|
|
3742
|
+
import { createAction } from "nanoeffects/actions";
|
|
3743
|
+
|
|
3744
|
+
// createAction — an event with a name; calling it builds { type, payload }
|
|
3745
|
+
export const searchLoaded = createAction("search/loaded");
|
|
3746
|
+
```
|
|
3747
|
+
|
|
3748
|
+
<!-- docs-check: skip - a .svelte file -->
|
|
3749
|
+
```svelte
|
|
3750
|
+
<script>
|
|
3751
|
+
import { eventStore } from "./bus";
|
|
3752
|
+
import { searchLoaded } from "./search";
|
|
3753
|
+
|
|
3754
|
+
// eventStore — like a store of [], but searchLoaded sets the value
|
|
3755
|
+
const found = eventStore(searchLoaded, []);
|
|
3756
|
+
</script>
|
|
3757
|
+
|
|
3758
|
+
<ul>
|
|
3759
|
+
{#each $found as title (title)}
|
|
3760
|
+
<li>{title}</li>
|
|
3761
|
+
{/each}
|
|
3762
|
+
</ul>
|
|
3763
|
+
```
|
|
3764
|
+
|
|
3765
|
+
In TypeScript the type of the store comes from the event: with `createAction<string[]>("search/loaded")` it is `Readable<string[]>`, and for an event with no payload type it comes from `initial`.
|
|
3766
|
+
|
|
3767
|
+
To send a value to the bus, call `bus.emit` from the handler of the event, for example `oninput`. Nothing is emitted when the component mounts, so no plugin function is needed for that direction.
|
|
3768
|
+
|
|
3769
|
+
### It is not a store of the app
|
|
3770
|
+
|
|
3771
|
+
The bus listens only while something reads the store: the first `$found` starts it, and the last one stops it. The bus keeps no values, so an event that came while nothing read the store is lost, and a component that mounts later starts from `initial`. To keep a value for components that come later, keep it in a store of your own and write to it from a handler of the event:
|
|
3772
|
+
|
|
3773
|
+
```js
|
|
3774
|
+
// found-store.js
|
|
3775
|
+
import { writable } from "svelte/store";
|
|
3776
|
+
import { bus } from "./bus";
|
|
3777
|
+
import { searchLoaded } from "./search";
|
|
3778
|
+
|
|
3779
|
+
// writable — a Svelte store that you keep for the whole app
|
|
3780
|
+
export const foundTitles = writable([]);
|
|
3781
|
+
|
|
3782
|
+
// bus.on — copy every searchLoaded into the store, whether or not a component is open
|
|
3783
|
+
bus.on(searchLoaded, (titles) => foundTitles.set(titles));
|
|
3784
|
+
```
|
|
3785
|
+
|
|
3786
|
+
## Where to go next
|
|
3787
|
+
|
|
3788
|
+
- The same job for React: [Build a sign-up form in React](react.md); for Vue: [Use nanoeffects in a Vue app](vue.md).
|
|
3789
|
+
- A policy in a component (a debounced save): in the script of a component make it once with [`withPolicy`](policies.md#use-a-policy-without-a-bus); Svelte runs the script once, so it needs no hook.
|
|
3790
|
+
|
|
3698
3791
|
---
|
|
3699
3792
|
Source: docs/context.md
|
|
3700
3793
|
|
|
@@ -4633,7 +4726,7 @@ Read this page if you know redux-saga and want to find the same job in nanoeffec
|
|
|
4633
4726
|
| `fork(fn, x)` | `ctx.run(task, x)` without `await` |
|
|
4634
4727
|
| `spawn(fn, x)` | `ctx.emit(event, x)` or `bus.emit(event, x)` |
|
|
4635
4728
|
| `put(action)` | `ctx.emit(action)` |
|
|
4636
|
-
| `take(pattern)` | [`take(ctx, event, { predicate })`](effects.md) |
|
|
4729
|
+
| `take(pattern)` | [`take(ctx, event, { predicate })`](effects.md), where `event` is a name or an action creator |
|
|
4637
4730
|
| `select` | a [context field](context.md), such as `store` |
|
|
4638
4731
|
| `all`, `race` | [`all`, `race`](effects.md) |
|
|
4639
4732
|
| `retry`, `delay` | [`retry`, `delay`](effects.md) |
|
|
@@ -4681,6 +4774,8 @@ bus.on(
|
|
|
4681
4774
|
|
|
4682
4775
|
Tasks are plain `async` functions. `ctx.signal` replaces `cancel`: pass it to `fetch` or to your client, and the request stops.
|
|
4683
4776
|
|
|
4777
|
+
To see the counter, `takeLatest` with `delay`, a cancel and a failing call running, open the [counter example](../examples/counter): it has a page without a store and a page with Redux Toolkit.
|
|
4778
|
+
|
|
4684
4779
|
## Start a task without waiting
|
|
4685
4780
|
|
|
4686
4781
|
`fork` and `spawn` differ in what happens when the caller is aborted. Pick by that.
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ Rules that generated code often gets wrong:
|
|
|
9
9
|
- A handler gets the payload and a task context: `(payload, ctx)`. The context has `signal`, `emit`, `on`, `run`. Pass `ctx.signal` to `fetch`.
|
|
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
|
-
- 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/vue` (`vuePlugin`, adds `bus.watchEmitter` and `bus.useEventRef`; needs Vue), `nanoeffects/trace` (`createTracer`, loggers, `tracePlugin`), `nanoeffects/dev`.
|
|
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
13
|
- `bus.emit(command(payload))` runs the command's handler and returns its result. Plain events return nothing.
|
|
14
14
|
|
|
15
15
|
## Start here
|
|
@@ -37,6 +37,7 @@ Rules that generated code often gets wrong:
|
|
|
37
37
|
- [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
|
|
38
38
|
- [Build a chat with domains, 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`
|
|
39
39
|
- [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)
|
|
40
|
+
- [Use nanoeffects in a Svelte app](docs/svelte.md): `sveltePlugin` adds `eventStore` (an event as a Svelte store)
|
|
40
41
|
- [Context fields](docs/context.md): put a store or client on every task
|
|
41
42
|
- [Scopes](docs/scope.md): fields for one command, action, job, module or `emit`, and a way to replace them in a test
|
|
42
43
|
- [Use the bus from a class](docs/oop.md): `Emitter`, `extendBus`, `@WithBus`, `@On`, `@Once`, and `dispose()` for the subscriptions of an instance
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nanoeffects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Cancel stale async work and fix race conditions in event handlers: a typed event bus where every handler is a task with an AbortSignal, plus latest, debounce, throttle, queue, retry, timeout and poll. About 1.4 kB gzip.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"event-bus",
|
|
@@ -126,6 +126,10 @@
|
|
|
126
126
|
"types": "./dist/react.d.ts",
|
|
127
127
|
"import": "./dist/react.js"
|
|
128
128
|
},
|
|
129
|
+
"./svelte": {
|
|
130
|
+
"types": "./dist/svelte.d.ts",
|
|
131
|
+
"import": "./dist/svelte.js"
|
|
132
|
+
},
|
|
129
133
|
"./vue": {
|
|
130
134
|
"types": "./dist/vue.d.ts",
|
|
131
135
|
"import": "./dist/vue.js"
|
|
@@ -183,6 +187,7 @@
|
|
|
183
187
|
"react-dom": "^19.3.1",
|
|
184
188
|
"redux": "^5.0.1",
|
|
185
189
|
"size-limit": "^14.2.0",
|
|
190
|
+
"svelte": "^5.57.3",
|
|
186
191
|
"tsdown": "0.23.1",
|
|
187
192
|
"typescript": "7.0.2",
|
|
188
193
|
"typescript-5": "npm:typescript@^5.9.3",
|
|
@@ -262,6 +267,14 @@
|
|
|
262
267
|
],
|
|
263
268
|
"limit": "1.24 kB"
|
|
264
269
|
},
|
|
270
|
+
{
|
|
271
|
+
"name": "svelte",
|
|
272
|
+
"path": "dist/svelte.js",
|
|
273
|
+
"ignore": [
|
|
274
|
+
"svelte"
|
|
275
|
+
],
|
|
276
|
+
"limit": "0.3 kB"
|
|
277
|
+
},
|
|
265
278
|
{
|
|
266
279
|
"name": "vue",
|
|
267
280
|
"path": "dist/vue.js",
|
|
@@ -283,12 +296,16 @@
|
|
|
283
296
|
],
|
|
284
297
|
"peerDependencies": {
|
|
285
298
|
"react": ">=18",
|
|
299
|
+
"svelte": ">=5",
|
|
286
300
|
"vue": ">=3.3"
|
|
287
301
|
},
|
|
288
302
|
"peerDependenciesMeta": {
|
|
289
303
|
"react": {
|
|
290
304
|
"optional": true
|
|
291
305
|
},
|
|
306
|
+
"svelte": {
|
|
307
|
+
"optional": true
|
|
308
|
+
},
|
|
292
309
|
"vue": {
|
|
293
310
|
"optional": true
|
|
294
311
|
}
|