nanoeffects 0.9.0 → 0.11.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/dist/{action-creator-BrZF6hXc.d.ts → action-creator-9e8jesRa.d.ts} +28 -12
- package/dist/actions.d.ts +12 -5
- package/dist/actions.js +1 -1
- package/dist/bus-Cdw5ajKF.js +1 -0
- package/dist/context-CJGW6OWj.js +1 -0
- package/dist/{context-plugin--HkNN1ha.d.ts → context-plugin-Xibukkbj.d.ts} +1 -1
- package/dist/{delay-CIHnOh_L.js → delay-BnBdGPh7.js} +1 -1
- package/dist/dev.d.ts +2 -2
- package/dist/effects.d.ts +2 -2
- package/dist/effects.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/lifecycle-key-8iX8RUVD.js +1 -0
- package/dist/match.d.ts +1 -1
- package/dist/match.js +1 -1
- package/dist/{module-DqKegXHt.d.ts → module-BlkU3i6B.d.ts} +3 -3
- package/dist/notrace/{action-creator-DHAEgwuh.d.ts → action-creator-DQTwMBwP.d.ts} +27 -11
- package/dist/notrace/actions.d.ts +12 -5
- package/dist/notrace/actions.js +1 -1
- package/dist/notrace/bus-G2AdGObY.js +1 -0
- package/dist/notrace/{context-plugin-BapQkGwQ.d.ts → context-plugin-DlZCkcX9.d.ts} +1 -1
- package/dist/notrace/effects.d.ts +2 -2
- package/dist/notrace/index.d.ts +2 -2
- package/dist/notrace/index.js +1 -1
- package/dist/notrace/lifecycle-key-8iX8RUVD.js +1 -0
- package/dist/notrace/origin-key-BbWN3gmN.js +1 -0
- package/dist/notrace/origin-key-CxK0SkeG.d.ts +10 -0
- package/dist/notrace/{plugin-Cxadz6uU.d.ts → plugin-DOYHG78z.d.ts} +2 -2
- package/dist/notrace/policies.d.ts +1 -1
- package/dist/notrace/policies.js +1 -1
- package/dist/notrace/redux.d.ts +4 -3
- package/dist/notrace/redux.js +1 -1
- package/dist/observable.d.ts +1 -1
- package/dist/oop.d.ts +13 -7
- package/dist/oop.js +1 -1
- package/dist/origin-key-BbWN3gmN.js +1 -0
- package/dist/origin-key-CxK0SkeG.d.ts +10 -0
- package/dist/{plugin-DkXCvsmB.d.ts → plugin-DHkamZdH.d.ts} +2 -2
- package/dist/policies.d.ts +2 -2
- package/dist/policies.js +1 -1
- package/dist/react.d.ts +15 -8
- package/dist/react.js +1 -1
- package/dist/redux.d.ts +4 -3
- package/dist/redux.js +1 -1
- package/dist/scope.d.ts +2 -2
- package/dist/scope.js +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/signal.js +1 -1
- package/dist/trace.d.ts +30 -1
- package/dist/trace.js +1 -1
- package/dist/{types-KavbvHix.d.ts → types-ByPzv7tw.d.ts} +2 -2
- package/dist/{with-policy-DBV9dh2F.js → with-policy-CW50Edgk.js} +1 -1
- package/dist/{with-policy-CqUS-VWW.d.ts → with-policy-DpCvSZRk.d.ts} +1 -1
- package/docs/actions.md +10 -3
- package/docs/migrating-from-effector.md +1 -1
- package/docs/oop.md +2 -0
- package/docs/package.md +1 -1
- package/docs/plugins.md +16 -1
- package/docs/scope.md +1 -1
- package/docs/stores.md +5 -1
- package/docs/tracing.md +121 -1
- package/llms-full.txt +158 -10
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/dist/bus-CkTHUAvq.js +0 -1
- package/dist/context-COgyNlrC.js +0 -1
- package/dist/notrace/bus-BmCTo8BG.js +0 -1
- package/dist/onAbort-BgMfspK4.js +0 -1
package/docs/tracing.md
CHANGED
|
@@ -21,12 +21,14 @@ Each `TraceEvent` has:
|
|
|
21
21
|
| Field | Meaning |
|
|
22
22
|
| ----- | ------- |
|
|
23
23
|
| `id` | Unique task id within the bus |
|
|
24
|
-
| `
|
|
24
|
+
| `parentId` | Id of the task that started this one (absent for handler tasks) |
|
|
25
|
+
| `rootId` | Id of the root task of the chain; absent on the root itself, and it is the same through a listener that reports nothing |
|
|
25
26
|
| `type` | Event type the task works on |
|
|
26
27
|
| `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, … |
|
|
27
28
|
| `phase` | `start`, `done`, `error`, `cancel`, or `drop` |
|
|
28
29
|
| `payload` | Event payload |
|
|
29
30
|
| `error` | Thrown value, only for `error` |
|
|
31
|
+
| `traceGroupId` | The flow of the task, when it or a task above has one |
|
|
30
32
|
|
|
31
33
|
Phases:
|
|
32
34
|
|
|
@@ -51,6 +53,124 @@ bus.on("load", async (id, ctx) => {
|
|
|
51
53
|
});
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
## Show which subscription began a chain
|
|
57
|
+
|
|
58
|
+
**Problem.** `useEmitter` and `bus.emitter` put an event on the bus from outside any task, such as a render or the callback of a store. In the trace that event has no sign of where it came from. With many such subscriptions it is hard to tell which one started a chain of commands and retries.
|
|
59
|
+
|
|
60
|
+
**When.** The bus has a tracer and the project has several `useEmitter` or `bus.emitter` calls. The names come from `tracePlugin`: use it on the bus, or the `traceId` is ignored.
|
|
61
|
+
|
|
62
|
+
Give each one a `traceId`. When the bus has a tracer, the emit is made from a root task with that `traceId` as its `type` and `run` as its effect. What the event starts is below it. Without a tracer nothing changes.
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { createBus } from "nanoeffects";
|
|
66
|
+
import { createAction, emitterPlugin } from "nanoeffects/actions";
|
|
67
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
68
|
+
|
|
69
|
+
// createTracer: reports every task phase to the console
|
|
70
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) })
|
|
71
|
+
.use(tracePlugin()) // tracePlugin: makes the root task that traceId names
|
|
72
|
+
.use(emitterPlugin());
|
|
73
|
+
|
|
74
|
+
const cartChanged = createAction("cart/changed");
|
|
75
|
+
|
|
76
|
+
// emitter: a callback for a store; traceId names it in the trace
|
|
77
|
+
const emitCartChanged = bus.emitter(cartChanged, { traceId: "cartStore" });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
In a component, the same option goes in the options of the hook: `useEmitter(nameChanged, name, { traceId: "Profile" })`. In the console:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
[nanoeffects] Profile · run start
|
|
84
|
+
[nanoeffects] user/save · command start
|
|
85
|
+
[nanoeffects] user/save · command done
|
|
86
|
+
[nanoeffects] Profile · run done
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A `traceId` is a string you choose and keep stable: the name of the component or of the store. Without one the root has the type of the event.
|
|
90
|
+
|
|
91
|
+
Two things to know:
|
|
92
|
+
|
|
93
|
+
- **`sample` sees the `traceId`.** The root of an emitter is a task like any other, so a `sample` function gets `{ type, payload }` with the `traceId` as the `type` and the emitted action as the `payload`. A function that matches on the type of the event, such as `event.type === "search"`, does not pick a root that has another `traceId`; match the `traceId`, or the `payload.type`.
|
|
94
|
+
- **Inside a handler, emit with `ctx.emit`.** `ctx.emit` keeps the chain: what it starts is below the handler. A plain `bus.emit` inside a handler starts a new root with no parent and no `rootId`, so the trace loses the link.
|
|
95
|
+
|
|
96
|
+
Every event of a task below another one has `rootId`, the id of the root of the chain, also when a listener, which reports nothing, stands in between. `parentId` is the task directly above, which can be such a listener.
|
|
97
|
+
|
|
98
|
+
## Follow one flow with a traceGroupId
|
|
99
|
+
|
|
100
|
+
**Problem.** A flow such as a checkout has several roots (a click, a timer, a store) and many branches. Parent ids give a tree, but you want to filter the log by the flow.
|
|
101
|
+
|
|
102
|
+
**When.** One business flow runs through several commands, and you read the log by flow.
|
|
103
|
+
|
|
104
|
+
Give the flow a `traceGroupId` in the scope of the command, the job or the action that starts it. Every task that it starts through `ctx.emit` or `ctx.run` has the same `traceGroupId`, and so does every trace event of those tasks. A task that declares its own `traceGroupId` uses it for itself and for what it starts.
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
import { createBus } from "nanoeffects";
|
|
108
|
+
import { createCommand } from "nanoeffects/actions";
|
|
109
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
110
|
+
|
|
111
|
+
// tracePlugin: reads traceGroupId from the scope of a call
|
|
112
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
|
|
113
|
+
|
|
114
|
+
// the scope names the flow; traceGroupId is for the tracer, not a field of ctx
|
|
115
|
+
const pay = createCommand("checkout/pay", async () => {}, {
|
|
116
|
+
traceGroupId: "checkout",
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
[nanoeffects] [checkout] checkout/pay · command start
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The emitters take it too: `useEmitter(cartChanged, cart, { traceId: "Cart", traceGroupId: "checkout" })`. `traceGroupId` works without `scopePlugin`, and needs `tracePlugin`.
|
|
125
|
+
|
|
126
|
+
## Name a listener
|
|
127
|
+
|
|
128
|
+
**Problem.** The log shows what started a chain and what it started, but a listener of an event reports nothing, so you cannot tell which of several listeners ran.
|
|
129
|
+
|
|
130
|
+
**When.** An event has more than one listener and you read the trace to see which one did the work.
|
|
131
|
+
|
|
132
|
+
Give the listener a `traceId` when you register it. The plugin makes each call of it a task with that name, and what it emits with `ctx.emit` is below it. `off` works as before. It also works in a module (`m.on(event, handler, { traceId })`) and on an instance made by `extendBus`.
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import { createBus } from "nanoeffects";
|
|
136
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
137
|
+
|
|
138
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
|
|
139
|
+
|
|
140
|
+
// traceId: the name of this listener in the trace
|
|
141
|
+
bus.on("name/changed", saveName, { traceId: "saveNotifier" });
|
|
142
|
+
bus.once("name/changed", (name) => console.log(name), { traceId: "auditLog" });
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
[nanoeffects] saveNotifier · run start
|
|
147
|
+
[nanoeffects] user/save · command start
|
|
148
|
+
[nanoeffects] saveNotifier · run done
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Keep the chain: `ctx.emit`, not `bus.emit`
|
|
152
|
+
|
|
153
|
+
Inside a handler, emit with `ctx.emit`. It keeps the chain: what it starts is below the handler, and its events have the `rootId` of the chain. A plain `bus.emit` inside a handler starts a new root with no `parentId` and no `rootId`, so the trace loses the link. The bus cannot warn about it, because it does not know which handler is running.
|
|
154
|
+
|
|
155
|
+
The names work wherever an event or a listener is made:
|
|
156
|
+
|
|
157
|
+
| Where | How to name it |
|
|
158
|
+
| ----- | -------------- |
|
|
159
|
+
| `useEmitter` and `bus.emitter` | `{ traceId, traceGroupId }` in the options |
|
|
160
|
+
| A Redux dispatch | `createEffectsMiddleware(bus, { traceId, traceGroupId })` |
|
|
161
|
+
| A listener, `bus.on` and `bus.once` | `{ traceId }` as the third argument |
|
|
162
|
+
| A listener of a module or of an instance of `extendBus` | the same third argument of its `on` and `once` |
|
|
163
|
+
| A method with `@On` or `@Once` | `{ traceId }` in the decorator |
|
|
164
|
+
| A command, a job or an action | `traceGroupId` in its scope |
|
|
165
|
+
|
|
166
|
+
## Turn the names off
|
|
167
|
+
|
|
168
|
+
`tracePlugin` is optional, and none of it changes how your code runs:
|
|
169
|
+
|
|
170
|
+
- **Without the plugin**, or on a bus without a tracer, `traceId` and `traceGroupId` are ignored. Handlers run as they are, with the same results, errors and rejections, and `onError` sees the same errors.
|
|
171
|
+
- **The types are the same** with and without the plugin, so you can use it only in development: `if (isDev) bus.use(tracePlugin())` compiles in both builds.
|
|
172
|
+
- **A `traceGroupId` in a scope** is never put on `ctx`.
|
|
173
|
+
|
|
54
174
|
## Use built-in loggers
|
|
55
175
|
|
|
56
176
|
A logger is `(event) => void`. `createTracer` takes one logger or an array. A logger that throws is reported to `console.error` and never breaks the task or the other loggers.
|
package/llms-full.txt
CHANGED
|
@@ -9,7 +9,7 @@ Rules that generated code often gets wrong:
|
|
|
9
9
|
- A handler gets the payload and a task context: `(payload, ctx)`. The context has `signal`, `emit`, `on`, `run`. Pass `ctx.signal` to `fetch`.
|
|
10
10
|
- Effects take `ctx` as the first argument: `retry(ctx, fn, options)`, `timeout(ctx, ms, fn)`, `take(ctx, "event")`.
|
|
11
11
|
- Policies wrap a handler: `bus.on("search", latest(async (query, ctx) => { ... }))`.
|
|
12
|
-
- Each part is imported from its own path: `nanoeffects` (`createBus`), `nanoeffects/policies`, `nanoeffects/effects`, `nanoeffects/actions`, `nanoeffects/redux`, `nanoeffects/observable`, `nanoeffects/react` (`reactPlugin`, adds `bus.useEmitter`; `usePolicy`; needs React), `nanoeffects/trace`, `nanoeffects/dev`.
|
|
12
|
+
- Each part is imported from its own path: `nanoeffects` (`createBus`), `nanoeffects/policies`, `nanoeffects/effects`, `nanoeffects/actions`, `nanoeffects/redux`, `nanoeffects/observable`, `nanoeffects/react` (`reactPlugin`, adds `bus.useEmitter`; `usePolicy`; needs React), `nanoeffects/trace` (`createTracer`, loggers, `tracePlugin`), `nanoeffects/dev`.
|
|
13
13
|
- `bus.emit(command(payload))` runs the command's handler and returns its result. Plain events return nothing.
|
|
14
14
|
|
|
15
15
|
---
|
|
@@ -566,7 +566,7 @@ Imports are split so you only load what you use. `createBus` stays small; the re
|
|
|
566
566
|
| `nanoeffects/observable` | `createObserve` |
|
|
567
567
|
| `nanoeffects/react` | `reactPlugin`: adds the `useEmitter` hook to a bus (needs React 18 or newer) |
|
|
568
568
|
| `nanoeffects/dev` | `devPlugin` (no-op in production) |
|
|
569
|
-
| `nanoeffects/trace` | `createTracer
|
|
569
|
+
| `nanoeffects/trace` | `createTracer`, the loggers and `tracePlugin` (names for the trace) |
|
|
570
570
|
|
|
571
571
|
The `...Plugin` names above are used with `bus.use`. [Plugins](plugins.md) lists all of them, what each returns, and the order to use them in.
|
|
572
572
|
|
|
@@ -680,6 +680,7 @@ Add one of these when you meet its need. None of them is required.
|
|
|
680
680
|
| [`signalPlugin`](#tie-an-emit-to-an-outside-signal-signalplugin) | `emit(call, { signal })` that aborts its tasks when an outside signal aborts. | `nanoeffects/signal` | The same bus, typed with the new `emit` |
|
|
681
681
|
| [`matchPlugin`](#listen-to-a-group-of-events-matchplugin) | `on` that listens to a group of events which a function picks. | `nanoeffects/match` | The same bus, typed with the new overloads |
|
|
682
682
|
| [`emitterPlugin`](#hand-the-bus-to-a-state-manager-emitterplugin) | A callback that emits an action, to give to a state manager. | `nanoeffects/actions` | The same bus, with `emitter` |
|
|
683
|
+
| [`tracePlugin`](#name-the-origin-and-the-flow-of-a-trace-traceplugin) | `traceId` and `traceGroupId` for a trace: the root of an emitter, a flow, a named listener. | `nanoeffects/trace` | The same bus |
|
|
683
684
|
| [`reactPlugin`](#tell-the-bus-when-a-react-value-changes-reactplugin) | A React hook that emits when a value of a component changes. | `nanoeffects/react` | The same bus, with `useEmitter` |
|
|
684
685
|
| [`devPlugin`](#find-leaks-while-you-develop-devplugin) | Warnings about listeners that pile up, and messages about modules, while you develop. | `nanoeffects/dev` | A function that disconnects it |
|
|
685
686
|
|
|
@@ -723,6 +724,20 @@ bus.emit(getUser("1"), { signal: controller.signal }); // runs the command
|
|
|
723
724
|
controller.abort(); // ctx.signal of the handler aborts with it
|
|
724
725
|
```
|
|
725
726
|
|
|
727
|
+
### Name the origin and the flow of a trace: `tracePlugin`
|
|
728
|
+
|
|
729
|
+
With a tracer on the bus, the plugin lets a trace say which subscription began a chain (`traceId` of `useEmitter` and `bus.emitter`), follow one flow by a `traceGroupId`, and show which listener ran (`bus.on(event, handler, { traceId })`). Without the plugin the options are ignored. More in [Tracing](tracing.md#show-which-subscription-began-a-chain).
|
|
730
|
+
|
|
731
|
+
```js
|
|
732
|
+
import { createBus } from "nanoeffects";
|
|
733
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
734
|
+
|
|
735
|
+
// tracePlugin — names the roots, groups and listeners of the trace
|
|
736
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
|
|
737
|
+
|
|
738
|
+
bus.on("name/changed", saveName, { traceId: "saveNotifier" }); // a listener you can find in the log
|
|
739
|
+
```
|
|
740
|
+
|
|
726
741
|
### Listen to a group of events: `matchPlugin`
|
|
727
742
|
|
|
728
743
|
`on`, `once` and `off` take a function that picks the events, such as every `user/*` event. A type guard narrows the payload. The subscription listens to every event, so for a few known events `on(creator, handler)` is cheaper. More in the [Guide](guide.md#pick-events-with-a-function).
|
|
@@ -759,7 +774,7 @@ $name.listen(bus.emitter(nameChanged)); // every change of $name emits nameChang
|
|
|
759
774
|
|
|
760
775
|
### Tell the bus when a React value changes: `reactPlugin`
|
|
761
776
|
|
|
762
|
-
`useEmitter(event, value)` emits after a render commits with a new value. It does not emit on mount, and not twice under StrictMode. It needs React 18 or newer. More in [Stores](stores.md#react-sharing-a-value-across-components).
|
|
777
|
+
`useEmitter(event, value, options?)` emits after a render commits with a new value. It does not emit on mount, and not twice under StrictMode. The options are `{ equals, traceId, traceGroupId }`; the last two name the emit in the [trace](tracing.md#show-which-subscription-began-a-chain). It needs React 18 or newer. More in [Stores](stores.md#react-sharing-a-value-across-components).
|
|
763
778
|
|
|
764
779
|
```jsx
|
|
765
780
|
import { useState } from "react";
|
|
@@ -1859,7 +1874,7 @@ The registry lives in `nanoeffects/actions`, so a bus without one carries none o
|
|
|
1859
1874
|
- When the task is aborted before the handler settles, emits `loadUser.cancel(reason)` instead of `success` or `fail`. After `pending`, exactly one of the three follows.
|
|
1860
1875
|
- `pending` / `success` / `fail` / `cancel` are a string (job makes an action creator) or an existing action creator. Missing ones become `<type>/pending`, `<type>/success`, `<type>/fail` or `<type>/cancel`.
|
|
1861
1876
|
- `bus.emit(loadUser(id))` returns a promise of the result. Handler errors are always handled: they go to `fail` and the promise resolves with `undefined`. For a result that must reject, use a command.
|
|
1862
|
-
- Outcome actions are emitted as the task that ran the job (also for a handler bound with `commands.handle`). `success` and `fail` are not sent after abort; `cancel` is,
|
|
1877
|
+
- Outcome actions are emitted as the task that ran the job (also for a handler bound with `commands.handle`). `success` and `fail` are not sent after abort; `cancel` is, at the moment of the abort.
|
|
1863
1878
|
- A missing handler is not an outcome: `emit` throws `No handler for command "..."` and nothing is sent.
|
|
1864
1879
|
- Calling the job only builds `{ type, payload }`; the handler runs on `emit`.
|
|
1865
1880
|
- The outcomes are events like any other: with `onUnhandled` set on the bus, it is called for a `pending` or a `cancel` that nobody listens to.
|
|
@@ -1926,9 +1941,16 @@ import { loadUser } from "./user";
|
|
|
1926
1941
|
bus.emit(loadUser("1"));
|
|
1927
1942
|
```
|
|
1928
1943
|
|
|
1929
|
-
`cancel` is sent
|
|
1944
|
+
`cancel` is sent at the moment of the abort, with the abort reason. A handler that ignores the signal and finishes later sends nothing more, so the flag is already `false`.
|
|
1945
|
+
|
|
1946
|
+
**With a policy.** A policy such as `latest`, `leading`, `debounce`, `throttle` or `queue` around the handler tells the job what it does with each call:
|
|
1930
1947
|
|
|
1931
|
-
|
|
1948
|
+
- `pending` is sent when the handler really starts: at once for `leading`, after the wait for `debounce`, when its turn comes for `queue`.
|
|
1949
|
+
- A call that the policy drops before it starts sends nothing, not even `pending`. A second click that `leading` ignores does not clear the flag of the first.
|
|
1950
|
+
- A run that `latest` replaces sends `cancel` before the next call sends `pending`.
|
|
1951
|
+
- Every `pending` is followed by exactly one of `success`, `fail` and `cancel`, and nothing is sent without `pending`.
|
|
1952
|
+
|
|
1953
|
+
A flag is exact when calls do not overlap, or when `latest` replaces them. If several runs can go at once (`queue` with `concurrency` above 1, or a job without a policy called many times), keep a counter: add one on `pending` and take one off on `success`, `fail` and `cancel`. A policy that you write yourself, or that you hide in your own function, does not report to the job; `pending` is then sent at the call, as for a job with no policy.
|
|
1932
1954
|
|
|
1933
1955
|
## Mount domains with modules
|
|
1934
1956
|
|
|
@@ -2119,6 +2141,10 @@ String event names (`"user/requested"`) work the same. Use your usual RTK action
|
|
|
2119
2141
|
|
|
2120
2142
|
The only option is `filter`, which keeps rejected actions away from the bus. Options of the bus (`onError`, `context`, `trace`) go to `createBus`. Use one bus for one store. Values that are not plain actions (thunks) go through untouched. Nothing here depends on the `redux` package at runtime.
|
|
2121
2143
|
|
|
2144
|
+
### Show which dispatch began a chain
|
|
2145
|
+
|
|
2146
|
+
With a tracer and `tracePlugin` on the bus, each dispatched action reaches the bus from a root task of the trace. Name it with `traceId`, and put the flow in a group with `traceGroupId`: `createEffectsMiddleware(bus, { traceId: "store", traceGroupId: "checkout" })`. Without a `traceId` the root has the type of the action. Without the plugin the options are ignored. More in [Tracing](tracing.md#show-which-subscription-began-a-chain).
|
|
2147
|
+
|
|
2122
2148
|
### Type `ctx.store` (optional)
|
|
2123
2149
|
|
|
2124
2150
|
Only if you want TypeScript to know `store` on handlers. **Skip this in JavaScript** — the middleware already puts `store` on the task at runtime. These lines do not connect the store across the app.
|
|
@@ -2497,7 +2523,7 @@ export function Results() {
|
|
|
2497
2523
|
Two limits to know:
|
|
2498
2524
|
|
|
2499
2525
|
- **All consumers re-render** on any change of the context value. Split it into two contexts (`query` and `items`) if that is slow.
|
|
2500
|
-
- **A new object counts as a change.** For a value that is rebuilt with the same content, pass
|
|
2526
|
+
- **A new object counts as a change.** For a value that is rebuilt with the same content, pass `equals` in the options, the third argument, to say when two values are the same: `useEmitter(profileChanged, profile, { equals: (previous, next) => previous.id === next.id })`. The same object takes `traceId` and `traceGroupId`, which name the subscription and the flow in the [trace](tracing.md#show-which-subscription-began-a-chain).
|
|
2501
2527
|
|
|
2502
2528
|
`useEmitter` emits after the render, not at the moment of `setQuery`. To emit on every call, even with the same value, call an emitter from the event handler (`bus.emitter(action)`, see [Zustand](#zustand)) instead.
|
|
2503
2529
|
|
|
@@ -3301,7 +3327,7 @@ When several places set the same field, the strongest one wins:
|
|
|
3301
3327
|
| 3 | the scope of the module that owns the handler |
|
|
3302
3328
|
| 4 (weakest) | the bus: `createBus({ context })`, `contextPlugin`, `effectsPlugin` |
|
|
3303
3329
|
|
|
3304
|
-
The fields of the library (`signal`, `abort`, `emit`, `on`, `off`, `run`, `type`, `payload`) are never replaced. A child task (`ctx.run`, an attempt of `retry`) has the fields of its parent.
|
|
3330
|
+
The fields of the library (`signal`, `abort`, `emit`, `on`, `off`, `run`, `type`, `payload`) are never replaced. The name `traceGroupId` is reserved: the tracer reads it, and it is not put on `ctx` ([Follow one flow](tracing.md#follow-one-flow-with-a-tracegroupid)). A child task (`ctx.run`, an attempt of `retry`) has the fields of its parent.
|
|
3305
3331
|
|
|
3306
3332
|
## Keep a generic effect generic
|
|
3307
3333
|
|
|
@@ -3576,6 +3602,8 @@ Things to know:
|
|
|
3576
3602
|
- On a class that `extendBus` did not make, a decorator does nothing.
|
|
3577
3603
|
- Both the decorators of the language and `experimentalDecorators` work. Your compiler has to support the one you use: TypeScript 5 or newer for the first, and the `experimentalDecorators` option for the second.
|
|
3578
3604
|
|
|
3605
|
+
Name a method for the [trace](tracing.md#name-a-listener) with `{ traceId }`, as the second argument or, after a wrapper, the third: `@On(queryChanged, { traceId: "searcher" })` or `@On(queryChanged, latest, { traceId: "searcher" })`. It needs `tracePlugin` on the bus and is ignored without it.
|
|
3606
|
+
|
|
3579
3607
|
## Decorate the class with `@WithBus`
|
|
3580
3608
|
|
|
3581
3609
|
`@WithBus(bus)` does what `extendBus(bus, Base)` does, as a class decorator. `@WithBus()` makes a bus for the class.
|
|
@@ -4244,7 +4272,7 @@ bus.on(
|
|
|
4244
4272
|
## What is different
|
|
4245
4273
|
|
|
4246
4274
|
- **No state.** There is no `createStore`, `combine`, `restore`, `$store.map`. The bus holds listeners only. Take a store such as Zustand for state ([Stores](stores.md)).
|
|
4247
|
-
- **`pending` is an action, not a store.** `createJob` sends `pending` when the handler starts, and `success`, `fail` or `cancel` when it ends; you keep the flag in your store ([Show a loading state](actions.md#show-a-loading-state-with-a-job)).
|
|
4275
|
+
- **`pending` is an action, not a store.** `createJob` sends `pending` when the handler starts, and `success`, `fail` or `cancel` when it ends; you keep the flag in your store ([Show a loading state](actions.md#show-a-loading-state-with-a-job)). Policies report to the job, so the flag also follows `latest`, `leading`, `debounce`, `throttle` and `queue`; that section says when to count instead. A running task is also visible through [Tracing](tracing.md).
|
|
4248
4276
|
- **Cancellation is the default tool.** An Effector effect runs every call to the end. Here `latest` and `ctx.signal` stop what is no longer needed.
|
|
4249
4277
|
- **Events are matched by name, not by identity.** An event is a string or a `createAction` creator, not a unit that you import and call. Two modules agree on the name.
|
|
4250
4278
|
- **No graph.** `sample` and `forward` build a static graph. A bus subscribes handlers at runtime and the call order is the order of `on`.
|
|
@@ -4533,12 +4561,14 @@ Each `TraceEvent` has:
|
|
|
4533
4561
|
| Field | Meaning |
|
|
4534
4562
|
| ----- | ------- |
|
|
4535
4563
|
| `id` | Unique task id within the bus |
|
|
4536
|
-
| `
|
|
4564
|
+
| `parentId` | Id of the task that started this one (absent for handler tasks) |
|
|
4565
|
+
| `rootId` | Id of the root task of the chain; absent on the root itself, and it is the same through a listener that reports nothing |
|
|
4537
4566
|
| `type` | Event type the task works on |
|
|
4538
4567
|
| `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, … |
|
|
4539
4568
|
| `phase` | `start`, `done`, `error`, `cancel`, or `drop` |
|
|
4540
4569
|
| `payload` | Event payload |
|
|
4541
4570
|
| `error` | Thrown value, only for `error` |
|
|
4571
|
+
| `traceGroupId` | The flow of the task, when it or a task above has one |
|
|
4542
4572
|
|
|
4543
4573
|
Phases:
|
|
4544
4574
|
|
|
@@ -4563,6 +4593,124 @@ bus.on("load", async (id, ctx) => {
|
|
|
4563
4593
|
});
|
|
4564
4594
|
```
|
|
4565
4595
|
|
|
4596
|
+
## Show which subscription began a chain
|
|
4597
|
+
|
|
4598
|
+
**Problem.** `useEmitter` and `bus.emitter` put an event on the bus from outside any task, such as a render or the callback of a store. In the trace that event has no sign of where it came from. With many such subscriptions it is hard to tell which one started a chain of commands and retries.
|
|
4599
|
+
|
|
4600
|
+
**When.** The bus has a tracer and the project has several `useEmitter` or `bus.emitter` calls. The names come from `tracePlugin`: use it on the bus, or the `traceId` is ignored.
|
|
4601
|
+
|
|
4602
|
+
Give each one a `traceId`. When the bus has a tracer, the emit is made from a root task with that `traceId` as its `type` and `run` as its effect. What the event starts is below it. Without a tracer nothing changes.
|
|
4603
|
+
|
|
4604
|
+
```js
|
|
4605
|
+
import { createBus } from "nanoeffects";
|
|
4606
|
+
import { createAction, emitterPlugin } from "nanoeffects/actions";
|
|
4607
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
4608
|
+
|
|
4609
|
+
// createTracer: reports every task phase to the console
|
|
4610
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) })
|
|
4611
|
+
.use(tracePlugin()) // tracePlugin: makes the root task that traceId names
|
|
4612
|
+
.use(emitterPlugin());
|
|
4613
|
+
|
|
4614
|
+
const cartChanged = createAction("cart/changed");
|
|
4615
|
+
|
|
4616
|
+
// emitter: a callback for a store; traceId names it in the trace
|
|
4617
|
+
const emitCartChanged = bus.emitter(cartChanged, { traceId: "cartStore" });
|
|
4618
|
+
```
|
|
4619
|
+
|
|
4620
|
+
In a component, the same option goes in the options of the hook: `useEmitter(nameChanged, name, { traceId: "Profile" })`. In the console:
|
|
4621
|
+
|
|
4622
|
+
```
|
|
4623
|
+
[nanoeffects] Profile · run start
|
|
4624
|
+
[nanoeffects] user/save · command start
|
|
4625
|
+
[nanoeffects] user/save · command done
|
|
4626
|
+
[nanoeffects] Profile · run done
|
|
4627
|
+
```
|
|
4628
|
+
|
|
4629
|
+
A `traceId` is a string you choose and keep stable: the name of the component or of the store. Without one the root has the type of the event.
|
|
4630
|
+
|
|
4631
|
+
Two things to know:
|
|
4632
|
+
|
|
4633
|
+
- **`sample` sees the `traceId`.** The root of an emitter is a task like any other, so a `sample` function gets `{ type, payload }` with the `traceId` as the `type` and the emitted action as the `payload`. A function that matches on the type of the event, such as `event.type === "search"`, does not pick a root that has another `traceId`; match the `traceId`, or the `payload.type`.
|
|
4634
|
+
- **Inside a handler, emit with `ctx.emit`.** `ctx.emit` keeps the chain: what it starts is below the handler. A plain `bus.emit` inside a handler starts a new root with no parent and no `rootId`, so the trace loses the link.
|
|
4635
|
+
|
|
4636
|
+
Every event of a task below another one has `rootId`, the id of the root of the chain, also when a listener, which reports nothing, stands in between. `parentId` is the task directly above, which can be such a listener.
|
|
4637
|
+
|
|
4638
|
+
## Follow one flow with a traceGroupId
|
|
4639
|
+
|
|
4640
|
+
**Problem.** A flow such as a checkout has several roots (a click, a timer, a store) and many branches. Parent ids give a tree, but you want to filter the log by the flow.
|
|
4641
|
+
|
|
4642
|
+
**When.** One business flow runs through several commands, and you read the log by flow.
|
|
4643
|
+
|
|
4644
|
+
Give the flow a `traceGroupId` in the scope of the command, the job or the action that starts it. Every task that it starts through `ctx.emit` or `ctx.run` has the same `traceGroupId`, and so does every trace event of those tasks. A task that declares its own `traceGroupId` uses it for itself and for what it starts.
|
|
4645
|
+
|
|
4646
|
+
```js
|
|
4647
|
+
import { createBus } from "nanoeffects";
|
|
4648
|
+
import { createCommand } from "nanoeffects/actions";
|
|
4649
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
4650
|
+
|
|
4651
|
+
// tracePlugin: reads traceGroupId from the scope of a call
|
|
4652
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
|
|
4653
|
+
|
|
4654
|
+
// the scope names the flow; traceGroupId is for the tracer, not a field of ctx
|
|
4655
|
+
const pay = createCommand("checkout/pay", async () => {}, {
|
|
4656
|
+
traceGroupId: "checkout",
|
|
4657
|
+
});
|
|
4658
|
+
```
|
|
4659
|
+
|
|
4660
|
+
```
|
|
4661
|
+
[nanoeffects] [checkout] checkout/pay · command start
|
|
4662
|
+
```
|
|
4663
|
+
|
|
4664
|
+
The emitters take it too: `useEmitter(cartChanged, cart, { traceId: "Cart", traceGroupId: "checkout" })`. `traceGroupId` works without `scopePlugin`, and needs `tracePlugin`.
|
|
4665
|
+
|
|
4666
|
+
## Name a listener
|
|
4667
|
+
|
|
4668
|
+
**Problem.** The log shows what started a chain and what it started, but a listener of an event reports nothing, so you cannot tell which of several listeners ran.
|
|
4669
|
+
|
|
4670
|
+
**When.** An event has more than one listener and you read the trace to see which one did the work.
|
|
4671
|
+
|
|
4672
|
+
Give the listener a `traceId` when you register it. The plugin makes each call of it a task with that name, and what it emits with `ctx.emit` is below it. `off` works as before. It also works in a module (`m.on(event, handler, { traceId })`) and on an instance made by `extendBus`.
|
|
4673
|
+
|
|
4674
|
+
```js
|
|
4675
|
+
import { createBus } from "nanoeffects";
|
|
4676
|
+
import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
|
|
4677
|
+
|
|
4678
|
+
const bus = createBus({ trace: createTracer(consoleLogger()) }).use(tracePlugin());
|
|
4679
|
+
|
|
4680
|
+
// traceId: the name of this listener in the trace
|
|
4681
|
+
bus.on("name/changed", saveName, { traceId: "saveNotifier" });
|
|
4682
|
+
bus.once("name/changed", (name) => console.log(name), { traceId: "auditLog" });
|
|
4683
|
+
```
|
|
4684
|
+
|
|
4685
|
+
```
|
|
4686
|
+
[nanoeffects] saveNotifier · run start
|
|
4687
|
+
[nanoeffects] user/save · command start
|
|
4688
|
+
[nanoeffects] saveNotifier · run done
|
|
4689
|
+
```
|
|
4690
|
+
|
|
4691
|
+
## Keep the chain: `ctx.emit`, not `bus.emit`
|
|
4692
|
+
|
|
4693
|
+
Inside a handler, emit with `ctx.emit`. It keeps the chain: what it starts is below the handler, and its events have the `rootId` of the chain. A plain `bus.emit` inside a handler starts a new root with no `parentId` and no `rootId`, so the trace loses the link. The bus cannot warn about it, because it does not know which handler is running.
|
|
4694
|
+
|
|
4695
|
+
The names work wherever an event or a listener is made:
|
|
4696
|
+
|
|
4697
|
+
| Where | How to name it |
|
|
4698
|
+
| ----- | -------------- |
|
|
4699
|
+
| `useEmitter` and `bus.emitter` | `{ traceId, traceGroupId }` in the options |
|
|
4700
|
+
| A Redux dispatch | `createEffectsMiddleware(bus, { traceId, traceGroupId })` |
|
|
4701
|
+
| A listener, `bus.on` and `bus.once` | `{ traceId }` as the third argument |
|
|
4702
|
+
| A listener of a module or of an instance of `extendBus` | the same third argument of its `on` and `once` |
|
|
4703
|
+
| A method with `@On` or `@Once` | `{ traceId }` in the decorator |
|
|
4704
|
+
| A command, a job or an action | `traceGroupId` in its scope |
|
|
4705
|
+
|
|
4706
|
+
## Turn the names off
|
|
4707
|
+
|
|
4708
|
+
`tracePlugin` is optional, and none of it changes how your code runs:
|
|
4709
|
+
|
|
4710
|
+
- **Without the plugin**, or on a bus without a tracer, `traceId` and `traceGroupId` are ignored. Handlers run as they are, with the same results, errors and rejections, and `onError` sees the same errors.
|
|
4711
|
+
- **The types are the same** with and without the plugin, so you can use it only in development: `if (isDev) bus.use(tracePlugin())` compiles in both builds.
|
|
4712
|
+
- **A `traceGroupId` in a scope** is never put on `ctx`.
|
|
4713
|
+
|
|
4566
4714
|
## Use built-in loggers
|
|
4567
4715
|
|
|
4568
4716
|
A logger is `(event) => void`. `createTracer` takes one logger or an array. A logger that throws is reported to `console.error` and never breaks the task or the other loggers.
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ Rules that generated code often gets wrong:
|
|
|
9
9
|
- A handler gets the payload and a task context: `(payload, ctx)`. The context has `signal`, `emit`, `on`, `run`. Pass `ctx.signal` to `fetch`.
|
|
10
10
|
- Effects take `ctx` as the first argument: `retry(ctx, fn, options)`, `timeout(ctx, ms, fn)`, `take(ctx, "event")`.
|
|
11
11
|
- Policies wrap a handler: `bus.on("search", latest(async (query, ctx) => { ... }))`.
|
|
12
|
-
- Each part is imported from its own path: `nanoeffects` (`createBus`), `nanoeffects/policies`, `nanoeffects/effects`, `nanoeffects/actions`, `nanoeffects/redux`, `nanoeffects/observable`, `nanoeffects/react` (`reactPlugin`, adds `bus.useEmitter`; `usePolicy`; needs React), `nanoeffects/trace`, `nanoeffects/dev`.
|
|
12
|
+
- Each part is imported from its own path: `nanoeffects` (`createBus`), `nanoeffects/policies`, `nanoeffects/effects`, `nanoeffects/actions`, `nanoeffects/redux`, `nanoeffects/observable`, `nanoeffects/react` (`reactPlugin`, adds `bus.useEmitter`; `usePolicy`; needs React), `nanoeffects/trace` (`createTracer`, loggers, `tracePlugin`), `nanoeffects/dev`.
|
|
13
13
|
- `bus.emit(command(payload))` runs the command's handler and returns its result. Plain events return nothing.
|
|
14
14
|
|
|
15
15
|
## Start here
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nanoeffects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Cancel stale async work and fix race conditions in event handlers: a typed event bus where every handler is a task with an AbortSignal, plus latest, debounce, throttle, queue, retry, timeout and poll. About 1.4 kB gzip.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"event-bus",
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
{
|
|
202
202
|
"name": "actions (all)",
|
|
203
203
|
"path": "dist/actions.js",
|
|
204
|
-
"limit": "2.
|
|
204
|
+
"limit": "2.87 kB"
|
|
205
205
|
},
|
|
206
206
|
{
|
|
207
207
|
"name": "match (plugin)",
|
|
@@ -239,7 +239,7 @@
|
|
|
239
239
|
"ignore": [
|
|
240
240
|
"react"
|
|
241
241
|
],
|
|
242
|
-
"limit": "1.
|
|
242
|
+
"limit": "1.18 kB"
|
|
243
243
|
},
|
|
244
244
|
{
|
|
245
245
|
"name": "dev",
|
package/dist/bus-CkTHUAvq.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e}from"./onAbort-BgMfspK4.js";import{a as t,n,r}from"./context-COgyNlrC.js";import{n as i}from"./promise-CSLpzwSR.js";import{n as a,t as o}from"./event-0RIBGYqS.js";const s=`*`,c=()=>{let e={},t={},n=n=>(t[n]=void 0,e[n]??=/* @__PURE__ */ new Map,e[n]),r=(e,t)=>{let r=n(e),i=r.get(t)??0;i>1?r.set(t,i-1):t?r.delete(t):r.clear()};return{get:n=>(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]),add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>{r(e,t)}},remove:r,clear:()=>{for(let t of Reflect.ownKeys(e))delete e[t];for(let e of Reflect.ownKeys(t))delete t[e]}}},l=Symbol(),u=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,l,{value:e,enumerable:!1}),r},d=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&l in e)return e[l]},f=(e,t,n)=>Reflect.apply(e,null,[t,n]),p=(s={})=>{let l=c(),u=(e,t)=>l.add(o(e),t),p=(e,t)=>{let n=u(e,(e,r)=>(n(),t(e,r)));return n},m=(e,t)=>{e===void 0?l.clear():l.remove(o(e),t)},h=(t,a)=>{let{onError:o}=s,{ctx:c}=n({event:a,bus:g,tracer:s.trace,parent:{ref:r()}});if(!o){t(a.payload,c);return}let l=t=>{try{o(t,a)}catch(t){e(`onError failed`,t)}};try{let e=t(a.payload,c);i(e)&&Promise.resolve(e).catch(l)}catch(e){l(e)}},g={on:u,once:p,off:m,emit:(e,i)=>{let c=o(e),u=a(e,i),p={type:c,payload:u},m=d(e),_=[...l.get(c),...l.get(`*`)];_.length===0&&!m&&s.onUnhandled?.(p);for(let e of _)h(e,p);if(m){let{ctx:e}=n({event:p,bus:g,tracer:s.trace,parent:{ref:r()}}),i=g.commands?.run??f;return t(e,`command`,()=>i(m,u,e))}},use:e=>e(g),ctx:s.context,trace:s.trace};return g};export{s as a,u as i,d as n,p as r,l as t};
|
package/dist/context-COgyNlrC.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
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=Symbol(),r=typeof __NANOEFFECTS_TRACE__>`u`||__NANOEFFECTS_TRACE__,i=/* @__PURE__ */ new WeakMap;let a;const o=()=>a,s=()=>{},c=(e,t,n,a)=>{let o=r&&i.get(e);o&&o.tracer.report(o.ref,t,n,a)},l=(e,t)=>{let n=r&&i.get(e);return n?n.tracer.settler(n.ref,t):s},u=(e,t,n)=>{let a=r&&i.get(e);return a?a.tracer.run(a.ref,t,e.signal,n):n()},d=(e,t)=>typeof e==`function`?e(t):e,f=({event:o,bus:c,tracer:l,parent:p})=>{let{controller:m,unlink:h}=t(p?.signal),{signal:g}=m,_=r&&l?{tracer:l,ref:l.task(o,p?.ref)}:void 0,v=(...e)=>{if(g.aborted&&!x[n])return;let t=c?.emit,r=a;a=_?.ref;try{return t?.(...e)}finally{a=r}},y=(...t)=>{if(g.aborted)return s;let n=c?.on,r=n?.(...t);if(!r)return s;let i=e({signal:g},r);return()=>{i(),r()}},b=async(e,...t)=>{g.throwIfAborted();let n=t[0],r=f({event:{type:o.type,payload:n},bus:c,tracer:l,parent:{signal:g,ref:_?.ref}});try{return await u(r.ctx,`run`,()=>e(n,r.ctx))}finally{r.unlink()}},x={...d(c?.ctx,o),type:o.type,payload:o.payload,signal:g,abort:e=>m.abort(e),emit:v,on:y,off:c?.off??s,run:b};return c?.addContext?.(x),_&&i.set(x,_),{ctx:x,unlink:h}},p=(e,t)=>{let n=r?i.get(e):void 0;return f({event:{type:t??e.type??``,payload:e.payload},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e},tracer:n?.tracer,parent:{signal:e.signal,ref:n?.ref}})};export{u as a,p as i,f as n,c as o,o as r,n as s,l as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,l as t,n,r}from"./context-yYU3_9lq.js";import{n as i}from"./promise-CSLpzwSR.js";const a=e=>typeof e==`string`?e:e.type,o=(e,t)=>typeof e==`object`?e.payload:t,s=`*`,c=()=>{let e={},t={},n=n=>(t[n]=void 0,e[n]??=/* @__PURE__ */ new Map,e[n]),r=(e,t)=>{let r=n(e),i=r.get(t)??0;i>1?r.set(t,i-1):t?r.delete(t):r.clear()};return{get:n=>(t[n]??=[...e[n]??[]].flatMap(([e,t])=>Array(t).fill(e)),t[n]),add:(e,t)=>{let i=n(e);return i.set(t,(i.get(t)??0)+1),()=>{r(e,t)}},remove:r,clear:()=>{for(let t of Reflect.ownKeys(e))delete e[t];for(let e of Reflect.ownKeys(t))delete t[e]}}},l=Symbol(),u=(e,t,n)=>{let r={type:t,payload:n};return Object.defineProperty(r,l,{value:e,enumerable:!1}),r},d=e=>{if(typeof e==`function`)return e;if(typeof e==`object`&&e&&l in e)return e[l]},f=(e,t,n)=>Reflect.apply(e,null,[t,n]),p=(s={})=>{let l=c(),u=(e,t)=>l.add(a(e),t),p=(e,t)=>{let n=u(e,(e,r)=>(n(),t(e,r)));return n},m=(e,t)=>{e===void 0?l.clear():l.remove(a(e),t)},h=(e,a)=>{let{onError:o}=s,{ctx:c}=n({event:a,bus:g,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)}},g={on:u,once:p,off:m,emit:(t,i)=>{let c=a(t),u=o(t,i),p={type:c,payload:u},m=d(t),_=[...l.get(c),...l.get(`*`)];_.length===0&&!m&&s.onUnhandled?.(p);for(let e of _)h(e,p);if(m){let{ctx:t}=n({event:p,bus:g,tracer:s.trace,parent:{ref:r()}}),i=g.commands?.run??f;return e(t,`command`,()=>i(m,u,t))}},use:e=>e(g),ctx:s.context,trace:s.trace};return g};export{a,s as i,p as n,u as r,l as t};
|
package/dist/onAbort-BgMfspK4.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
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};
|