nanoeffects 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +96 -2
- package/dist/action-rqZEnRIp.d.ts +303 -0
- package/dist/actions.d.ts +58 -0
- package/dist/actions.js +1 -0
- package/dist/bus-CRnlwmQ4.js +1 -0
- package/dist/context-DlA80GOA.js +1 -0
- package/dist/delay-CIHnOh_L.js +1 -0
- package/dist/dev.d.ts +46 -0
- package/dist/dev.js +1 -0
- package/dist/effects.d.ts +284 -0
- package/dist/effects.js +1 -0
- package/dist/event-0RIBGYqS.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/module-BSCdR9D1.d.ts +167 -0
- package/dist/notrace/action-rqZEnRIp.d.ts +303 -0
- package/dist/notrace/actions.d.ts +222 -0
- package/dist/notrace/actions.js +1 -0
- package/dist/notrace/bus-Dx0l9X6h.js +1 -0
- package/dist/notrace/context-yAWj0BFZ.js +1 -0
- package/dist/notrace/delay-Cgvn16oY.js +1 -0
- package/dist/notrace/effects.d.ts +284 -0
- package/dist/notrace/effects.js +1 -0
- package/dist/notrace/event-0RIBGYqS.js +1 -0
- package/dist/notrace/index.d.ts +2 -0
- package/dist/notrace/index.js +1 -0
- package/dist/notrace/policies.d.ts +171 -0
- package/dist/notrace/policies.js +1 -0
- package/dist/notrace/promise-CSLpzwSR.js +1 -0
- package/dist/notrace/redux.d.ts +92 -0
- package/dist/notrace/redux.js +1 -0
- package/dist/observable.d.ts +20 -0
- package/dist/observable.js +1 -0
- package/dist/onAbort-BgMfspK4.js +1 -0
- package/dist/policies.d.ts +171 -0
- package/dist/policies.js +1 -0
- package/dist/production/dev.js +1 -0
- package/dist/promise-CSLpzwSR.js +1 -0
- package/dist/redux.d.ts +92 -0
- package/dist/redux.js +1 -0
- package/dist/trace.d.ts +162 -0
- package/dist/trace.js +1 -0
- package/docs/README.md +15 -0
- package/docs/actions.md +78 -0
- package/docs/context.md +15 -0
- package/docs/effects.md +137 -0
- package/docs/errors.md +23 -0
- package/docs/guide.md +229 -0
- package/docs/hmr.md +36 -0
- package/docs/observable.md +22 -0
- package/docs/policies.md +53 -0
- package/docs/semantics.md +7 -0
- package/docs/stores.md +50 -0
- package/docs/tracing.md +117 -0
- package/package.json +184 -5
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { A as Keys, N as Task, k as EventMap, u as Context } from "./action-rqZEnRIp.js";
|
|
2
|
+
//#region src/utils/delay.d.ts
|
|
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
|
|
26
|
+
//#region src/utils/onAbort.d.ts
|
|
27
|
+
export declare const onAbort: (ctx: {
|
|
28
|
+
signal?: AbortSignal | undefined;
|
|
29
|
+
}, fn: (reason: unknown) => void) => (() => void);
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/effects/run.d.ts
|
|
32
|
+
/** Context passed into `race` / `all` effect starters. */
|
|
33
|
+
type EffectContext = {
|
|
34
|
+
signal: AbortSignal;
|
|
35
|
+
abort: (reason?: unknown) => void;
|
|
36
|
+
};
|
|
37
|
+
type Starter<T> = (ctx: EffectContext) => T | PromiseLike<T>;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/effects/all.d.ts
|
|
40
|
+
/**
|
|
41
|
+
* Runs all starters; waits for every result. On failure or parent abort, cancels the rest.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* bus.on("load", async (id, ctx) => {
|
|
46
|
+
* const { user, posts } = await all(ctx, {
|
|
47
|
+
* user: ({ signal }) => fetchUser(id, { signal }),
|
|
48
|
+
* posts: ({ signal }) => fetchPosts(id, { signal }),
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* // arrays work too: const [user, posts] = await all(ctx, [getUser, getPosts]);
|
|
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
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function allSettled<T extends Record<string, Starter<unknown>>>(ctx: {
|
|
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]>>>; }>;
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/effects/cancellable.d.ts
|
|
89
|
+
type CancellableHandle<T> = {
|
|
90
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
91
|
+
reject: (reason?: unknown) => void;
|
|
92
|
+
/** Registers a cleanup that runs if the promise is cancelled before it settles. */
|
|
93
|
+
onCancel: (cleanup: () => void) => void;
|
|
94
|
+
};
|
|
95
|
+
type CancellableExecutor<T> = (handle: CancellableHandle<T>) => void;
|
|
96
|
+
/**
|
|
97
|
+
* A promise tied to `ctx.signal`. It works like `new Promise`, but the
|
|
98
|
+
* executor gets one object, `{ resolve, reject, onCancel }`. If the signal
|
|
99
|
+
* aborts before the promise settles, the cleanups run (last registered first)
|
|
100
|
+
* and the promise rejects with the abort reason. If the signal is already
|
|
101
|
+
* aborted, the executor does not run. After the promise settles, the abort
|
|
102
|
+
* listener is removed and the cleanups are dropped.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* bus.on(
|
|
107
|
+
* "wait",
|
|
108
|
+
* latest((_, ctx) =>
|
|
109
|
+
* cancellable(ctx, ({ resolve, onCancel }) => {
|
|
110
|
+
* const timer = setTimeout(resolve, 5000);
|
|
111
|
+
* onCancel(() => clearTimeout(timer));
|
|
112
|
+
* }),
|
|
113
|
+
* ),
|
|
114
|
+
* );
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare const cancellable: <T = void>(ctx: {
|
|
118
|
+
signal: AbortSignal;
|
|
119
|
+
}, executor: CancellableExecutor<T>) => Promise<T>;
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/effects/poll.d.ts
|
|
122
|
+
type PollOptions = {
|
|
123
|
+
/** Also call `fn` at once, not only after the first `ms`. Default `false`. */
|
|
124
|
+
leading?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Start the pause after a call finishes, so calls never overlap. By default
|
|
127
|
+
* the calls come every `ms` whatever the duration of `fn`.
|
|
128
|
+
*/
|
|
129
|
+
waitForFn?: boolean;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Calls a function every `ms`, like patronum `interval`: the first call comes
|
|
133
|
+
* after `ms` (with `leading` also at once), and the next ticks keep a steady
|
|
134
|
+
* rate whatever the duration of the function, so a slow one may overlap with
|
|
135
|
+
* the next call (`waitForFn` starts each pause when the call ends instead). An
|
|
136
|
+
* error stops the polling and rejects; an abort of the caller ends it without
|
|
137
|
+
* an error and clears the timer. Two forms:
|
|
138
|
+
*
|
|
139
|
+
* - `poll(ms, task, options)` wraps a Task `(payload, ctx) => result` and
|
|
140
|
+
* returns a Task. Each call gets a context of its own, a child of the
|
|
141
|
+
* caller's: `bus.on("watch", poll(10_000, refresh))`.
|
|
142
|
+
* - `poll(ctx, fn, ms, options)` is for one call inside a body; `fn` gets the
|
|
143
|
+
* number of the call, starting at 0.
|
|
144
|
+
*
|
|
145
|
+
* `ms` can be a function of the number of the call that the pause leads to, for
|
|
146
|
+
* a growing or random pause: `(call) => Math.min(1000 * 2 ** call, 60_000)`.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* bus.on("watch", poll(10_000, (_, { signal }) => refresh({ signal })));
|
|
151
|
+
*
|
|
152
|
+
* bus.on("watch", async (_, ctx) => {
|
|
153
|
+
* await poll(ctx, () => refresh(), 10_000);
|
|
154
|
+
* });
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
type PollMs = number | ((call: number) => number);
|
|
158
|
+
export declare function poll<P, E extends EventMap, R>(ms: PollMs, task: Task<P, E, R>, options?: PollOptions): Task<P, E, Promise<void>>;
|
|
159
|
+
export declare function poll(ctx: {
|
|
160
|
+
signal: AbortSignal;
|
|
161
|
+
}, fn: (tick: number) => unknown, ms: PollMs, options?: PollOptions): Promise<void>;
|
|
162
|
+
//#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
|
+
//#region src/effects/retry.d.ts
|
|
190
|
+
type RetryOptions = {
|
|
191
|
+
/** Total number of attempts, the first one included. Default 3. */
|
|
192
|
+
times?: number;
|
|
193
|
+
/**
|
|
194
|
+
* Pause before the next attempt, in ms: a number, or a function of the
|
|
195
|
+
* number of the attempt that just failed and its error. Default 0.
|
|
196
|
+
*/
|
|
197
|
+
delay?: number | ((attempt: number, error: unknown) => number);
|
|
198
|
+
/** Return `false` to stop retrying and throw `error`. Default: always retry. */
|
|
199
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Calls a function until it succeeds or `times` attempts are used, then throws
|
|
203
|
+
* the last error. If the caller aborts, retrying stops and the result rejects
|
|
204
|
+
* with the abort reason. Two forms:
|
|
205
|
+
*
|
|
206
|
+
* - `retry(task, options)` wraps a Task `(payload, ctx) => result` and returns
|
|
207
|
+
* a Task. Each attempt gets a context of its own, a child of the caller's, so
|
|
208
|
+
* it can go straight into `on` and into `ctx.run`:
|
|
209
|
+
* `bus.on("load", latest(retry(load, { times: 4 })))`. The number of the
|
|
210
|
+
* attempt is not passed; use the other form when it is needed.
|
|
211
|
+
* - `retry(ctx, fn, options)` is for one call inside a body. `fn` gets the
|
|
212
|
+
* number of the attempt, starting at 1.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* bus.on("load", latest(retry(loadUser, { times: 4, delay: (attempt) => 2 ** attempt * 100 })));
|
|
217
|
+
*
|
|
218
|
+
* bus.on("load", async (id, ctx) => {
|
|
219
|
+
* const user = await retry(ctx, () => fetchUser(id, { signal: ctx.signal }), { times: 4 });
|
|
220
|
+
* });
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
type RetryFn<T> = (attempt: number) => T | PromiseLike<T>;
|
|
224
|
+
export declare function retry<P, E extends EventMap, R>(task: Task<P, E, R>, options?: RetryOptions): Task<P, E, Promise<Awaited<R>>>;
|
|
225
|
+
export declare function retry<T>(ctx: {
|
|
226
|
+
signal?: AbortSignal;
|
|
227
|
+
}, fn: RetryFn<T>, options?: RetryOptions): Promise<T>;
|
|
228
|
+
//#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
|
+
//#region src/effects/timeout.d.ts
|
|
258
|
+
/**
|
|
259
|
+
* Runs a function; if it takes longer than `ms`, aborts its signal and rejects
|
|
260
|
+
* with a `TimeoutError` (a `DOMException`, like `AbortSignal.timeout`), even if
|
|
261
|
+
* it ignores the signal. If the caller aborts, rejects with its reason. Two
|
|
262
|
+
* forms:
|
|
263
|
+
*
|
|
264
|
+
* - `timeout(ms, task)` wraps a Task `(payload, ctx) => result` and returns a
|
|
265
|
+
* Task. The Task gets a context of its own whose `signal` the time limit
|
|
266
|
+
* aborts: `bus.on("load", latest(timeout(5000, load)))`.
|
|
267
|
+
* - `timeout(ctx, ms, starter)` is for one call inside a body; `starter` gets
|
|
268
|
+
* `{ signal, abort }`.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```ts
|
|
272
|
+
* bus.on("load", timeout(5000, (id, { signal }) => fetchData(id, { signal })));
|
|
273
|
+
*
|
|
274
|
+
* bus.on("load", async (id, ctx) => {
|
|
275
|
+
* const data = await timeout(ctx, 5000, ({ signal }) => fetchData(id, { signal }));
|
|
276
|
+
* });
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export declare function timeout<P, E extends EventMap, R>(ms: number, task: Task<P, E, R>): Task<P, E, Promise<Awaited<R>>>;
|
|
280
|
+
export declare function timeout<T>(ctx: {
|
|
281
|
+
signal?: AbortSignal;
|
|
282
|
+
}, ms: number, starter: Starter<T>): Promise<T>;
|
|
283
|
+
//#endregion
|
|
284
|
+
export type { CancellableExecutor, CancellableHandle, EffectContext, PollOptions, RaceStarter, RetryOptions, TakeOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,i as n,o as r,s as i,t as a}from"./context-yAWj0BFZ.js";import{n as o,r as s,t as c}from"./delay-Cgvn16oY.js";const l=(e,t)=>t?.signal?new Promise((n,r)=>{let a=i(t,r);e.then(n,r).finally(a)}):e,u=async(e,t,o,s)=>{t?.signal?.throwIfAborted();let{ctx:c,unlink:u}=n(t??{}),d=a(c,e);r(c,e,`start`),i(c,()=>d(`cancel`));try{let e=await l(o(c),t);return d(`done`),s?.(!1),e}catch(e){throw d(`error`,e),s?.(!0),e}finally{u()}},d=async(t,r)=>{let{ctx:i,unlink:a}=n(t);try{return await e(i,`run`,()=>r(i))}finally{a()}},f=e=>e,p=(e,t)=>n=>e(t,f(n)),m=e=>Array.isArray(e)?Array(e.length):{},h=(t,r)=>{let i=Object.entries(r).map(([r,i])=>{let a=n(t),o={signal:a.ctx.signal,abort:a.ctx.abort},s=()=>i(o);return{key:r,task:new Promise(t=>t(e(a.ctx,`run`,s))).finally(a.unlink),cancel:a.ctx.abort}});return{tasks:i.map(({key:e,task:t})=>[e,t]),cancelAll:()=>{for(let e of i)e.cancel()}}},g=(e,t,n,r,i)=>{let a=()=>{};return u(e,t,async e=>{let t=h(e,n);return a=t.cancelAll,r(t.tasks)},e=>{(e||i===`end`)&&a()})};function _(e,t){return g(`all`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>e)),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const v=e=>e.then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}));function y(e,t){return g(`allSettled`,e,t,async e=>{let n=await Promise.all(e.map(([,e])=>v(e))),r=m(t);for(let[t,[i]]of e.entries())r[i]=n[t];return r},`failure`)}const b=(e,n)=>e.signal.aborted?Promise.reject(e.signal.reason):new Promise((r,a)=>{let o=[],s=`pending`,c=e=>{try{e()}catch(e){t(`cancel cleanup failed`,e)}},l=i(e,e=>{s=`cancelled`;for(let e of o.reverse())c(e);a(e)}),u=e=>(...t)=>{s===`pending`&&(s=`settled`,l(),o.length=0,e(...t))},d=e=>{s===`pending`?o.push(e):s===`cancelled`&&c(e)};try{n({resolve:u(r),reject:u(a),onCancel:d})}catch(e){u(a)(e)}}),x=(e,t,n,r)=>{if(e.signal.aborted)return Promise.resolve();let a=r.leading??!1,c=r.waitForFn??!1;return u(`poll`,e,e=>new Promise((r,l)=>{let u=0,f=null,p=()=>{f!==null&&(o(f),f=null)},m=e=>{try{f=s(h,typeof n==`function`?n(e):n)}catch(e){p(),l(e)}},h=()=>{f=null,c||m(u+1),g()},g=()=>{let n=u++;d(e,e=>t(e,n)).then(()=>{c&&!e.signal.aborted&&m(u)},e=>{p(),l(e)})};i(e,p),a?(g(),c||m(u)):m(0)})).catch(t=>{if(!e.signal.aborted)throw t})};function S(e,t,n,r={}){if(typeof e!=`object`){let r=t,i=n??{};return(t,n)=>x(n,p(r,t),e,i)}let i=t;return x(e,(e,t)=>i(t),n,r)}function C(e,t){let n=m(t);return Object.keys(t).length===0?Promise.resolve(n):g(`race`,e,t,async e=>{let[t,r]=await Promise.race(e.map(([e,t])=>t.then(t=>[e,t])));return n[t]=r,n},`end`)}const w=(e,t,n)=>typeof e==`function`?e(t,n):e,T=e=>({times:e.times??3,delay:e.delay??0,shouldRetry:e.shouldRetry??(()=>!0)}),E=(e,t,n)=>u(`retry`,e,async e=>{let{times:r,delay:i,shouldRetry:a}=T(n);for(let n=1;;n++)try{return await d(e,e=>t(e,n))}catch(t){if(e.signal.throwIfAborted(),n>=r||!a(t,n))throw t;let o=w(i,n,t);o>0&&await c(e,o)}});function D(e,t,n={}){if(typeof e==`function`){let n=e;return(e,r)=>E(r,p(n,e),t??{})}let r=t;return E(e,(e,t)=>r(t),n)}const O=(e,t,n={})=>u(`take`,e,r=>new Promise((a,o)=>{let{predicate:s}=n,c=[],l=()=>{for(let e of c)e()},u=e=>{l(),o(e)};c.push(e.on(t,e=>{(!s||s(e))&&(l(),a(e))})),c.push(i(r,u)),c.push(i({signal:n.signal},u))})),k=(t,r,i)=>u(`timeout`,t,async t=>{let a=n(t),c=s(()=>a.ctx.abort(new DOMException(`Timed out after ${r}ms`,`TimeoutError`)),r);try{return await l(Promise.resolve(e(a.ctx,`run`,()=>i(a.ctx))),a.ctx)}finally{o(c),a.unlink()}});function A(e,t,n){if(typeof e==`number`){let n=t;return(t,r)=>k(r,e,p(n,t))}let r=t,i=n;return k(e,r,e=>i({signal:e.signal,abort:e.abort}))}export{_ as all,y as allSettled,b as cancellable,c as delay,i as onAbort,S as poll,C as race,D as retry,O as take,A as timeout};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=e=>typeof e==`string`?e:e.type,t=(e,t)=>typeof e==`object`?e.payload:t;export{t as n,e as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as Keys, C as WithPayload, D as anyEventType, E as Tracer, F as TraceEvent, I as TraceLogger, L as UnsubscribeCallback, M as Phase, N as Task, O as EffectName, P as Trace, S as WithContext, T as createBus, _ as EventPayload, a as AnyEvent, b as Off, c as Command, d as ContextOf, f as ContextOption, g as EventLike, h as EventKey, i as Action, j as PayloadOf, k as EventMap, l as Commands, m as Emittable, o as Bus, p as Emit, s as BusOptions, u as Context, v as Handler, w as WithType, x as On, y as HandlerRef } from "./action-rqZEnRIp.js";
|
|
2
|
+
export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type Commands, type Context, type ContextOf, type ContextOption, type EffectName, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type Handler, type HandlerRef, type Keys, type Off, type On, type PayloadOf, type Phase, type Task, type Trace, type TraceEvent, type TraceLogger, type Tracer, type UnsubscribeCallback, type WithContext, type WithPayload, type WithType, anyEventType, createBus };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{n as anyEventType,t as createBus}from"./bus-Dx0l9X6h.js";
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { N as Task, k as EventMap } from "./action-rqZEnRIp.js";
|
|
2
|
+
//#region src/policies/keyed.d.ts
|
|
3
|
+
/** Options shared by every policy that can keep its state per key. */
|
|
4
|
+
type KeyOption<P> = {
|
|
5
|
+
/**
|
|
6
|
+
* Gives each key its own state: calls with different keys do not affect
|
|
7
|
+
* each other. The state of a key is dropped when that key goes idle, so
|
|
8
|
+
* keys can be unbounded (ids, urls).
|
|
9
|
+
*/
|
|
10
|
+
key?: (payload: P) => unknown;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/policies/debounce.d.ts
|
|
14
|
+
type DebounceResult<R> = Promise<R | undefined> | Promise<Awaited<R> | undefined> | undefined;
|
|
15
|
+
type DebounceOptions<P = never> = KeyOption<P> & {
|
|
16
|
+
/** Run the first call of a burst at once. Default `false`. */
|
|
17
|
+
leading?: boolean;
|
|
18
|
+
/** Run the last call of a burst after `ms` of quiet. Default `true`. */
|
|
19
|
+
trailing?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Waits for `ms` of quiet, then runs; a new call restarts the wait and drops
|
|
23
|
+
* the previous. With `leading` the first call of a burst runs at once, and the
|
|
24
|
+
* trailing run happens only if more calls follow it. With `trailing: false`
|
|
25
|
+
* only the leading call runs. With `options.key` each key has its own wait.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* bus.on(
|
|
30
|
+
* "input",
|
|
31
|
+
* debounce(300, async (text, { signal }) => {
|
|
32
|
+
* const res = await fetch(`/api?q=${text}`, { signal });
|
|
33
|
+
* showResults(await res.json());
|
|
34
|
+
* }),
|
|
35
|
+
* );
|
|
36
|
+
*
|
|
37
|
+
* bus.emit("input", "r");
|
|
38
|
+
* bus.emit("input", "re"); // only the last call runs, 300 ms after it
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare const debounce: <P, E extends EventMap, R>(ms: number, fn: Task<P, E, R>, options?: DebounceOptions<P>) => Task<P, E, DebounceResult<R>>;
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/policies/latest.d.ts
|
|
44
|
+
type LatestOptions<P = never> = KeyOption<P>;
|
|
45
|
+
/**
|
|
46
|
+
* Aborts the previous run when a new call arrives; stale results are dropped.
|
|
47
|
+
* With `options.key` each key has its own latest run.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* bus.on(
|
|
52
|
+
* "search",
|
|
53
|
+
* latest(async (query, { signal }) => {
|
|
54
|
+
* const res = await fetch(`/api?q=${query}`, { signal });
|
|
55
|
+
* showResults(await res.json());
|
|
56
|
+
* }),
|
|
57
|
+
* );
|
|
58
|
+
*
|
|
59
|
+
* bus.emit("search", "re"); // aborted by the next call
|
|
60
|
+
* bus.emit("search", "redux"); // only this one finishes
|
|
61
|
+
*
|
|
62
|
+
* // one latest run per user instead of one for all
|
|
63
|
+
* bus.on("load", latest(loadUser, { key: (user) => user.id }));
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare const latest: <P, E extends EventMap, R>(fn: Task<P, E, R>, options?: LatestOptions<P>) => Task<P, E, R | Promise<Awaited<R> | undefined> | undefined>;
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/policies/leading.d.ts
|
|
69
|
+
type LeadingOptions<P = never> = KeyOption<P>;
|
|
70
|
+
/**
|
|
71
|
+
* Drops new calls while the task is still running. The lock is released when
|
|
72
|
+
* the run settles or when its task is aborted, whichever comes first, so a
|
|
73
|
+
* `fn` that ignores the abort signal may overlap with the next run. With
|
|
74
|
+
* `options.key` each key has its own lock.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* bus.on(
|
|
79
|
+
* "save",
|
|
80
|
+
* leading(async (draft) => {
|
|
81
|
+
* await api.save(draft);
|
|
82
|
+
* }),
|
|
83
|
+
* );
|
|
84
|
+
*
|
|
85
|
+
* bus.emit("save", draft); // runs
|
|
86
|
+
* bus.emit("save", draft); // dropped while the first save is in flight
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare const leading: <P, E extends EventMap, R>(fn: Task<P, E, R>, options?: LeadingOptions<P>) => Task<P, E, R | Promise<Awaited<R>> | undefined>;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/policies/queue.d.ts
|
|
92
|
+
type QueueOptions<P = never> = KeyOption<P> & {
|
|
93
|
+
/** How many calls may run at the same time. Default 1. */
|
|
94
|
+
concurrency?: number;
|
|
95
|
+
/** How many calls may wait for a free slot. Default: no limit. */
|
|
96
|
+
limit?: number;
|
|
97
|
+
/**
|
|
98
|
+
* What to drop when a call arrives and `limit` calls already wait: the new
|
|
99
|
+
* call (`"drop-new"`, the default) or the oldest waiting one (`"drop-old"`,
|
|
100
|
+
* which keeps the most recent calls). A dropped call resolves with `undefined`.
|
|
101
|
+
*/
|
|
102
|
+
overflow?: "drop-new" | "drop-old";
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Runs calls one after another, in the order they arrived: a call starts when
|
|
106
|
+
* fewer than `concurrency` calls are running. Every call returns a promise with
|
|
107
|
+
* the result of its own run; an error rejects only that call, and the queue
|
|
108
|
+
* goes on. The first call on an idle queue starts synchronously.
|
|
109
|
+
*
|
|
110
|
+
* With `limit` the number of waiting calls is capped. `limit: 1` with
|
|
111
|
+
* `overflow: "drop-old"` keeps only the latest call while one is running, and
|
|
112
|
+
* runs it when the current one is done.
|
|
113
|
+
*
|
|
114
|
+
* With `options.key` each key has its own line.
|
|
115
|
+
*
|
|
116
|
+
* A call whose task aborts while it waits is skipped and resolves with
|
|
117
|
+
* `undefined`. When the task of a running call aborts, its slot is freed at
|
|
118
|
+
* once and its result is dropped; a `fn` that ignores the abort signal can
|
|
119
|
+
* then overlap with the next run.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* bus.on(
|
|
124
|
+
* "save",
|
|
125
|
+
* queue(async (draft, { signal }) => {
|
|
126
|
+
* await api.save(draft, { signal });
|
|
127
|
+
* }),
|
|
128
|
+
* );
|
|
129
|
+
*
|
|
130
|
+
* bus.emit("save", a); // runs
|
|
131
|
+
* bus.emit("save", b); // waits for a, then runs
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare const queue: <P, E extends EventMap, R>(fn: Task<P, E, R>, options?: QueueOptions<P>) => Task<P, E, Promise<Awaited<R> | undefined>>;
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/policies/throttle.d.ts
|
|
137
|
+
type ThrottleOptions<P = never> = KeyOption<P> & {
|
|
138
|
+
/** Run the first call at once, when the throttle is idle. Default `true`. */
|
|
139
|
+
leading?: boolean;
|
|
140
|
+
/** Run the latest call made while waiting, at the end of the window. Default `true`. */
|
|
141
|
+
trailing?: boolean;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* At most one run per `ms`, with the semantics of lodash `throttle` (and
|
|
145
|
+
* patronum): a call made when the throttle is idle, that is, at least `ms`
|
|
146
|
+
* after the previous call and the previous run, runs at once (`leading`); calls
|
|
147
|
+
* made while it waits are held, and the latest of them runs when the window
|
|
148
|
+
* closes (`trailing`). Under a steady stream of calls it runs every `ms`.
|
|
149
|
+
*
|
|
150
|
+
* A held call returns a promise that settles with its result; calls superseded
|
|
151
|
+
* by a newer one, or not allowed to wait (`trailing: false`), resolve with
|
|
152
|
+
* `undefined`. With `leading: false` even the first call waits for the end of
|
|
153
|
+
* its window. With `options.key` each key has its own throttle.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* bus.on(
|
|
158
|
+
* "scroll",
|
|
159
|
+
* throttle(200, (position) => {
|
|
160
|
+
* saveScrollPosition(position);
|
|
161
|
+
* }),
|
|
162
|
+
* );
|
|
163
|
+
*
|
|
164
|
+
* bus.emit("scroll", 10); // runs now
|
|
165
|
+
* bus.emit("scroll", 20); // superseded
|
|
166
|
+
* bus.emit("scroll", 30); // runs once the 200 ms window closes
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export declare const throttle: <P, E extends EventMap, R>(ms: number, fn: Task<P, E, R>, options?: ThrottleOptions<P>) => Task<P, E, R | Promise<R | undefined> | undefined>;
|
|
170
|
+
//#endregion
|
|
171
|
+
export type { DebounceOptions, LatestOptions, LeadingOptions, QueueOptions, ThrottleOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,i as t,o as n,s as r,t as i}from"./context-yAWj0BFZ.js";import{n as a,r as o,t as s}from"./delay-Cgvn16oY.js";import{n as c,t as l}from"./promise-CSLpzwSR.js";const u=()=>{},d=(e,t)=>{let n=/* @__PURE__ */ new Map;return(r,i)=>{let a=e(r),o=n.get(a);if(!o){let e=t(()=>{n.get(a)===e&&n.delete(a)});o=e,n.set(a,e)}return o(r,i)}},f=(e,t)=>e?d(e,t):t(u),p=(e,t={})=>f(t.key,t=>m(`latest`,(t,n,r)=>(r(),e(t,n)),t)),m=(e,a,o=u)=>{let s=null;return(l,u)=>{if(s?.abort(),u.signal.aborted)return;let{ctx:d,unlink:f}=t(u);s=d;let p=i(d,e),m=!1,h=()=>{d.signal.aborted||(m=!0,n(d,e,`start`))};r(d,()=>p(m?`cancel`:`drop`));let g=()=>{f(),s===d&&(s=null,o())},_=e=>{if(!d.signal.aborted)return p(m?`done`:`drop`),e};try{let e=a(l,d,h);return c(e)?Promise.resolve(e).then(_,e=>{if(!d.signal.aborted)throw p(`error`,e),e}).finally(g):(g(),_(e))}catch(e){if(g(),d.signal.aborted)return;throw p(`error`,e),e}}},h=(e,t,n={})=>f(n.key,r=>_(e,t,n,r)),g=(t,r,i,s)=>{let c=s!==u,l=null,d=null;return(u,f)=>{if(f.signal.aborted)return;let p=Date.now(),m=l===null||p-l>=t||p-l<0;return l=p,d!==null&&a(d),d=c?o(s,t):null,i&&m?Promise.resolve(e(f,`debounce`,()=>r(u,f))):(n(f,`debounce`,`drop`),Promise.resolve(void 0))}},_=(t,n,i,a)=>{let o=i.leading??!1,c=i.trailing??!0;if(!c)return g(t,n,o,a);let l=!1,u=0,d=m(`debounce`,([e,i],a,o)=>{let d=u;return r(a,()=>{u===d&&(l=!1)}),s(a,t).then(()=>{if(l=!1,c&&!i)return o(),n(e,a)})},a);return(t,r)=>{if(r.signal.aborted)return;let i=o&&!l;l=!0,u++;let a=d([t,i],r);return i?Promise.resolve(e(r,`debounce`,()=>n(t,r))):a}},v=(t,i={})=>f(i.key,i=>{let a=null;return(o,s)=>{if(s.signal.aborted)return;if(a){n(s,`leading`,`drop`);return}let c={};a=c;let u=()=>{d(),a===c&&(a=null,i())},d=r(s,u);try{return l(e(s,`leading`,()=>t(o,s)),u)}catch(e){throw u(),e}}}),y=(e,t={})=>f(t.key,n=>b(e,t,n)),b=(t,i,a)=>{let o=i.concurrency??1,s=i.limit??1/0,c=[],l=0,d=()=>{for(;l<o;){let e=c.shift();if(!e)return;e.start()}};return(f,p)=>new Promise((m,h)=>{if(p.signal.aborted){m(void 0);return}let g=u,_=()=>{g(),n(p,`queue`,`drop`),m(void 0)},v=()=>{if(g(),p.signal.aborted){_();return}l++;let n=!0,i=()=>{n&&(n=!1,l--,d(),l===0&&c.length===0&&a())},o=r(p,()=>{i(),m(void 0)}),s;try{s=Promise.resolve(e(p,`queue`,()=>t(f,p)))}catch(e){s=Promise.reject(e)}let u=()=>{o(),i()};s.then(e=>{u(),m(p.signal.aborted?void 0:e)},e=>{u(),p.signal.aborted?m(void 0):h(e)})};if(l<o){v();return}if(c.length>=s){if(i.overflow!==`drop-old`||s<=0){_();return}c.shift()?.drop()}let y={start:v,drop:_};c.push(y),g=r(p,()=>{let e=c.indexOf(y);e>=0&&c.splice(e,1),_()})})},x=(e,t,n={})=>f(n.key,r=>S(e,t,n,r)),S=(t,r,i,s)=>{let c=i.leading??!0,l=i.trailing??!0,d=s!==u,f=null,p=0,m=null,h=null,g=null,_=!1,v=e=>f===null||e-f>=t||e-f<0||e-p>=t,y=e=>n(e,`throttle`,`drop`),b=()=>{let e=g;g=null,e&&(y(e.ctx),e.resolve())},x=(e,t)=>(b(),new Promise((n,r)=>{g={payload:e,ctx:t,resolve:n,reject:r}})),S=(t,n,i)=>{b(),p=i,_=!0;try{return e(n,`throttle`,()=>r(t,n))}finally{_=!1}},C=()=>{d&&(h=o(w,t))},w=()=>{if(h=null,!d||m!==null||g)return;let e=Date.now(),n=Math.max(t-(e-(f??-1/0)),t-(e-p));n<=0?s():h=o(w,n)},T=e=>{m=null;let t=g;if(l&&t&&!t.ctx.signal.aborted){g=null;try{t.resolve(S(t.payload,t.ctx,e))}catch(e){t.reject(e)}}else b();w()},E=()=>{let e=Date.now();v(e)?T(e):m=o(E,Math.min(t-(e-(f??e)),t-(e-p)))},D=()=>{h!==null&&(a(h),h=null)},O=(e,t)=>{if(!l)y(t);else return x(e,t)},k=(e,t,n,r)=>{if(!r)y(t);else if(p=n,C(),!c)y(t);else return S(e,t,n)},A=(e,n,r,i)=>i&&m!==null?(a(m),m=o(E,t),S(e,n,r)):m===null&&(m=o(E,t),i&&(p=r,c))?S(e,n,r):O(e,n);return(e,t)=>{if(t.signal.aborted)return;let n=Date.now(),r=!_&&v(n);return f=n,D(),l?A(e,t,n,r):k(e,t,n,r)}};export{h as debounce,p as latest,v as leading,y as queue,x as throttle};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=e=>typeof e!=`object`||!e?!1:`then`in e&&typeof e.then==`function`,t=(t,n)=>e(t)?Promise.resolve(t).finally(n):(n(),t);export{e as n,t};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { k as EventMap, o as Bus, s as BusOptions } from "./action-rqZEnRIp.js";
|
|
2
|
+
//#region src/store/redux/middleware.d.ts
|
|
3
|
+
/** What Redux passes through a middleware: an action is a plain object with a `type`. */
|
|
4
|
+
type ReduxAction = {
|
|
5
|
+
type: string;
|
|
6
|
+
payload?: unknown;
|
|
7
|
+
};
|
|
8
|
+
type NanoeffectsMiddlewareOptions = {
|
|
9
|
+
/** Return `false` for the actions that the bus should not get. Default: all of them. */
|
|
10
|
+
filter?: (action: ReduxAction) => boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* A Redux middleware that sends every action to the bus, after the reducers
|
|
14
|
+
* have handled it, so a handler that reads the store sees the new state. It
|
|
15
|
+
* does not depend on Redux: it only has the shape of a middleware. Values that
|
|
16
|
+
* are not plain actions (the functions of redux-thunk) go through untouched.
|
|
17
|
+
*
|
|
18
|
+
* The other way needs no adapter: give the store to the tasks with
|
|
19
|
+
* `createBus({ context: { store } })` and dispatch from a handler.
|
|
20
|
+
*
|
|
21
|
+
* Take care of loops: a handler must not dispatch the action it handles.
|
|
22
|
+
* Errors of handlers reach the caller of `dispatch`, as for `bus.emit`; set
|
|
23
|
+
* `onError` on the bus to isolate them.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const store = configureStore({
|
|
28
|
+
* reducer,
|
|
29
|
+
* middleware: (getDefault) => getDefault().concat(nanoeffectsMiddleware(bus)),
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* bus.on(userRequested, latest(async (id, { store, signal }) => {
|
|
33
|
+
* store.dispatch(userLoaded(await fetchUser(id, { signal })));
|
|
34
|
+
* }));
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare const nanoeffectsMiddleware: <E extends EventMap>(bus: Bus<E>, options?: NanoeffectsMiddlewareOptions) => () => (next: (action: unknown) => unknown) => (action: unknown) => unknown;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/store/redux/effects.d.ts
|
|
40
|
+
/** What Redux gives a middleware: the parts of the store that it may use. */
|
|
41
|
+
type ReduxStoreApi<State = unknown> = {
|
|
42
|
+
dispatch: (action: {
|
|
43
|
+
type: string;
|
|
44
|
+
payload?: unknown;
|
|
45
|
+
}) => unknown;
|
|
46
|
+
getState: () => State;
|
|
47
|
+
};
|
|
48
|
+
type EffectsMiddlewareOptions<E extends EventMap> = Omit<BusOptions<E>, "context"> & NanoeffectsMiddlewareOptions & {
|
|
49
|
+
/** More fields for the context of every task, next to `store`. */
|
|
50
|
+
context?: object;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Creates a bus and the Redux middleware that feeds it: the effects of an app
|
|
54
|
+
* in one place, without sagas. Every task gets `ctx.store`, the `dispatch` and
|
|
55
|
+
* `getState` of the store, as soon as the middleware is added to it. Type it
|
|
56
|
+
* in the event map:
|
|
57
|
+
*
|
|
58
|
+
* ```ts
|
|
59
|
+
* type Events = { userRequested: string } & WithContext<{ store: ReduxStoreApi<RootState> }>;
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* Two things are looked after for you:
|
|
63
|
+
*
|
|
64
|
+
* - **A handler does not loop on its own action.** An action that a handler
|
|
65
|
+
* dispatches through `ctx.store` and that has the type the handler works on
|
|
66
|
+
* does not go back to the bus (a handler of `a` that dispatches `a`). Other
|
|
67
|
+
* handlers of that type do not get it either. Longer cycles, `a` to `b` to
|
|
68
|
+
* `a`, are yours to avoid.
|
|
69
|
+
* - **A failing handler does not break `dispatch`.** Errors go to `onError`,
|
|
70
|
+
* by default to `console.error`, as in Redux Toolkit listeners; reducers
|
|
71
|
+
* still throw to the caller.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const effects = createEffectsMiddleware<Events>();
|
|
76
|
+
*
|
|
77
|
+
* const store = configureStore({
|
|
78
|
+
* reducer,
|
|
79
|
+
* middleware: (getDefault) => getDefault().concat(effects.middleware),
|
|
80
|
+
* });
|
|
81
|
+
*
|
|
82
|
+
* effects.bus.on(userRequested, latest(async (id, { store, signal }) => {
|
|
83
|
+
* store.dispatch(userLoaded(await fetchUser(id, { signal })));
|
|
84
|
+
* }));
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare const createEffectsMiddleware: <E extends EventMap = EventMap>(options?: EffectsMiddlewareOptions<E>) => {
|
|
88
|
+
bus: Bus<E>;
|
|
89
|
+
middleware: (storeApi: ReduxStoreApi) => (next: (action: unknown) => unknown) => (action: unknown) => unknown;
|
|
90
|
+
};
|
|
91
|
+
//#endregion
|
|
92
|
+
export type { EffectsMiddlewareOptions, NanoeffectsMiddlewareOptions, ReduxStoreApi };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as e}from"./context-yAWj0BFZ.js";import{t}from"./bus-Dx0l9X6h.js";const n=e=>typeof e==`object`&&!!e&&`type`in e,r=(e,t={})=>{let r=e.emit;return()=>e=>i=>{let a=e(i);return n(i)&&(t.filter?.(i)??!0)&&r(i),a}},i=(n={})=>{let{filter:i,context:a,onError:o,...s}=n,c,l=/* @__PURE__ */ new WeakMap,u=e=>({...a,store:c&&{getState:c.getState,dispatch:t=>{typeof t==`object`&&t&&l.set(t,e.type);let n=t;return c?.dispatch(n)}}}),d=t({...s,onError:o??((t,{type:n})=>{e(`handler of "${n}" failed`,t)}),context:u}),f=r(d,{filter:e=>l.get(e)!==e.type&&(i?.(e)??!0)});return{bus:d,middleware:e=>(c=e,f())}};export{i as createEffectsMiddleware,r as nanoeffectsMiddleware};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { A as Keys, k as EventMap, o as Bus } from "./action-rqZEnRIp.js";
|
|
2
|
+
//#region src/observable.d.ts
|
|
3
|
+
declare global {
|
|
4
|
+
interface SymbolConstructor {
|
|
5
|
+
readonly observable: symbol;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare const $$observable: symbol;
|
|
9
|
+
export type Observer<T> = ((value: T) => void) | {
|
|
10
|
+
next?: (value: T) => void;
|
|
11
|
+
};
|
|
12
|
+
export interface Subscribable<T> {
|
|
13
|
+
subscribe(observer: Observer<T>): {
|
|
14
|
+
unsubscribe(): void;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export type Observable<T> = Subscribable<T> & {};
|
|
18
|
+
export type Observe<E extends EventMap> = <K extends Keys<E>>(event: K) => Observable<E[K]>;
|
|
19
|
+
export declare const createObserve: <E extends EventMap>(bus: Bus<E>) => Observe<E>;
|
|
20
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=typeof Symbol==`function`&&Symbol.observable||`@@observable`,t=t=>n=>({subscribe:e=>({unsubscribe:t.on(n,t=>{typeof e==`function`?e(t):e.next?.(t)})}),[e](){return this}});export{e as $$observable,t as createObserve};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=(e,t)=>{globalThis.console?.error(`[nanoeffects] ${e}`,t)},t=()=>{},n=/* @__PURE__ */ new WeakMap,r=t=>{let r=n.get(t);if(r)return r;let i=/* @__PURE__ */ new Set;return n.set(t,i),t.addEventListener(`abort`,()=>{let n=[...i];i.clear();for(let{fn:r}of n)try{r(t.reason)}catch(t){e(`abort callback failed`,t)}},{once:!0}),i},i=(e,n)=>{let{signal:i}=e;if(!i)return t;if(i.aborted)return n(i.reason),t;let a=r(i),o={fn:n};return a.add(o),()=>{a.delete(o)}};export{e as n,i as t};
|