nanoeffects 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/{action-creator-jeEqIKgK.d.ts → action-creator-6rlBTmH9.d.ts} +27 -2
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +1 -1
- package/dist/{bus-0uYn_kRj.js → bus-DFlmNJ6h.js} +1 -1
- package/dist/context-B5Z2RpJt.js +1 -0
- package/dist/{context-types-Bme1mzkn.d.ts → context-types-DKNc5mkS.d.ts} +1 -1
- package/dist/context.d.ts +2 -2
- package/dist/dev.d.ts +49 -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 +1 -1
- package/dist/index.js +1 -1
- package/dist/match.d.ts +1 -1
- package/dist/{module-BWMW83qk.d.ts → module-G39MaVOt.d.ts} +2 -2
- package/dist/notrace/{action-creator-xpQ_I2h8.d.ts → action-creator-BrMSO3RP.d.ts} +1 -0
- package/dist/notrace/actions.d.ts +2 -2
- package/dist/notrace/effects.d.ts +2 -2
- package/dist/notrace/index.d.ts +1 -1
- package/dist/notrace/{plugin-6LhNx0th.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 +1 -1
- package/dist/oop.d.ts +1 -1
- package/dist/oop.js +1 -1
- package/dist/{plugin-BaErevLy.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 +2 -2
- package/dist/signal.d.ts +22 -2
- 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 +151 -2
- package/dist/trace.js +3 -1
- package/dist/{types-D5xsR37j.d.ts → types-N_cdRr1r.d.ts} +2 -2
- package/dist/vue.d.ts +1 -1
- package/dist/{with-policy-C6BIiPIi.js → with-policy-CeM1rtES.js} +1 -1
- package/dist/{with-policy-BZVRBUOm.d.ts → with-policy-CthiBp_z.d.ts} +1 -1
- package/docs/README.md +62 -31
- package/docs/actions.md +44 -27
- package/docs/cancel-stale-requests.md +2 -0
- package/docs/chat-example.md +3 -1
- package/docs/chat-widget.md +2 -0
- package/docs/context.md +322 -9
- package/docs/debounce-and-abort.md +3 -1
- package/docs/debugging-loops.md +318 -0
- package/docs/effects.md +255 -138
- package/docs/errors.md +4 -2
- package/docs/examples.md +60 -0
- package/docs/guide.md +39 -22
- package/docs/hmr.md +35 -1
- package/docs/loop-guard.md +109 -0
- package/docs/migrating-from-effector.md +4 -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 +2 -0
- package/docs/oop.md +3 -1
- package/docs/package.md +4 -1
- package/docs/pass-the-signal.md +346 -0
- package/docs/plugins.md +22 -5
- package/docs/policies.md +73 -46
- package/docs/prevent-double-submit.md +3 -1
- package/docs/query-libraries.md +3 -1
- package/docs/react.md +42 -13
- 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 +402 -0
- package/docs/ru/debounce-and-abort.md +105 -0
- package/docs/ru/debugging-loops.md +318 -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 +346 -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 +2 -0
- package/docs/server.md +2 -0
- package/docs/starter.md +2 -0
- package/docs/stores.md +2 -0
- package/docs/svelte.md +3 -1
- package/docs/tracing.md +58 -1
- package/docs/typescript.md +2 -0
- package/docs/vue.md +3 -1
- package/llms-full.txt +2087 -418
- package/llms.txt +11 -8
- package/package.json +23 -6
- package/dist/context-DtUnzC-1.js +0 -1
- package/docs/scope.md +0 -278
- package/docs/stability.md +0 -90
package/dist/scope.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as EventMap, I as WithSignal, c as Scope, f as Effect, m as Signaled, p as Effects, s as EmitScope, v as Bus } from "./action-creator-
|
|
2
|
-
import { n as ScopeSlots, r as Scoped } from "./types-
|
|
1
|
+
import { H as EventMap, I as WithSignal, c as Scope, f as Effect, m as Signaled, p as Effects, s as EmitScope, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
import { n as ScopeSlots, r as Scoped } from "./types-N_cdRr1r.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/signal.d.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
import { A as HandledCall, C as Emit, E as EventLike, H as EventMap, s as EmitScope, u as WithScope, v as Bus } from "./action-creator-
|
|
2
|
-
import { n as FromSignal } from "./context-types-
|
|
1
|
+
import { A as HandledCall, C as Emit, E as EventLike, H as EventMap, s as EmitScope, u as WithScope, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
import { n as FromSignal } from "./context-types-DKNc5mkS.js";
|
|
3
|
+
//#region src/signal/fetch.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A `fetch` that carries a given signal, so the request stops when the signal aborts and the caller never passes
|
|
6
|
+
* it. Give it to `fromSignal` and every task gets a `ctx.fetch` made from its own signal. A call that has a
|
|
7
|
+
* `signal` of its own stops with whichever aborts first.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const bus = createBus().use(
|
|
12
|
+
* contextPlugin({ fetch: fromSignal(signalFetch) }),
|
|
13
|
+
* );
|
|
14
|
+
*
|
|
15
|
+
* bus.on("search", latest(async (query, ctx) => {
|
|
16
|
+
* const response = await ctx.fetch(`/api?q=${query}`); // stops when the policy cancels the handler
|
|
17
|
+
* ctx.emit("results", await response.json());
|
|
18
|
+
* }));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const signalFetch: (signal: AbortSignal) => typeof fetch;
|
|
22
|
+
//#endregion
|
|
3
23
|
//#region src/signal/from-signal.d.ts
|
|
4
24
|
/**
|
|
5
25
|
* Marks a field of `contextPlugin` as a value that needs the signal of its task: a
|
package/dist/signal.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./onAbort-BgMfspK4.js";import{n as t}from"./promise-CSLpzwSR.js";import{n}from"./bus-
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.js";import{n as t}from"./promise-CSLpzwSR.js";import{n}from"./bus-DFlmNJ6h.js";import{t as r}from"./signal-field-key-CWT-3OXC.js";const i=e=>(t,n)=>{let r=n?.signal,i=r?AbortSignal.any([e,r]):e;return fetch(t,{...n,signal:i})},a=e=>({[r]:e}),o=(t,n)=>(r,i,a)=>{let o=a?.signal;if(!o)return t(r,i,a);let s=t(r,n?(e,t)=>(c(),i(e,t)):i,a),c=e({signal:o},s);return()=>{c(),s()}},s=e=>typeof e==`object`&&!!e,c=(e,t)=>{let n=t[0];if(!s(n)||!s(e)&&typeof e!=`function`)return;let r=Reflect.get(n,`signal`);return r instanceof AbortSignal?r:void 0},l=(r,i,a,o)=>{let s=e({signal:r},e=>{for(let{target:t}of i)t.abort(e)});return t(o)?o.then(s,s):n(a)&&s(),o},u=()=>e=>{let t,n,r=e.addContext;e.addContext=e=>{r?.(e),t?.push({target:e}),n?.aborted&&e.abort(n.reason)};let i=e.emit;e.emit=(e,...r)=>{let a=c(e,r);if(!(a||t))return i(e,...r);let o=t,s=n,u=[];t=a?u:void 0,n=a;let d;try{d=i(e,...r)}finally{t=o,n=s}return a?l(a,u,e,d):d};let a=e.on,s=e.once;return e.on=o(a,!1),e.once=o(s,!0),e};export{a as fromSignal,i as signalFetch,u as signalPlugin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=/* @__PURE__ */ new Map,t=(t,n)=>{let r=e.get(t);e.set(t,{site:r?.site??n,count:(r?.count??0)+1});let i=!1;return()=>{if(i)return;i=!0;let r=(e.get(t)?.count??1)-1;r<=0?e.delete(t):e.set(t,{site:e.get(t)?.site??n,count:r})}},n=t=>e.get(t)?.site;export{n,t};
|
package/dist/svelte.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as EventMap, T as EventKey, a as FreeActionCreator, t as ActionCreator, v as Bus } from "./action-creator-
|
|
1
|
+
import { H as EventMap, T as EventKey, a as FreeActionCreator, t as ActionCreator, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
2
|
import { t as StateOf } from "./state-of-C19H6BoV.js";
|
|
3
3
|
import { Readable } from "svelte/store";
|
|
4
4
|
//#region src/svelte/plugin.d.ts
|
package/dist/trace.d.ts
CHANGED
|
@@ -1,4 +1,141 @@
|
|
|
1
|
-
import { B as Tracer, H as EventMap, J as
|
|
1
|
+
import { B as Tracer, H as EventMap, J as TaskInfo, K as Phase, V as EffectName, X as TraceEvent, Y as Trace, Z as TraceLogger, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
//#region src/graph/serialize.d.ts
|
|
3
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
4
|
+
[key: string]: JsonValue;
|
|
5
|
+
};
|
|
6
|
+
type SerializeOptions = {
|
|
7
|
+
/** How deep to go before a value becomes a marker like `"[Object]"`. Default 6. */
|
|
8
|
+
maxDepth?: number;
|
|
9
|
+
/** How many values, in all, to visit before the rest is cut. Default 1000. */
|
|
10
|
+
maxSize?: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Makes any value a plain JSON value: never throws, never loops, never calls
|
|
14
|
+
* a getter or `toJSON`, and cuts a value that is too deep or too big.
|
|
15
|
+
* `Error` keeps its name, message, stack and cause; a cycle is `"[Circular]"`;
|
|
16
|
+
* `Map` and `Set` become `{ $map }` and `{ $set }`; an instance of a class
|
|
17
|
+
* keeps its own fields and its name as `$class`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* safeSerialize({ user: new User("Ann"), tags: new Set(["a"]) });
|
|
22
|
+
* // { user: { $class: "User", name: "Ann" }, tags: { $set: ["a"] } }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const safeSerialize: (value: unknown, options?: SerializeOptions) => JsonValue;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/graph/types.d.ts
|
|
28
|
+
/** A loop that the logger found, or an event that is emitted too often. */
|
|
29
|
+
type Finding = {
|
|
30
|
+
/** `cycle`: a node repeats in a chain; `storm`: too many emits from outside any task; `runaway`: one root started too many tasks. */
|
|
31
|
+
kind: "cycle" | "storm" | "runaway";
|
|
32
|
+
/** The names from the first turn of the loop back to it: `a`, `b`, `a`. For a storm, the event. */
|
|
33
|
+
path: string[];
|
|
34
|
+
/** How many times the repeating node was in the chain when it was found. */
|
|
35
|
+
repeats: number;
|
|
36
|
+
/** How many times the loop has gone round since it was found, the first time included. */
|
|
37
|
+
turns: number;
|
|
38
|
+
/** The first node of the chain, or the event of a storm. */
|
|
39
|
+
root: string;
|
|
40
|
+
traceGroupId?: string;
|
|
41
|
+
/** `chain`: it ran as one chain; `graph`: the edges of different runs close it. */
|
|
42
|
+
source: "chain" | "graph";
|
|
43
|
+
};
|
|
44
|
+
type GraphLoggerOptions = {
|
|
45
|
+
/** Put before every report line and in the title of the exports. */
|
|
46
|
+
name?: string;
|
|
47
|
+
/** How many times a node may be in one chain before it is a cycle. Default 3. */
|
|
48
|
+
repeats?: number;
|
|
49
|
+
/** How many tasks one root may start before the chain is called runaway. Default 5000. */
|
|
50
|
+
maxSteps?: number;
|
|
51
|
+
/** How many emits of one event from outside any task, in how many ms, make a storm. Default 100 in 1000; `false` turns it off. */
|
|
52
|
+
storm?: false | {
|
|
53
|
+
count: number;
|
|
54
|
+
perMs: number;
|
|
55
|
+
};
|
|
56
|
+
/** Names that are not counted as repeats, steps or storms (they stay in the graph). */
|
|
57
|
+
ignore?: readonly string[] | ((name: string) => boolean);
|
|
58
|
+
/** Receives each finding once, in place of `console.warn`. */
|
|
59
|
+
onCycle?: (finding: Finding) => void;
|
|
60
|
+
/** Also send each finding to Redux DevTools as `cycle/detected`. */
|
|
61
|
+
devtools?: boolean;
|
|
62
|
+
/** The clock, in ms. Default `performance.now`. */
|
|
63
|
+
now?: () => number;
|
|
64
|
+
/** Where the default report goes. Default `globalThis.console`. */
|
|
65
|
+
console?: Pick<Console, "warn">;
|
|
66
|
+
/** The Redux DevTools extension. Default `globalThis.__REDUX_DEVTOOLS_EXTENSION__`. */
|
|
67
|
+
extension?: DevtoolsExtension;
|
|
68
|
+
};
|
|
69
|
+
type DevtoolsExtension = {
|
|
70
|
+
connect: (options?: {
|
|
71
|
+
name?: string;
|
|
72
|
+
}) => {
|
|
73
|
+
init: (state: unknown) => void;
|
|
74
|
+
send: (action: unknown, state: unknown) => void;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
type NodeSnapshot = {
|
|
78
|
+
name: string;
|
|
79
|
+
count: number;
|
|
80
|
+
/** Where the listener was made, when `devPlugin` is used. */
|
|
81
|
+
site?: string;
|
|
82
|
+
last?: {
|
|
83
|
+
payload: JsonValue;
|
|
84
|
+
error?: JsonValue;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
type EdgeSnapshot = {
|
|
88
|
+
from: string;
|
|
89
|
+
to: string;
|
|
90
|
+
count: number;
|
|
91
|
+
inCycle: boolean;
|
|
92
|
+
};
|
|
93
|
+
type GraphSnapshot = {
|
|
94
|
+
name?: string;
|
|
95
|
+
nodes: NodeSnapshot[];
|
|
96
|
+
edges: EdgeSnapshot[];
|
|
97
|
+
cycles: Finding[];
|
|
98
|
+
};
|
|
99
|
+
type ExportOptions = {
|
|
100
|
+
/** Put the nodes in groups: `"prefix"` is the part of the name before the first `/`, or a function of the name. */
|
|
101
|
+
group?: "prefix" | ((name: string) => string | undefined);
|
|
102
|
+
};
|
|
103
|
+
type JsonOptions = {
|
|
104
|
+
/** Add the last payload and error of each node, made safe. Default `false`. */
|
|
105
|
+
payload?: boolean;
|
|
106
|
+
};
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/graph/logger.d.ts
|
|
109
|
+
type GraphLogger = TraceLogger & {
|
|
110
|
+
/** The loops found so far: the ones that ran as one chain, and the cycles that the edges of different runs close. */
|
|
111
|
+
cycles: () => Finding[];
|
|
112
|
+
/** The graph as a Mermaid flowchart; the edges of a cycle are red. */
|
|
113
|
+
toMermaid: (options?: ExportOptions) => string;
|
|
114
|
+
/** The graph as a Graphviz digraph. */
|
|
115
|
+
toDot: (options?: ExportOptions) => string;
|
|
116
|
+
/** The graph as plain data that `JSON.stringify` takes. */
|
|
117
|
+
toJSON: (options?: JsonOptions) => GraphSnapshot;
|
|
118
|
+
/** Forgets the graph, the findings and the counters. */
|
|
119
|
+
clear: () => void;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* A trace logger that builds the map of which event leads to which, finds loops
|
|
123
|
+
* while the app runs, and writes the map as Mermaid, Graphviz or JSON. Pass it to
|
|
124
|
+
* `createTracer` like any logger; it needs no other setup, and works best with
|
|
125
|
+
* `tracePlugin` (it sees every emit and the names of listeners).
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* const graph = graphLogger({ name: "cart" });
|
|
130
|
+
* const bus = createBus({ trace: createTracer(graph) }).use(tracePlugin());
|
|
131
|
+
*
|
|
132
|
+
* // a loop is written to the console once; read the rest when you want
|
|
133
|
+
* graph.cycles();
|
|
134
|
+
* graph.toMermaid();
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export declare const graphLogger: (options?: GraphLoggerOptions) => GraphLogger;
|
|
138
|
+
//#endregion
|
|
2
139
|
//#region src/trace/console.d.ts
|
|
3
140
|
type ConsoleLoggerOptions = {
|
|
4
141
|
/** Defaults to `globalThis.console`. */
|
|
@@ -86,6 +223,11 @@ type Recorder<E = Record<string, unknown>> = TraceLogger<E> & {
|
|
|
86
223
|
*/
|
|
87
224
|
readonly events: TraceEvent<E>[];
|
|
88
225
|
clear: () => void;
|
|
226
|
+
/**
|
|
227
|
+
* The events as plain data that `JSON.stringify` takes: the payload and the error are made safe, so a
|
|
228
|
+
* circular object, an `Error`, a `Map` or a class instance cannot break it. `JSON.stringify(recorder)` uses it.
|
|
229
|
+
*/
|
|
230
|
+
toJSON: () => JsonValue[];
|
|
89
231
|
};
|
|
90
232
|
/**
|
|
91
233
|
* Trace logger that keeps events in memory. With no option it keeps all of
|
|
@@ -175,6 +317,13 @@ type ReduxDevtoolsOptions = {
|
|
|
175
317
|
payload?: PayloadMode;
|
|
176
318
|
/** The number of actions that DevTools keeps; its own default is 50. */
|
|
177
319
|
maxAge?: number;
|
|
320
|
+
/**
|
|
321
|
+
* Makes the payload and the error of an action plain data before they reach the extension. The default
|
|
322
|
+
* keeps any value safe: an `Error`, a `Map`, a class instance, a circular object, a function. Pass your
|
|
323
|
+
* own function to change what is sent, or `false` to send values as they are (the extension then
|
|
324
|
+
* serializes them, and can fail on a circular one).
|
|
325
|
+
*/
|
|
326
|
+
serialize?: false | ((value: unknown) => unknown);
|
|
178
327
|
};
|
|
179
328
|
type ReduxDevtoolsState = {
|
|
180
329
|
/** Tasks that started and have not settled yet, by trace id. */
|
|
@@ -239,4 +388,4 @@ type TracerOptions = {
|
|
|
239
388
|
*/
|
|
240
389
|
export declare const createTracer: <E = Record<string, unknown>>(trace: Trace<E>, options?: TracerOptions) => Tracer;
|
|
241
390
|
//#endregion
|
|
242
|
-
export type { ConsoleLoggerOptions, EffectName, PayloadMode, PerformanceLoggerOptions, Phase, PolicyStage, PolicyStages, Recorder, ReduxDevtoolsOptions, ReduxDevtoolsState, ReduxExtension, Trace, TraceEvent, TraceLogger, Tracer, TracerOptions };
|
|
391
|
+
export type { ConsoleLoggerOptions, EffectName, ExportOptions, Finding, GraphLogger, GraphLoggerOptions, GraphSnapshot, JsonOptions, JsonValue, PayloadMode, PerformanceLoggerOptions, Phase, PolicyStage, PolicyStages, Recorder, ReduxDevtoolsOptions, ReduxDevtoolsState, ReduxExtension, SerializeOptions, TaskInfo, Trace, TraceEvent, TraceLogger, Tracer, TracerOptions };
|
package/dist/trace.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
import{n as e,t}from"./onAbort-BgMfspK4.js";import{a as n,c as r,n as i,o as a,r as o,s}from"./context-
|
|
1
|
+
import{n as e,t}from"./onAbort-BgMfspK4.js";import{a as n,c as r,n as i,o as a,r as o,s}from"./context-B5Z2RpJt.js";import{n as c}from"./promise-CSLpzwSR.js";import{t as l}from"./origin-key-BbWN3gmN.js";import{n as u}from"./site-DLvUoXs3.js";import{t as d}from"./scope-key-B7l7mUS5.js";const f=e=>{let t=/* @__PURE__ */ new Map;for(let{from:n,to:r}of e){let e=t.get(n)??[];e.push(r),t.set(n,e),t.has(r)||t.set(r,[])}let n=/* @__PURE__ */ new Map,r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Set,a=[],o=[],s=0,c=(e,t)=>{n.set(e,s),r.set(e,s),s+=1,a.push(e),i.add(e),t.push({name:e,next:0})},l=e=>{let n=[],r;do{if(r=a.pop(),r===void 0)break;i.delete(r),n.push(r)}while(r!==e);let[s]=n,c=n.length===1&&s!==void 0&&t.get(s)?.includes(s);(n.length>1||c)&&o.push(n.reverse())},u=(e,t)=>{r.set(e,Math.min(r.get(e)??t,t))},d=(e,r)=>{let a=t.get(r.name)?.[r.next];return a!==void 0&&(r.next+=1,n.has(a)?i.has(a)&&u(r.name,n.get(a)??0):c(a,e),!0)},f=(e,t)=>{e.pop();let i=e.at(-1);i&&u(i.name,r.get(t.name)??0),r.get(t.name)===n.get(t.name)&&l(t.name)},p=e=>{let t=[];c(e,t);for(let e=t.at(-1);e;e=t.at(-1))d(t,e)||f(t,e)};for(let e of t.keys())n.has(e)||p(e);return o},p=(e,t)=>{if(t===`prefix`){let t=e.indexOf(`/`);return t>0?e.slice(0,t):void 0}return t?.(e)},m=e=>e.replaceAll(`"`,`#quot;`).replaceAll(`<`,`#lt;`).replaceAll(`>`,`#gt;`).replaceAll(/\r?\n/g,` `),h=e=>e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`).replaceAll(/\r?\n/g,`\\n`),g=e=>new Map(e.nodes.map((e,t)=>[e.name,`n${t}`])),_=(e,t)=>{let n=/* @__PURE__ */ new Map;for(let{name:r}of e.nodes){let e=p(r,t);e!==void 0&&n.set(e,[...n.get(e)??[],r])}return n},v=(e,t={})=>{let n=g(e),r=[`flowchart LR`],i=/* @__PURE__ */ new Set;for(let[a,o]of _(e,t.group)){r.push(` subgraph g_${r.length}["${m(a)}"]`);for(let e of o)i.add(e),r.push(` ${n.get(e)}["${m(e)}"]`);r.push(` end`)}for(let{name:t}of e.nodes)i.has(t)||r.push(` ${n.get(t)}["${m(t)}"]`);let a=[];for(let[t,i]of e.edges.entries())r.push(` ${n.get(i.from)} -->|x${i.count}| ${n.get(i.to)}`),i.inCycle&&a.push(t);return a.length>0&&r.push(` linkStyle ${a.join(`,`)} stroke:red,stroke-width:2px`),r.join(`
|
|
2
|
+
`)},y=(e,t={})=>{let n=g(e),r=[`digraph events {`];e.name!==void 0&&r.push(` label="${h(e.name)}";`);let i=/* @__PURE__ */ new Set;for(let[a,o]of _(e,t.group)){r.push(` subgraph "cluster_${h(a)}" {`,` label="${h(a)}";`);for(let e of o)i.add(e),r.push(` ${n.get(e)} [label="${h(e)}"];`);r.push(` }`)}for(let{name:t}of e.nodes)i.has(t)||r.push(` ${n.get(t)} [label="${h(t)}"];`);for(let t of e.edges){let e=t.inCycle?`, color=red, penwidth=2`:``;r.push(` ${n.get(t.from)} -> ${n.get(t.to)} [label="x${t.count}"${e}];`)}return r.push(`}`),r.join(`
|
|
3
|
+
`)},b=e=>e.kind===`storm`?"an emit from a component or a timer repeats: stop it with `equals` of useEmitter, debounce, or a guard":e.kind===`runaway`?`one chain started a huge number of tasks: look for event names that are made from a counter or an id`:e.path.length<=2?`a handler emits its own event: emit a different event for the result`:`handlers feed each other: break the loop with a condition, or emit a different event for the result`,x=(e,t)=>{let n=t===void 0?`[nanoeffects]`:`[nanoeffects ${t}]`,r=e.traceGroupId===void 0?``:` (flow ${e.traceGroupId})`,i=e.kind===`cycle`?`cycle: ${e.path.join(` → `)} (repeated ${e.repeats} times, started at ${e.root})`:`${e.kind}: ${e.path.join(` → `)} (${e.repeats} in a row, started at ${e.root})`,a=[...new Set(e.path)].flatMap(e=>{let t=u(e);return t===void 0?[]:[`${e} at ${t}`]});return`${n} ${i}${r}${a.length>0?`\n ${a.join(`; `)}`:``}\n ${b(e)}`},S=(e,t)=>{if(!e)return;let n=e.connect({name:t});n.init({findings:0});let r=0;return e=>{r+=1,n.send({type:`cycle/detected`,kind:e.kind,path:e.path,repeats:e.repeats,root:e.root},{findings:r})}},C=2e3,w=e=>{let t=Object.getPrototypeOf(e),n=t===null?void 0:Reflect.get(t,`constructor`),r=typeof n==`function`?n.name:``;return typeof r==`string`&&r!==``?r:`Object`},T=e=>{let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype},E=e=>typeof Reflect.get(e,`nodeType`)==`number`&&typeof Reflect.get(e,`nodeName`)==`string`,D=e=>e.length>C?`${e.slice(0,C)}…`:e,O=e=>{switch(typeof e){case`string`:return D(e);case`boolean`:return e;case`number`:return Number.isFinite(e)?e:String(e);case`bigint`:return`${e}n`;case`symbol`:return String(e);case`function`:return`[Function ${e.name||`anonymous`}]`;default:return e===null?null:void 0}},k=e=>{if(e instanceof WeakMap)return`[WeakMap]`;if(e instanceof WeakSet)return`[WeakSet]`;if(e instanceof Promise)return`[Promise]`;if(E(e))return`[DOM ${String(Reflect.get(e,`nodeName`))}]`;if(e instanceof Date)return Number.isNaN(e.getTime())?`[Invalid Date]`:e.toISOString()},A=(e,t,n)=>{if(t.budget<=0)return`[Cut]`;--t.budget;let r=O(e);return r===void 0?typeof e!=`object`||!e?`[undefined]`:j(e,t,n):r},j=(e,t,n)=>{try{let r=k(e);return r===void 0?t.path.has(e)?`[Circular]`:n>=t.maxDepth?Array.isArray(e)?`[Array]`:`[${w(e)}]`:M(e,t,n):r}catch{return`[Unserializable]`}},M=(e,t,n)=>{t.path.add(e);try{return N(e,t,n)}finally{t.path.delete(e)}},N=(e,t,n)=>e instanceof Error?P(e,t,n):Array.isArray(e)?F(e,t,n):e instanceof Map?{$map:F([...e.entries()],t,n)}:e instanceof Set?{$set:F([...e.values()],t,n)}:ArrayBuffer.isView(e)?{$class:w(e),length:Number(Reflect.get(e,`length`))}:I(e,t,n,T(e)?void 0:w(e)),P=(e,t,n)=>{let r={name:D(e.name),message:D(e.message)};return typeof e.stack==`string`&&(r.stack=D(e.stack)),`cause`in e&&(r.cause=A(e.cause,t,n+1)),r},F=(e,t,n)=>{let r=[];for(let[i,a]of e.entries()){if(t.budget<=0){r.push(`[${e.length-i} more]`);break}r.push(A(a,t,n+1))}return r},I=(e,t,n,r)=>{let i={};r!==void 0&&(i.$class=r);for(let r of Object.keys(e)){let a=Object.getOwnPropertyDescriptor(e,r);a&&(i[r]=`value`in a?A(a.value,t,n+1):`[Getter]`)}return i},L=(e,t={})=>{let n={maxDepth:t.maxDepth??6,budget:t.maxSize??1e3,path:/* @__PURE__ */ new Set};try{return A(e,n,0)}catch{return`[Unserializable]`}},R=(e,t)=>`${e}\u0000${t}`,z=e=>{if(typeof e==`function`)return e;let t=new Set(e);return e=>t.has(e)},B=e=>e.event===void 0?[e.node]:[e.event,e.node],V=e=>{let t=e.slice(0,-1),n=t.reduce((e,n,r)=>n<(t[e]??n)?r:e,0);return[...t.slice(n),...t.slice(0,n)].join(`\0`)},H=(e,t)=>{let{node:n}=t,r=[],i=e;for(;i&&i.node!==n;)r.push(i),i=i.parent;let a=r.reverse();return{path:[n,...a.flatMap(B),...B(t)],names:[n,...a.map(e=>e.node),n]}},U=(e={})=>{let t=e.repeats??3,n=e.maxSteps??5e3,r=e.storm===!1?void 0:e.storm??{count:100,perMs:1e3},i=z(e.ignore),a=e.now??(()=>globalThis.performance.now()),o=e.console??globalThis.console,s=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map,l=/* @__PURE__ */ new Map,d=/* @__PURE__ */ new WeakMap,p=/* @__PURE__ */ new Map,m=/* @__PURE__ */ new Set,h=!1,g=!1,_,b,C=!1,w=!1,T=e=>{let t=s.get(e)??{count:0};t.count+=1,s.set(e,t)},E=(e,t)=>{let n=R(e,t),r=c.get(n)??{from:e,to:t,count:0,inCycle:!1};r.count+=1,c.set(n,r)},D=t=>{if(e.devtools&&!C){C=!0;let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;b=S(t,e.name??`nanoeffects graph`)}b?.(t),e.onCycle?e.onCycle(t):o?.warn(x(t,e.name))},O=(e,t)=>{let n=l.get(e);if(n){n.turns+=1;return}let r=t();if(l.set(e,r),!w){w=!0;try{D(r)}catch(e){globalThis.console?.error(`[nanoeffects] graph logger report failed`,e)}finally{w=!1}}},k=(e,t)=>{let n=new Map(e.counts);n.set(t,(n.get(t)??0)+1),e.counts=n},A=(e,n)=>{let r=e.counts.get(e.node)??0;if(r<t||i(e.node))return;let{path:a,names:o}=H(e.parent,e);O(`cycle:${V(o)}`,()=>{let t={kind:`cycle`,path:a,repeats:r,turns:1,root:e.root.name,source:`chain`};return n.traceGroupId!==void 0&&(t.traceGroupId=n.traceGroupId),t})},j=e=>{e.root.steps+=1,!(e.root.steps<=n||e.root.flagged)&&(e.root.flagged=!0,O(`runaway:${e.root.name}`,()=>({kind:`runaway`,path:[e.root.name],repeats:e.root.steps,turns:1,root:e.root.name,source:`chain`})))},M=e=>{if(!r||i(e))return;let t=a(),n=(p.get(e)??[]).filter(e=>t-e<r.perMs);n.push(t),p.set(e,n),n.length<r.count?m.delete(e):m.has(e)||(m.add(e),O(`storm:${e}:${t}`,()=>({kind:`storm`,path:[e],repeats:n.length,turns:1,root:e,source:`chain`})))},N=(e,t)=>{let n=_;return n!==void 0&&n.parent===e&&n.type===t},P=(e,t,n)=>({node:e.type,parent:t,counts:t?.counts??/* @__PURE__ */ new Map,root:t?.root??{name:e.type,steps:0,flagged:!1},inner:n}),F=()=>{g||(g=!0,queueMicrotask(()=>{h||o?.warn(`[nanoeffects] graphLogger got tasks but no emit event: add bus.use(tracePlugin()) for a finer graph.`)}))},I=(e,t,n,r)=>{k(t,e.type),(e.leaf===!0||r||!h)&&(T(e.type),n?E(n.node,e.type):M(e.type)),e.leaf||i(e.type)||(j(t),A(t,e))},B=e=>{let t=e.parent?d.get(e.parent):void 0;e.leaf?(h=!0,_={parent:t,type:e.type}):F();let n=e.emitted===!1,r=!e.leaf&&h&&!n&&!N(t,e.type),i=r&&!t,a=P(e,t,n||r&&!i);d.set(e.ref,a),a.inner||I(e,a,t,i)},U=(e,t)=>{let n=new Map(e.counts),r=e.node;n.set(r,Math.max(0,(n.get(r)??1)-1)),n.set(t.type,(n.get(t.type)??0)+1),e.counts=n,e.node=t.type,e.event=t.event,T(t.type),t.event!==void 0&&E(t.event,t.type),e.inner||A(e,t)},W=e=>{let t=d.get(e.ref);t?U(t,e):B(e)},G=e=>{if(e.phase!==`done`&&e.phase!==`error`)return;let t=s.get(e.type);t&&(t.last=e.phase===`error`?{payload:e.payload,error:e.error}:{payload:e.payload})},K=()=>{let t=J(),n=/* @__PURE__ */ new Set;for(let e of t)q(e,n);let r={nodes:[...s].map(([e,t])=>{let n=u(e);return n===void 0?{name:e,count:t.count}:{name:e,count:t.count,site:n}}),edges:[...c.values()].map(e=>({...e,inCycle:n.has(R(e.from,e.to))})),cycles:t};return e.name!==void 0&&(r.name=e.name),r},q=(e,t)=>{if(e.source===`chain`){for(let[n,r]of e.path.entries()){let i=e.path[n+1];i!==void 0&&t.add(R(r,i))}return}let n=new Set(e.path);for(let e of c.values())n.has(e.from)&&n.has(e.to)&&t.add(R(e.from,e.to))},J=()=>{let e=[...l.values()].filter(e=>e.kind===`cycle`),t=new Set(e.flatMap(e=>e.path)),n=[],r=[...c.values()].filter(e=>!(i(e.from)||i(e.to)));for(let e of f(r))e.every(e=>t.has(e))||n.push({kind:`cycle`,path:e,repeats:e.length,turns:0,root:e[0]??``,source:`graph`});return[...l.values(),...n.filter(t=>!e.some(e=>e.path.join()===t.path.join()))]};return Object.assign(G,{task:W,cycles:()=>J(),toMermaid:e=>v(K(),e),toDot:e=>y(K(),e),toJSON:(e={})=>{let t=K();if(!e.payload)return t;for(let e of t.nodes){let t=s.get(e.name)?.last;t&&(e.last={payload:L(t.payload),...t.error!==void 0&&{error:L(t.error)}})}return t},clear:()=>{s=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map,l=/* @__PURE__ */ new Map,d=/* @__PURE__ */ new WeakMap,p=/* @__PURE__ */ new Map,m=/* @__PURE__ */ new Set,_=void 0}})},W=(e={})=>{let t=e.console??globalThis.console,n=e.prefix??`[nanoeffects]`,r=/* @__PURE__ */ new Map;return({id:e,parentId:i,type:a,effect:o,phase:s,payload:c,error:l,traceGroupId:u})=>{r.has(e)||r.set(e,typeof i==`number`?(r.get(i)??0)+1:0);let d=` `.repeat(r.get(e)??0);s!==`start`&&r.delete(e);let f=u===void 0?``:` [${u}]`,p=`${n}${f} ${d}${a} · ${o} ${s}`;s===`error`?t?.error(p,l):t?.debug(p,c)}},G=e=>{if(e instanceof Error)return`${e.name}: ${e.message}`;let t=L(e);return typeof t==`string`?t:JSON.stringify(t)},K=(e={})=>{let t=e.performance??globalThis.performance,n=/* @__PURE__ */ new Map;return({id:e,parentId:r,rootId:i,traceGroupId:a,type:o,effect:s,phase:c,error:l})=>{if(!t)return;if(c===`start`){n.set(e,t.now());return}let u=`${o} · ${s} (${c})`,d={id:e,parentId:r,...i!==void 0&&{rootId:i},...a!==void 0&&{traceGroupId:a},phase:c,...c===`error`&&{error:G(l)}},f=n.get(e);n.delete(e),typeof f==`number`?t.measure(u,{start:f,end:t.now(),detail:d}):t.mark(u,{detail:d})}};let q;const J=()=>q,Y=(e,t)=>{let n=q;q=e;try{return t()}finally{q=n}},X=(e,t,n={})=>{let{trace:r}=e;if(!r){e.emit(t);return}let{traceId:a,traceGroupId:o}=n,{ctx:c,unlink:l}=i({event:{type:a??t.type,payload:t,...o===void 0?{}:{traceGroupId:o}},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e.ctx,addContext:e.addContext},tracer:r});s(c,`run`,`start`);try{c.emit(t),s(c,`run`,`done`)}catch(e){throw s(c,`run`,`error`,e),e}finally{l()}},Z=e=>typeof e==`string`?e:Reflect.get(Object(e),`type`),Q=(e,t)=>typeof e==`object`?Reflect.get(Object(e),`payload`):t[0],$=()=>e=>{if(!(r&&e.trace))return e;let{trace:t}=e,i=(e,n)=>{let r=t.task({type:Z(e),payload:Q(e,n),leaf:!0},o());t.report(r,`emit`,`done`)},s=e.emit;e.emit=(e,...t)=>Y(e,()=>(i(e,t),s(e,...t))),Reflect.set(e,l,(t,n)=>{X(e,t,n)});let c=/* @__PURE__ */ new WeakMap,u=(e,t,n)=>{let r=c.get(t)??/* @__PURE__ */ new Map,i=Z(e);r.set(i,[...r.get(i)??[],n]),c.set(t,r)},d=(e,t)=>(c.get(t)?.get(Z(e)))?.pop(),f=(e,t)=>(r,i)=>(n(i,t),a(i,`run`,()=>e(r,i))),p=(e,t)=>(n,r,i)=>{let a=Reflect.get(Object(i),`traceId`);if(typeof a!=`string`)return e(n,r,i);let o=t?(e,t)=>(d(n,r),f(r,a)(e,t)):f(r,a);u(n,r,o);let s=e(n,o,i);return()=>{s()}},m=e.off,h=(e,t)=>{t===void 0?m(e):m(e,d(e,t)??t)},g=e.on,_=e.once;return e.on=p(g,!1),e.once=p(_,!0),e.off=h,e},ee=e=>{let t=e?.capacity??0,n=Number.isInteger(t)&&t>0?t:0,r=[],i=0,a=e=>{n===0?r.push(e):(r[i%n]=e,i+=1)},o=()=>{r.length=0,i=0},s=()=>{let e=Math.min(i,n),t=i>n?i%n:0,a=[];for(let i=0;i<e;i+=1){let e=r[(t+i)%n];e&&a.push(e)}return a},c=Object.assign(a,{clear:o,events:r,toJSON:()=>(n>0?s():r).map(e=>L(e))});return n>0&&Object.defineProperty(c,"events",{get:s}),c},te=()=>{},ne=/* @__PURE__ */ new Set([`cancel`,`drop`,`error`]),re=()=>!1,ie=()=>!0,ae=e=>{if(typeof e==`function`)return e;if(Array.isArray(e)){let t=new Set(e);return({phase:e})=>t.has(e)}return e?ie:re},oe=(e={})=>{let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;if(!t)return te;let{starts:n=!1,depth:r=0,maxAge:i}=e,a=ae(e.policies),o=e.payload??`essential`,s=e.serialize===!1?e=>e:e.serialize??(e=>L(e)),c=t.connect({name:e.name??`nanoeffects`,...i!==void 0&&{maxAge:i}}),l=/* @__PURE__ */ new Map,u=/* @__PURE__ */ new Map,d=()=>({running:Object.fromEntries(l)});c.init(d());let f=(e,t,n)=>n===0?a({effect:e,phase:t}):n<=r,p=(e,t)=>{e.phase===`start`?(u.set(e.id,t),f(e.effect,`start`,t)&&l.set(e.id,{type:e.type,effect:e.effect,...e.traceGroupId!==void 0&&{traceGroupId:e.traceGroupId}})):(u.delete(e.id),l.delete(e.id))},m=!1,h=!1,g=(e,t)=>{m||h||t!==0||e===`start`||(h=!0,globalThis.console?.warn(`[nanoeffects] reduxDevtools got a task but no emit event: add bus.use(tracePlugin()) to see the emits.`))},_=e=>{let{payload:t,...n}=e;return o===`all`||o===`essential`&&(e.effect===`emit`||ne.has(e.phase))?{...n,payload:s(t)}:n},v=(e,t)=>{c.send({..._(e),type:`${e.type}/${e.effect}/${e.phase}`,event:e.type,...e.phase===`error`&&{error:t instanceof Error?t.message:s(t)}},d())};return({error:e,...t})=>{if(t.effect===`emit`){m=!0,c.send({..._(t),event:t.type},d());return}let r=(t.parentId===void 0?-1:u.get(t.parentId)??-1)+1;p(t,r),g(t.phase,r),(t.phase===`error`&&r===0||f(t.effect,t.phase,r))&&(t.phase!==`start`||n)&&v(t,e)}},se=t=>{let n=typeof t==`function`?[t]:[...t??[]];return n.length===0?null:t=>{for(let r of n)try{r(t)}catch(t){e(`trace logger failed`,t)}}},ce=()=>{},le=e=>{if(typeof e.traceGroupId==`string`)return e.traceGroupId;let t=J();if(typeof t!=`object`&&typeof t!=`function`||t===null)return;let n=Reflect.get(t,d),r=typeof n==`object`&&n?Reflect.get(n,`traceGroupId`):void 0;return typeof r==`string`?r:void 0},ue=(n,r={})=>{let i=n,a=se(i),{filter:s,sample:l=1}=r,u=0,d=(Array.isArray(i)?i:[i]).flatMap(e=>e.task?[e.task]:[]),f=t=>{let n={ref:t,id:t.id,type:t.type};t.parentRef&&(n.parent=t.parentRef),t.event!==void 0&&(n.event=t.event),t.leaf&&(n.leaf=!0),n.emitted=t.emitted,t.group!==void 0&&(n.traceGroupId=t.group);for(let t of d)try{t(n)}catch(t){e(`trace logger failed`,t)}},p=e=>typeof l==`function`?l({type:e.type,payload:e.payload}):Math.random()<l,m={task:(e,t)=>{let n={id:++u,root:t?.root??u,parent:t?.id,parentRef:d.length>0?t:void 0,leaf:e.leaf,emitted:o()===t,type:e.type,payload:e.payload,sampled:t?.sampled??p(e),group:le(e)??t?.group};return d.length>0&&n.sampled&&f(n),n},rename:(e,t)=>{e.event??=e.type,e.type=t,d.length>0&&e.sampled&&f(e)},report:(e,t,n,r)=>{if(!e.sampled)return;let i={id:e.id,type:e.type,effect:t,phase:n,payload:e.payload};typeof e.parent==`number`&&(i.parentId=e.parent),e.root!==e.id&&(i.rootId=e.root),e.group!==void 0&&(i.traceGroupId=e.group),n===`error`&&(i.error=r),(!s||s(i))&&a?.(i)},settler:(e,t)=>{if(!e.sampled)return ce;let n=!1;return(r,i)=>{n||(n=!0,m.report(e,t,r,i))}},run:(e,n,r,i)=>{if(!e.sampled)return i();let a=m.settler(e,n);m.report(e,n,`start`),t({signal:r},()=>a(`cancel`));let o;try{o=i()}catch(e){throw a(`error`,e),e}return c(o)?Promise.resolve(o).then(e=>(a(`done`),e),e=>{throw a(`error`,e),e}):(a(`done`),o)}};return m};export{W as consoleLogger,ee as createTraceRecorder,ue as createTracer,U as graphLogger,K as performanceLogger,oe as reduxDevtools,L as safeSerialize,$ as tracePlugin};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as EventMap, c as Scope, l as WithFields, p as Effects } from "./action-creator-
|
|
2
|
-
import { t as BoundOf } from "./plugin-
|
|
1
|
+
import { H as EventMap, c as Scope, l as WithFields, p as Effects } from "./action-creator-6rlBTmH9.js";
|
|
2
|
+
import { t as BoundOf } from "./plugin-BWNP23hr.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/dist/vue.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as EventMap, T as EventKey, a as FreeActionCreator, t as ActionCreator, v as Bus } from "./action-creator-
|
|
1
|
+
import { H as EventMap, T as EventKey, a as FreeActionCreator, t as ActionCreator, v as Bus } from "./action-creator-6rlBTmH9.js";
|
|
2
2
|
import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
|
|
3
3
|
import { t as StateOf } from "./state-of-C19H6BoV.js";
|
|
4
4
|
import { ShallowRef, WatchSource, WatchStopHandle } from "vue";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./context-
|
|
1
|
+
import{n as e}from"./context-B5Z2RpJt.js";const t=Reflect.get(Symbol,`dispose`),n=n=>{let r=new AbortController,i=async(...t)=>{let i=t[0],{ctx:a,unlink:o}=e({event:{type:`withPolicy`,payload:i},parent:{signal:r.signal}});try{return await n(i,a)}finally{o()}},a=e=>{r.abort(e),r=new AbortController},o=Object.assign(i,{cancel:a});return t&&Reflect.set(o,t,()=>a()),o};export{n as t};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as PayloadOf, H as EventMap, q as Task } from "./action-creator-
|
|
1
|
+
import { G as PayloadOf, H as EventMap, q as Task } from "./action-creator-6rlBTmH9.js";
|
|
2
2
|
//#region src/policies/with-policy.d.ts
|
|
3
3
|
/** What `withPolicy` returns: the task as a plain function that can be cancelled. */
|
|
4
4
|
type PolicyFunction<P, R> = {
|
package/docs/README.md
CHANGED
|
@@ -1,26 +1,66 @@
|
|
|
1
1
|
# Documentation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
nanoeffects moves business logic and side effects out of UI components into plain `async` functions. Policies (`latest`, `debounce`, `queue`) and effects (`retry`, `timeout`, `poll`) handle races, retries and cancellation.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```js
|
|
6
|
+
import { createBus } from "nanoeffects";
|
|
7
|
+
import { latest } from "nanoeffects/policies";
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
const bus = createBus(); // event bus
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
// The search logic lives outside the component; latest aborts the previous request
|
|
12
|
+
bus.on(
|
|
13
|
+
"search",
|
|
14
|
+
latest(async (query, { signal }) => {
|
|
15
|
+
const items = await api.search(query, { signal });
|
|
16
|
+
bus.emit("found", items);
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// The component only listens for the result and sends events
|
|
21
|
+
bus.on("found", (items) => render(items));
|
|
22
|
+
bus.emit("search", "nano");
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The same example with a UI framework: [React](react.md), [Vue](vue.md), [Svelte](svelte.md). Ready projects: [Start a project from a template](starter.md).
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install nanoeffects
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To start a new project from a template ([Start a project from a template](starter.md)):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm create nanoeffects@latest my-app
|
|
37
|
+
```
|
|
10
38
|
|
|
11
|
-
##
|
|
39
|
+
## Where to start
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
1. Follow the [Guide](guide.md#1-create-a-bus): from the first bus to modules.
|
|
42
|
+
2. Common async UI problems (stale responses, double clicks, retries) are in [Common tasks](recipes.md).
|
|
14
43
|
|
|
15
|
-
|
|
16
|
-
| ------ | --------- |
|
|
17
|
-
| `nanoeffects` | `createBus`: `on`, `emit`, `ctx` |
|
|
18
|
-
| `nanoeffects/policies` | `latest`, `debounce`, ... for races in async UI |
|
|
19
|
-
| `nanoeffects/effects` | `retry`, `timeout`, `poll`, ... for an unreliable network |
|
|
44
|
+
## The ladder, from A to F
|
|
20
45
|
|
|
21
|
-
|
|
46
|
+
Read the docs from the level of your project and stop when it has what it needs. Each level adds one idea. A page says at its top which level it is for and what to read first.
|
|
22
47
|
|
|
23
|
-
|
|
48
|
+
| Level | Your project | You can stop here when | Read |
|
|
49
|
+
| ----- | ------------ | ---------------------- | ---- |
|
|
50
|
+
| **A** | A small app, widget or MVP with rare races | Events and handlers do the job | [Guide](guide.md#1-create-a-bus) |
|
|
51
|
+
| **B** | A SPA with async UI: search, sending messages, unmounting components | A newer request must replace an older one | [Policies](policies.md), [Guide](guide.md#3-abort-the-previous-run-with-latest) |
|
|
52
|
+
| **C** | Complex business logic | Retries, timeouts and waiting are handled | [Effects](effects.md), [Guide](guide.md#5-add-retry-and-timeout) |
|
|
53
|
+
| **D** | Domain logic that must live off the UI | Features are actions and modules, not components | [Actions, jobs and modules](actions.md), [Guide](guide.md#7-name-events-three-ways) |
|
|
54
|
+
| **E** | An existing Redux, Zustand, MobX or Pinia app, or a data library | The bus runs the async work next to your store | [Stores](stores.md), [Query libraries](query-libraries.md) |
|
|
55
|
+
| **F** | A platform, an SDK or a long-lived app | You can see what ran and reload modules | [Tracing](tracing.md), [Hot reload](hmr.md), [Semantics](semantics.md) |
|
|
56
|
+
|
|
57
|
+
A small app never needs level B or higher. Move up when the first problem of that level appears, not before.
|
|
58
|
+
|
|
59
|
+
Code for every guide step: [`examples/guide.ts`](../examples/guide.ts) (type-checked and tested).
|
|
60
|
+
|
|
61
|
+
## Jump by need
|
|
62
|
+
|
|
63
|
+
For a problem, start from [Common tasks](recipes.md). For a project shape:
|
|
24
64
|
|
|
25
65
|
| Project shape | First page |
|
|
26
66
|
| ------------- | ---------- |
|
|
@@ -35,29 +75,20 @@ The other entries and plugins: [Package entries](package.md) and [Plugins](plugi
|
|
|
35
75
|
| A Vue app | [Use nanoeffects in a Vue app](vue.md) |
|
|
36
76
|
| A Svelte app | [Use nanoeffects in a Svelte app](svelte.md) |
|
|
37
77
|
| Old responses overwrite new ones | [Cancel stale requests](cancel-stale-requests.md) |
|
|
38
|
-
| Debounced input that must also cancel the old request | [
|
|
78
|
+
| Debounced input that must also cancel the old request | [Pause between requests](debounce-and-abort.md) |
|
|
39
79
|
| A second click sends a second request | [Prevent a double submit](prevent-double-submit.md) |
|
|
40
|
-
| Using mitt
|
|
80
|
+
| Using mitt or nanoevents | [Coming from mitt](migrating-from-mitt.md) |
|
|
41
81
|
| Moving from redux-saga | [Coming from redux-saga](migrating-from-saga.md) |
|
|
42
82
|
| Using RxJS | [Use nanoeffects next to RxJS](migrating-from-rxjs.md) |
|
|
43
83
|
| Moving from Effector | [Coming from Effector](migrating-from-effector.md) |
|
|
44
84
|
|
|
45
85
|
## Reference
|
|
46
86
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
51
|
-
- [
|
|
52
|
-
- [
|
|
53
|
-
- [
|
|
54
|
-
- [Use the bus from a class](oop.md) — `extendBus`, `@WithBus`, `@On`, and `dispose()` for the subscriptions of an instance
|
|
55
|
-
- [TypeScript in many files](typescript.md) — keep the types of the bus in actions and modules
|
|
56
|
-
- [Errors and unheard events](errors.md)
|
|
57
|
-
- [Package entries](package.md) — which import holds what
|
|
58
|
-
- [Stability](stability.md) — which entries are stable, what is breaking, supported versions
|
|
59
|
-
- [Plugins](plugins.md) — every plugin of a bus, what it returns, and the order to use them in
|
|
60
|
-
- [Tracing](tracing.md) — loggers, Redux DevTools, `notrace`
|
|
61
|
-
- [Observable](observable.md) — events as TC39 Observables
|
|
87
|
+
Look these up when you need the exact rule.
|
|
88
|
+
|
|
89
|
+
- [Package entries](package.md): which import holds what
|
|
90
|
+
- [Plugins](plugins.md): every plugin of a bus, what it returns, and the order to use them in
|
|
91
|
+
- [Semantics](semantics.md): the exact rules of emit, abort and ordering
|
|
92
|
+
- [Tracing](tracing.md): loggers, Redux DevTools, `notrace`
|
|
93
|
+
- [Observable](observable.md): events as TC39 Observables
|
|
62
94
|
- [Hot reload and dev diagnostics](hmr.md)
|
|
63
|
-
- [Semantics](semantics.md)
|
package/docs/actions.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# Actions, jobs and modules
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Level D.** Read first: [Guide](guide.md#1-create-a-bus) steps 1 to 6.
|
|
4
|
+
|
|
5
|
+
When a project has a lot of business logic, you need to split it. One way is to split it by domains, or entities. In a shop, the domains could be products and product categories. Then the code that belongs to the actions of one domain goes into one file (`product.js`), its handlers into another, and the module that mounts them into a third. A domain can then be mounted, replaced or unmounted without touching the others. For the shop the files could look like this:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
src/
|
|
9
|
+
bus.js
|
|
10
|
+
product/
|
|
11
|
+
product.js
|
|
12
|
+
product-handlers.js
|
|
13
|
+
product-module.js
|
|
14
|
+
category/
|
|
15
|
+
category.js
|
|
16
|
+
category-handlers.js
|
|
17
|
+
category-module.js
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The examples below show the content of such files.
|
|
4
21
|
|
|
5
22
|
If you only need `on` / `emit` or policies, stay in the [Guide](guide.md) until you need a result from `emit` or a mountable domain.
|
|
6
23
|
|
|
@@ -10,37 +27,37 @@ A chat with two entities and three features, jobs under policies and effects, an
|
|
|
10
27
|
|
|
11
28
|
A whole project with two domains, a job and React as a thin layer: `npm create nanoeffects@latest my-app -- --template domains-ts` (see [Start a project from a template](starter.md#start-with-domains)).
|
|
12
29
|
|
|
13
|
-
##
|
|
30
|
+
## Action, job and module
|
|
14
31
|
|
|
15
|
-
Four
|
|
32
|
+
Four forms, one idea each. Take the first row that fits.
|
|
16
33
|
|
|
17
|
-
|
|
|
34
|
+
| Form | What it is | Use it when |
|
|
18
35
|
| ---- | ---------- | ----------- |
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
36
|
+
| `createAction(type)` | An action with no handler: a name and a payload. | The UI or a handler tells that something happened. |
|
|
37
|
+
| `createAction(type, handler)` | An action with a handler: `emit` runs the handler and returns its result. | You want the result of the handler from `emit`. |
|
|
38
|
+
| `createJob(type, handler)` | An action with a handler that returns its result as the actions `success` and `fail`, and also sends `pending` and `cancel`. | You want the result of the work of a handler and, if you need it, to subscribe to the state of that work. |
|
|
39
|
+
| `createModule(name, setup)` | A group of actions and jobs with their handlers, mounted and unmounted with one call. | One domain must start and stop as a unit. |
|
|
23
40
|
|
|
24
|
-
Start with
|
|
41
|
+
Start with actions. Add a handler when you need a result, a job when you need a status, a module when you need a unit.
|
|
25
42
|
|
|
26
43
|
## On this page
|
|
27
44
|
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
45
|
+
- [Action, job and module](#action-job-and-module)
|
|
46
|
+
- [Get the result of `emit`](#get-the-result-of-emit)
|
|
47
|
+
- [Actions, handlers and jobs](#actions-handlers-and-jobs)
|
|
31
48
|
- [Actions](#actions)
|
|
32
49
|
- [Bind a handler later](#bind-a-handler-later)
|
|
33
|
-
- [
|
|
50
|
+
- [The result type of a bound handler](#the-result-type-of-a-bound-handler)
|
|
34
51
|
- [Jobs](#jobs)
|
|
35
|
-
- [
|
|
36
|
-
- [
|
|
37
|
-
- [
|
|
52
|
+
- [The state of a job](#the-state-of-a-job)
|
|
53
|
+
- [Modules](#modules)
|
|
54
|
+
- [Extend the context of actions and modules](#extend-the-context-of-actions-and-modules)
|
|
38
55
|
|
|
39
|
-
##
|
|
56
|
+
## Get the result of `emit`
|
|
40
57
|
|
|
41
|
-
|
|
58
|
+
When you want an answer from a handler at the `emit` call, give `createAction` the handler. A handler is a function `(payload, ctx) => result` that runs when the action is emitted. `emit` runs it and returns its result. There is one creator for an action with and without a handler; the handler is the only difference.
|
|
42
59
|
|
|
43
|
-
The bus lives in one file and its
|
|
60
|
+
The bus lives in one file and its actions in another, so any module can import both without a cycle.
|
|
44
61
|
|
|
45
62
|
```js
|
|
46
63
|
// bus.js
|
|
@@ -65,7 +82,7 @@ import { double } from "./math";
|
|
|
65
82
|
bus.emit(double(21)); // 42
|
|
66
83
|
```
|
|
67
84
|
|
|
68
|
-
##
|
|
85
|
+
## Actions, handlers and jobs
|
|
69
86
|
|
|
70
87
|
`nanoeffects/actions` has the pieces to name what a bus does, apart from the code that does it.
|
|
71
88
|
|
|
@@ -100,7 +117,7 @@ These are the events of the user domain, in a file of their own (`user.js`). The
|
|
|
100
117
|
- `userLoaded.match(action)` — narrows an action
|
|
101
118
|
- `createAction(type, handler)` — a function as the second argument is the handler; `bus.emit(action(payload))` tells the listeners, runs the handler as a task and returns its result
|
|
102
119
|
- `createAction(type, { prepare })` — build the payload from several arguments
|
|
103
|
-
- `createAction(type, { handle, prepare, ...scope })` — everything in one object: `handle` and `prepare` are taken out, and every other field is the [scope](
|
|
120
|
+
- `createAction(type, { handle, prepare, ...scope })` — everything in one object: `handle` and `prepare` are taken out, and every other field is the [scope](context.md#scope-fields-for-an-action-a-module-and-a-call)
|
|
104
121
|
- Works as the event of its type: `bus.emit(userLoaded(user))`, `bus.on(userLoaded, fn)`
|
|
105
122
|
|
|
106
123
|
A scope goes after a handler as the last argument, `createAction(type, handler, scope)`; `prepare` with a handler goes in that last object: `createAction(type, handler, { prepare, ...scope })`.
|
|
@@ -150,7 +167,7 @@ A handler bound in the registry makes `bus.emit(sendMail(mail))` run it and retu
|
|
|
150
167
|
|
|
151
168
|
The registry lives in `nanoeffects/actions`, so a bus without one carries none of that code. Without a registry, `emit` runs the handler that the action carries. `bus.use(actionsPlugin())` sets `bus.actions`. A second `actionsPlugin` binds its list in that registry and returns it. There is no `actions` option in `createBus`; assign a custom runner with `bus.actions = { run }`, and add `bound(type)` to it to answer for actions that have no handler of their own.
|
|
152
169
|
|
|
153
|
-
###
|
|
170
|
+
### The result type of a bound handler
|
|
154
171
|
|
|
155
172
|
TypeScript cannot see which handler the registry has, so the type of `emit` for an action without a handler is `void`. Tell it what the bound handler returns with `Call<Payload, Result>`. This is a promise of yours: nothing checks it against the handler.
|
|
156
173
|
|
|
@@ -194,7 +211,7 @@ On a bus without an event map you can say it where you call: `const id = bus.emi
|
|
|
194
211
|
- Calling the job only builds `{ type, payload }`; the handler runs on `emit`.
|
|
195
212
|
- The outcomes are events like any other: with `onUnhandled` set on the bus, it is called for a `pending` or a `cancel` that nobody listens to.
|
|
196
213
|
|
|
197
|
-
##
|
|
214
|
+
## The state of a job
|
|
198
215
|
|
|
199
216
|
**Problem.** A spinner needs a flag that is `true` while a request runs. Setting it by hand in the handler (`loading = true`, then `try` / `finally`) breaks when the task is aborted: `ctx.emit` does nothing after an abort, so a flag that is cleared through the bus stays `true`.
|
|
200
217
|
|
|
@@ -267,7 +284,7 @@ bus.emit(loadUser("1"));
|
|
|
267
284
|
|
|
268
285
|
A flag is exact when calls do not overlap, or when `latest` replaces them. If several runs can go at once (`queue` with `concurrency` above 1, or a job without a policy called many times), keep a counter: add one on `pending` and take one off on `success`, `fail` and `cancel`. A policy that you write yourself, or that you hide in your own function, does not report to the job; `pending` is then sent at the call, as for a job with no policy.
|
|
269
286
|
|
|
270
|
-
##
|
|
287
|
+
## Modules
|
|
271
288
|
|
|
272
289
|
**Problem.** A domain needs several subscriptions and handler bindings. With plain `bus.on` / `actions.handle` you must remember every unsubscribe.
|
|
273
290
|
|
|
@@ -360,9 +377,9 @@ If the `setup` of one module throws, the modules of that call that were already
|
|
|
360
377
|
- `spawn` — background task, aborted on unmount.
|
|
361
378
|
- An action that carries a handler keeps answering after unmount (`use` only tracks registration). Use `handle` for a handler that must live only as long as the module.
|
|
362
379
|
|
|
363
|
-
##
|
|
380
|
+
## Extend the context of actions and modules
|
|
364
381
|
|
|
365
|
-
An action with a handler can need its own client, a module its own logger. Pass a scope, a plain object whose keys go on `ctx` (`$effects` binds functions to the task), as the last argument of `createAction`, `
|
|
382
|
+
An action with a handler can need its own client, a module its own logger. Pass a scope, a plain object whose keys go on `ctx` (`$effects` binds functions to the task), as the last argument of `createAction`, `createModule` and `createJob`, and turn it on with `scopePlugin`:
|
|
366
383
|
|
|
367
384
|
```js
|
|
368
385
|
// bus.js
|
|
@@ -394,4 +411,4 @@ const fakeApi = { getUser: async (id) => ({ id, name: "test" }) };
|
|
|
394
411
|
bus.emit(getUser("1"), { api: fakeApi });
|
|
395
412
|
```
|
|
396
413
|
|
|
397
|
-
The handler gets the fields typed. Without `scopePlugin` the scope is ignored. The full picture, with jobs, actions, modules and which value wins, is in [scopes](
|
|
414
|
+
The handler gets the fields typed. Without `scopePlugin` the scope is ignored. The full picture, with jobs, actions, modules and which value wins, is in [scopes](context.md#scope-fields-for-an-action-a-module-and-a-call).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Cancel stale requests and fix race conditions
|
|
2
2
|
|
|
3
|
+
> **Level B.** Read first: the bus from the [README](../README.md).
|
|
4
|
+
|
|
3
5
|
Read this page if an older response can overwrite a newer one: a search box that shows the results of the previous query, a filter that flickers, a form that submits twice. It shows the cause, the fix with `latest`, and how to add a pause, a retry and a timeout. It needs only the bus from the [README](../README.md).
|
|
4
6
|
|
|
5
7
|
## See the problem
|
package/docs/chat-example.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Build a chat with entities, features, modules and a small server
|
|
2
2
|
|
|
3
|
+
> **Level D.** Read first: [Actions, jobs and modules](actions.md).
|
|
4
|
+
|
|
3
5
|
Use this page when your app has several features that react to each other and you want to see where each piece of logic goes. The example is a chat with rooms, a history that loads, messages that can fail to send, a search across all rooms, and new messages that arrive on their own. A small Fastify server in the example answers every request, and it misbehaves on a known schedule, so each problem on this page really happens.
|
|
4
6
|
|
|
5
7
|
It is a level D example. Start with [Actions, jobs and modules](actions.md) for the API, or with the `domains-ts` template in [Start a project from a template](starter.md#start-with-domains) for a smaller project.
|
|
@@ -88,7 +90,7 @@ import { latest } from "nanoeffects/policies";
|
|
|
88
90
|
export const fetchHistory = latest((roomId, { signal }) => api.loadHistory(roomId, { signal }));
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
The module gives the job its work and keeps the state from the outcomes, so an aborted load clears the flag too (see [Show a loading state with a job](actions.md#
|
|
93
|
+
The module gives the job its work and keeps the state from the outcomes, so an aborted load clears the flag too (see [Show a loading state with a job](actions.md#the-state-of-a-job)):
|
|
92
94
|
|
|
93
95
|
```js
|
|
94
96
|
// message-module.js
|