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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vladimir Yazykov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,97 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nanoeffects
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/nanoeffects)
|
|
4
|
+
[](https://github.com/nanoeffects/nanoeffects/actions/workflows/ci.yml)
|
|
5
|
+
[](https://bundlephobia.com/package/nanoeffects)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
A small typed event bus. Handlers are tasks with an `AbortSignal`, and policies (`latest`, `debounce`, `throttle`, `queue`) and effects (`retry`, `timeout`) are plain functions that go straight into `on`.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { createBus } from "nanoeffects";
|
|
12
|
+
|
|
13
|
+
type Events = { greet: string };
|
|
14
|
+
|
|
15
|
+
const bus = createBus<Events>();
|
|
16
|
+
|
|
17
|
+
const unbind = bus.on("greet", (name) => console.log(`hello ${name}`));
|
|
18
|
+
|
|
19
|
+
bus.emit("greet", "Ada"); // hello Ada
|
|
20
|
+
unbind(); // `on` returns the unsubscribe function, as in nanoevents
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`emit` takes the event as a string, as an object `{ type, payload }` (a Redux action fits as it is), or as a function with a `type`, a command that runs and returns its result:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
bus.emit("greet", "Ada");
|
|
27
|
+
bus.emit({ type: "greet", payload: "Ada" });
|
|
28
|
+
bus.emit(double, 21); // a command: runs, returns 42
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then the same bus with a policy, no other change:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { latest } from "nanoeffects/policies";
|
|
35
|
+
|
|
36
|
+
// a newer search aborts the older one
|
|
37
|
+
bus.on(
|
|
38
|
+
"search",
|
|
39
|
+
latest(async (query, ctx) => {
|
|
40
|
+
const items = await api.search(query, { signal: ctx.signal });
|
|
41
|
+
ctx.emit("results", items);
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **Tiny.** `createBus` is about 1.2 kB gzip; every other entry is bundled only when imported.
|
|
47
|
+
- **Cancellation first.** Aborting a task aborts its children and `ctx.on` subscriptions and drops its result.
|
|
48
|
+
- **One model** instead of `AbortController`, lodash `debounce`, `p-queue` and a hand-written retry.
|
|
49
|
+
- **Typed** commands, actions, jobs and modules; **traced** with Redux DevTools; the trace can be cut out of the build.
|
|
50
|
+
- **Checked** against lodash on random call sequences, and against known issues of RxJS, p-queue, p-retry and effector.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install nanoeffects
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
ESM only, no dependencies, types included.
|
|
59
|
+
|
|
60
|
+
## Entries
|
|
61
|
+
|
|
62
|
+
| Import | What is in it |
|
|
63
|
+
| ------------------------ | ----------------------------------------------------------------------------- |
|
|
64
|
+
| `nanoeffects` | `createBus`, the types |
|
|
65
|
+
| `nanoeffects/policies` | `latest`, `leading`, `debounce`, `throttle`, `queue` |
|
|
66
|
+
| `nanoeffects/effects` | `retry`, `timeout`, `poll`, `delay`, `take`, `all`, `allSettled`, `race` |
|
|
67
|
+
| `nanoeffects/actions` | `createAction`, `createCommand`, `createJob`, `createModule`, `modulePlugin` |
|
|
68
|
+
| `nanoeffects/redux` | `createEffectsMiddleware`, `nanoeffectsMiddleware` |
|
|
69
|
+
| `nanoeffects/observable` | `createObserve`: an event as an Observable (RxJS) |
|
|
70
|
+
| `nanoeffects/dev` | `devPlugin`: warnings about listeners that pile up and messages about modules; a no-op in production |
|
|
71
|
+
| `nanoeffects/trace` | `createTracer` and the loggers |
|
|
72
|
+
|
|
73
|
+
## The task context
|
|
74
|
+
|
|
75
|
+
`bus.on(event, handler)` calls `handler(payload, ctx)`. Each call is a task:
|
|
76
|
+
|
|
77
|
+
| `ctx` | Meaning |
|
|
78
|
+
| ----------------- | -------------------------------------------------------- |
|
|
79
|
+
| `signal`, `abort` | the signal of the task, and a way to abort it |
|
|
80
|
+
| `emit` | emit as this task; does nothing after an abort |
|
|
81
|
+
| `on`, `off` | subscribe; the subscription ends when the task aborts |
|
|
82
|
+
| `run(fn, ...)` | a child task, aborted with this one |
|
|
83
|
+
| `type`, `payload` | the event the task works on |
|
|
84
|
+
|
|
85
|
+
The event map (`type Events = { name: Payload }`) is optional: `createBus()` takes any string event with an `unknown` payload. `createAction("user/loaded")` takes its payload type from the map, and `createAction<User>("user/loaded")` gives it when there is none.
|
|
86
|
+
|
|
87
|
+
## Docs
|
|
88
|
+
|
|
89
|
+
[**Guide**](docs/guide.md) · [Policies](docs/policies.md) · [Effects](docs/effects.md) · [Actions, commands, modules](docs/actions.md) · [Context fields](docs/context.md) · [Errors](docs/errors.md) · [Tracing](docs/tracing.md) · [Redux, Zustand, MobX](docs/stores.md) · [Observable](docs/observable.md) · [Hot reload and dev diagnostics](docs/hmr.md) · [Semantics](docs/semantics.md) · [Examples](examples)
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pnpm verify # what CI runs: types, lint, tests, build, integration, package checks, size
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
[`AGENTS.md`](AGENTS.md) describes the structure and the rules of the code, for people and AI assistants.
|
|
@@ -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?: Tracer;
|
|
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,58 @@
|
|
|
1
|
+
import { N as Task, c as Command, k as EventMap, n as FreeActionCreator, r as createAction, t as ActionCreator } from "./action-rqZEnRIp.js";
|
|
2
|
+
import { a as modulePlugin, c as createCommand, i as createModule, l as createCommands, n as ModuleApi, o as Binding, r as ModuleContext, s as bindHandler, t as Module } from "./module-BSCdR9D1.js";
|
|
3
|
+
//#region src/actions/job.d.ts
|
|
4
|
+
type Creator = {
|
|
5
|
+
(...args: never[]): {
|
|
6
|
+
type: string;
|
|
7
|
+
payload?: unknown;
|
|
8
|
+
};
|
|
9
|
+
readonly type: string;
|
|
10
|
+
};
|
|
11
|
+
type Outcome = string | Creator;
|
|
12
|
+
/** What `createJob` returns: the command of the job and the actions of its outcome. */
|
|
13
|
+
type Job<P, R, T extends string, S extends Outcome, F extends Outcome> = {
|
|
14
|
+
command: Command<P, Promise<Awaited<R> | undefined>> & {
|
|
15
|
+
readonly type: T;
|
|
16
|
+
};
|
|
17
|
+
/** Made from a string with the result as its payload, or the creator that was given. */
|
|
18
|
+
successAction: S extends string ? ActionCreator<Awaited<R>, S> : S;
|
|
19
|
+
/** Made from a string with the error as its payload, or the creator that was given. */
|
|
20
|
+
failAction: F extends string ? ActionCreator<unknown, F> : F;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Creates a job: a command whose outcome is sent on as actions. When the
|
|
24
|
+
* handler succeeds, `successAction(result)` is emitted on the bus, and when it
|
|
25
|
+
* fails, `failAction(error)`. It is made for the case where nobody waits for
|
|
26
|
+
* the result and the bus carries it on, like `createAsyncThunk` or an effector
|
|
27
|
+
* effect. It returns the three together: `{ command, successAction, failAction }`.
|
|
28
|
+
*
|
|
29
|
+
* `success` and `fail` are a type string, from which the job makes an action
|
|
30
|
+
* creator with `createAction`, or an action creator that exists already, which
|
|
31
|
+
* is used and returned as it is. A missing one is made with the type
|
|
32
|
+
* `<type>/success` or `<type>/fail`.
|
|
33
|
+
*
|
|
34
|
+
* `bus.emit(command, payload)` returns a promise of the result. An error of the
|
|
35
|
+
* handler always counts as handled: it goes to `failAction` and the promise
|
|
36
|
+
* resolves with `undefined`; a command is the tool for a result that must reject.
|
|
37
|
+
* The actions are emitted as the task that ran the job, also for a handler bound
|
|
38
|
+
* with `commands.handle`, and are skipped once the task is aborted. A missing
|
|
39
|
+
* handler is not an outcome: `emit` throws `MissingHandlerError`. Calling the
|
|
40
|
+
* command directly runs only its handler.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const { command: loadUser, successAction: userLoaded, failAction: userFailed } =
|
|
45
|
+
* createJob("user/load", fetchUser, { success: "user/loaded", fail: "user/failed" });
|
|
46
|
+
*
|
|
47
|
+
* bus.on(userLoaded, (user) => render(user));
|
|
48
|
+
* bus.emit(loadUser, "1"); // userLoaded(user) or userFailed(error) comes later
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
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?: {
|
|
52
|
+
/** Gets the result when the handler succeeds: a type to make an action creator of, or an action creator. */
|
|
53
|
+
success?: S;
|
|
54
|
+
/** Gets the error when the handler fails: a type to make an action creator of, or an action creator. */
|
|
55
|
+
fail?: F;
|
|
56
|
+
}) => Job<P, R, T, S, F>;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { type ActionCreator, type Binding, type FreeActionCreator, type Job, type Module, type ModuleApi, type ModuleContext, bindHandler, createAction, createCommand, createCommands, createModule, modulePlugin };
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.js";import{i as t,n}from"./context-DlA80GOA.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,t)=>Object.assign((r,i)=>{if(!t)throw new a(`No handler for command "${e}"`);return t(r,i??n({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=n({event:{type:`app`,payload:void 0},bus:i,tracer:i.trace}),l=n=>{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=t(n,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=(t,n)=>{let r=Reflect.apply(o.handle,null,[t,n]);return e(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{n as e}from"./onAbort-BgMfspK4.js";import{a as t,n,r}from"./context-DlA80GOA.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=(t,a)=>{let{onError:o}=s,{ctx:c}=n({event:a,bus:m,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)}},m={on:d,off:f,emit:(e,i)=>{let c=o(e),d=a(e,i),f={type:c,payload:d},h=[...u.get(c),...u.get(`*`)];h.length===0&&typeof e!=`function`&&s.onUnhandled?.(f);for(let e of h)p(e,f);if(typeof e==`function`){let{ctx:i}=n({event:f,bus:m,tracer:s.trace,parent:{ref:r()}}),a=m.commands?.run??l;return t(i,`command`,()=>a(e,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
|
+
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?.context,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 _&&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,context: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 @@
|
|
|
1
|
+
import{t as e}from"./onAbort-BgMfspK4.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};
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { L as UnsubscribeCallback, k as EventMap, o as Bus } from "./action-rqZEnRIp.js";
|
|
2
|
+
import { n as ModuleApi } from "./module-BSCdR9D1.js";
|
|
3
|
+
//#region src/dev/types.d.ts
|
|
4
|
+
/** The parts of what `modulePlugin` returned that the diagnostics report. */
|
|
5
|
+
type ReportedApp<E extends EventMap> = Pick<ModuleApi<E>, "mount" | "unmount" | "isMounted">;
|
|
6
|
+
type DevOptions<E extends EventMap = EventMap> = {
|
|
7
|
+
/** Warn when more listeners than this are added to one event and not unsubscribed. Default 10. */
|
|
8
|
+
maxListeners?: number;
|
|
9
|
+
/** Check the number of listeners. Default `true`. */
|
|
10
|
+
leaks?: boolean;
|
|
11
|
+
/** Report the modules of `app` that are mounted, replaced and unmounted. Default `true`. */
|
|
12
|
+
modules?: boolean;
|
|
13
|
+
/** Receives every message. Default: `console.warn` for leaks and `console.info` for modules. */
|
|
14
|
+
log?: (message: string) => void;
|
|
15
|
+
/** What `modulePlugin` returned, to report its modules. */
|
|
16
|
+
app?: ReportedApp<E>;
|
|
17
|
+
};
|
|
18
|
+
/** A plugin that connects the diagnostics to a bus; the function it returns disconnects them. */
|
|
19
|
+
type DevPlugin = <E extends EventMap>(options?: DevOptions<E>) => (bus: Bus<E>) => UnsubscribeCallback;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/dev/plugin.d.ts
|
|
22
|
+
/**
|
|
23
|
+
* A plugin with development diagnostics for a bus, used while the app is built
|
|
24
|
+
* and while its files are reloaded:
|
|
25
|
+
*
|
|
26
|
+
* - **Listeners that pile up.** When more than `maxListeners` listeners are
|
|
27
|
+
* added to one event and not unsubscribed, one warning names the event and
|
|
28
|
+
* the count: a file that was reloaded and subscribes again, or an
|
|
29
|
+
* unsubscribe that is never called. The count follows the function that `on`
|
|
30
|
+
* returns; `bus.off(event, fn)` is not seen.
|
|
31
|
+
* - **Modules.** With `app` (what `modulePlugin` returned) it reports a
|
|
32
|
+
* module that is mounted, replaced by name and unmounted.
|
|
33
|
+
*
|
|
34
|
+
* It wraps `bus.on`, `app.mount` and `app.unmount`; the function that `bus.use` returns
|
|
35
|
+
* puts the originals back. In a production build `nanoeffects/dev` is a no-op, so
|
|
36
|
+
* the call can stay as it is.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const app = bus.use(modulePlugin([userModule]));
|
|
41
|
+
* const disconnect = bus.use(devPlugin({ app })); // warnings and messages in the console while developing
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare const devPlugin: DevPlugin;
|
|
45
|
+
//#endregion
|
|
46
|
+
export type { DevOptions, DevPlugin };
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./event-0RIBGYqS.js";const t=`[nanoeffects]`,n=(e={})=>t=>{let{maxListeners:n=10,leaks:a=!0,modules:o=!0,app:s}=e,c=e.log??(e=>console.warn(e)),l=e.log??(e=>console.info(e)),u=[];return a&&u.push(r(t,n,c)),o&&s&&u.push(i(s,l)),()=>{for(let e of u.splice(0))e()}},r=(n,r,i)=>{let a=n.on,o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Set;return n.on=(...c)=>{let[l]=c,u=e(l),d=(o.get(u)??0)+1;o.set(u,d),d>r&&!s.has(u)&&(s.add(u),i(`${t} ${d} listeners of "${u}" were added and not unsubscribed (more than ${r}). A file that is reloaded and subscribes again, or an unsubscribe that is never called?`));let f=Reflect.apply(a,n,c),p=!1;return()=>{if(!p){p=!0;let e=(o.get(u)??1)-1;o.set(u,e),e<=r&&s.delete(u)}f()}},()=>{n.on=a}},i=(e,n)=>{let{mount:r,unmount:i}=e,a=/* @__PURE__ */ new Map;return e.mount=i=>{let o=a.get(i.name);if(o===i&&e.isMounted(i))return r(i);let s=o!==void 0&&e.isMounted(o),c=r(i);return a.set(i.name,i),n(`${t} module "${i.name}" ${s?`replaced`:`mounted`}`),()=>{e.isMounted(i)&&n(`${t} module "${i.name}" unmounted`),c()}},e.unmount=r=>{e.isMounted(r)&&n(`${t} module "${r.name}" unmounted`),i(r)},()=>{e.mount=r,e.unmount=i}};export{n as devPlugin};
|