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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -2
  3. package/dist/action-rqZEnRIp.d.ts +303 -0
  4. package/dist/actions.d.ts +58 -0
  5. package/dist/actions.js +1 -0
  6. package/dist/bus-CRnlwmQ4.js +1 -0
  7. package/dist/context-DlA80GOA.js +1 -0
  8. package/dist/delay-CIHnOh_L.js +1 -0
  9. package/dist/dev.d.ts +46 -0
  10. package/dist/dev.js +1 -0
  11. package/dist/effects.d.ts +284 -0
  12. package/dist/effects.js +1 -0
  13. package/dist/event-0RIBGYqS.js +1 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +1 -0
  16. package/dist/module-BSCdR9D1.d.ts +167 -0
  17. package/dist/notrace/action-rqZEnRIp.d.ts +303 -0
  18. package/dist/notrace/actions.d.ts +222 -0
  19. package/dist/notrace/actions.js +1 -0
  20. package/dist/notrace/bus-Dx0l9X6h.js +1 -0
  21. package/dist/notrace/context-yAWj0BFZ.js +1 -0
  22. package/dist/notrace/delay-Cgvn16oY.js +1 -0
  23. package/dist/notrace/effects.d.ts +284 -0
  24. package/dist/notrace/effects.js +1 -0
  25. package/dist/notrace/event-0RIBGYqS.js +1 -0
  26. package/dist/notrace/index.d.ts +2 -0
  27. package/dist/notrace/index.js +1 -0
  28. package/dist/notrace/policies.d.ts +171 -0
  29. package/dist/notrace/policies.js +1 -0
  30. package/dist/notrace/promise-CSLpzwSR.js +1 -0
  31. package/dist/notrace/redux.d.ts +92 -0
  32. package/dist/notrace/redux.js +1 -0
  33. package/dist/observable.d.ts +20 -0
  34. package/dist/observable.js +1 -0
  35. package/dist/onAbort-BgMfspK4.js +1 -0
  36. package/dist/policies.d.ts +171 -0
  37. package/dist/policies.js +1 -0
  38. package/dist/production/dev.js +1 -0
  39. package/dist/promise-CSLpzwSR.js +1 -0
  40. package/dist/redux.d.ts +92 -0
  41. package/dist/redux.js +1 -0
  42. package/dist/trace.d.ts +162 -0
  43. package/dist/trace.js +1 -0
  44. package/docs/README.md +15 -0
  45. package/docs/actions.md +78 -0
  46. package/docs/context.md +15 -0
  47. package/docs/effects.md +137 -0
  48. package/docs/errors.md +23 -0
  49. package/docs/guide.md +229 -0
  50. package/docs/hmr.md +36 -0
  51. package/docs/observable.md +22 -0
  52. package/docs/policies.md +53 -0
  53. package/docs/semantics.md +7 -0
  54. package/docs/stores.md +50 -0
  55. package/docs/tracing.md +117 -0
  56. package/package.json +184 -5
@@ -0,0 +1,303 @@
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
27
+ //#region src/core/store.d.ts
28
+ declare const anyEventType = "*";
29
+ //#endregion
30
+ //#region src/core/tracer.d.ts
31
+ /**
32
+ * What the bus needs from tracing. The implementation, `createTracer` in
33
+ * `nanoeffects/trace`, holds all the machinery (ids, events, loggers), so a
34
+ * bus without `trace` carries none of it. `Ref` is an opaque handle that
35
+ * `task` makes for each task.
36
+ */
37
+ interface Tracer<Ref extends object = object> {
38
+ /** Registers a task and returns its handle; `parent` is the handle of its parent task. */
39
+ task(event: {
40
+ type: string;
41
+ payload?: unknown;
42
+ }, parent?: Ref): Ref;
43
+ /** Reports a phase of a task. */
44
+ report(ref: Ref, effect: EffectName, phase: Phase, error?: unknown): void;
45
+ /** Returns `settle(phase, error?)` that reports only the first phase it gets. */
46
+ settler(ref: Ref, effect: EffectName): (phase: Phase, error?: unknown) => void;
47
+ /**
48
+ * Runs `fn`, reporting `start` and then `done` / `error` of its (sync or
49
+ * async) result, or `cancel` if `signal` aborts first.
50
+ */
51
+ run<R>(ref: Ref, effect: EffectName, signal: AbortSignal, fn: () => R): R;
52
+ }
53
+ //#endregion
54
+ //#region src/core/bus.d.ts
55
+ /** Subscribes to every event; handlers receive the payload of any of them. */
56
+ type AnyEvent = typeof anyEventType;
57
+ type EventKey<E extends EventMap> = Keys<E>;
58
+ type EventPayload<E extends EventMap> = E[EventKey<E>];
59
+ type WithType<K extends string> = {
60
+ type: K;
61
+ };
62
+ type HandlerRef<E extends EventMap, Response = unknown> = (action: Action<E>) => Response;
63
+ type Handler<E extends EventMap, Response = unknown, K extends EventKey<E> = EventKey<E>> = { [P in K]: {
64
+ type: P;
65
+ (payload: E[P]): Response;
66
+ }; }[K];
67
+ type Action<E extends EventMap> = {
68
+ type: EventKey<E>;
69
+ payload: EventPayload<E>;
70
+ };
71
+ type WithPayload<E extends EventMap, K extends EventKey<E>> = undefined extends E[K] ? {
72
+ type: K;
73
+ payload?: E[K];
74
+ } : {
75
+ type: K;
76
+ payload: E[K];
77
+ };
78
+ type EventLike<E extends EventMap> = { [K in EventKey<E>]: WithPayload<E, K>; }[Keys<E>];
79
+ /**
80
+ * A typed request for a result. `bus.emit(command, payload)` runs the handler
81
+ * of the command as a task and returns what it returns. Made by
82
+ * `createCommand` in `nanoeffects/actions`.
83
+ */
84
+ type Command<P = never, R = unknown> = {
85
+ (payload: P, ctx?: never): R;
86
+ readonly type: string;
87
+ /** Wraps the handler that really runs, e.g. to route its outcome. */
88
+ readonly wrap?: (run: (payload: never, ctx: never) => unknown) => (payload: never, ctx: never) => unknown;
89
+ };
90
+ /**
91
+ * What the bus needs to run commands. The implementation, `createCommands` in
92
+ * `nanoeffects/actions`, holds the handlers bound to commands and runs the
93
+ * right one, so a bus without `commands` carries none of that.
94
+ */
95
+ interface Commands {
96
+ run(command: Command, payload: unknown, ctx: unknown): unknown;
97
+ }
98
+ /** Anything `emit` takes as its first argument: an event type, an action or a command. */
99
+ type Emittable<E extends EventMap> = EventKey<E> | Command | EventLike<E>;
100
+ type Emit<E extends EventMap> = {
101
+ <K extends EventKey<E>>(event: K, ...payload: PayloadOf<E[K]>): void;
102
+ <K extends EventKey<E>, R>(event: Handler<E, R, K>, ...payload: PayloadOf<E[K]>): R;
103
+ <P, R>(command: Command<P, R>, ...payload: PayloadOf<P>): R;
104
+ (event: EventLike<E>): void;
105
+ };
106
+ type On<E extends EventMap> = {
107
+ (event: AnyEvent, fn: Task<EventPayload<E>, E>): UnsubscribeCallback;
108
+ <K extends EventKey<E>>(event: K, fn: Task<E[K], E>): UnsubscribeCallback;
109
+ /** An action creator without a payload type: the payload comes from the event map. */
110
+ <K extends EventKey<E>>(creator: FreeActionCreator<K>, fn: Task<E[K], E>): UnsubscribeCallback;
111
+ /** An action creator: the payload comes from it, so an event map is not needed. */
112
+ <P>(creator: ActionCreator<P>, fn: Task<P, E>): UnsubscribeCallback;
113
+ /** A command: the payload comes from it too. */
114
+ <P>(command: Command<P, unknown>, fn: Task<P, E>): UnsubscribeCallback;
115
+ <K extends EventKey<E>, Event extends {
116
+ type: K;
117
+ }>(event: Event & {
118
+ type: K;
119
+ }, fn: Task<E[K], E>): UnsubscribeCallback;
120
+ };
121
+ type Off<E extends EventMap> = {
122
+ (event: AnyEvent): void;
123
+ (event: AnyEvent, fn: Task<EventPayload<E>, E>): void;
124
+ <K extends EventKey<E>>(event: K): void;
125
+ <K extends EventKey<E>>(event: K, fn: Task<E[K], E>): void;
126
+ <K extends EventKey<E>, Event extends {
127
+ type: K;
128
+ }>(event: Event & {
129
+ type: K;
130
+ }, fn: Task<E[K], E>): void;
131
+ <P>(command: Command<P, unknown>, fn?: Task<P, E>): void;
132
+ };
133
+ declare const contextKey: unique symbol;
134
+ /**
135
+ * Your own fields of every task context, in the event map: add it to the
136
+ * events with `&` and give the values in `createBus({ context })`. The fields
137
+ * are in the context of every task of that bus, and the compiler checks them.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * type Events = { save: Draft } & WithContext<{ store: Store }>;
142
+ *
143
+ * const bus = createBus<Events>({ context: { store } });
144
+ * bus.on("save", (draft, { store }) => store.put(draft));
145
+ * ```
146
+ */
147
+ type WithContext<Fields extends object> = {
148
+ readonly [contextKey]?: Fields;
149
+ };
150
+ /** The fields that the event map `E` adds to the context of a task. */
151
+ type ContextOf<E extends EventMap> = typeof contextKey extends keyof E ? NonNullable<E[typeof contextKey]> : unknown;
152
+ /** Fields for the context of every task, or a function that makes them for one task. */
153
+ type ContextOption<E extends EventMap> = ContextOf<E> | ((event: {
154
+ type: string;
155
+ payload?: unknown;
156
+ }) => ContextOf<E>);
157
+ /** Task context inside an effect. */
158
+ type Context<E extends EventMap> = EventLike<E> & ContextOf<E> & {
159
+ signal: AbortSignal;
160
+ /** Abort this task (and attached children). */
161
+ abort: (reason?: unknown) => void;
162
+ /** Emit as this task; no-op after the task is aborted. */
163
+ emit: Emit<E>;
164
+ /**
165
+ * Subscribe as this task. The subscription is removed when the task aborts;
166
+ * after that this is a no-op that returns a no-op.
167
+ */
168
+ on: On<E>;
169
+ /** Same as the bus `off`. */
170
+ off: Off<E>;
171
+ /** Child task: aborted with the parent; errors propagate to the parent. */
172
+ run<P, R>(fn: Task<P, E, R>, ...args: PayloadOf<P>): Promise<Awaited<R>>;
173
+ };
174
+ interface Bus<E extends EventMap> {
175
+ on: On<E>;
176
+ off: Off<E>;
177
+ emit: Emit<E>;
178
+ /**
179
+ * Calls `plugin` with the bus and returns what it returns: an api, a
180
+ * function that undoes it. A plugin is any function of the bus; the bus keeps
181
+ * no list of them. See `modulePlugin` in `nanoeffects/actions`.
182
+ */
183
+ use<R>(plugin: (bus: Bus<E>) => R): R;
184
+ /** `BusOptions.context` and `.trace`, and `commands`, for what makes tasks of its own, like `modulePlugin`. */
185
+ readonly context?: ContextOption<E> | undefined;
186
+ /** What runs the commands; `createCommands(bus)` and `modulePlugin` set it. Without one a command runs its own handler. */
187
+ commands?: Commands | undefined;
188
+ readonly trace?: Tracer | undefined;
189
+ }
190
+ type BusOptions<E extends EventMap> = {
191
+ /** Tracing of every task phase; make it with `createTracer` from `nanoeffects/trace`. */
192
+ trace?: never;
193
+ /**
194
+ * Receives errors of handlers: thrown ones and rejections of async ones.
195
+ * Without it handlers run like in a plain loop: the first error stops the
196
+ * rest and is thrown to the caller of `emit`, and rejections of async
197
+ * handlers are left unhandled. With it every handler runs and errors go
198
+ * here; an `onError` that throws is logged to `console.error`.
199
+ */
200
+ onError?: (error: unknown, event: Action<E>) => void;
201
+ /**
202
+ * Called when an event is emitted and nobody listens to it: the usual sign
203
+ * of a typo in an event name. Not called for commands, which always have a
204
+ * handler to run. Typically a warning in development only.
205
+ *
206
+ * @example
207
+ * ```ts
208
+ * createBus<Events>({
209
+ * onUnhandled: import.meta.env.DEV ? ({ type }) => console.warn(`Nobody listens to "${type}"`) : void 0,
210
+ * });
211
+ * ```
212
+ */
213
+ onUnhandled?: (event: Action<E>) => void;
214
+ /**
215
+ * Fields added to the context of every task of this bus, its child tasks
216
+ * and the tasks of effects included: a store, a logger, a client. Their
217
+ * types come from `WithContext` in the event map. The fields of the library
218
+ * win over these when the names are the same. A function gets
219
+ * `{ type, payload }` of the task and returns its fields, for fields that
220
+ * depend on the task.
221
+ */
222
+ context?: ContextOption<E>;
223
+ };
224
+ declare const createBus: <Events extends EventMap>(options?: BusOptions<Events>) => Bus<Events>;
225
+ //#endregion
226
+ //#region src/actions/action.d.ts
227
+ type Absent<P> = [P] extends [void] ? undefined : P;
228
+ type Made<P, T extends string> = WithPayload<{ [K in T]: Absent<P>; }, T>;
229
+ declare const free: unique symbol;
230
+ type Free = typeof free;
231
+ /**
232
+ * A creator whose payload type was not given: each call has the type of what
233
+ * it is given, and the bus checks it against its event map, so
234
+ * `createAction("user/loaded")` needs no generics and keeps its literal type.
235
+ */
236
+ interface FreeActionCreator<T extends string> {
237
+ (): {
238
+ type: T;
239
+ payload: undefined;
240
+ };
241
+ <P>(payload: P): {
242
+ type: T;
243
+ payload: P;
244
+ };
245
+ readonly type: T;
246
+ /** Tells whether an action is one of this creator. */
247
+ match(action: {
248
+ type: string;
249
+ payload?: unknown;
250
+ }): action is {
251
+ type: T;
252
+ payload: unknown;
253
+ };
254
+ toString(): T;
255
+ }
256
+ /**
257
+ * Makes actions of one type. It also works as the event of that type:
258
+ * `bus.on(creator, fn)` and `bus.emit(creator(payload))`.
259
+ */
260
+ interface ActionCreator<P = void, T extends string = string, A extends unknown[] = PayloadOf<P>> {
261
+ (...args: A): Made<P, T>;
262
+ readonly type: T;
263
+ /** Tells whether an action is one of this creator, and narrows its type. */
264
+ match(action: {
265
+ type: string;
266
+ payload?: unknown;
267
+ }): action is Made<P, T>;
268
+ toString(): T;
269
+ }
270
+ /**
271
+ * Creates an action creator, as in Redux Toolkit.
272
+ *
273
+ * - `createAction("user/loaded")` needs no generics: the payload type comes
274
+ * from the event map of the bus that the action goes to, and the literal
275
+ * type of the action is kept.
276
+ * - `createAction<User>("user/loaded")` gives the payload type, for a bus
277
+ * without an event map or to check the calls of the creator. The type of
278
+ * the action is then `string`; to keep the literal, give it as the second
279
+ * generic: `createAction<User, "user/loaded">("user/loaded")`.
280
+ * - With `prepare` the arguments of the creator can differ from the payload,
281
+ * and both types are inferred.
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * const added = createAction<number>("counter/added");
286
+ * added(2); // { type: "counter/added", payload: 2 }
287
+ * added.type; // "counter/added"
288
+ * added.match(action); // true for actions of this type
289
+ *
290
+ * const renamed = createAction("user/renamed", (id: string, name: string) => ({
291
+ * payload: { id, name },
292
+ * }));
293
+ *
294
+ * bus.emit(added(2));
295
+ * bus.on(added, (n) => console.log(n));
296
+ * ```
297
+ */
298
+ declare function createAction<P = Free, T extends string = string>(type: T): [P] extends [Free] ? FreeActionCreator<T> : ActionCreator<P, T>;
299
+ declare function createAction<A extends unknown[], P, T extends string = string>(type: T, prepare: (...args: A) => {
300
+ payload: P;
301
+ }): ActionCreator<P, T, A>;
302
+ //#endregion
303
+ export { Keys as A, WithPayload as C, anyEventType as D, Tracer as E, TraceEvent as F, TraceLogger as I, UnsubscribeCallback as L, Phase as M, Task as N, EffectName as O, Trace as P, WithContext as S, createBus as T, EventPayload as _, AnyEvent as a, Off as b, Command as c, ContextOf as d, ContextOption as f, EventLike as g, EventKey as h, Action as i, PayloadOf as j, EventMap as k, Commands as l, Emittable as m, FreeActionCreator as n, Bus as o, Emit as p, createAction as r, BusOptions as s, ActionCreator as t, Context as u, Handler as v, WithType as w, On as x, HandlerRef as y };
@@ -0,0 +1,222 @@
1
+ import { L as UnsubscribeCallback, N as Task, c as Command, j as PayloadOf, k as EventMap, n as FreeActionCreator, o as Bus, r as createAction, t as ActionCreator, u as Context } from "./action-rqZEnRIp.js";
2
+ //#region src/actions/command.d.ts
3
+ /**
4
+ * Creates a command: a typed request for a result. `bus.emit(command, payload)`
5
+ * runs its handler as a task, with a context, and returns what the handler
6
+ * returns. Listeners of the command's type are told first, as for any event.
7
+ *
8
+ * The handler is optional. A command without one is only a contract, and the
9
+ * bus gets its implementation from the `commands` registry (see
10
+ * `createCommands` and `bindHandler`). A handler bound there wins over the one
11
+ * the command was created with, so a test can give a bus its own implementation.
12
+ *
13
+ * A handler can be wrapped in a policy, e.g. `latest(fn)`. Calling the command
14
+ * directly, `command(payload)`, runs its own handler outside of any bus.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const getUser = createCommand("user/get", (id: string, { signal }) =>
19
+ * fetchUser(id, { signal }),
20
+ * );
21
+ * const sendMail = createCommand<Mail, void>("mail/send"); // implemented elsewhere
22
+ *
23
+ * const commands = createCommands([bindHandler(sendMail, smtpSend)]);
24
+ * const bus = createBus({ commands });
25
+ * const user = await bus.emit(getUser, "1");
26
+ *
27
+ * commands.handle(getUser, async () => fakeUser); // in a test
28
+ * ```
29
+ */
30
+ export declare const createCommand: <P = void, R = void, T extends string = string, E extends EventMap = EventMap>(type: T, handler?: Task<P, E, R>) => Command<P, R> & {
31
+ readonly type: T;
32
+ };
33
+ /** A command (or its type) with the handler to bind to it; `bindHandler` makes one. */
34
+ type Binding<E extends EventMap = EventMap> = readonly [Command<never, unknown> | string, Task<never, E, unknown>];
35
+ /**
36
+ * Pairs a command with the handler to bind to it, for `createCommands`. It
37
+ * checks that the handler fits the command.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * createCommands([bindHandler(sendMail, smtpSend)]);
42
+ * ```
43
+ */
44
+ export declare const bindHandler: <P, R, E extends EventMap>(command: Command<P, R>, fn: Task<P, E, R>) => Binding<E>;
45
+ /**
46
+ * The handlers bound to commands of `bus`: it makes the registry, sets it as
47
+ * the commands of the bus and returns it. Each bus has its own, so each test
48
+ * can have its own implementation. `handle` binds more later and returns the
49
+ * function that unbinds. A second `createCommands(bus)` replaces the registry
50
+ * of that bus.
51
+ *
52
+ * What the bus runs for a command: the handler bound last, else the one the
53
+ * command was created with, else it throws `No handler for command "..."`.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const bus = createBus<Events>();
58
+ * const commands = createCommands(bus, [bindHandler(sendMail, smtpSend)]);
59
+ *
60
+ * const unbind = commands.handle(getUser, async () => fakeUser);
61
+ * unbind(); // the command's own handler is back
62
+ * ```
63
+ */
64
+ export declare const createCommands: <E extends EventMap>(bus: Bus<E>, bindings?: readonly Binding<E>[]) => {
65
+ run: (command: Command, payload: unknown, ctx: unknown) => unknown;
66
+ handle: {
67
+ <P, R>(command: Command<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
68
+ (type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
69
+ (bindings: readonly Binding<E>[]): UnsubscribeCallback;
70
+ };
71
+ };
72
+ //#endregion
73
+ //#region src/actions/job.d.ts
74
+ type Creator = {
75
+ (...args: never[]): {
76
+ type: string;
77
+ payload?: unknown;
78
+ };
79
+ readonly type: string;
80
+ };
81
+ type Outcome = string | Creator;
82
+ /** What `createJob` returns: the command of the job and the actions of its outcome. */
83
+ type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
84
+ command: Command<P, Promise<Awaited<R> | undefined>> & {
85
+ readonly type: T;
86
+ };
87
+ /** Made from a string with the result as its payload, or the creator that was given. */
88
+ successAction: S extends string ? ActionCreator<Awaited<R>, S> : S;
89
+ /** Made from a string with the error as its payload, or the creator that was given. */
90
+ failAction: F extends string ? ActionCreator<unknown, F> : F;
91
+ };
92
+ /**
93
+ * Creates a job: a command whose outcome is sent on as actions. When the
94
+ * handler succeeds, `successAction(result)` is emitted on the bus, and when it
95
+ * fails, `failAction(error)`. It is made for the case where nobody waits for
96
+ * the result and the bus carries it on, like `createAsyncThunk` or an effector
97
+ * effect. It returns the three together: `{ command, successAction, failAction }`.
98
+ *
99
+ * `success` and `fail` are a type string, from which the job makes an action
100
+ * creator with `createAction`, or an action creator that exists already, which
101
+ * is used and returned as it is. A missing one is made with the type
102
+ * `<type>/success` or `<type>/fail`.
103
+ *
104
+ * `bus.emit(command, payload)` returns a promise of the result. An error of the
105
+ * handler always counts as handled: it goes to `failAction` and the promise
106
+ * resolves with `undefined`; a command is the tool for a result that must reject.
107
+ * The actions are emitted as the task that ran the job, also for a handler bound
108
+ * with `commands.handle`, and are skipped once the task is aborted. A missing
109
+ * handler is not an outcome: `emit` throws `MissingHandlerError`. Calling the
110
+ * command directly runs only its handler.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const { command: loadUser, successAction: userLoaded, failAction: userFailed } =
115
+ * createJob("user/load", fetchUser, { success: "user/loaded", fail: "user/failed" });
116
+ *
117
+ * bus.on(userLoaded, (user) => render(user));
118
+ * bus.emit(loadUser, "1"); // userLoaded(user) or userFailed(error) comes later
119
+ * ```
120
+ */
121
+ 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`>(type: T, handler?: Task<P, E, R>, outcomes?: {
122
+ /** Gets the result when the handler succeeds: a type to make an action creator of, or an action creator. */
123
+ success?: S;
124
+ /** Gets the error when the handler fails: a type to make an action creator of, or an action creator. */
125
+ fail?: F;
126
+ }) => Job<P, R, T, S, F>;
127
+ //#endregion
128
+ //#region src/actions/module.d.ts
129
+ /**
130
+ * What a module's `setup` gets: the context of the module's task, so `on`,
131
+ * `emit` and `run` work as in a handler, plus `handle` to bind commands, `use`
132
+ * to mount sub-modules and `spawn` to start a background task. Everything the
133
+ * module does is undone when it is unmounted.
134
+ */
135
+ type ModuleContext<E extends EventMap = EventMap> = Context<E> & {
136
+ /** Binds the handler of a command, or a list of them; undone on unmount. */
137
+ handle<P, R>(command: Command<P, R>, fn: Task<P, E, R>): UnsubscribeCallback;
138
+ handle(type: string, fn: Task<never, E, unknown>): UnsubscribeCallback;
139
+ handle(bindings: readonly Binding<E>[]): UnsubscribeCallback;
140
+ /**
141
+ * Brings sub-modules and commands, in any order. A sub-module is mounted and
142
+ * unmounted with this one. A command is bound to the handler it carries, for
143
+ * as long as this module is mounted; a command that answers by itself keeps
144
+ * answering after the module is gone, since the handler is its own. The
145
+ * returned function undoes just this call, e.g. for what was added later from
146
+ * a listener.
147
+ */
148
+ use(...items: Array<Module<E> | Command<never, unknown>>): UnsubscribeCallback;
149
+ /**
150
+ * Starts a background task that lives as long as the module: it is aborted
151
+ * on unmount, and an error of it is left unhandled, as in any handler.
152
+ */
153
+ spawn<P>(fn: Task<P, E, unknown>, ...payload: PayloadOf<P>): void;
154
+ };
155
+ type Module<E extends EventMap = EventMap> = {
156
+ readonly name: string;
157
+ readonly setup: (m: ModuleContext<E>) => void;
158
+ };
159
+ /**
160
+ * Describes one domain of an app: the commands it handles, the events it
161
+ * listens to, its sub-modules and its background tasks. The `name` shows up as
162
+ * the type of the module's tasks in the trace.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * export const userModule = createModule<Events>("user", (m) => {
167
+ * m.handle(getUser, fetchUser);
168
+ * m.handle([bindHandler(sendMail, smtpSend)]);
169
+ * m.use(authModule);
170
+ *
171
+ * m.on(loginRequested, (user, ctx) => ctx.emit(userLoaded(user)));
172
+ * m.spawn(async (_, ctx) => {
173
+ * for (;;) await handleNext(await take(ctx, "job"));
174
+ * });
175
+ * });
176
+ * ```
177
+ */
178
+ export declare const createModule: <E extends EventMap = EventMap>(name: string, setup: (m: ModuleContext<E>) => void) => Module<E>;
179
+ type Registry<E extends EventMap> = ReturnType<typeof createCommands<E>>;
180
+ /** What `modulePlugin` returns. */
181
+ type ModuleApi<E extends EventMap = EventMap> = {
182
+ /** The registry of command handlers of the bus: the one it was made with, or the app's own. */
183
+ commands: Registry<E>;
184
+ /** Mounts a module and returns the function that unmounts it; a module that is mounted already is not run again. */
185
+ mount(module: Module<E>): UnsubscribeCallback;
186
+ unmount(module: Module<E>): void;
187
+ isMounted(module: Module<E>): boolean;
188
+ /** Unmounts every module; a disposed app cannot mount modules. */
189
+ dispose(): void;
190
+ };
191
+ /**
192
+ * The plugin that assembles an app from modules: `bus.use(modulePlugin(modules))`
193
+ * mounts `modules` at once, in the order given, and returns `mount`, `unmount`,
194
+ * `isMounted`, `dispose` and `commands` for the modules that come and go later. The bus runs the commands that the
195
+ * modules bind: if `createCommands(bus)` was called before, that registry is
196
+ * used, otherwise the app attaches its own, as `app.commands`.
197
+ *
198
+ * A module is known by its `name`: mounting a module whose name is taken
199
+ * replaces the mounted one, which is unmounted first. That is what hot reload
200
+ * needs (see `docs/hmr.md`): the new version of a module file mounts again and
201
+ * the old version goes away with its subscriptions, commands and tasks.
202
+ *
203
+ * Unmounting undoes everything the module did: its subscriptions, its bound
204
+ * commands, its sub-modules and its background tasks. `dispose` unmounts them
205
+ * all, and a disposed app cannot mount modules.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const bus = createBus<Events>();
210
+ * const app = bus.use(modulePlugin([userModule]));
211
+ *
212
+ * const unmount = app.mount(orderModule); // later, e.g. after a lazy import
213
+ * unmount(); // or app.unmount(orderModule)
214
+ *
215
+ * bus.emit(getUser, "1");
216
+ * app.commands.handle(getUser, fakeUser); // in a test
217
+ * app.dispose();
218
+ * ```
219
+ */
220
+ export declare const modulePlugin: <E extends EventMap>(modules?: readonly Module<E>[]) => (bus: Bus<E>) => ModuleApi<E>;
221
+ //#endregion
222
+ export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, createAction };
@@ -0,0 +1 @@
1
+ import{i as e,n as t,s as n}from"./context-yAWj0BFZ.js";import{t as r}from"./event-0RIBGYqS.js";function i(e,t){return Object.assign((...n)=>({type:e,payload:t?t(...n).payload:n[0]}),{type:e,match:t=>t.type===e,toString:()=>e})}var a=class extends Error{};const o=(e,n)=>Object.assign((r,i)=>{if(!n)throw new a(`No handler for command "${e}"`);return n(r,i??t({event:{type:e,payload:r}}).ctx)},{type:e}),s=(e,t)=>[e,t],c=(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(t);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 e.commands=o,o},l=e=>typeof e==`string`?i(e):e,u=(e,t,n)=>{let r=e.emit;r(Reflect.apply(t,null,[n]))},d=e=>{try{return Promise.resolve(e())}catch(e){if(e instanceof a)throw e;return Promise.reject(e)}},f=(e,t,n={})=>{let r=o(e,t),i=l(n.success??`${e}/success`),a=l(n.fail??`${e}/fail`),s=(e,t,n)=>{let r=n;return d(()=>Reflect.apply(e,null,[t,n])).then(e=>(u(r,i,e),e),e=>{u(r,a,e)})};return{command:Object.assign((e,t)=>t?s(r,e,t):d(()=>r(e)),{type:e,wrap:e=>(t,n)=>s(e,t,n)}),successAction:i,failAction:a}},p=(e,t)=>({name:e,setup:t}),m=(r=[])=>i=>{let a=i.commands??c(i),o={handle:(...e)=>{if(!a.handle)throw Error("modulePlugin: the `commands` of the bus is not a registry from `createCommands()`");return Reflect.apply(a.handle,null,e)}},s=t({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),l=t=>{let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map;return{mount:a=>{let s=r.get(a);if(s)return s;let c=i.get(a.name);c&&r.get(c)?.();let u=e(t,a.name),d=u.ctx,f=l(d),p=()=>{r.delete(a),i.get(a.name)===a&&i.delete(a.name),d.abort(),u.unlink()};r.set(a,p),i.set(a.name,a);let m=(e,t)=>{let r=Reflect.apply(o.handle,null,[e,t]);return n(d,r),r},h=(...e)=>{let t=e.map(e=>typeof e==`function`?m(e,e):f.mount(e));return()=>{for(let e of t)e()}},g=(e,...t)=>{d.run(e,...t).catch(e=>{if(!d.signal.aborted)throw e})};try{a.setup(Object.assign(d,{handle:m,use:h,spawn:g}))}catch(e){throw p(),e}return p},unmount:e=>r.get(e)?.(),isMounted:e=>r.has(e)}},u=l(s.ctx);for(let e of r)u.mount(e);return{commands:a,mount:e=>(s.ctx.signal.throwIfAborted(),u.mount(e)),unmount:u.unmount,isMounted:u.isMounted,dispose:()=>s.ctx.abort()}};export{s as bindHandler,i as createAction,o as createCommand,c as createCommands,f as createJob,p as createModule,m as modulePlugin};
@@ -0,0 +1 @@
1
+ import{a as e,c as t,n,r}from"./context-yAWj0BFZ.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=/* @__PURE__ */ new WeakSet,n=n=>{let r=e[n]??[];if(!t.has(r))return e[n]=r,r;let i=[...r];return e[n]=i,i},r=(t,r)=>{if(!r){e[t]=[];return}let i=(e[t]??[]).indexOf(r);i>=0&&n(t).splice(i,1)};return{get:n=>{let r=e[n]??[];return t.add(r),r},add:(e,t)=>(n(e).push(t),()=>{r(e,t)}),remove:r}},l=(e,t,n)=>Reflect.apply(e,null,[t,n]),u=(s={})=>{let u=c(),d=(e,t)=>u.add(o(e),t),f=(e,t)=>u.remove(o(e),t),p=(e,a)=>{let{onError:o}=s,{ctx:c}=n({event:a,bus:m,tracer:s.trace,parent:{ref:r()}});if(!o){e(a.payload,c);return}let l=e=>{try{o(e,a)}catch(e){t(`onError failed`,e)}};try{let t=e(a.payload,c);i(t)&&Promise.resolve(t).catch(l)}catch(e){l(e)}},m={on:d,off:f,emit:(t,i)=>{let c=o(t),d=a(t,i),f={type:c,payload:d},h=[...u.get(c),...u.get(`*`)];h.length===0&&typeof t!=`function`&&s.onUnhandled?.(f);for(let e of h)p(e,f);if(typeof t==`function`){let{ctx:i}=n({event:f,bus:m,tracer:s.trace,parent:{ref:r()}}),a=m.commands?.run??l;return e(i,`command`,()=>a(t,d,i))}},use:e=>e(m),context:s.context,trace:s.trace};return m};export{s as n,u as t};
@@ -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)}},a=e=>{let t=new AbortController;return{controller:t,unlink:i({signal:e},e=>t.abort(e))}};let o;const s=()=>o,c=()=>{},l=(e,t,n,r)=>{},u=(e,t)=>c,d=(e,t,n)=>n(),f=(e,t)=>typeof e==`function`?e(t):e,p=e=>e,m=({event:e,bus:t,tracer:n,parent:r})=>{let{controller:s,unlink:l}=a(r?.signal),{signal:u}=s,h=(...e)=>{if(u.aborted)return;let n=t?.emit,r=o;o=void 0;try{return n?.(...e)}finally{o=r}},g=(...e)=>{if(u.aborted)return c;let n=t?.on,r=n?.(...e);if(!r)return c;let a=i({signal:u},r);return()=>{a(),r()}},_=async(r,...i)=>{u.throwIfAborted();let a=i[0],o=m({event:{type:e.type,payload:a},bus:t,tracer:n,parent:{signal:u,ref:void 0}});try{return await d(o.ctx,`run`,()=>r(a,o.ctx))}finally{o.unlink()}};return{ctx:p({...f(t?.context,e),type:e.type,payload:e.payload,signal:u,abort:e=>s.abort(e),emit:h,on:g,off:t?.off??c,run:_}),unlink:l}},h=(e,t)=>m({event:{type:t??e.type??``,payload:e.payload},bus:{emit:e.emit,on:e.on,off:e.off,context:e},tracer:void 0,parent:{signal:e.signal,ref:void 0}});export{d as a,e as c,h as i,m as n,l as o,s as r,i as s,u as t};
@@ -0,0 +1 @@
1
+ import{s as e}from"./context-yAWj0BFZ.js";const t=2**31-1,n=(e,n)=>{let r={id:null};if(n===1/0)return r;let i=n=>{r.id=setTimeout(()=>{n>t?i(n-t):(r.id=null,e())},Math.min(n,t))};return i(n>0?n:0),r},r=e=>{e.id!==null&&(clearTimeout(e.id),e.id=null)},i=(t,i)=>new Promise((a,o)=>{let s=n(()=>{c(),a()},typeof i==`function`?i():i),c=e(t,e=>{r(s),o(e)})});export{r as n,n as r,i as t};