nanoeffects 0.4.0 → 0.6.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 +4 -1
- package/dist/{action-creator-D48fp13T.d.ts → action-creator-c292nK7v.d.ts} +130 -34
- package/dist/actions.d.ts +54 -5
- package/dist/actions.js +1 -1
- package/dist/{bus-Cho463Sq.js → bus-BAs9x7IQ.js} +1 -1
- package/dist/{context-DlA80GOA.js → context-CK0ZomWt.js} +1 -1
- package/dist/context-plugin-1pYmSQEG.d.ts +34 -0
- package/dist/dev.d.ts +2 -2
- package/dist/effects.d.ts +27 -129
- package/dist/effects.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/{module-DVX8aYlq.d.ts → module-BlF2KSgy.d.ts} +34 -5
- package/dist/notrace/{action-creator-D48fp13T.d.ts → action-creator-DKG_sIia.d.ts} +130 -34
- package/dist/notrace/actions.d.ts +96 -7
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/{bus-B12SvxLv.js → bus-Bdk0YyFQ.js} +1 -1
- package/dist/notrace/{context-yAWj0BFZ.js → context-CctVmHLC.js} +1 -1
- package/dist/notrace/context-plugin-Dt1IgVo8.d.ts +34 -0
- package/dist/notrace/{delay-Cgvn16oY.js → delay-B_W1tnkS.js} +1 -1
- package/dist/notrace/effects.d.ts +27 -129
- package/dist/notrace/effects.js +1 -1
- package/dist/notrace/index.d.ts +3 -2
- package/dist/notrace/index.js +1 -1
- package/dist/notrace/plugin-B0AXKusc.d.ts +207 -0
- package/dist/notrace/policies.d.ts +1 -1
- package/dist/notrace/policies.js +1 -1
- package/dist/notrace/redux.d.ts +18 -20
- package/dist/notrace/redux.js +1 -1
- package/dist/observable.d.ts +1 -1
- package/dist/plugin-CCYlC0XS.d.ts +207 -0
- package/dist/policies.d.ts +1 -1
- package/dist/policies.js +1 -1
- package/dist/react.d.ts +50 -0
- package/dist/react.js +1 -0
- package/dist/redux.d.ts +18 -20
- package/dist/redux.js +1 -1
- package/dist/scope-key-B7l7mUS5.js +1 -0
- package/dist/scope.d.ts +58 -0
- package/dist/scope.js +1 -0
- package/dist/trace.d.ts +1 -1
- package/dist/types-mLaAx-EZ.d.ts +15 -0
- package/docs/README.md +7 -1
- package/docs/actions.md +25 -1
- package/docs/context.md +26 -2
- package/docs/effects.md +83 -5
- package/docs/guide.md +24 -0
- package/docs/migrating-from-effector.md +293 -0
- package/docs/migrating-from-rxjs.md +127 -0
- package/docs/migrating-from-saga.md +93 -0
- package/docs/observable.md +1 -0
- package/docs/package.md +13 -2
- package/docs/policies.md +1 -0
- package/docs/react.md +157 -0
- package/docs/scope.md +226 -0
- package/docs/stores.md +429 -40
- package/docs/typescript.md +153 -0
- package/llms-full.txt +2925 -0
- package/llms.txt +49 -0
- package/package.json +44 -6
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ npm install nanoeffects
|
|
|
15
15
|
|
|
16
16
|
ESM only, no dependencies, types included.
|
|
17
17
|
|
|
18
|
+
Want to try it first? [Open the playground](https://stackblitz.com/github/nanoeffects/nanoeffects/tree/main/examples/playground): a search box with `debounce` and `latest`, running in your browser, no install. More playgrounds (React) and recipes: [Examples](examples/README.md).
|
|
19
|
+
|
|
18
20
|
## Create a bus
|
|
19
21
|
|
|
20
22
|
Subscribe with `on`, send with `emit`. `on` returns a function that unsubscribes.
|
|
@@ -48,6 +50,7 @@ const bus = createBus();
|
|
|
48
50
|
bus.on(
|
|
49
51
|
"search",
|
|
50
52
|
latest(async (query, ctx) => {
|
|
53
|
+
// signal — lets latest cancel this request, not only drop its result
|
|
51
54
|
const items = await api.search(query, { signal: ctx.signal });
|
|
52
55
|
ctx.emit("results", items);
|
|
53
56
|
}),
|
|
@@ -85,7 +88,7 @@ Start with the [Guide](docs/guide.md) (bus → policies → effects → modules)
|
|
|
85
88
|
| Domain logic off the UI | [Actions, commands, modules](docs/actions.md) |
|
|
86
89
|
| Redux / Zustand / MobX | [Stores](docs/stores.md) |
|
|
87
90
|
|
|
88
|
-
[Full index, package entries, tracing](docs/README.md) · [Examples](examples)
|
|
91
|
+
[Full index, package entries, tracing](docs/README.md) · [Examples](examples/README.md)
|
|
89
92
|
|
|
90
93
|
- **Tiny.** `createBus` is about 1.3 kB gzip; other entries load only when imported.
|
|
91
94
|
- **Cancellation first.** Aborting a task aborts its children and drops its result.
|
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
//#region src/core/types.d.ts
|
|
2
|
+
type PayloadOf<P> = undefined extends P ? [payload?: P] : [payload: P];
|
|
3
|
+
type EventMap = Record<string, unknown>;
|
|
4
|
+
type Keys<E> = keyof E & string;
|
|
5
|
+
type Task<P, E extends EventMap, R = unknown> = (payload: P, ctx: Context<E>) => R;
|
|
6
|
+
type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command";
|
|
7
|
+
type Phase = "start" | "done" | "error" | "cancel" | "drop";
|
|
8
|
+
type TraceEvent<E = Record<string, unknown>> = { [K in Keys<E>]: {
|
|
9
|
+
id: number;
|
|
10
|
+
parent?: number;
|
|
11
|
+
type: K;
|
|
12
|
+
effect: EffectName;
|
|
13
|
+
phase: Phase;
|
|
14
|
+
payload: E[K];
|
|
15
|
+
error?: unknown;
|
|
16
|
+
}; }[Keys<E>];
|
|
17
|
+
/**
|
|
18
|
+
* The contract of every trace logger: receives each task phase of a bus.
|
|
19
|
+
* Adapters in `nanoeffects/trace` are factories that return one; pass it
|
|
20
|
+
* through `createTracer` to `createBus({ trace })`.
|
|
21
|
+
*/
|
|
22
|
+
type TraceLogger<E = Record<string, unknown>> = (event: TraceEvent<E>) => void;
|
|
23
|
+
/** One logger or several; a throwing logger never breaks the task or the others. */
|
|
24
|
+
type Trace<E = Record<string, unknown>> = TraceLogger<E> | readonly TraceLogger<E>[];
|
|
25
|
+
type UnsubscribeCallback = () => void;
|
|
26
|
+
//#endregion
|
|
1
27
|
//#region src/core/store.d.ts
|
|
2
28
|
declare const anyEventType = "*";
|
|
3
29
|
//#endregion
|
|
@@ -83,14 +109,21 @@ type Emit<E extends EventMap> = {
|
|
|
83
109
|
<K extends EventKey<E>>(event: K, ...payload: PayloadOf<E[K]>): void;
|
|
84
110
|
/** Before Handler: a command is callable too, and must return its result type. */
|
|
85
111
|
<P, R>(command: Command<P, R>, ...payload: PayloadOf<P>): R;
|
|
86
|
-
|
|
112
|
+
/** A scope as the second argument replaces context fields for the tasks of this emit; see `scopePlugin`. */
|
|
113
|
+
<P, R, F>(call: CommandCall<P, R> & WithScope<F>, scope?: EmitScope<E, F>): R;
|
|
114
|
+
<P, R>(call: CommandCall<P, R>, scope?: EmitScope<E>): R;
|
|
87
115
|
<K extends EventKey<E>, R>(event: Handler<E, R, K>, ...payload: PayloadOf<E[K]>): R;
|
|
88
|
-
(event: EventLike<E>): void;
|
|
116
|
+
<F>(event: EventLike<E> & WithScope<F>, scope?: EmitScope<E, F>): void;
|
|
117
|
+
(event: EventLike<E>, scope?: EmitScope<E>): void;
|
|
89
118
|
};
|
|
90
119
|
type On<E extends EventMap> = {
|
|
91
120
|
(event: AnyEvent, fn: Task<EventPayload<E>, E>): UnsubscribeCallback;
|
|
92
121
|
<K extends EventKey<E>>(event: K, fn: Task<E[K], E>): UnsubscribeCallback;
|
|
93
122
|
/** An action creator without a payload type: the payload comes from the event map. */
|
|
123
|
+
/** The creators and commands that carry a scope come first: the handler sees their fields. */
|
|
124
|
+
<K extends EventKey<E>, F>(creator: FreeActionCreator<K, F> & WithScope<F>, fn: Task<E[K], WithFields<E, F>>): UnsubscribeCallback;
|
|
125
|
+
<P, F>(creator: ActionCreator<P, string, PayloadOf<P>, F> & WithScope<F>, fn: Task<P, WithFields<E, F>>): UnsubscribeCallback;
|
|
126
|
+
<P, F>(command: Command<P, unknown> & WithScope<F>, fn: Task<P, WithFields<E, F>>): UnsubscribeCallback;
|
|
94
127
|
<K extends EventKey<E>>(creator: FreeActionCreator<K>, fn: Task<E[K], E>): UnsubscribeCallback;
|
|
95
128
|
/** An action creator: the payload comes from it, so an event map is not needed. */
|
|
96
129
|
<P>(creator: ActionCreator<P>, fn: Task<P, E>): UnsubscribeCallback;
|
|
@@ -157,6 +190,18 @@ type Context<E extends EventMap> = EventLike<E> & ContextOf<E> & {
|
|
|
157
190
|
/** Child task: aborted with the parent; errors propagate to the parent. */
|
|
158
191
|
run<P, R>(fn: Task<P, E, R>, ...args: PayloadOf<P>): Promise<Awaited<R>>;
|
|
159
192
|
};
|
|
193
|
+
/**
|
|
194
|
+
* The event map of a bus, with the context fields that plugins added. Use it to
|
|
195
|
+
* name the types of a bus in other files: `type AppEvents = EventsOf<typeof bus>`.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const bus = createBus<Events>().use(effectsPlugin({ race }));
|
|
200
|
+
* type AppEvents = EventsOf<typeof bus>;
|
|
201
|
+
* type AppContext = Context<AppEvents>; // has `race`
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
type EventsOf<B> = B extends Bus<infer E extends EventMap> ? E : never;
|
|
160
205
|
interface Bus<E extends EventMap> {
|
|
161
206
|
on: On<E>;
|
|
162
207
|
/**
|
|
@@ -173,10 +218,16 @@ interface Bus<E extends EventMap> {
|
|
|
173
218
|
*/
|
|
174
219
|
use<R>(plugin: (bus: Bus<E>) => R): R;
|
|
175
220
|
/** `BusOptions.context` and `.trace`, and `commands`, for what makes tasks of its own, like `modulePlugin`. */
|
|
176
|
-
readonly
|
|
221
|
+
readonly ctx?: ContextOption<E> | undefined;
|
|
177
222
|
/** What runs the commands; `commandsPlugin` and `modulePlugin` set it. Without one a command runs its own handler. */
|
|
178
223
|
commands?: Commands | undefined;
|
|
179
224
|
readonly trace?: Tracer | undefined;
|
|
225
|
+
/** Called with every new task context to add fields to it, for what needs the task, like `effectsPlugin`. Chain to the previous one. */
|
|
226
|
+
addContext?: ((ctx: object) => void) | undefined;
|
|
227
|
+
/** Puts the fields of a module's scope on the context of one of its tasks; `scopePlugin` sets it. Without it a module's scope is ignored. */
|
|
228
|
+
applyScope?: ((ctx: object, scope: Scope) => void) | undefined;
|
|
229
|
+
/** Wraps a handler so it runs with the fields of a module's scope; `scopePlugin` sets it. */
|
|
230
|
+
fitScope?: ((scope: Scope, fn: (payload: never, ctx: never) => unknown) => unknown) | undefined;
|
|
180
231
|
}
|
|
181
232
|
type BusOptions<E extends EventMap> = {
|
|
182
233
|
/** Tracing of every task phase; make it with `createTracer` from `nanoeffects/trace`. */
|
|
@@ -214,31 +265,76 @@ type BusOptions<E extends EventMap> = {
|
|
|
214
265
|
};
|
|
215
266
|
declare const createBus: <Events extends EventMap>(options?: BusOptions<Events>) => Bus<Events>;
|
|
216
267
|
//#endregion
|
|
217
|
-
//#region src/core/types.d.ts
|
|
218
|
-
|
|
219
|
-
type
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
type
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
type: K;
|
|
228
|
-
effect: EffectName;
|
|
229
|
-
phase: Phase;
|
|
230
|
-
payload: E[K];
|
|
231
|
-
error?: unknown;
|
|
232
|
-
}; }[Keys<E>];
|
|
268
|
+
//#region src/core/effect-types.d.ts
|
|
269
|
+
/** What an effect needs of the task it runs for: its signal. A task context fits, and so does `{ signal }`. */
|
|
270
|
+
type Signaled = {
|
|
271
|
+
signal: AbortSignal;
|
|
272
|
+
};
|
|
273
|
+
/** An effect: a function whose first parameter takes `{ signal }`, like `retry(ctx, fn)`. */
|
|
274
|
+
type SignalFirst = (ctx: Signaled, ...args: never[]) => unknown;
|
|
275
|
+
/** The effects of a scope or of `effectsPlugin`, by the name they get on the context. */
|
|
276
|
+
type Effects = Record<string, SignalFirst>;
|
|
277
|
+
declare const bound: unique symbol;
|
|
233
278
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
279
|
+
* An effect that says what it looks like once it is bound to a task (without
|
|
280
|
+
* the `ctx` parameter). A plain effect has its first parameter removed, which
|
|
281
|
+
* erases its generics; this type keeps them, because `F` is carried as it is.
|
|
282
|
+
* It changes the checker only: the function is the same at run time.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* type First = <T>(items: T[]) => T; // how a handler calls it
|
|
287
|
+
* const first: Effect<First> = (ctx, items) => items[0];
|
|
288
|
+
* ```
|
|
237
289
|
*/
|
|
238
|
-
type
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
290
|
+
type Effect<F extends (...args: never[]) => unknown> = ((ctx: Signaled, ...args: Parameters<F>) => ReturnType<F>) & {
|
|
291
|
+
readonly [bound]?: F;
|
|
292
|
+
};
|
|
293
|
+
/** The bound form of a plain effect: the first parameter is gone, and so are its generics. */
|
|
294
|
+
type Strip<F> = F extends ((ctx: never, ...args: infer A) => infer R) ? (...args: A) => R : never;
|
|
295
|
+
/** The bound type an `Effect<F>` carries, or `Strip` of a plain function. */
|
|
296
|
+
type BoundForm<F> = typeof bound extends keyof F ? NonNullable<F[typeof bound & keyof F]> : Strip<F>;
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/core/scope-types.d.ts
|
|
299
|
+
declare const scopeFields: unique symbol;
|
|
300
|
+
/** What an action creator, a command or a call knows of the scope it was made with: its fields, for the checker. */
|
|
301
|
+
type WithScope<F> = {
|
|
302
|
+
readonly [scopeFields]: F;
|
|
303
|
+
};
|
|
304
|
+
/** `X`, marked with the scope fields `F` it carries; `X` itself when there are none, so unscoped types stay as they were. */
|
|
305
|
+
type Carrying<X, F> = X & ([unknown] extends [F] ? unknown : WithScope<F>);
|
|
306
|
+
/**
|
|
307
|
+
* What a task gets on top of the context of its bus, for one place: every key
|
|
308
|
+
* is a context value and goes on `ctx` as it is, except `effects`, whose
|
|
309
|
+
* functions (each takes `{ signal }` first) are bound to the task. Made work by
|
|
310
|
+
* `scopePlugin` from `nanoeffects/scope`; without it a scope is ignored.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* const scope = { cache, effects: { race } }; // ctx.cache, and ctx.race bound to the task
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
type Scope = {
|
|
318
|
+
readonly effects?: Effects;
|
|
319
|
+
readonly [field: string]: unknown;
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* What a call may replace of what it already has: the fields of the bus and
|
|
323
|
+
* those of its own scope, and its effects. The types must match the ones they
|
|
324
|
+
* replace; a field that neither declares has no type to check against, so it is
|
|
325
|
+
* an error.
|
|
326
|
+
*/
|
|
327
|
+
type EmitScope<E extends EventMap, F = unknown> = ([unknown] extends [ContextOf<E> & F] ? unknown : Partial<ContextOf<E> & F>) & {
|
|
328
|
+
effects?: Partial<Effects>;
|
|
329
|
+
};
|
|
330
|
+
/** `A` with the fields of `B` replacing those of the same name, not intersected with them. */
|
|
331
|
+
type Merge<A, B> = Omit<A, keyof B> & B;
|
|
332
|
+
/**
|
|
333
|
+
* The event map `E` with the fields `F` of a scope on top of its context
|
|
334
|
+
* fields; `E` itself when there are none. A field of the scope replaces the
|
|
335
|
+
* one of the bus in the type too.
|
|
336
|
+
*/
|
|
337
|
+
type WithFields<E extends EventMap, F> = [unknown] extends [F] ? E : Omit<E, keyof WithContext<object>> & WithContext<Merge<ContextOf<E>, F>>;
|
|
242
338
|
//#endregion
|
|
243
339
|
//#region src/core/action-creator.d.ts
|
|
244
340
|
type WithPayload<E extends EventMap, K extends Keys<E>> = undefined extends E[K] ? {
|
|
@@ -257,15 +353,15 @@ type Free = typeof free;
|
|
|
257
353
|
* it is given, and the bus checks it against its event map, so
|
|
258
354
|
* `createAction("user/loaded")` needs no generics and keeps its literal type.
|
|
259
355
|
*/
|
|
260
|
-
interface FreeActionCreator<T extends string> {
|
|
261
|
-
(): {
|
|
356
|
+
interface FreeActionCreator<T extends string, F = unknown> {
|
|
357
|
+
(): Carrying<{
|
|
262
358
|
type: T;
|
|
263
359
|
payload: undefined;
|
|
264
|
-
}
|
|
265
|
-
<P>(payload: P): {
|
|
360
|
+
}, F>;
|
|
361
|
+
<P>(payload: P): Carrying<{
|
|
266
362
|
type: T;
|
|
267
363
|
payload: P;
|
|
268
|
-
}
|
|
364
|
+
}, F>;
|
|
269
365
|
readonly type: T;
|
|
270
366
|
/** Tells whether an action is one of this creator. */
|
|
271
367
|
match(action: {
|
|
@@ -281,8 +377,8 @@ interface FreeActionCreator<T extends string> {
|
|
|
281
377
|
* Makes actions of one type. It also works as the event of that type:
|
|
282
378
|
* `bus.on(creator, fn)` and `bus.emit(creator(payload))`.
|
|
283
379
|
*/
|
|
284
|
-
interface ActionCreator<P = void, T extends string = string, A extends unknown[] = PayloadOf<P
|
|
285
|
-
(...args: A): Made<P, T>;
|
|
380
|
+
interface ActionCreator<P = void, T extends string = string, A extends unknown[] = PayloadOf<P>, F = unknown> {
|
|
381
|
+
(...args: A): Carrying<Made<P, T>, F>;
|
|
286
382
|
readonly type: T;
|
|
287
383
|
/** Tells whether an action is one of this creator, and narrows its type. */
|
|
288
384
|
match(action: {
|
|
@@ -292,4 +388,4 @@ interface ActionCreator<P = void, T extends string = string, A extends unknown[]
|
|
|
292
388
|
toString(): T;
|
|
293
389
|
}
|
|
294
390
|
//#endregion
|
|
295
|
-
export {
|
|
391
|
+
export { HandlerRef as A, EventMap as B, Emit as C, EventPayload as D, EventLike as E, commandCall as F, Trace as G, PayloadOf as H, createBus as I, UnsubscribeCallback as J, TraceEvent as K, Tracer as L, On as M, WithContext as N, EventsOf as O, WithType as P, anyEventType as R, ContextOption as S, EventKey as T, Phase as U, Keys as V, Task as W, Command as _, EmitScope as a, Context as b, WithScope as c, Effects as d, Signaled as f, BusOptions as g, Bus as h, WithPayload as i, Off as j, Handler as k, BoundForm as l, AnyEvent as m, Free as n, Scope as o, Action as p, TraceLogger as q, FreeActionCreator as r, WithFields as s, ActionCreator as t, Effect as u, CommandCall as v, Emittable as w, ContextOf as x, Commands as y, EffectName as z };
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { n as Free, o as
|
|
2
|
-
import {
|
|
1
|
+
import { B as EventMap, H as PayloadOf, T as EventKey, W as Task, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator } from "./action-creator-c292nK7v.js";
|
|
2
|
+
import { n as Scoped, t as FieldsOfScope } from "./types-mLaAx-EZ.js";
|
|
3
|
+
import { a as modulePlugin, c as MadeCommand, d as createCommand, i as createModule, l as bindHandler, n as ModuleApi, o as Binding, r as ModuleContext, s as CarryingCalls, t as Module, u as commandsPlugin } from "./module-BlF2KSgy.js";
|
|
3
4
|
//#region src/actions/action.d.ts
|
|
5
|
+
type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCreator<T, F> & WithScope<F> : ActionCreator<P, T, PayloadOf<P>, F> & WithScope<F>;
|
|
4
6
|
/**
|
|
5
7
|
* Creates an action creator, as in Redux Toolkit.
|
|
6
8
|
*
|
|
@@ -13,6 +15,9 @@ import { a as modulePlugin, c as bindHandler, i as createModule, l as commandsPl
|
|
|
13
15
|
* generic: `createAction<User, "user/loaded">("user/loaded")`.
|
|
14
16
|
* - With `prepare` the arguments of the creator can differ from the payload,
|
|
15
17
|
* and both types are inferred.
|
|
18
|
+
* - A scope as the last argument, `{ cache, effects: { race } }`, gives the
|
|
19
|
+
* listeners of its calls those context fields; it works on a bus that uses `scopePlugin`
|
|
20
|
+
* from `nanoeffects/scope` and is ignored on any other.
|
|
16
21
|
*
|
|
17
22
|
* @example
|
|
18
23
|
* ```ts
|
|
@@ -33,6 +38,45 @@ export declare function createAction<P = Free, T extends string = string>(type:
|
|
|
33
38
|
export declare function createAction<A extends unknown[], P, T extends string = string>(type: T, prepare: (...args: A) => {
|
|
34
39
|
payload: P;
|
|
35
40
|
}): ActionCreator<P, T, A>;
|
|
41
|
+
export declare function createAction<P = Free, T extends string = string, S extends Scope = Record<never, never>>(type: T, scope: S): ScopedCreator<P, T, FieldsOfScope<EventMap, S>>;
|
|
42
|
+
export declare function createAction<A extends unknown[], P, T extends string, S extends Scope>(type: T, prepare: (...args: A) => {
|
|
43
|
+
payload: P;
|
|
44
|
+
}, scope: S): ActionCreator<P, T, A, FieldsOfScope<EventMap, S>> & WithScope<FieldsOfScope<EventMap, S>>;
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/actions/emitter.d.ts
|
|
47
|
+
/** What `emitterPlugin` adds to the bus. */
|
|
48
|
+
type EmitterApi<E extends EventMap> = {
|
|
49
|
+
/**
|
|
50
|
+
* Turns an action creator into a function that emits its action on this
|
|
51
|
+
* bus, to pass as a callback. The function takes what the creator takes.
|
|
52
|
+
*/
|
|
53
|
+
emitter: {
|
|
54
|
+
/** A creator without a payload type: the payload comes from the event map. */
|
|
55
|
+
<K extends EventKey<E>>(creator: FreeActionCreator<K>): (...payload: PayloadOf<E[K]>) => void;
|
|
56
|
+
<P, T extends string, A extends unknown[]>(creator: ActionCreator<P, T, A>): (...args: A) => void;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Adds `bus.emitter(creator)`: a function that emits the action of `creator`
|
|
61
|
+
* on this bus. Use it where something wants a callback, such as the
|
|
62
|
+
* subscription of a state manager, instead of `(value) => bus.emit(creator(value))`.
|
|
63
|
+
* The creator stays a plain `createAction`, so `bus.emit(creator(value))` and
|
|
64
|
+
* `bus.on(creator, …)` work as before. Returns the same bus.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { createBus } from "nanoeffects";
|
|
69
|
+
* import { createAction, emitterPlugin } from "nanoeffects/actions";
|
|
70
|
+
*
|
|
71
|
+
* const bus = createBus().use(emitterPlugin());
|
|
72
|
+
* const nameChanged = createAction<string>("name/changed");
|
|
73
|
+
* const emitNameChanged = bus.emitter(nameChanged);
|
|
74
|
+
*
|
|
75
|
+
* // nanostores: the store calls it with the new value
|
|
76
|
+
* $name.listen(emitNameChanged);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare const emitterPlugin: () => <E extends EventMap>(bus: Bus<E>) => Bus<E> & EmitterApi<E>;
|
|
36
80
|
//#endregion
|
|
37
81
|
//#region src/actions/job.d.ts
|
|
38
82
|
type Creator = {
|
|
@@ -58,6 +102,9 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCom
|
|
|
58
102
|
* effect. It returns the command, with the two actions on it: `job.success` and
|
|
59
103
|
* `job.fail`.
|
|
60
104
|
*
|
|
105
|
+
* A scope as the last argument, `{ cache, effects: { race } }`, gives the handler
|
|
106
|
+
* and the listeners of the outcome those context fields; see `scopePlugin`.
|
|
107
|
+
*
|
|
61
108
|
* `success` and `fail` are a type string, from which the job makes an action
|
|
62
109
|
* creator with `createAction`, or an action creator that exists already, which
|
|
63
110
|
* is used and returned as it is. A missing one is made with the type
|
|
@@ -82,11 +129,13 @@ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = MadeCom
|
|
|
82
129
|
* bus.emit(loadUser("1")); // loadUser.success(user) or loadUser.fail(error) comes later
|
|
83
130
|
* ```
|
|
84
131
|
*/
|
|
85
|
-
export declare const createJob: <P = void, R = void, T extends string = string, E extends EventMap = EventMap, S extends Outcome = `${T}/success`, F extends Outcome = `${T}/fail
|
|
132
|
+
export declare const createJob: <P = void, R = void, T extends string = string, E extends EventMap = EventMap, S extends Outcome = `${T}/success`, F extends Outcome = `${T}/fail`, X extends Scope = Record<never, never>>(type: T, handler?: Task<P, Scoped<E, X>, R>, outcomes?: {
|
|
86
133
|
/** Gets the result when the handler succeeds: a type to make an action creator of, or an action creator. */
|
|
87
134
|
success?: S;
|
|
88
135
|
/** Gets the error when the handler fails: a type to make an action creator of, or an action creator. */
|
|
89
136
|
fail?: F;
|
|
90
|
-
}
|
|
137
|
+
},
|
|
138
|
+
/** Context values and `effects` for the handler and for the listeners of the outcome; see `scopePlugin`. */
|
|
139
|
+
scope?: X) => CarryingCalls<Job<P, R, T, S, F>, P, Promise<Awaited<R> | undefined>, FieldsOfScope<E, X>>;
|
|
91
140
|
//#endregion
|
|
92
|
-
export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, commandsPlugin, createCommand, createModule, modulePlugin };
|
|
141
|
+
export { type ActionCreator, type Binding, type EmitterApi, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, commandsPlugin, createCommand, createModule, modulePlugin };
|
package/dist/actions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-CK0ZomWt.js";import{t as r}from"./event-0RIBGYqS.js";import{r as i}from"./bus-BAs9x7IQ.js";import{t as a}from"./scope-key-B7l7mUS5.js";const o=(e,t)=>(t&&Object.defineProperty(e,a,{value:t}),e);function s(e,t,n){let r=typeof t==`function`?t:void 0,i=typeof t==`function`?n:t;return Object.assign(o((...t)=>o({type:e,payload:r?r(...t).payload:t[0]},i),i),{type:e,match:t=>t.type===e,toString:()=>e})}var c=class extends Error{};function l(e,t,n){let r=t=>t.type===e;function a(r,a){if(typeof a==`object`){if(!t)throw new c(`No handler for command "${e}"`);return t(r,a)}return o(i(l,e,r),n)}let s=Object.assign(a,{type:e,match:r}),l=o(s,n);return l}const u=(e,t)=>[e,t],d=(e=[])=>t=>{let n=/* @__PURE__ */ new Map,i=(e,t)=>{let r={fn:t};return n.set(e,[...n.get(e)??[],r]),()=>{let t=(n.get(e)??[]).filter(e=>e!==r);t.length>0?n.set(e,t):n.delete(e)}};function a(e,t){if(t)return i(r(e),t);let n=e.map(([e,t])=>i(r(e),t));return()=>{for(let e of n)e()}}a(e);let o={run:(e,t,r)=>{let i=(n.get(e.type)??[]).at(-1)?.fn,a=i&&e.wrap?e.wrap(i):i??e;return Reflect.apply(a,null,[t,r])},handle:a};return t.commands=o,o},f=()=>e=>{function t(t){return(...n)=>{let r=t(...n);e.emit(r)}}return Object.assign(e,{emitter:t})},p=e=>typeof e==`string`?s(e):e,m=(e,t,n,r)=>{let i=e.emit;i(Reflect.apply(t,null,[n]),r)},h=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof c)throw e;return Promise.reject(e)}},g=(e,t,n={},r)=>{let a=l(e,t),s=p(n.success??`${e}/success`),c=p(n.fail??`${e}/fail`),u=(e,t,n)=>{let i=n;return h(()=>Reflect.apply(e,null,[t,n])).then(e=>(m(i,s,e,r),e),e=>{m(i,c,e,r)})},d=e=>(t,n)=>u(e,t,n);function f(t,n){return typeof n==`object`?u(a,t,n):o(i(_,e,t),r)}let g=Object.assign(f,{type:e,match:a.match,wrap:d,success:s,fail:c}),_=o(g,r);return _};function _(e,t,n){return n?{name:e,setup:t,scope:n}:{name:e,setup:t}}const v=(e,t)=>n=>t&&typeof n==`function`?e.fitScope?.(t,n)??n:n,y=(e,t,n)=>{n&&e.applyScope?.(t,n)},b=(e,t,n)=>Array.isArray(t)?[t.map(([t,n])=>[t,e(n)]),n]:[t,e(n)],x=(e,t)=>{let n=e.on;return(...e)=>{let[r,i]=e;return Reflect.apply(n,null,[r,t(i)])}},S=e=>(t,...n)=>{e.run(t,...n).catch(t=>{if(!e.signal.aborted)throw t})},C=(r=[])=>i=>{let a=i.commands??d()(i),o={handle:(...e)=>{if(!a.handle)throw Error("modulePlugin: the `commands` of the bus is not a registry from `commandsPlugin()`");return Reflect.apply(a.handle,null,e)}},s=n({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),c=n=>{let r=/* @__PURE__ */ new Map,a=/* @__PURE__ */ new Map;return{mount:s=>{let l=r.get(s);if(l)return l;let u=a.get(s.name);u&&r.get(u)?.();let d=t(n,s.name),f=d.ctx,p=c(f),m=()=>{r.delete(s),a.get(s.name)===s&&a.delete(s.name),f.abort(),d.unlink()};r.set(s,m),a.set(s.name,s);let h=s.scope,g=v(i,h),_=(t,n)=>{let r=Reflect.apply(o.handle,null,b(g,t,n));return e(f,r),r},C=(...e)=>{let t=e.map(e=>typeof e==`function`?_(e,e):p.mount(e));return()=>{for(let e of t)e()}},w=S(f),T=x(f,g);try{y(i,f,h),s.setup(Object.assign(f,{handle:_,use:C,spawn:w,on:T}))}catch(e){throw m(),e}return m},unmount:e=>r.get(e)?.(),isMounted:e=>r.has(e)}},l=c(s.ctx);for(let e of r)l.mount(e);return{commands:a,mount:e=>(s.ctx.signal.throwIfAborted(),l.mount(e)),unmount:l.unmount,isMounted:l.isMounted,dispose:()=>s.ctx.abort(),createModule:_}};export{u as bindHandler,d as commandsPlugin,s as createAction,l as createCommand,g as createJob,_ as createModule,f as emitterPlugin,C as modulePlugin};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./onAbort-BgMfspK4.js";import{a as t,n,r}from"./context-
|
|
1
|
+
import{n as e}from"./onAbort-BgMfspK4.js";import{a as t,n,r}from"./context-CK0ZomWt.js";import{n as i}from"./promise-CSLpzwSR.js";import{n as a,t as o}from"./event-0RIBGYqS.js";const s=`*`,c=()=>{let e={},t={},n=n=>(t[n]=void 0,e[n]??=/* @__PURE__ */ new Map,e[n]),r=(e,t)=>{let r=n(e),i=r.get(t)??0;i>1?r.set(t,i-1):t?r.delete(t):r.clear()};return{get:n=>(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]),add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>{r(e,t)}},remove:r,clear:()=>{for(let t of Reflect.ownKeys(e))delete e[t];for(let e of Reflect.ownKeys(t))delete t[e]}}},l=Symbol(`nanoeffects.commandCall`),u=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,l,{value:e,enumerable:!1}),r},d=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&l in e)return e[l]},f=(e,t,n)=>Reflect.apply(e,null,[t,n]),p=(s={})=>{let l=c(),u=(e,t)=>l.add(o(e),t),p=(e,t)=>{let n=u(e,(e,r)=>(n(),t(e,r)));return n},m=(e,t)=>{e===void 0?l.clear():l.remove(o(e),t)},h=(t,a)=>{let{onError:o}=s,{ctx:c}=n({event:a,bus:g,tracer:s.trace,parent:{ref:r()}});if(!o){t(a.payload,c);return}let l=t=>{try{o(t,a)}catch(t){e(`onError failed`,t)}};try{let e=t(a.payload,c);i(e)&&Promise.resolve(e).catch(l)}catch(e){l(e)}},g={on:u,once:p,off:m,emit:(e,i)=>{let c=o(e),u=a(e,i),p={type:c,payload:u},m=d(e),_=[...l.get(c),...l.get(`*`)];_.length===0&&!m&&s.onUnhandled?.(p);for(let e of _)h(e,p);if(m){let{ctx:e}=n({event:p,bus:g,tracer:s.trace,parent:{ref:r()}}),i=g.commands?.run??f;return t(e,`command`,()=>i(m,u,e))}},use:e=>e(g),ctx:s.context,trace:s.trace};return g};export{s as i,p as n,u as r,l as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./onAbort-BgMfspK4.js";const t=t=>{let n=new AbortController;return{controller:n,unlink:e({signal:t},e=>n.abort(e))}},n=typeof __NANOEFFECTS_TRACE__>`u`||__NANOEFFECTS_TRACE__,r=/* @__PURE__ */ new WeakMap;let i;const a=()=>i,o=()=>{},s=(e,t,i,a)=>{let o=n&&r.get(e);o&&o.tracer.report(o.ref,t,i,a)},c=(e,t)=>{let i=n&&r.get(e);return i?i.tracer.settler(i.ref,t):o},l=(e,t,i)=>{let a=n&&r.get(e);return a?a.tracer.run(a.ref,t,e.signal,i):i()},u=(e,t)=>typeof e==`function`?e(t):e,d=e=>e,f=({event:a,bus:s,tracer:c,parent:p})=>{let{controller:m,unlink:h}=t(p?.signal),{signal:g}=m,_=n&&c?{tracer:c,ref:c.task(a,p?.ref)}:void 0,v=(...e)=>{if(g.aborted)return;let t=s?.emit,n=i;i=_?.ref;try{return t?.(...e)}finally{i=n}},y=(...t)=>{if(g.aborted)return o;let n=s?.on,r=n?.(...t);if(!r)return o;let i=e({signal:g},r);return()=>{i(),r()}},b=async(e,...t)=>{g.throwIfAborted();let n=t[0],r=f({event:{type:a.type,payload:n},bus:s,tracer:c,parent:{signal:g,ref:_?.ref}});try{return await l(r.ctx,`run`,()=>e(n,r.ctx))}finally{r.unlink()}},x=d({...u(s?.
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.js";const t=t=>{let n=new AbortController;return{controller:n,unlink:e({signal:t},e=>n.abort(e))}},n=typeof __NANOEFFECTS_TRACE__>`u`||__NANOEFFECTS_TRACE__,r=/* @__PURE__ */ new WeakMap;let i;const a=()=>i,o=()=>{},s=(e,t,i,a)=>{let o=n&&r.get(e);o&&o.tracer.report(o.ref,t,i,a)},c=(e,t)=>{let i=n&&r.get(e);return i?i.tracer.settler(i.ref,t):o},l=(e,t,i)=>{let a=n&&r.get(e);return a?a.tracer.run(a.ref,t,e.signal,i):i()},u=(e,t)=>typeof e==`function`?e(t):e,d=e=>e,f=({event:a,bus:s,tracer:c,parent:p})=>{let{controller:m,unlink:h}=t(p?.signal),{signal:g}=m,_=n&&c?{tracer:c,ref:c.task(a,p?.ref)}:void 0,v=(...e)=>{if(g.aborted)return;let t=s?.emit,n=i;i=_?.ref;try{return t?.(...e)}finally{i=n}},y=(...t)=>{if(g.aborted)return o;let n=s?.on,r=n?.(...t);if(!r)return o;let i=e({signal:g},r);return()=>{i(),r()}},b=async(e,...t)=>{g.throwIfAborted();let n=t[0],r=f({event:{type:a.type,payload:n},bus:s,tracer:c,parent:{signal:g,ref:_?.ref}});try{return await l(r.ctx,`run`,()=>e(n,r.ctx))}finally{r.unlink()}},x=d({...u(s?.ctx,a),type:a.type,payload:a.payload,signal:g,abort:e=>m.abort(e),emit:v,on:y,off:s?.off??o,run:b});return s?.addContext?.(x),_&&r.set(x,_),{ctx:x,unlink:h}},p=(e,t)=>{let i=n?r.get(e):void 0;return f({event:{type:t??e.type??``,payload:e.payload},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e},tracer:i?.tracer,parent:{signal:e.signal,ref:i?.ref}})};export{l as a,p as i,f as n,s as o,a as r,c as t};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { B as EventMap, N as WithContext, h as Bus, x as ContextOf } from "./action-creator-c292nK7v.js";
|
|
2
|
+
//#region src/core/context-plugin.d.ts
|
|
3
|
+
/** What a function that makes the fields of a task gets to see. */
|
|
4
|
+
type ContextTask = {
|
|
5
|
+
type: string;
|
|
6
|
+
payload?: unknown;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* `unknown` when none of the `fields` is already a context field of the bus,
|
|
10
|
+
* else `never`: the call does not compile. Used as `bus: Bus<E> & NoClash<…>`.
|
|
11
|
+
*/
|
|
12
|
+
type NoClash<Fields extends object, E extends EventMap> = [keyof Fields & keyof ContextOf<E>] extends [never] ? unknown : never;
|
|
13
|
+
/**
|
|
14
|
+
* Puts `fields` on the context of every task of the bus: a store, a client, a
|
|
15
|
+
* logger. The types come from the value, so the event map needs no
|
|
16
|
+
* `WithContext`. Returns the same bus, typed with the fields. A function gets
|
|
17
|
+
* `{ type, payload }` of the task and returns its fields.
|
|
18
|
+
*
|
|
19
|
+
* Several uses merge with each other, with the `context` option of `createBus`
|
|
20
|
+
* and with `effectsPlugin`. A name that the bus already has is a type error. The
|
|
21
|
+
* fields of the library (`signal`, `emit`, `run`, …) win, and a field that the
|
|
22
|
+
* context already has is not replaced.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* type Events = { "user/loaded": User };
|
|
27
|
+
*
|
|
28
|
+
* const bus = createBus<Events>().use(contextPlugin({ store }));
|
|
29
|
+
* bus.on("user/loaded", (user, { store }) => store.put(user)); // ctx.store: Store
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare const contextPlugin: <Fields extends object>(fields: Fields | ((task: ContextTask) => Fields)) => <E extends EventMap>(bus: Bus<E> & NoClash<Fields, E>) => Bus<E & WithContext<Fields>>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { NoClash as n, contextPlugin as r, ContextTask as t };
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ModuleApi } from "./module-
|
|
1
|
+
import { B as EventMap, J as UnsubscribeCallback, h as Bus } from "./action-creator-c292nK7v.js";
|
|
2
|
+
import { n as ModuleApi } from "./module-BlF2KSgy.js";
|
|
3
3
|
//#region src/dev/types.d.ts
|
|
4
4
|
/** The parts of what `modulePlugin` returned that the diagnostics report. */
|
|
5
5
|
type ReportedApp<E extends EventMap> = Pick<ModuleApi<E>, "mount" | "unmount" | "isMounted">;
|
package/dist/effects.d.ts
CHANGED
|
@@ -1,89 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Resolves after `ms`, or rejects early if `ctx.signal` aborts. `ms` can be a
|
|
5
|
-
* function that returns the number of ms, called once when the delay starts:
|
|
6
|
-
* handy for a random pause (jitter).
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* await delay({}, 500);
|
|
11
|
-
*
|
|
12
|
-
* // 200 to 400 ms
|
|
13
|
-
* await delay({}, () => 200 + Math.random() * 200);
|
|
14
|
-
*
|
|
15
|
-
* // inside a task: stops waiting when the task is aborted
|
|
16
|
-
* bus.on("retry", async (_, ctx) => {
|
|
17
|
-
* await delay(ctx, 1000);
|
|
18
|
-
* await load();
|
|
19
|
-
* });
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export declare const delay: (ctx: {
|
|
23
|
-
signal?: AbortSignal | undefined;
|
|
24
|
-
}, ms: number | (() => number)) => Promise<void>;
|
|
25
|
-
//#endregion
|
|
1
|
+
import { B as EventMap, W as Task, u as Effect } from "./action-creator-c292nK7v.js";
|
|
2
|
+
import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-CCYlC0XS.js";
|
|
26
3
|
//#region src/utils/onAbort.d.ts
|
|
27
4
|
export declare const onAbort: (ctx: {
|
|
28
5
|
signal?: AbortSignal | undefined;
|
|
29
6
|
}, fn: (reason: unknown) => void) => (() => void);
|
|
30
7
|
//#endregion
|
|
31
|
-
//#region src/effects/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
8
|
+
//#region src/effects/backoff.d.ts
|
|
9
|
+
type BackoffOptions = {
|
|
10
|
+
/** Pause before the second attempt, in ms. Default 100. */
|
|
11
|
+
base?: number;
|
|
12
|
+
/** How many times each pause is longer than the one before. Default 2. */
|
|
13
|
+
factor?: number;
|
|
14
|
+
/** Longest pause, in ms. Default `Infinity`. */
|
|
15
|
+
max?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Use a random pause from 0 up to the computed one ("full jitter"), so
|
|
18
|
+
* clients that failed together do not retry together. Default `false`.
|
|
19
|
+
*/
|
|
20
|
+
jitter?: boolean;
|
|
36
21
|
};
|
|
37
|
-
type Starter<T> = (ctx: EffectContext) => T | PromiseLike<T>;
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/effects/all.d.ts
|
|
40
22
|
/**
|
|
41
|
-
*
|
|
23
|
+
* Builds the `delay` of `retry`: the pause grows as `base * factor ** (attempt - 1)`
|
|
24
|
+
* and never passes `max`. `retry` counts attempts from 1; `poll` counts calls from
|
|
25
|
+
* 0, so there the first pause is `base / factor`.
|
|
42
26
|
*
|
|
43
27
|
* @example
|
|
44
28
|
* ```ts
|
|
45
|
-
* bus.on(
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* });
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export declare function all<T extends Record<string, Starter<unknown>>>(ctx: {
|
|
56
|
-
signal?: AbortSignal;
|
|
57
|
-
}, effects: T): Promise<{ [K in keyof T]: Awaited<ReturnType<T[K]>>; }>;
|
|
58
|
-
export declare function all<const T extends readonly Starter<unknown>[]>(ctx: {
|
|
59
|
-
signal?: AbortSignal;
|
|
60
|
-
}, effects: T): Promise<{ -readonly [K in keyof T]: Awaited<ReturnType<T[K]>>; }>;
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/effects/allSettled.d.ts
|
|
63
|
-
/**
|
|
64
|
-
* Runs all starters and waits for every one to settle, whether it succeeds or
|
|
65
|
-
* fails: nothing is cancelled when a starter fails. The result has, for each
|
|
66
|
-
* starter, `{ status: "fulfilled", value }` or `{ status: "rejected", reason }`.
|
|
67
|
-
* Only an abort of `ctx` rejects it, with the abort reason.
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```ts
|
|
71
|
-
* bus.on("load", async (id, ctx) => {
|
|
72
|
-
* const { user, posts } = await allSettled(ctx, {
|
|
73
|
-
* user: ({ signal }) => fetchUser(id, { signal }),
|
|
74
|
-
* posts: ({ signal }) => fetchPosts(id, { signal }),
|
|
75
|
-
* });
|
|
76
|
-
*
|
|
77
|
-
* if (user.status === "fulfilled") render(user.value);
|
|
78
|
-
* });
|
|
29
|
+
* bus.on(
|
|
30
|
+
* "load",
|
|
31
|
+
* retry(loadUser, {
|
|
32
|
+
* times: 5,
|
|
33
|
+
* delay: backoff({ base: 200, max: 5000, jitter: true }), // 200, 400, 800, ... up to 5 s, randomised
|
|
34
|
+
* }),
|
|
35
|
+
* );
|
|
79
36
|
* ```
|
|
80
37
|
*/
|
|
81
|
-
export declare
|
|
82
|
-
signal?: AbortSignal;
|
|
83
|
-
}, effects: T): Promise<{ [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
|
|
84
|
-
export declare function allSettled<const T extends readonly Starter<unknown>[]>(ctx: {
|
|
85
|
-
signal?: AbortSignal;
|
|
86
|
-
}, effects: T): Promise<{ -readonly [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
|
|
38
|
+
export declare const backoff: (options?: BackoffOptions) => (attempt: number) => number;
|
|
87
39
|
//#endregion
|
|
88
40
|
//#region src/effects/cancellable.d.ts
|
|
89
41
|
type CancellableHandle<T> = {
|
|
@@ -160,32 +112,6 @@ export declare function poll(ctx: {
|
|
|
160
112
|
signal: AbortSignal;
|
|
161
113
|
}, fn: (tick: number) => unknown, ms: PollMs, options?: PollOptions): Promise<void>;
|
|
162
114
|
//#endregion
|
|
163
|
-
//#region src/effects/race.d.ts
|
|
164
|
-
type RaceStarter<T> = Starter<T>;
|
|
165
|
-
/**
|
|
166
|
-
* Runs all starters; first to settle wins, losers are cancelled via `ctx.abort`.
|
|
167
|
-
* The result holds only the winner's value, under its key or index.
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* ```ts
|
|
171
|
-
* bus.on("load", async (id, ctx) => {
|
|
172
|
-
* const result = await race(ctx, {
|
|
173
|
-
* data: ({ signal }) => fetchData(id, { signal }),
|
|
174
|
-
* timeout: ({ signal }) => delay({ signal }, 5000),
|
|
175
|
-
* });
|
|
176
|
-
*
|
|
177
|
-
* if ("timeout" in result) throw new Error("timed out");
|
|
178
|
-
* render(result.data);
|
|
179
|
-
* });
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
export declare function race<T extends Record<string, RaceStarter<unknown>>>(ctx: {
|
|
183
|
-
signal?: AbortSignal;
|
|
184
|
-
}, effects: T): Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
|
|
185
|
-
export declare function race<const T extends readonly RaceStarter<unknown>[]>(ctx: {
|
|
186
|
-
signal?: AbortSignal;
|
|
187
|
-
}, effects: T): Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
|
|
188
|
-
//#endregion
|
|
189
115
|
//#region src/effects/retry.d.ts
|
|
190
116
|
type RetryOptions = {
|
|
191
117
|
/** Total number of attempts, the first one included. Default 3. */
|
|
@@ -226,34 +152,6 @@ export declare function retry<T>(ctx: {
|
|
|
226
152
|
signal?: AbortSignal;
|
|
227
153
|
}, fn: RetryFn<T>, options?: RetryOptions): Promise<T>;
|
|
228
154
|
//#endregion
|
|
229
|
-
//#region src/effects/take.d.ts
|
|
230
|
-
type TakeOptions<P> = {
|
|
231
|
-
/** Wait for the first payload this accepts; others are skipped. */
|
|
232
|
-
predicate?: (payload: P) => boolean;
|
|
233
|
-
/** Stop waiting when this aborts, e.g. the signal of a `race` starter. */
|
|
234
|
-
signal?: AbortSignal | undefined;
|
|
235
|
-
};
|
|
236
|
-
/**
|
|
237
|
-
* Waits for the next `event` on the bus and resolves with its payload. It sees
|
|
238
|
-
* only events emitted after the call, takes only one, and unsubscribes when it
|
|
239
|
-
* is done. If `ctx` (or `options.signal`) aborts first, it unsubscribes and
|
|
240
|
-
* rejects with the abort reason.
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* ```ts
|
|
244
|
-
* bus.on("login", async (user, ctx) => {
|
|
245
|
-
* const reason = await take(ctx, "logout");
|
|
246
|
-
* cleanup(user, reason);
|
|
247
|
-
* });
|
|
248
|
-
*
|
|
249
|
-
* // wait for a matching event, but not longer than 5 s
|
|
250
|
-
* const done = await timeout(ctx, 5000, ({ signal }) =>
|
|
251
|
-
* take(ctx, "saved", { signal, predicate: (id) => id === draftId }),
|
|
252
|
-
* );
|
|
253
|
-
* ```
|
|
254
|
-
*/
|
|
255
|
-
export declare const take: <E extends EventMap, K extends Keys<E>>(ctx: Context<E>, event: K, options?: TakeOptions<E[K]>) => Promise<E[K]>;
|
|
256
|
-
//#endregion
|
|
257
155
|
//#region src/effects/timeout.d.ts
|
|
258
156
|
/**
|
|
259
157
|
* Runs a function; if it takes longer than `ms`, aborts its signal and rejects
|
|
@@ -281,4 +179,4 @@ export declare function timeout<T>(ctx: {
|
|
|
281
179
|
signal?: AbortSignal;
|
|
282
180
|
}, ms: number, starter: Starter<T>): Promise<T>;
|
|
283
181
|
//#endregion
|
|
284
|
-
export type
|
|
182
|
+
export { type BackoffOptions, type CancellableExecutor, type CancellableHandle, type Effect, type EffectContext, type PollOptions, type RaceStarter, type RetryOptions, type TakeOptions, all, allSettled, delay, effectsPlugin, race, take };
|