nanoeffects 0.28.0 → 0.30.1
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 +6 -5
- package/dist/{action-creator-CcAEvUnw.d.ts → action-creator-6rlBTmH9.d.ts} +60 -33
- package/dist/actions.d.ts +18 -18
- package/dist/actions.js +1 -1
- package/dist/bus-DFlmNJ6h.js +1 -0
- package/dist/context-B5Z2RpJt.js +1 -0
- package/dist/context-types-DKNc5mkS.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 +44 -3
- package/dist/dev.js +2 -1
- package/dist/effects.d.ts +2 -2
- package/dist/effects.js +1 -1
- package/dist/emit-key-C6FL7Ewn.js +1 -0
- package/dist/guard.d.ts +59 -0
- package/dist/guard.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-C_oEXw1n.d.ts → module-G39MaVOt.d.ts} +38 -38
- package/dist/notrace/{action-creator-DxOvVo8I.d.ts → action-creator-BrMSO3RP.d.ts} +35 -32
- package/dist/notrace/actions.d.ts +52 -52
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/{bus-DmKn4SDD.js → bus-37nlVljc.js} +1 -1
- 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-lJjjd-SN.d.ts → plugin-7GUL2EW5.d.ts} +1 -1
- package/dist/notrace/policies.d.ts +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-CuvIgjnX.d.ts → plugin-BWNP23hr.d.ts} +2 -2
- package/dist/policies.d.ts +2 -2
- package/dist/policies.js +1 -1
- package/dist/production/dev.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/site-DLvUoXs3.js +1 -0
- package/dist/svelte.d.ts +1 -1
- package/dist/trace.d.ts +170 -6
- package/dist/trace.js +3 -1
- package/dist/{types-FV8fuvI9.d.ts → types-N_cdRr1r.d.ts} +2 -2
- package/dist/vue.d.ts +1 -1
- package/dist/{with-policy-D-SQRkPh.js → with-policy-CeM1rtES.js} +1 -1
- package/dist/{with-policy-3bhFLEXs.d.ts → with-policy-CthiBp_z.d.ts} +1 -1
- package/docs/README.md +65 -22
- package/docs/actions.md +56 -13
- package/docs/cancel-stale-requests.md +2 -0
- package/docs/chat-example.md +15 -1
- package/docs/chat-widget.md +9 -7
- package/docs/context.md +352 -8
- package/docs/debounce-and-abort.md +3 -1
- package/docs/debugging-loops.md +283 -0
- package/docs/effects.md +259 -131
- package/docs/errors.md +4 -2
- package/docs/examples.md +60 -0
- package/docs/guide.md +57 -22
- package/docs/hmr.md +35 -1
- package/docs/loop-guard.md +109 -0
- package/docs/migrating-from-effector.md +15 -2
- package/docs/migrating-from-mitt.md +6 -4
- package/docs/migrating-from-rxjs.md +2 -0
- package/docs/migrating-from-saga.md +2 -0
- package/docs/motivation.md +14 -21
- package/docs/observable.md +45 -0
- package/docs/oop.md +20 -1
- package/docs/package.md +25 -13
- package/docs/pass-the-signal.md +281 -0
- package/docs/plugins.md +90 -15
- package/docs/policies.md +73 -46
- package/docs/prevent-double-submit.md +3 -1
- package/docs/query-libraries.md +276 -6
- package/docs/react.md +50 -11
- package/docs/recipes.md +528 -0
- package/docs/ru/README.md +62 -0
- package/docs/ru/actions.md +412 -0
- package/docs/ru/cancel-stale-requests.md +106 -0
- package/docs/ru/chat-example.md +259 -0
- package/docs/ru/chat-widget.md +131 -0
- package/docs/ru/context.md +401 -0
- package/docs/ru/debounce-and-abort.md +105 -0
- package/docs/ru/debugging-loops.md +283 -0
- package/docs/ru/effects.md +538 -0
- package/docs/ru/errors.md +36 -0
- package/docs/ru/examples.md +60 -0
- package/docs/ru/guide.md +530 -0
- package/docs/ru/hmr.md +83 -0
- package/docs/ru/loop-guard.md +109 -0
- package/docs/ru/migrating-from-effector.md +339 -0
- package/docs/ru/migrating-from-mitt.md +99 -0
- package/docs/ru/migrating-from-rxjs.md +197 -0
- package/docs/ru/migrating-from-saga.md +97 -0
- package/docs/ru/motivation.md +47 -0
- package/docs/ru/observable.md +233 -0
- package/docs/ru/oop.md +431 -0
- package/docs/ru/package.md +42 -0
- package/docs/ru/pass-the-signal.md +281 -0
- package/docs/ru/plugins.md +398 -0
- package/docs/ru/policies.md +201 -0
- package/docs/ru/prevent-double-submit.md +122 -0
- package/docs/ru/query-libraries.md +392 -0
- package/docs/ru/react.md +309 -0
- package/docs/ru/recipes.md +528 -0
- package/docs/ru/semantics.md +19 -0
- package/docs/ru/server.md +204 -0
- package/docs/ru/starter.md +118 -0
- package/docs/ru/stores.md +673 -0
- package/docs/ru/svelte.md +78 -0
- package/docs/ru/tracing.md +466 -0
- package/docs/ru/typescript.md +170 -0
- package/docs/ru/vue.md +92 -0
- package/docs/semantics.md +12 -0
- package/docs/server.md +5 -3
- package/docs/starter.md +2 -0
- package/docs/stores.md +2 -0
- package/docs/svelte.md +3 -1
- package/docs/tracing.md +132 -7
- package/docs/typescript.md +3 -1
- package/docs/vue.md +3 -1
- package/llms-full.txt +2718 -545
- package/llms.txt +17 -14
- package/package.json +25 -8
- package/skills/nanoeffects/SKILL.md +2 -2
- package/dist/bus-DlpEluzd.js +0 -1
- package/dist/context-UcmrAIhP.js +0 -1
- package/dist/context-types-hCKgU8sC.d.ts +0 -14
- package/docs/scope.md +0 -266
- package/docs/stability.md +0 -90
package/dist/guard.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { H as EventMap, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
//#region src/guard/plugin.d.ts
|
|
3
|
+
/** What a guard found when it refused an emit. */
|
|
4
|
+
type LimitFinding = {
|
|
5
|
+
/** `repeats`: one event is in the chain too often; `depth`: the chain is too long; `storm`: too many emits from outside any task. */
|
|
6
|
+
limit: "repeats" | "depth" | "storm";
|
|
7
|
+
/** The event names of the loop, from the earlier turn of the refused event to it; for a storm, the event. */
|
|
8
|
+
path: string[];
|
|
9
|
+
};
|
|
10
|
+
type LoopGuardOptions = {
|
|
11
|
+
/** How many times one event may be in a chain; the emit that would pass it is refused. Default 50. */
|
|
12
|
+
maxRepeats?: number;
|
|
13
|
+
/** How many emits long a chain may be, whatever the names are. Default 1000. */
|
|
14
|
+
maxDepth?: number;
|
|
15
|
+
/** Events from outside any task, per second, for each type; over it they are refused. Default: no limit. */
|
|
16
|
+
maxPerSecond?: number;
|
|
17
|
+
/** Event names (or a predicate) that the repeat count skips; `maxDepth` still applies. */
|
|
18
|
+
ignore?: readonly string[] | ((name: string) => boolean);
|
|
19
|
+
/** What to do with a refused emit: `"drop"` does not send it (default), `"throw"` throws a `LoopError` from it. */
|
|
20
|
+
onLimit?: "drop" | "throw";
|
|
21
|
+
/** Receives each finding once for a path. Default: `console.error` with the path. */
|
|
22
|
+
report?: (finding: LimitFinding) => void;
|
|
23
|
+
/** The clock for `maxPerSecond`, in ms. Default `performance.now`. */
|
|
24
|
+
now?: () => number;
|
|
25
|
+
};
|
|
26
|
+
/** Thrown by an emit that the guard refused, with `onLimit: "throw"`. */
|
|
27
|
+
export declare class LoopError extends Error {
|
|
28
|
+
readonly limit: LimitFinding["limit"];
|
|
29
|
+
readonly path: string[];
|
|
30
|
+
constructor(finding: LimitFinding);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Cuts a loop while the app runs. It follows the chain of emits that `ctx.emit`
|
|
34
|
+
* makes, across `await` and timers, and refuses the emit that would pass a limit:
|
|
35
|
+
* the same event too often in one chain (`maxRepeats`), or a chain too long
|
|
36
|
+
* (`maxDepth`). Nothing else is touched: other chains go on, and the next emit
|
|
37
|
+
* from outside starts a fresh chain.
|
|
38
|
+
*
|
|
39
|
+
* A refused emit is not sent (`onLimit: "drop"`, the default) or throws a
|
|
40
|
+
* `LoopError`, and is always reported once for each path. The state of the app can
|
|
41
|
+
* be half done when a loop is cut. The plugin needs no tracer and nothing else, so
|
|
42
|
+
* it can stay on in production as a safety net. Returns the same bus.
|
|
43
|
+
*
|
|
44
|
+
* A chain that repeats on purpose (a handler that emits the same event again after
|
|
45
|
+
* a timer, forever) is cut too: list its event in `ignore`.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { createBus } from "nanoeffects";
|
|
50
|
+
* import { loopGuardPlugin } from "nanoeffects/guard";
|
|
51
|
+
*
|
|
52
|
+
* const bus = createBus().use(
|
|
53
|
+
* loopGuardPlugin({ maxRepeats: 50, onLimit: "drop" }),
|
|
54
|
+
* );
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare const loopGuardPlugin: (options?: LoopGuardOptions) => <E extends EventMap>(bus: Bus<E>) => Bus<E>;
|
|
58
|
+
//#endregion
|
|
59
|
+
export type { LimitFinding, LoopGuardOptions };
|
package/dist/guard.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=class extends Error{limit;path;constructor(e){super(`event loop stopped (${e.limit}): ${e.path.join(` → `)}`),this.name=`LoopError`,this.limit=e.limit,this.path=e.path}};const t=e=>typeof e==`string`?e:String(Reflect.get(Object(e),`type`)),n=e=>{if(typeof e==`function`)return e;let t=new Set(e);return e=>t.has(e)},r=(e,t)=>{let n=0;for(let r=e;r;r=r.parent)!r.inner&&r.type===t&&(n+=1);return n},i=(e,t)=>{let n=[];for(let r=e;r&&!(!r.inner&&(n.push(r.type),r.type===t));r=r.parent);return[...n.reverse(),t]},a=(a={})=>s=>{let c=a.maxRepeats??50,l=a.maxDepth??1e3,u=a.onLimit??`drop`,d=n(a.ignore),f=a.now??(()=>globalThis.performance.now()),p=/* @__PURE__ */ new WeakMap,m=/* @__PURE__ */ new Set,h=/* @__PURE__ */ new Map,g,_=!1,v=e=>{let t=`${e.limit}:${e.path.join(`\0`)}`;if(!m.has(t)){m.add(t);try{(a.report??o)(e)}catch(e){globalThis.console?.error(`[nanoeffects] loop guard report failed`,e)}}},y=t=>{if(v(t),u===`throw`)throw new e(t)},b=e=>g?g.depth+1>l?(y({limit:`depth`,path:i(g,e).slice(-20)}),!1):!d(e)&&r(g,e)>=c?(y({limit:`repeats`,path:i(g,e)}),!1):!0:x(e),x=e=>{let t=a.maxPerSecond;if(t===void 0)return!0;let n=f(),r=(h.get(e)??[]).filter(e=>n-e<1e3);return r.length>=t?(h.set(e,r),y({limit:`storm`,path:[e]}),!1):(r.push(n),h.set(e,r),!0)},S=s.addContext;s.addContext=e=>{S?.(e);let t=e,n=g,r=_&&n!==void 0,i={type:t.type,parent:n,depth:n?n.depth+ +!r:0,inner:r};p.set(e,i);let a=t.emit;t.emit=(e,...t)=>{let n=g;g=i;try{return a(e,...t)}finally{g=n}};let o=t.run;t.run=(...e)=>{let t=g,n=_;g=i,_=!0;try{return o(...e)}finally{g=t,_=n}}};let C=s.emit;return s.emit=(e,...n)=>{if(!b(t(e)))return;let r=_;_=!1;try{return C(e,...n)}finally{_=r}},s},o=e=>{globalThis.console?.error(`[nanoeffects] loop guard: ${e.limit} limit reached: ${e.path.join(` → `)}. The emit was refused.`)};export{e as LoopError,a as loopGuardPlugin};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { type Action, type Actions, type AnyEvent, type Bus, type BusOptions, type Call, type
|
|
1
|
+
import { A as HandledCall, C as Emit, D as EventPayload, E as EventLike, F as WithContext, G as PayloadOf, H as EventMap, I as WithSignal, L as WithType, M as HandlerRef, N as Off, O as EventsOf, P as On, Q as UnsubscribeCallback, R as createBus, S as ContextOption, T as EventKey, U as Keys, _ as AnyEvent, b as Context, g as Actions, h as Action, j as Handler, k as Handled, n as Call, o as WithPayload, q as Task, v as Bus, w as Emittable, x as ContextOf, y as BusOptions } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
export { type Action, type Actions, type AnyEvent, type Bus, type BusOptions, type Call, type Context, type ContextOf, type ContextOption, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type EventsOf, type Handled, type HandledCall, type Handler, type HandlerRef, type Keys, type Off, type On, type PayloadOf, type Task, type UnsubscribeCallback, type WithContext, type WithPayload, type WithSignal, type WithType, createBus };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{t as createBus}from"./bus-DFlmNJ6h.js";
|
package/dist/match.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as EventMap,
|
|
1
|
+
import { D as EventPayload, H as EventMap, Q as UnsubscribeCallback, h as Action, q as Task, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
2
|
//#region src/match/plugin.d.ts
|
|
3
3
|
/** A function over the action of an event: `{ type, payload }`. */
|
|
4
4
|
type Predicate<E extends EventMap> = (action: Action<E>) => boolean;
|
|
@@ -23,8 +23,8 @@ type MatchApi<E extends EventMap> = {
|
|
|
23
23
|
* to subscribe to the events that it picks instead of to one named event. The
|
|
24
24
|
* predicate gets `{ type, payload }` of every event; when it returns true the
|
|
25
25
|
* handler runs with the payload and the task context of that event. A type guard
|
|
26
|
-
* narrows the payload of the handler. An action creator
|
|
27
|
-
* a string `type`,
|
|
26
|
+
* narrows the payload of the handler. An action creator, which has
|
|
27
|
+
* a string `type`, keeps its meaning. Returns the same bus.
|
|
28
28
|
*
|
|
29
29
|
* The subscription listens to `"*"`, so it runs after the listeners of the event
|
|
30
30
|
* itself, makes a task for every event whether or not the predicate accepts it,
|
package/dist/match.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=e=>typeof e==`function`&&typeof Reflect.get(e,`type`)!=`string`,t=e=>e,n=()=>n=>{let{on:r,once:i,off:a}=n,o=/* @__PURE__ */ new Map,s=(e,t,i)=>{let
|
|
1
|
+
const e=e=>typeof e==`function`&&typeof Reflect.get(e,`type`)!=`string`,t=e=>e,n=()=>n=>{let{on:r,once:i,off:a}=n,o=/* @__PURE__ */ new Map,s=(e,t,i,a)=>{let s=Reflect.apply(r,n,[`*`,(n,r)=>{if(e({type:Reflect.get(r,`type`),payload:n}))return i&&l(),t(n,r)},a]),c={handler:t,remove:l};o.set(e,[...o.get(e)??[],c]);function l(){s();let t=(o.get(e)??[]).filter(e=>e!==c);t.length>0?o.set(e,t):o.delete(e)}return l},c=(t,a,o,c)=>e(t)?s(t,a,o,c):Reflect.apply(o?i:r,n,[t,a,c]);return Object.assign(n,t({on:(e,t,n)=>c(e,t,!1,n),once:(e,t,n)=>c(e,t,!0,n),off:(t,r)=>{if(r===void 0&&(t===void 0||t===`*`)&&o.clear(),!e(t))return Reflect.apply(a,n,[t,r]);let i=o.get(t)??[],s=i.filter(e=>e.handler===r),c=r===void 0?i:s.slice(0,1);for(let e of c)e.remove()}}))};export{n as matchPlugin};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ScopeSlots, r as Scoped } from "./types-
|
|
3
|
-
//#region src/actions/
|
|
4
|
-
/** What `
|
|
5
|
-
type
|
|
1
|
+
import { A as HandledCall, G as PayloadOf, H as EventMap, Q as UnsubscribeCallback, b as Context, c as Scope, g as Actions, k as Handled, q as Task, u as WithScope, v as Bus, z as handledCall } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
import { n as ScopeSlots, r as Scoped } from "./types-N_cdRr1r.js";
|
|
3
|
+
//#region src/actions/handled.d.ts
|
|
4
|
+
/** What `createAction` returns: an action that also tells whether an action is of its type. */
|
|
5
|
+
type MadeHandled<P, R, T extends string> = Handled<P, R> & {
|
|
6
6
|
readonly type: T;
|
|
7
|
-
/** Tells whether an action has the type of this
|
|
7
|
+
/** Tells whether an action has the type of this action, and narrows its type. */
|
|
8
8
|
match(action: {
|
|
9
9
|
type: string;
|
|
10
10
|
payload?: unknown;
|
|
@@ -14,14 +14,14 @@ type MadeCommand<P, R, T extends string> = Command<P, R> & {
|
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
16
|
/** `Made`, called so that its calls carry the scope fields `F`; `Made` itself when there are none. */
|
|
17
|
-
type CarryingCalls<Made, P, R, F> = [unknown] extends [F] ? Made : ((payload: P) =>
|
|
18
|
-
/**
|
|
19
|
-
type
|
|
20
|
-
/**
|
|
21
|
-
type Binding<E extends EventMap = EventMap> = readonly [
|
|
17
|
+
type CarryingCalls<Made, P, R, F> = [unknown] extends [F] ? Made : ((payload: P) => HandledCall<P, R> & WithScope<F>) & Made & WithScope<F>;
|
|
18
|
+
/** An action made with a scope: its calls carry the fields of that scope, for the checker. */
|
|
19
|
+
type ScopedHandled<P, R, T extends string, F> = ((payload: P) => HandledCall<P, R> & WithScope<F>) & MadeHandled<P, R, T> & WithScope<F>;
|
|
20
|
+
/** An action (or its type) with the handler to bind to it; `setHandler` makes one. */
|
|
21
|
+
type Binding<E extends EventMap = EventMap> = readonly [Handled<never, unknown> | string, Task<never, E, unknown>];
|
|
22
22
|
/**
|
|
23
23
|
* Pairs an action with the handler to bind to it, for `actionsPlugin`. For a
|
|
24
|
-
*
|
|
24
|
+
* action (an action made with a handler, or with a `Call` type) it checks that
|
|
25
25
|
* the handler fits; any other action is accepted with a handler of any payload.
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
@@ -29,21 +29,21 @@ type Binding<E extends EventMap = EventMap> = readonly [Command<never, unknown>
|
|
|
29
29
|
* bus.use(actionsPlugin([setHandler(sendMail, smtpSend)]));
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
declare function setHandler<P, R, E extends EventMap>(
|
|
32
|
+
declare function setHandler<P, R, E extends EventMap>(handled: Handled<P, R>, fn: Task<P, E, R>): Binding<E>;
|
|
33
33
|
declare function setHandler<A extends {
|
|
34
34
|
readonly type: string;
|
|
35
35
|
}, E extends EventMap>(action: A, fn: LooseHandler<A, E>): Binding<E>;
|
|
36
36
|
/**
|
|
37
|
-
* The handler an action that is not
|
|
38
|
-
* (a creator whose calls are
|
|
37
|
+
* The handler an action that is not an action can have: any. An action
|
|
38
|
+
* (a creator whose calls are calls of an action with a handler) is not accepted here, so its own
|
|
39
39
|
* overload checks the handler against its payload and result.
|
|
40
40
|
*/
|
|
41
41
|
type LooseHandler<A, E extends EventMap> = A extends ((...args: never[]) => {
|
|
42
|
-
readonly [
|
|
42
|
+
readonly [handledCall]: unknown;
|
|
43
43
|
}) ? never : Task<never, E, unknown>;
|
|
44
44
|
/** The registry of handlers that `actionsPlugin` returns: `run` for the bus, `handle` to bind more. */
|
|
45
45
|
interface Registry$1<E extends EventMap> extends Actions {
|
|
46
|
-
handle<P, R>(
|
|
46
|
+
handle<P, R>(handled: Handled<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
|
|
47
47
|
/** Also an action made without a handler: what is emitted for its type runs `fn` and returns its result. */
|
|
48
48
|
handle<A extends {
|
|
49
49
|
readonly type: string;
|
|
@@ -52,25 +52,25 @@ interface Registry$1<E extends EventMap> extends Actions {
|
|
|
52
52
|
handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* The plugin that gives a bus the handlers bound to
|
|
55
|
+
* The plugin that gives a bus the handlers bound to actions:
|
|
56
56
|
* `bus.use(actionsPlugin(bindings))` makes the registry, sets it as the
|
|
57
|
-
*
|
|
57
|
+
* actions of the bus and returns it. Each bus has its own, so each test can
|
|
58
58
|
* have its own implementation. `handle` binds more later and returns the
|
|
59
59
|
* function that unbinds. A second `actionsPlugin` on the same bus binds its
|
|
60
60
|
* list in the registry that is there and returns it, so the order of the
|
|
61
|
-
* plugins does not matter. A `
|
|
61
|
+
* plugins does not matter. A `actions` that is not such a registry (a custom
|
|
62
62
|
* runner) is replaced.
|
|
63
63
|
*
|
|
64
|
-
* What the bus runs for
|
|
65
|
-
*
|
|
64
|
+
* What the bus runs for an action: the handler bound last, else the one the
|
|
65
|
+
* action was created with, else it throws `No handler for action "..."`.
|
|
66
66
|
*
|
|
67
67
|
* @example
|
|
68
68
|
* ```ts
|
|
69
69
|
* const bus = createBus<Events>();
|
|
70
|
-
* const
|
|
70
|
+
* const actions = bus.use(actionsPlugin([setHandler(sendMail, smtpSend)]));
|
|
71
71
|
*
|
|
72
|
-
* const unbind =
|
|
73
|
-
* unbind(); // the
|
|
72
|
+
* const unbind = actions.handle(getUser, async () => fakeUser);
|
|
73
|
+
* unbind(); // the action's own handler is back
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
76
|
declare const actionsPlugin: <E extends EventMap>(bindings?: readonly Binding<E>[]) => (bus: Bus<E>) => Registry$1<E>;
|
|
@@ -78,23 +78,23 @@ declare const actionsPlugin: <E extends EventMap>(bindings?: readonly Binding<E>
|
|
|
78
78
|
//#region src/actions/module.d.ts
|
|
79
79
|
/**
|
|
80
80
|
* What a module's `setup` gets: the context of the module's task, so `on`,
|
|
81
|
-
* `emit` and `run` work as in a handler, plus `handle` to bind
|
|
81
|
+
* `emit` and `run` work as in a handler, plus `handle` to bind actions, `use`
|
|
82
82
|
* to mount sub-modules and `spawn` to start a background task. Everything the
|
|
83
83
|
* module does is undone when it is unmounted.
|
|
84
84
|
*/
|
|
85
85
|
type ModuleContext<E extends EventMap = EventMap> = Context<E> & {
|
|
86
|
-
/** Binds the handler of
|
|
87
|
-
handle<P, R>(
|
|
88
|
-
/** Also an action made without a handler: the handler answers it as it would a
|
|
86
|
+
/** Binds the handler of an action, or a list of them; undone on unmount. */
|
|
87
|
+
handle<P, R>(handled: Handled<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
|
|
88
|
+
/** Also an action made without a handler: the handler answers it as it would one made with a handler. */
|
|
89
89
|
handle<A extends {
|
|
90
90
|
readonly type: string;
|
|
91
91
|
}>(action: A, fn: LooseHandler<A, E>): UnsubscribeCallback;
|
|
92
92
|
handle(type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
|
|
93
93
|
handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
|
|
94
94
|
/**
|
|
95
|
-
* Brings sub-modules and
|
|
96
|
-
* unmounted with this one.
|
|
97
|
-
* as long as this module is mounted;
|
|
95
|
+
* Brings sub-modules and actions, in any order. A sub-module is mounted and
|
|
96
|
+
* unmounted with this one. An action is bound to the handler it carries, for
|
|
97
|
+
* as long as this module is mounted; an action that answers by itself keeps
|
|
98
98
|
* answering after the module is gone, since the handler is its own. The
|
|
99
99
|
* returned function undoes just this call, e.g. for what was added later from
|
|
100
100
|
* a listener.
|
|
@@ -116,7 +116,7 @@ type Module<E extends EventMap = EventMap> = {
|
|
|
116
116
|
readonly scope?: Scope;
|
|
117
117
|
};
|
|
118
118
|
/**
|
|
119
|
-
* Describes one domain of an app: the
|
|
119
|
+
* Describes one domain of an app: the actions it handles, the events it
|
|
120
120
|
* listens to, its sub-modules and its background tasks. The `name` shows up as
|
|
121
121
|
* the type of the module's tasks in the trace.
|
|
122
122
|
*
|
|
@@ -158,7 +158,7 @@ interface WithApp {
|
|
|
158
158
|
}
|
|
159
159
|
/** What `modulePlugin` returns. */
|
|
160
160
|
type ModuleApi<E extends EventMap = EventMap> = {
|
|
161
|
-
/** The registry of
|
|
161
|
+
/** The registry of action handlers of the bus: the one it was made with, or the app's own. */
|
|
162
162
|
actions: Registry<E>;
|
|
163
163
|
/**
|
|
164
164
|
* Mounts modules, in the order given, and returns the function that unmounts them, last
|
|
@@ -187,17 +187,17 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
187
187
|
* of one of its methods. By then every plugin is on the bus, so the plugins can
|
|
188
188
|
* be used in any order. The `on`, `once` and `off` calls made in between are
|
|
189
189
|
* held and made right after the modules, so the listeners of a module stay
|
|
190
|
-
* ahead of them. The bus runs the
|
|
190
|
+
* ahead of them. The bus runs the actions that the modules bind: the registry
|
|
191
191
|
* that the bus has is used (a later `actionsPlugin` binds in it), otherwise the
|
|
192
192
|
* app attaches its own, as `app.actions`. The app is also `bus.app`.
|
|
193
193
|
*
|
|
194
194
|
* A module is known by its `name`: mounting a module whose name is taken
|
|
195
195
|
* replaces the mounted one, which is unmounted first. That is what hot reload
|
|
196
196
|
* needs (see `docs/hmr.md`): the new version of a module file mounts again and
|
|
197
|
-
* the old version goes away with its subscriptions,
|
|
197
|
+
* the old version goes away with its subscriptions, actions and tasks.
|
|
198
198
|
*
|
|
199
199
|
* Unmounting undoes everything the module did: its subscriptions, its bound
|
|
200
|
-
*
|
|
200
|
+
* actions, its sub-modules and its background tasks. `dispose` unmounts them
|
|
201
201
|
* all, and a disposed app cannot mount modules.
|
|
202
202
|
*
|
|
203
203
|
* @example
|
|
@@ -220,4 +220,4 @@ type ModuleApi<E extends EventMap = EventMap> = {
|
|
|
220
220
|
*/
|
|
221
221
|
declare const modulePlugin: <E extends EventMap>(modules?: readonly Module<E>[]) => (bus: Bus<E> & ScopeSlots & WithApp) => ModuleApi<E>;
|
|
222
222
|
//#endregion
|
|
223
|
-
export { createModule as a, CarryingCalls as c, actionsPlugin as d, setHandler as f, WithApp as i,
|
|
223
|
+
export { createModule as a, CarryingCalls as c, actionsPlugin as d, setHandler as f, WithApp as i, MadeHandled as l, ModuleApi as n, modulePlugin as o, ModuleContext as r, Binding as s, Module as t, ScopedHandled as u };
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
//#region src/core/types.d.ts
|
|
2
|
-
/** The third argument of `on` and `once`: read by
|
|
2
|
+
/** The third argument of `on` and `once`: each field is read by the plugin that names it, ignored without it. */
|
|
3
3
|
type ListenerOptions = {
|
|
4
|
-
/** Names the listener in the trace: each call of it is a task with this `type`. */
|
|
4
|
+
/** Names the listener in the trace: each call of it is a task with this `type`. Read by `tracePlugin`. */
|
|
5
5
|
traceId?: string;
|
|
6
|
+
/** Removes the listener when this aborts, and adds none if it aborted already. Read by `signalPlugin`. */
|
|
7
|
+
signal?: AbortSignal;
|
|
6
8
|
};
|
|
7
9
|
type PayloadOf<P> = undefined extends P ? [payload?: P] : [payload: P];
|
|
8
10
|
type EventMap = Record<string, unknown>;
|
|
9
11
|
type Keys<E> = keyof E & string;
|
|
10
12
|
type Task<P, E extends EventMap, R = unknown> = (payload: P, ctx: Context<E>) => R;
|
|
11
|
-
type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "
|
|
13
|
+
type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "action" | "emit";
|
|
12
14
|
type Phase = "start" | "done" | "error" | "cancel" | "drop";
|
|
13
15
|
type UnsubscribeCallback = () => void;
|
|
14
16
|
//#endregion
|
|
@@ -52,6 +54,7 @@ interface Tracer<Ref extends object = object> {
|
|
|
52
54
|
type: string;
|
|
53
55
|
payload?: unknown;
|
|
54
56
|
traceGroupId?: string;
|
|
57
|
+
leaf?: boolean;
|
|
55
58
|
}, parent?: Ref): Ref;
|
|
56
59
|
/** Gives the task another `type`, for a task that is named after it was made (a named listener). A tracer may leave it out. */
|
|
57
60
|
rename?(ref: Ref, type: string): void;
|
|
@@ -98,27 +101,27 @@ type Action<E extends EventMap> = {
|
|
|
98
101
|
};
|
|
99
102
|
type EventLike<E extends EventMap> = { [K in EventKey<E>]: WithPayload<E, K>; }[Keys<E>];
|
|
100
103
|
/**
|
|
101
|
-
* Brands `{ type, payload }` from a
|
|
104
|
+
* Brands `{ type, payload }` from a call of an action with a handler so `emit` can run the action.
|
|
102
105
|
* Not enumerable on the object.
|
|
103
106
|
*/
|
|
104
|
-
declare const
|
|
107
|
+
declare const handledCall: unique symbol;
|
|
105
108
|
/**
|
|
106
|
-
* What `
|
|
107
|
-
* that
|
|
109
|
+
* What `action(payload)` returns: an action-shaped value that `emit` runs as
|
|
110
|
+
* that action. A plain `{ type, payload }` without this brand is notify-only.
|
|
108
111
|
*/
|
|
109
|
-
type
|
|
112
|
+
type HandledCall<P = unknown, R = unknown> = {
|
|
110
113
|
type: string;
|
|
111
114
|
payload: P;
|
|
112
|
-
readonly [
|
|
115
|
+
readonly [handledCall]: Handled<P, R>;
|
|
113
116
|
};
|
|
114
117
|
/**
|
|
115
118
|
* A typed request for a result. Calling it builds `{ type, payload }` like
|
|
116
|
-
* `createAction`; `bus.emit(
|
|
119
|
+
* `createAction`; `bus.emit(action(payload))` (or `bus.emit(action, payload)`)
|
|
117
120
|
* runs the handler as a task and returns what it returns. Made by
|
|
118
|
-
* `
|
|
121
|
+
* `createAction` in `nanoeffects/actions`.
|
|
119
122
|
*/
|
|
120
|
-
type
|
|
121
|
-
(payload: P):
|
|
123
|
+
type Handled<P = never, R = unknown> = {
|
|
124
|
+
(payload: P): HandledCall<P, R>;
|
|
122
125
|
readonly type: string;
|
|
123
126
|
/** Wraps the handler that really runs, e.g. to route its outcome. */
|
|
124
127
|
readonly wrap?: (run: (payload: never, ctx: never) => unknown) => (payload: never, ctx: never) => unknown;
|
|
@@ -128,15 +131,15 @@ type Command<P = never, R = unknown> = {
|
|
|
128
131
|
* `actionsPlugin` in `nanoeffects/actions`, holds the handlers bound to
|
|
129
132
|
* actions and runs the right one, so a bus without `actions` carries none of
|
|
130
133
|
* that. `bound` tells, for an event type, whether a handler is bound to it: an
|
|
131
|
-
* action with no handler of its own is
|
|
132
|
-
* no `bound` runs
|
|
134
|
+
* action with no handler of its own is an action only then. A runner that has
|
|
135
|
+
* no `bound` runs actions only.
|
|
133
136
|
*/
|
|
134
137
|
interface Actions {
|
|
135
|
-
run(
|
|
136
|
-
bound?(type: string):
|
|
138
|
+
run(handled: Handled, payload: unknown, ctx: unknown): unknown;
|
|
139
|
+
bound?(type: string): Handled | undefined;
|
|
137
140
|
}
|
|
138
|
-
/** Anything `emit` takes as its first argument: an event type, an action or a
|
|
139
|
-
type Emittable<E extends EventMap> = EventKey<E> |
|
|
141
|
+
/** Anything `emit` takes as its first argument: an event type, an action creator or a call of one. */
|
|
142
|
+
type Emittable<E extends EventMap> = EventKey<E> | Handled | HandledCall | EventLike<E>;
|
|
140
143
|
type Emit<E extends EventMap> = {
|
|
141
144
|
/**
|
|
142
145
|
* On a bus without an event map the caller can say what a handler bound for
|
|
@@ -145,11 +148,11 @@ type Emit<E extends EventMap> = {
|
|
|
145
148
|
*/
|
|
146
149
|
<R = void>(type: string extends EventKey<E> ? string : never, ...payload: unknown[]): R;
|
|
147
150
|
<K extends EventKey<E>>(event: K, ...payload: PayloadOf<DataOf<E[K]>>): ResultOf<E[K]>;
|
|
148
|
-
/** Before Handler:
|
|
149
|
-
<P, R>(
|
|
151
|
+
/** Before Handler: an action is callable too, and must return its result type. */
|
|
152
|
+
<P, R>(handled: Handled<P, R>, ...payload: PayloadOf<P>): R;
|
|
150
153
|
/** A scope as the second argument replaces context fields for the tasks of this emit; see `scopePlugin`. */
|
|
151
|
-
<P, R, F>(call:
|
|
152
|
-
<P, R>(call:
|
|
154
|
+
<P, R, F>(call: HandledCall<P, R> & WithScope<F>, scope?: EmitScope<E, F>): R;
|
|
155
|
+
<P, R>(call: HandledCall<P, R>, scope?: EmitScope<E>): R;
|
|
153
156
|
<K extends EventKey<E>, R>(event: Handler<E, R, K>, ...payload: PayloadOf<DataOf<E[K]>>): R;
|
|
154
157
|
<F>(event: EventLike<E> & WithScope<F>, scope?: EmitScope<E, F>): void;
|
|
155
158
|
(event: EventLike<E>, scope?: EmitScope<E>): void;
|
|
@@ -158,15 +161,15 @@ type On<E extends EventMap> = {
|
|
|
158
161
|
(event: AnyEvent, fn: Task<EventPayload<E>, E>, options?: ListenerOptions): UnsubscribeCallback;
|
|
159
162
|
<K extends EventKey<E>>(event: K, fn: Task<DataOf<E[K]>, E>, options?: ListenerOptions): UnsubscribeCallback;
|
|
160
163
|
/** An action creator without a payload type: the payload comes from the event map. */
|
|
161
|
-
/** The creators and
|
|
164
|
+
/** The creators and actions that carry a scope come first: the handler sees their fields. */
|
|
162
165
|
<K extends EventKey<E>, F>(creator: FreeActionCreator<K, F> & WithScope<F>, fn: Task<DataOf<E[K]>, WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
|
|
163
166
|
<P, F>(creator: ActionCreator<P, string, PayloadOf<P>, F> & WithScope<F>, fn: Task<P, WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
|
|
164
|
-
<P, F>(
|
|
167
|
+
<P, F>(handled: Handled<P, unknown> & WithScope<F>, fn: Task<P, WithFields<E, F>>, options?: ListenerOptions): UnsubscribeCallback;
|
|
165
168
|
<K extends EventKey<E>>(creator: FreeActionCreator<K>, fn: Task<DataOf<E[K]>, E>, options?: ListenerOptions): UnsubscribeCallback;
|
|
166
169
|
/** An action creator: the payload comes from it, so an event map is not needed. */
|
|
167
170
|
<P>(creator: ActionCreator<P>, fn: Task<P, E>, options?: ListenerOptions): UnsubscribeCallback;
|
|
168
|
-
/**
|
|
169
|
-
<P>(
|
|
171
|
+
/** An action: the payload comes from it too. */
|
|
172
|
+
<P>(handled: Handled<P, unknown>, fn: Task<P, E>, options?: ListenerOptions): UnsubscribeCallback;
|
|
170
173
|
<K extends EventKey<E>, Event extends {
|
|
171
174
|
type: K;
|
|
172
175
|
}>(event: Event & {
|
|
@@ -185,7 +188,7 @@ type Off<E extends EventMap> = {
|
|
|
185
188
|
}>(event: Event & {
|
|
186
189
|
type: K;
|
|
187
190
|
}, fn: Task<DataOf<E[K]>, E>): void;
|
|
188
|
-
<P>(
|
|
191
|
+
<P>(handled: Handled<P, unknown>, fn?: Task<P, E>): void;
|
|
189
192
|
};
|
|
190
193
|
declare const contextKey: unique symbol;
|
|
191
194
|
/**
|
|
@@ -275,7 +278,7 @@ interface Bus<E extends EventMap> {
|
|
|
275
278
|
use<R>(plugin: (bus: Bus<E>) => R): R;
|
|
276
279
|
/** `BusOptions.context` and `.trace`, and `actions`, for what makes tasks of its own, like `modulePlugin`. */
|
|
277
280
|
readonly ctx?: ContextOption<E> | undefined;
|
|
278
|
-
/** What runs the handlers of actions; `actionsPlugin` and `modulePlugin` set it. Without one
|
|
281
|
+
/** What runs the handlers of actions; `actionsPlugin` and `modulePlugin` set it. Without one an action runs its own handler. */
|
|
279
282
|
actions?: Actions | undefined;
|
|
280
283
|
readonly trace?: Tracer | undefined;
|
|
281
284
|
/** Called with every new task context to add fields to it, for what needs the task, like `effectsPlugin`. Chain to the previous one. */
|
|
@@ -294,7 +297,7 @@ type BusOptions<E extends EventMap> = {
|
|
|
294
297
|
onError?: (error: unknown, event: Action<E>) => void;
|
|
295
298
|
/**
|
|
296
299
|
* Called when an event is emitted and nobody listens to it: the usual sign
|
|
297
|
-
* of a typo in an event name. Not called for
|
|
300
|
+
* of a typo in an event name. Not called for actions, nor for an action with
|
|
298
301
|
* a handler bound in `actions`, which always have a handler to run. Typically a warning in development only.
|
|
299
302
|
*
|
|
300
303
|
* @example
|
|
@@ -345,7 +348,7 @@ type BoundForm<F> = typeof bound extends keyof F ? NonNullable<F[typeof bound &
|
|
|
345
348
|
//#endregion
|
|
346
349
|
//#region src/core/scope-types.d.ts
|
|
347
350
|
declare const scopeFields: unique symbol;
|
|
348
|
-
/** What an action creator
|
|
351
|
+
/** What an action creator or a call knows of the scope it was made with: its fields, for the checker. */
|
|
349
352
|
type WithScope<F> = {
|
|
350
353
|
readonly [scopeFields]: F;
|
|
351
354
|
};
|
|
@@ -454,4 +457,4 @@ interface ActionCreator<P = void, T extends string = string, A extends unknown[]
|
|
|
454
457
|
toString(): T;
|
|
455
458
|
}
|
|
456
459
|
//#endregion
|
|
457
|
-
export { Off as A, Task as B,
|
|
460
|
+
export { Off as A, Task as B, EventLike as C, HandledCall as D, Handled as E, createBus as F, handledCall as I, EventMap as L, WithContext as M, WithSignal as N, Handler as O, WithType as P, Keys as R, EventKey as S, EventsOf as T, UnsubscribeCallback as V, Context as _, WithPayload as a, Emit as b, WithScope as c, Effects as d, Action as f, BusOptions as g, Bus as h, FreeActionCreator as i, On as j, HandlerRef as k, BoundForm as l, AnyEvent as m, Call as n, Scope as o, Actions as p, Free as r, WithFields as s, ActionCreator as t, Effect as u, ContextOf as v, EventPayload as w, Emittable as x, ContextOption as y, PayloadOf as z };
|