nanoeffects 0.11.0 → 0.12.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.
@@ -8,7 +8,7 @@ type PayloadOf<P> = undefined extends P ? [payload?: P] : [payload: P];
8
8
  type EventMap = Record<string, unknown>;
9
9
  type Keys<E> = keyof E & string;
10
10
  type Task<P, E extends EventMap, R = unknown> = (payload: P, ctx: Context<E>) => R;
11
- type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command";
11
+ type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command" | "emit";
12
12
  type Phase = "start" | "done" | "error" | "cancel" | "drop";
13
13
  type TraceEvent<E = Record<string, unknown>> = { [K in Keys<E>]: {
14
14
  id: number;
package/dist/actions.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { B as EventMap, G as Task, T as EventKey, U as PayloadOf, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator } from "./action-creator-9e8jesRa.js";
2
- import { r as Scoped, t as FieldsOfScope } from "./types-ByPzv7tw.js";
3
- import { a as createModule, c as CarryingCalls, d as commandsPlugin, f as createCommand, l as MadeCommand, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as bindHandler } from "./module-BlkU3i6B.js";
1
+ import { B as EventMap, G as Task, T as EventKey, U as PayloadOf, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator } from "./action-creator-CQlFgQm4.js";
2
+ import { r as Scoped, t as FieldsOfScope } from "./types-bzocBVYc.js";
3
+ import { a as createModule, c as CarryingCalls, d as commandsPlugin, f as createCommand, l as MadeCommand, n as ModuleApi, o as modulePlugin, r as ModuleContext, s as Binding, t as Module, u as bindHandler } from "./module-woawvR28.js";
4
4
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
5
5
  //#region src/actions/action.d.ts
6
6
  type ScopedCreator<P, T extends string, F> = [P] extends [Free] ? FreeActionCreator<T, F> & WithScope<F> : ActionCreator<P, T, PayloadOf<P>, F> & WithScope<F>;
@@ -1,4 +1,4 @@
1
- import { B as EventMap, N as WithContext, h as Bus, x as ContextOf } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, N as WithContext, h as Bus, x as ContextOf } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/core/context-plugin.d.ts
3
3
  /** What a function that makes the fields of a task gets to see. */
4
4
  type ContextTask = {
package/dist/dev.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as EventMap, Y as UnsubscribeCallback, h as Bus } from "./action-creator-9e8jesRa.js";
2
- import { i as WithApp, n as ModuleApi } from "./module-BlkU3i6B.js";
1
+ import { B as EventMap, Y as UnsubscribeCallback, h as Bus } from "./action-creator-CQlFgQm4.js";
2
+ import { i as WithApp, n as ModuleApi } from "./module-woawvR28.js";
3
3
  //#region src/dev/types.d.ts
4
4
  /** The parts of what `modulePlugin` returned that the diagnostics report. */
5
5
  type ReportedApp<E extends EventMap> = Pick<ModuleApi<E>, "mount" | "unmount" | "isMounted">;
package/dist/effects.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as EventMap, G as Task, u as Effect } from "./action-creator-9e8jesRa.js";
2
- import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-DHkamZdH.js";
1
+ import { B as EventMap, G as Task, u as Effect } from "./action-creator-CQlFgQm4.js";
2
+ import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-BrTsPg2Y.js";
3
3
  //#region src/utils/onAbort.d.ts
4
4
  export declare const onAbort: (ctx: {
5
5
  signal?: AbortSignal | undefined;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { A as HandlerRef, B as EventMap, C as Emit, D as EventPayload, E as EventLike, F as commandCall, G as Task, I as createBus, J as TraceLogger, K as Trace, L as Tracer, M as On, N as WithContext, O as EventsOf, P as WithType, R as anyEventType, S as ContextOption, T as EventKey, U as PayloadOf, V as Keys, W as Phase, Y as UnsubscribeCallback, _ as Command, b as Context, g as BusOptions, h as Bus, i as WithPayload, j as Off, k as Handler, m as AnyEvent, p as Action, q as TraceEvent, v as CommandCall, w as Emittable, x as ContextOf, y as Commands, z as EffectName } from "./action-creator-9e8jesRa.js";
2
- import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-Xibukkbj.js";
1
+ import { A as HandlerRef, B as EventMap, C as Emit, D as EventPayload, E as EventLike, F as commandCall, G as Task, I as createBus, J as TraceLogger, K as Trace, L as Tracer, M as On, N as WithContext, O as EventsOf, P as WithType, R as anyEventType, S as ContextOption, T as EventKey, U as PayloadOf, V as Keys, W as Phase, Y as UnsubscribeCallback, _ as Command, b as Context, g as BusOptions, h as Bus, i as WithPayload, j as Off, k as Handler, m as AnyEvent, p as Action, q as TraceEvent, v as CommandCall, w as Emittable, x as ContextOf, y as Commands, z as EffectName } from "./action-creator-CQlFgQm4.js";
2
+ import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-Cphk8jfq.js";
3
3
  export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type CommandCall, type Commands, type Context, type ContextOf, type ContextOption, type ContextTask, type EffectName, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type EventsOf, type Handler, type HandlerRef, type Keys, type NoClash, type Off, type On, type PayloadOf, type Phase, type Task, type Trace, type TraceEvent, type TraceLogger, type Tracer, type UnsubscribeCallback, type WithContext, type WithPayload, type WithType, anyEventType, commandCall, contextPlugin, createBus };
package/dist/match.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, D as EventPayload, G as Task, Y as UnsubscribeCallback, h as Bus, p as Action } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, D as EventPayload, G as Task, Y as UnsubscribeCallback, h as Bus, p as Action } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/match/plugin.d.ts
3
3
  /** A function over the action of an event: `{ type, payload }`. */
4
4
  type Predicate<E extends EventMap> = (action: Action<E>) => boolean;
@@ -1,5 +1,5 @@
1
- import { B as EventMap, G as Task, U as PayloadOf, Y as UnsubscribeCallback, _ as Command, b as Context, c as WithScope, h as Bus, o as Scope, v as CommandCall, y as Commands } from "./action-creator-9e8jesRa.js";
2
- import { n as ScopeSlots, r as Scoped, t as FieldsOfScope } from "./types-ByPzv7tw.js";
1
+ import { B as EventMap, G as Task, U as PayloadOf, Y as UnsubscribeCallback, _ as Command, b as Context, c as WithScope, h as Bus, o as Scope, v as CommandCall, y as Commands } from "./action-creator-CQlFgQm4.js";
2
+ import { n as ScopeSlots, r as Scoped, t as FieldsOfScope } from "./types-bzocBVYc.js";
3
3
  //#region src/actions/command.d.ts
4
4
  /** What `createCommand` returns: a command that also tells whether an action is of its type. */
5
5
  type MadeCommand<P, R, T extends string> = Command<P, R> & {
@@ -8,7 +8,7 @@ type PayloadOf<P> = undefined extends P ? [payload?: P] : [payload: P];
8
8
  type EventMap = Record<string, unknown>;
9
9
  type Keys<E> = keyof E & string;
10
10
  type Task<P, E extends EventMap, R = unknown> = (payload: P, ctx: Context<E>) => R;
11
- type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command";
11
+ type EffectName = "every" | "latest" | "leading" | "debounce" | "throttle" | "run" | "all" | "race" | "retry" | "timeout" | "poll" | "take" | "queue" | "allSettled" | "command" | "emit";
12
12
  type Phase = "start" | "done" | "error" | "cancel" | "drop";
13
13
  type TraceEvent<E = Record<string, unknown>> = { [K in Keys<E>]: {
14
14
  id: number;
@@ -1,5 +1,5 @@
1
- import { B as PayloadOf, C as EventKey, H as Task, K as UnsubscribeCallback, R as EventMap, _ as Commands, a as Scope, g as CommandCall, h as Command, n as Free, o as WithFields, p as Bus, r as FreeActionCreator, s as WithScope, t as ActionCreator, u as Effects, v as Context } from "./action-creator-DQTwMBwP.js";
2
- import { t as BoundOf } from "./plugin-DOYHG78z.js";
1
+ import { B as PayloadOf, C as EventKey, H as Task, K as UnsubscribeCallback, R as EventMap, _ as Commands, a as Scope, g as CommandCall, h as Command, n as Free, o as WithFields, p as Bus, r as FreeActionCreator, s as WithScope, t as ActionCreator, u as Effects, v as Context } from "./action-creator-Ls3Gy3jk.js";
2
+ import { t as BoundOf } from "./plugin-Cw45_-Bo.js";
3
3
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
4
4
  //#region src/scope/types.d.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { R as EventMap, j as WithContext, p as Bus, y as ContextOf } from "./action-creator-DQTwMBwP.js";
1
+ import { R as EventMap, j as WithContext, p as Bus, y as ContextOf } from "./action-creator-Ls3Gy3jk.js";
2
2
  //#region src/core/context-plugin.d.ts
3
3
  /** What a function that makes the fields of a task gets to see. */
4
4
  type ContextTask = {
@@ -1,5 +1,5 @@
1
- import { H as Task, R as EventMap, l as Effect } from "./action-creator-DQTwMBwP.js";
2
- import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-DOYHG78z.js";
1
+ import { H as Task, R as EventMap, l as Effect } from "./action-creator-Ls3Gy3jk.js";
2
+ import { a as RaceStarter, c as all, d as delay, i as take, l as EffectContext, n as effectsPlugin, o as race, r as TakeOptions, s as allSettled, u as Starter } from "./plugin-Cw45_-Bo.js";
3
3
  //#region src/utils/onAbort.d.ts
4
4
  export declare const onAbort: (ctx: {
5
5
  signal?: AbortSignal | undefined;
@@ -1,3 +1,3 @@
1
- import { A as On, B as PayloadOf, C as EventKey, D as Handler, E as EventsOf, F as Tracer, G as TraceLogger, H as Task, I as anyEventType, K as UnsubscribeCallback, L as EffectName, M as WithType, N as commandCall, O as HandlerRef, P as createBus, R as EventMap, S as Emittable, T as EventPayload, U as Trace, V as Phase, W as TraceEvent, _ as Commands, b as ContextOption, d as Action, f as AnyEvent, g as CommandCall, h as Command, i as WithPayload, j as WithContext, k as Off, m as BusOptions, p as Bus, v as Context, w as EventLike, x as Emit, y as ContextOf, z as Keys } from "./action-creator-DQTwMBwP.js";
2
- import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-DlZCkcX9.js";
1
+ import { A as On, B as PayloadOf, C as EventKey, D as Handler, E as EventsOf, F as Tracer, G as TraceLogger, H as Task, I as anyEventType, K as UnsubscribeCallback, L as EffectName, M as WithType, N as commandCall, O as HandlerRef, P as createBus, R as EventMap, S as Emittable, T as EventPayload, U as Trace, V as Phase, W as TraceEvent, _ as Commands, b as ContextOption, d as Action, f as AnyEvent, g as CommandCall, h as Command, i as WithPayload, j as WithContext, k as Off, m as BusOptions, p as Bus, v as Context, w as EventLike, x as Emit, y as ContextOf, z as Keys } from "./action-creator-Ls3Gy3jk.js";
2
+ import { n as NoClash, r as contextPlugin, t as ContextTask } from "./context-plugin-BQa-wtbj.js";
3
3
  export { type Action, type AnyEvent, type Bus, type BusOptions, type Command, type CommandCall, type Commands, type Context, type ContextOf, type ContextOption, type ContextTask, type EffectName, type Emit, type Emittable, type EventKey, type EventLike, type EventMap, type EventPayload, type EventsOf, type Handler, type HandlerRef, type Keys, type NoClash, type Off, type On, type PayloadOf, type Phase, type Task, type Trace, type TraceEvent, type TraceLogger, type Tracer, type UnsubscribeCallback, type WithContext, type WithPayload, type WithType, anyEventType, commandCall, contextPlugin, createBus };
@@ -1,5 +1,5 @@
1
- import { R as EventMap, c as BoundForm, j as WithContext, p as Bus, u as Effects, v as Context, z as Keys } from "./action-creator-DQTwMBwP.js";
2
- import { n as NoClash } from "./context-plugin-DlZCkcX9.js";
1
+ import { R as EventMap, c as BoundForm, j as WithContext, p as Bus, u as Effects, v as Context, z as Keys } from "./action-creator-Ls3Gy3jk.js";
2
+ import { n as NoClash } from "./context-plugin-BQa-wtbj.js";
3
3
  //#region src/utils/delay.d.ts
4
4
  /**
5
5
  * Resolves after `ms`, or rejects early if `ctx.signal` aborts. `ms` can be a
@@ -1,4 +1,4 @@
1
- import { B as PayloadOf, H as Task, R as EventMap } from "./action-creator-DQTwMBwP.js";
1
+ import { B as PayloadOf, H as Task, R as EventMap } from "./action-creator-Ls3Gy3jk.js";
2
2
  //#region src/policies/keyed.d.ts
3
3
  /** Options shared by every policy that can keep its state per key. */
4
4
  type KeyOption<P> = {
@@ -1,4 +1,4 @@
1
- import { R as EventMap, p as Bus } from "./action-creator-DQTwMBwP.js";
1
+ import { R as EventMap, p as Bus } from "./action-creator-Ls3Gy3jk.js";
2
2
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
3
3
  //#region src/store/redux/middleware.d.ts
4
4
  /** What Redux passes through a middleware: an action is a plain object with a `type`. */
@@ -1,4 +1,4 @@
1
- import { B as EventMap, V as Keys, h as Bus } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, V as Keys, h as Bus } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/observable.d.ts
3
3
  declare global {
4
4
  interface SymbolConstructor {
package/dist/oop.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, H as ListenerOptions, _ as Command, h as Bus, t as ActionCreator } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, H as ListenerOptions, _ as Command, h as Bus, t as ActionCreator } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/oop/types.d.ts
3
3
  type Slots = "ctx" | "commands" | "trace" | "addContext" | "applyScope" | "fitScope" | "app" | "use";
4
4
  type AnyFunction = (...args: never[]) => unknown;
@@ -1,5 +1,5 @@
1
- import { B as EventMap, N as WithContext, V as Keys, b as Context, d as Effects, h as Bus, l as BoundForm } from "./action-creator-9e8jesRa.js";
2
- import { n as NoClash } from "./context-plugin-Xibukkbj.js";
1
+ import { B as EventMap, N as WithContext, V as Keys, b as Context, d as Effects, h as Bus, l as BoundForm } from "./action-creator-CQlFgQm4.js";
2
+ import { n as NoClash } from "./context-plugin-Cphk8jfq.js";
3
3
  //#region src/utils/delay.d.ts
4
4
  /**
5
5
  * Resolves after `ms`, or rejects early if `ctx.signal` aborts. `ms` can be a
@@ -1,5 +1,5 @@
1
- import { B as EventMap, G as Task } from "./action-creator-9e8jesRa.js";
2
- import { n as withPolicy, t as PolicyFunction } from "./with-policy-DpCvSZRk.js";
1
+ import { B as EventMap, G as Task } from "./action-creator-CQlFgQm4.js";
2
+ import { n as withPolicy, t as PolicyFunction } from "./with-policy-_zP3OKGy.js";
3
3
  //#region src/policies/keyed.d.ts
4
4
  /** Options shared by every policy that can keep its state per key. */
5
5
  type KeyOption<P> = {
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { B as EventMap, G as Task, T as EventKey, h as Bus, r as FreeActionCreator, t as ActionCreator } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, G as Task, T as EventKey, h as Bus, r as FreeActionCreator, t as ActionCreator } from "./action-creator-CQlFgQm4.js";
2
2
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
3
- import { t as PolicyFunction } from "./with-policy-DpCvSZRk.js";
3
+ import { t as PolicyFunction } from "./with-policy-_zP3OKGy.js";
4
4
  //#region src/react/plugin.d.ts
5
5
  /** Tells whether two values are the same: `Object.is` unless the caller gives another. */
6
6
  type Equals<V> = (previous: V, next: V) => boolean;
package/dist/redux.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, h as Bus } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, h as Bus } from "./action-creator-CQlFgQm4.js";
2
2
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
3
3
  //#region src/store/redux/middleware.d.ts
4
4
  /** What Redux passes through a middleware: an action is a plain object with a `type`. */
package/dist/scope.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as EventMap, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect } from "./action-creator-9e8jesRa.js";
2
- import { n as ScopeSlots, r as Scoped } from "./types-ByPzv7tw.js";
1
+ import { B as EventMap, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect } from "./action-creator-CQlFgQm4.js";
2
+ import { n as ScopeSlots, r as Scoped } from "./types-bzocBVYc.js";
3
3
  //#region src/scope/effect.d.ts
4
4
  /**
5
5
  * Marks a function whose first parameter is `{ signal }` as an effect, so that
package/dist/signal.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, C as Emit, E as EventLike, a as EmitScope, c as WithScope, h as Bus, v as CommandCall } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, C as Emit, E as EventLike, a as EmitScope, c as WithScope, h as Bus, v as CommandCall } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/signal/plugin.d.ts
3
3
  /** What the second argument of `emit` takes besides the fields of a scope. */
4
4
  type SignalOption = {
package/dist/trace.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as EventMap, J as TraceLogger, K as Trace, L as Tracer, h as Bus, q as TraceEvent } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, J as TraceLogger, K as Trace, L as Tracer, W as Phase, h as Bus, q as TraceEvent, z as EffectName } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/trace/console.d.ts
3
3
  type ConsoleLoggerOptions = {
4
4
  /** Defaults to `globalThis.console`. */
@@ -57,7 +57,9 @@ export declare const performanceLogger: (options?: PerformanceLoggerOptions) =>
57
57
  * - `useEmitter` and `bus.emitter` emit from a root task named by their
58
58
  * `traceId`, so a chain says which subscription began it;
59
59
  * - `bus.on(event, handler, { traceId })` and `once` name a listener: each call of
60
- * it is a task with that `traceId`, and what it emits is below it.
60
+ * it is a task with that `traceId`, and what it emits is below it;
61
+ * - each emit is reported as one event with `effect: "emit"`, also when nothing
62
+ * listens to it, so a log can show what was emitted and not only what ran.
61
63
  *
62
64
  * The plugin does nothing on a bus without a tracer, and without it all of these
63
65
  * options are ignored, so a project can leave it out, or use it only in
@@ -108,21 +110,38 @@ type ReduxExtension = {
108
110
  maxAge?: number;
109
111
  }) => ReduxConnection;
110
112
  };
113
+ /** A stage of a handler task: which policy (or `run`) and which phase of it. */
114
+ type PolicyStage = {
115
+ effect: EffectName;
116
+ phase: Phase;
117
+ };
118
+ /**
119
+ * Which stages of the handler tasks to show: `true` for all, a list of phases
120
+ * (`["cancel", "drop"]`), or a function of the stage.
121
+ */
122
+ type PolicyStages = boolean | readonly Phase[] | ((stage: PolicyStage) => boolean);
111
123
  type ReduxDevtoolsOptions = {
112
124
  /** Instance name in the Redux DevTools picker. */
113
125
  name?: string;
114
126
  /** Defaults to `globalThis.__REDUX_DEVTOOLS_EXTENSION__`. */
115
127
  extension?: ReduxExtension;
116
128
  /**
117
- * Also send the start of every task as an action. Default `false`: a task
118
- * is one action, sent when it ends, and the tasks that are running are in
119
- * the state.
129
+ * Also show the stages of the handler tasks, the tasks that a policy
130
+ * (`latest`, `debounce`, ...) or `ctx.run` starts: `true` for every phase, a
131
+ * list of phases, or a function of `{ effect, phase }`. Default: none, the
132
+ * log has the emits only. The error of a handler task is always shown.
133
+ */
134
+ policies?: PolicyStages;
135
+ /**
136
+ * Also send the start of every shown task as an action. Default `false`: a
137
+ * task is one action, sent when it ends, and the tasks that are running are
138
+ * in the state.
120
139
  */
121
140
  starts?: boolean;
122
141
  /**
123
- * How deep in the tree of tasks to show: `0` is the handler tasks only, `1`
124
- * also the effects they start (`retry`, `timeout`, ...), and so on. The
125
- * attempts and the starters under them are hidden at the default, `1`.
142
+ * How deep below the handler tasks to show: `0` is none, `1` also the
143
+ * effects they start (`retry`, `timeout`, ...), and so on. Default `0`.
144
+ * The handler tasks themselves are chosen by `policies`.
126
145
  */
127
146
  depth?: number;
128
147
  /** The number of actions that DevTools keeps; its own default is 50. */
@@ -137,18 +156,21 @@ type ReduxDevtoolsState = {
137
156
  }>;
138
157
  };
139
158
  /**
140
- * Trace logger that sends the tasks to Redux DevTools as actions
141
- * `type/effect/phase`, with the running tasks as state. By default it is quiet:
142
- * a task is one action, when it ends, and only the handler tasks and the
143
- * effects they start are shown (see `starts` and `depth` to show more).
144
- * Read-only: there is no time travel. Does nothing when the extension is not
145
- * installed.
159
+ * Trace logger that sends the trace to Redux DevTools. By default it is quiet:
160
+ * one action for each emit (`type` is the event name, it needs `tracePlugin`),
161
+ * and the failures of handlers. `policies` adds the stages of the handler tasks
162
+ * as actions `event/policy/phase`, and `depth` the effects below them; the
163
+ * running tasks are in the state. Read-only: there is no time travel. Does
164
+ * nothing when the extension is not installed.
146
165
  *
147
166
  * @example
148
167
  * ```ts
149
168
  * const bus = createBus<Events>({
150
169
  * trace: createTracer(reduxDevtools({ name: "app" })),
151
- * });
170
+ * }).use(tracePlugin());
171
+ *
172
+ * // and the cancelled and dropped calls of the policies
173
+ * reduxDevtools({ name: "app", policies: ["cancel", "drop"] });
152
174
  * ```
153
175
  */
154
176
  export declare const reduxDevtools: (options?: ReduxDevtoolsOptions) => TraceLogger;
package/dist/trace.js CHANGED
@@ -1 +1 @@
1
- import{a as e,c as t,d as n,n as r,o as i,s as a,u as o}from"./context-CJGW6OWj.js";import{n as s}from"./promise-CSLpzwSR.js";import{t as c}from"./origin-key-BbWN3gmN.js";import{t as l}from"./scope-key-B7l7mUS5.js";const u=(e={})=>{let t=e.console??globalThis.console,n=e.prefix??`[nanoeffects]`,r=/* @__PURE__ */ new Map;return({id:e,parentId:i,type:a,effect:o,phase:s,payload:c,error:l,traceGroupId:u})=>{r.has(e)||r.set(e,typeof i==`number`?(r.get(i)??0)+1:0);let d=` `.repeat(r.get(e)??0);s!==`start`&&r.delete(e);let f=u===void 0?``:` [${u}]`,p=`${n}${f} ${d}${a} · ${o} ${s}`;s===`error`?t?.error(p,l):t?.debug(p,c)}},d=(e={})=>{let t=e.performance??globalThis.performance,n=/* @__PURE__ */ new Map;return({id:e,parentId:r,rootId:i,traceGroupId:a,type:o,effect:s,phase:c,error:l})=>{if(!t)return;if(c===`start`){n.set(e,t.now());return}let u=`${o} · ${s} (${c})`,d={id:e,parentId:r,...i!==void 0&&{rootId:i},...a!==void 0&&{traceGroupId:a},phase:c,...c===`error`&&{error:String(l)}},f=n.get(e);n.delete(e),typeof f==`number`?t.measure(u,{start:f,end:t.now(),detail:d}):t.mark(u,{detail:d})}};let f;const p=()=>f,m=(e,t)=>{let n=f;f=e;try{return t()}finally{f=n}},h=(e,t,n={})=>{let{trace:i}=e;if(!i){e.emit(t);return}let{traceId:o,traceGroupId:s}=n,{ctx:c,unlink:l}=r({event:{type:o??t.type,payload:t,...s===void 0?{}:{traceGroupId:s}},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e.ctx,addContext:e.addContext},tracer:i});a(c,`run`,`start`);try{c.emit(t),a(c,`run`,`done`)}catch(e){throw a(c,`run`,`error`,e),e}finally{l()}},g=e=>typeof e==`string`?e:Reflect.get(Object(e),`type`),_=()=>n=>{if(!(t&&n.trace))return n;let r=n.emit;n.emit=(e,...t)=>m(e,()=>r(e,...t)),Reflect.set(n,c,(e,t)=>{h(n,e,t)});let a=/* @__PURE__ */ new WeakMap,o=(e,t,n)=>{let r=a.get(t)??/* @__PURE__ */ new Map,i=g(e);r.set(i,[...r.get(i)??[],n]),a.set(t,r)},s=(e,t)=>(a.get(t)?.get(g(e)))?.pop(),l=(t,n)=>(r,a)=>(e(a,n),i(a,`run`,()=>t(r,a))),u=(e,t)=>(n,r,i)=>{let a=Reflect.get(Object(i),`traceId`);if(typeof a!=`string`)return e(n,r);let c=t?(e,t)=>(s(n,r),l(r,a)(e,t)):l(r,a);o(n,r,c);let u=e(n,c);return()=>{u()}},d=n.off,f=(e,t)=>{t===void 0?d(e):d(e,s(e,t)??t)},p=n.on,_=n.once;return n.on=u(p,!1),n.once=u(_,!0),n.off=f,n},v=()=>{let e=[];return Object.assign(t=>{e.push(t)},{events:e,clear:()=>{e.length=0}})},y=()=>{},b=(e={})=>{let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;if(!t)return y;let{starts:n=!1,depth:r=1,maxAge:i}=e,a=t.connect({name:e.name??`nanoeffects`,...i!==void 0&&{maxAge:i}}),o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Map,c=()=>({running:Object.fromEntries(o)});return a.init(c()),({error:e,...t})=>{let i=(t.parentId===void 0?-1:s.get(t.parentId)??-1)+1,l=i<=r;t.phase===`start`?(s.set(t.id,i),l&&o.set(t.id,{type:t.type,effect:t.effect,...t.traceGroupId!==void 0&&{traceGroupId:t.traceGroupId}})):(s.delete(t.id),o.delete(t.id)),l&&(t.phase!==`start`||n)&&a.send({...t,type:`${t.type}/${t.effect}/${t.phase}`,event:t.type,...t.phase===`error`&&{error:e instanceof Error?e.message:e}},c())}},x=e=>{let t=typeof e==`function`?[e]:[...e??[]];return t.length===0?null:e=>{for(let r of t)try{r(e)}catch(e){n(`trace logger failed`,e)}}},S=()=>{},C=e=>{if(typeof e.traceGroupId==`string`)return e.traceGroupId;let t=p();if(typeof t!=`object`&&typeof t!=`function`||t===null)return;let n=Reflect.get(t,l),r=typeof n==`object`&&n?Reflect.get(n,`traceGroupId`):void 0;return typeof r==`string`?r:void 0},w=(e,t={})=>{let n=x(e),{filter:r,sample:i=1}=t,a=0,c=e=>typeof i==`function`?i({type:e.type,payload:e.payload}):Math.random()<i,l={task:(e,t)=>({id:++a,root:t?.root??a,parent:t?.id,type:e.type,payload:e.payload,sampled:t?.sampled??c(e),group:C(e)??t?.group}),rename:(e,t)=>{e.type=t},report:(e,t,i,a)=>{if(!e.sampled)return;let o={id:e.id,type:e.type,effect:t,phase:i,payload:e.payload};typeof e.parent==`number`&&(o.parentId=e.parent),e.root!==e.id&&(o.rootId=e.root),e.group!==void 0&&(o.traceGroupId=e.group),i===`error`&&(o.error=a),(!r||r(o))&&n?.(o)},settler:(e,t)=>{if(!e.sampled)return S;let n=!1;return(r,i)=>{n||(n=!0,l.report(e,t,r,i))}},run:(e,t,n,r)=>{if(!e.sampled)return r();let i=l.settler(e,t);l.report(e,t,`start`),o({signal:n},()=>i(`cancel`));let a;try{a=r()}catch(e){throw i(`error`,e),e}return s(a)?Promise.resolve(a).then(e=>(i(`done`),e),e=>{throw i(`error`,e),e}):(i(`done`),a)}};return l};export{u as consoleLogger,v as createTraceRecorder,w as createTracer,d as performanceLogger,b as reduxDevtools,_ as tracePlugin};
1
+ import{a as e,c as t,d as n,n as r,o as i,r as a,s as o,u as s}from"./context-CJGW6OWj.js";import{n as c}from"./promise-CSLpzwSR.js";import{t as l}from"./origin-key-BbWN3gmN.js";import{t as u}from"./scope-key-B7l7mUS5.js";const d=(e={})=>{let t=e.console??globalThis.console,n=e.prefix??`[nanoeffects]`,r=/* @__PURE__ */ new Map;return({id:e,parentId:i,type:a,effect:o,phase:s,payload:c,error:l,traceGroupId:u})=>{r.has(e)||r.set(e,typeof i==`number`?(r.get(i)??0)+1:0);let d=` `.repeat(r.get(e)??0);s!==`start`&&r.delete(e);let f=u===void 0?``:` [${u}]`,p=`${n}${f} ${d}${a} · ${o} ${s}`;s===`error`?t?.error(p,l):t?.debug(p,c)}},f=(e={})=>{let t=e.performance??globalThis.performance,n=/* @__PURE__ */ new Map;return({id:e,parentId:r,rootId:i,traceGroupId:a,type:o,effect:s,phase:c,error:l})=>{if(!t)return;if(c===`start`){n.set(e,t.now());return}let u=`${o} · ${s} (${c})`,d={id:e,parentId:r,...i!==void 0&&{rootId:i},...a!==void 0&&{traceGroupId:a},phase:c,...c===`error`&&{error:String(l)}},f=n.get(e);n.delete(e),typeof f==`number`?t.measure(u,{start:f,end:t.now(),detail:d}):t.mark(u,{detail:d})}};let p;const m=()=>p,h=(e,t)=>{let n=p;p=e;try{return t()}finally{p=n}},g=(e,t,n={})=>{let{trace:i}=e;if(!i){e.emit(t);return}let{traceId:a,traceGroupId:s}=n,{ctx:c,unlink:l}=r({event:{type:a??t.type,payload:t,...s===void 0?{}:{traceGroupId:s}},bus:{emit:e.emit,on:e.on,off:e.off,ctx:e.ctx,addContext:e.addContext},tracer:i});o(c,`run`,`start`);try{c.emit(t),o(c,`run`,`done`)}catch(e){throw o(c,`run`,`error`,e),e}finally{l()}},_=e=>typeof e==`string`?e:Reflect.get(Object(e),`type`),v=(e,t)=>typeof e==`object`?Reflect.get(Object(e),`payload`):t[0],y=()=>n=>{if(!(t&&n.trace))return n;let{trace:r}=n,o=(e,t)=>{let n=r.task({type:_(e),payload:v(e,t)},a());r.report(n,`emit`,`done`)},s=n.emit;n.emit=(e,...t)=>h(e,()=>(o(e,t),s(e,...t))),Reflect.set(n,l,(e,t)=>{g(n,e,t)});let c=/* @__PURE__ */ new WeakMap,u=(e,t,n)=>{let r=c.get(t)??/* @__PURE__ */ new Map,i=_(e);r.set(i,[...r.get(i)??[],n]),c.set(t,r)},d=(e,t)=>(c.get(t)?.get(_(e)))?.pop(),f=(t,n)=>(r,a)=>(e(a,n),i(a,`run`,()=>t(r,a))),p=(e,t)=>(n,r,i)=>{let a=Reflect.get(Object(i),`traceId`);if(typeof a!=`string`)return e(n,r);let o=t?(e,t)=>(d(n,r),f(r,a)(e,t)):f(r,a);u(n,r,o);let s=e(n,o);return()=>{s()}},m=n.off,y=(e,t)=>{t===void 0?m(e):m(e,d(e,t)??t)},b=n.on,x=n.once;return n.on=p(b,!1),n.once=p(x,!0),n.off=y,n},b=()=>{let e=[];return Object.assign(t=>{e.push(t)},{events:e,clear:()=>{e.length=0}})},x=()=>{},S=()=>!1,C=()=>!0,w=e=>{if(typeof e==`function`)return e;if(Array.isArray(e)){let t=new Set(e);return({phase:e})=>t.has(e)}return e?C:S},T=(e={})=>{let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;if(!t)return x;let{starts:n=!1,depth:r=0,maxAge:i}=e,a=w(e.policies),o=t.connect({name:e.name??`nanoeffects`,...i!==void 0&&{maxAge:i}}),s=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map,l=()=>({running:Object.fromEntries(s)});o.init(l());let u=(e,t,n)=>n===0?a({effect:e,phase:t}):n<=r,d=(e,t)=>{e.phase===`start`?(c.set(e.id,t),u(e.effect,`start`,t)&&s.set(e.id,{type:e.type,effect:e.effect,...e.traceGroupId!==void 0&&{traceGroupId:e.traceGroupId}})):(c.delete(e.id),s.delete(e.id))},f=!1,p=!1,m=(e,t)=>{f||p||t!==0||e===`start`||(p=!0,globalThis.console?.warn(`[nanoeffects] reduxDevtools got a task but no emit event: add bus.use(tracePlugin()) to see the emits.`))},h=(e,t)=>{o.send({...e,type:`${e.type}/${e.effect}/${e.phase}`,event:e.type,...e.phase===`error`&&{error:t instanceof Error?t.message:t}},l())};return({error:e,...t})=>{if(t.effect===`emit`){f=!0,o.send({...t,event:t.type},l());return}let r=(t.parentId===void 0?-1:c.get(t.parentId)??-1)+1;d(t,r),m(t.phase,r),(t.phase===`error`&&r===0||u(t.effect,t.phase,r))&&(t.phase!==`start`||n)&&h(t,e)}},E=e=>{let t=typeof e==`function`?[e]:[...e??[]];return t.length===0?null:e=>{for(let r of t)try{r(e)}catch(e){n(`trace logger failed`,e)}}},D=()=>{},O=e=>{if(typeof e.traceGroupId==`string`)return e.traceGroupId;let t=m();if(typeof t!=`object`&&typeof t!=`function`||t===null)return;let n=Reflect.get(t,u),r=typeof n==`object`&&n?Reflect.get(n,`traceGroupId`):void 0;return typeof r==`string`?r:void 0},k=(e,t={})=>{let n=E(e),{filter:r,sample:i=1}=t,a=0,o=e=>typeof i==`function`?i({type:e.type,payload:e.payload}):Math.random()<i,l={task:(e,t)=>({id:++a,root:t?.root??a,parent:t?.id,type:e.type,payload:e.payload,sampled:t?.sampled??o(e),group:O(e)??t?.group}),rename:(e,t)=>{e.type=t},report:(e,t,i,a)=>{if(!e.sampled)return;let o={id:e.id,type:e.type,effect:t,phase:i,payload:e.payload};typeof e.parent==`number`&&(o.parentId=e.parent),e.root!==e.id&&(o.rootId=e.root),e.group!==void 0&&(o.traceGroupId=e.group),i===`error`&&(o.error=a),(!r||r(o))&&n?.(o)},settler:(e,t)=>{if(!e.sampled)return D;let n=!1;return(r,i)=>{n||(n=!0,l.report(e,t,r,i))}},run:(e,t,n,r)=>{if(!e.sampled)return r();let i=l.settler(e,t);l.report(e,t,`start`),s({signal:n},()=>i(`cancel`));let a;try{a=r()}catch(e){throw i(`error`,e),e}return c(a)?Promise.resolve(a).then(e=>(i(`done`),e),e=>{throw i(`error`,e),e}):(i(`done`),a)}};return l};export{d as consoleLogger,b as createTraceRecorder,k as createTracer,f as performanceLogger,T as reduxDevtools,y as tracePlugin};
@@ -1,5 +1,5 @@
1
- import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-9e8jesRa.js";
2
- import { t as BoundOf } from "./plugin-DHkamZdH.js";
1
+ import { B as EventMap, d as Effects, o as Scope, s as WithFields } from "./action-creator-CQlFgQm4.js";
2
+ import { t as BoundOf } from "./plugin-BrTsPg2Y.js";
3
3
  //#region src/scope/types.d.ts
4
4
  /**
5
5
  * The slots `scopePlugin` fills for `modulePlugin`. They are not on `Bus`: a bus
@@ -1,4 +1,4 @@
1
- import { B as EventMap, G as Task, U as PayloadOf } from "./action-creator-9e8jesRa.js";
1
+ import { B as EventMap, G as Task, U as PayloadOf } from "./action-creator-CQlFgQm4.js";
2
2
  //#region src/policies/with-policy.d.ts
3
3
  /** What `withPolicy` returns: the task as a plain function that can be cancelled. */
4
4
  type PolicyFunction<P, R> = {
package/docs/tracing.md CHANGED
@@ -24,7 +24,7 @@ Each `TraceEvent` has:
24
24
  | `parentId` | Id of the task that started this one (absent for handler tasks) |
25
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 |
26
26
  | `type` | Event type the task works on |
27
- | `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, … |
27
+ | `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, …; `emit` for the emit event of `tracePlugin` |
28
28
  | `phase` | `start`, `done`, `error`, `cancel`, or `drop` |
29
29
  | `payload` | Event payload |
30
30
  | `error` | Thrown value, only for `error` |
@@ -53,6 +53,32 @@ bus.on("load", async (id, ctx) => {
53
53
  });
54
54
  ```
55
55
 
56
+ ## Show every emit
57
+
58
+ **When.** You want the log to say what was emitted, also when nothing listens or the listener is a plain function. Without it the trace has only the tasks that report: a policy (`latest`, ...) or a `ctx.run`.
59
+
60
+ `tracePlugin` reports one event for each emit, with `effect: "emit"` and `phase: "done"`, before the handlers run. `type` is the event name and `payload` is what was emitted. A `ctx.emit` has the task that emits as its `parentId`.
61
+
62
+ ```js
63
+ // bus.js
64
+ import { createBus } from "nanoeffects";
65
+ import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
66
+
67
+ export const bus = createBus({ trace: createTracer(consoleLogger()) })
68
+ .use(tracePlugin()); // tracePlugin: reports each emit as an event
69
+ ```
70
+
71
+ ```js
72
+ import { bus } from "./bus";
73
+
74
+ bus.emit("search", "nano"); // logs: search · emit done
75
+ ```
76
+
77
+ The event changes nothing else: the other tasks keep their `parentId`, `rootId` and `traceGroupId`. Without the plugin the trace has no `emit` events.
78
+
79
+ - **What counts as an emit.** Every `bus.emit`, also the ones a library makes for you: a job emits its `pending` and `success` (or `fail`, `cancel`), so one call of a job is several `emit` events.
80
+ - **Cost.** One small task per emit, about 60 ns on a bus that already has a tracer (a bus with a tracer and no plugin: about 250 ns more than a bus without one). `sample` and `filter` of `createTracer` cut it in the log, not in the work.
81
+
56
82
  ## Show which subscription began a chain
57
83
 
58
84
  **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.
@@ -186,7 +212,7 @@ const bus = createBus({
186
212
 
187
213
  | Logger | What it does |
188
214
  | ------ | ------------ |
189
- | `reduxDevtools()` | Sends tasks to Redux DevTools as `type/effect/phase`; state is `{ running }` |
215
+ | `reduxDevtools()` | Sends the emits to Redux DevTools, and with `policies` the stages of the handlers as `type/effect/phase`; state is `{ running }` |
190
216
  | `consoleLogger()` | Prints one line per phase, indented by task depth |
191
217
  | `performanceLogger()` | User Timing measures for the browser Performance panel |
192
218
  | `createTraceRecorder()` | Keeps events in `recorder.events`, for tests |
@@ -209,19 +235,49 @@ createTracer(logger, {
209
235
 
210
236
  Read-only (the bus owns no state, so there is no time travel). Does nothing when the extension is not installed. Has its own instance in the DevTools picker, apart from your store.
211
237
 
212
- Quiet by default:
238
+ Quiet by default, so you see what happened before you see how:
239
+
240
+ - **One action per emit**, named after the event (`search`), with its payload. This needs `bus.use(tracePlugin())` (see [Show every emit](#show-every-emit)); without it the default log is empty, and the logger prints one `console.warn` when the first handler task ends with no emit before it.
241
+ - **A failed handler** is sent as `search/latest/error`.
242
+ - Nothing else: no start, no `done`, no policy stages, no effects.
213
243
 
214
- - **A task is one action**, sent when it ends (`done`, `error`, `cancel`, `drop`). Running tasks are in `{ running }`. `starts: true` also sends every start.
215
- - **Only the top is shown**: handler tasks and the effects they start. Attempts and starters under them are hidden. `depth: 0` — handlers only; `depth: 3` — more; `Infinity` — everything.
244
+ Turn on more when you need it:
245
+
246
+ - `policies` — the stages of the handler tasks, as `event/policy/phase`. `true` shows every phase, a list shows those phases, a function gets `{ effect, phase }`.
247
+ - `starts: true` — also send the start of each shown task. Running tasks are in the state `{ running }`.
248
+ - `depth: 1` — also the effects a handler starts (`retry`, `timeout`); `depth: Infinity` — everything below.
216
249
  - `maxAge: 200` — how many actions DevTools keeps (its default is 50).
217
250
 
251
+ ```js
252
+ // bus.js
253
+ import { createBus } from "nanoeffects";
254
+ import { createTracer, reduxDevtools, tracePlugin } from "nanoeffects/trace";
255
+
256
+ // reduxDevtools: sends the trace to the extension; it has the emits and the failures only
257
+ const logger = reduxDevtools({ name: "app" });
258
+
259
+ export const bus = createBus({ trace: createTracer(logger) })
260
+ .use(tracePlugin()); // tracePlugin: makes the emit events
261
+ ```
262
+
218
263
  ```js
219
264
  import { reduxDevtools } from "nanoeffects/trace";
220
265
 
221
- reduxDevtools({ name: "app", depth: 0 }); // one line per handler call
222
- reduxDevtools({ name: "app", starts: true, depth: Infinity }); // everything
266
+ // debugging a race: which calls did a policy cancel or drop?
267
+ reduxDevtools({ name: "app", policies: ["cancel", "drop"] });
268
+
269
+ // every stage of every policy, one function for the choice
270
+ reduxDevtools({
271
+ name: "app",
272
+ policies: ({ effect, phase }) => effect === "latest" && phase !== "start",
273
+ });
274
+
275
+ // the old log: every stage, and the effects below the handlers
276
+ reduxDevtools({ name: "app", policies: true, depth: 1 });
223
277
  ```
224
278
 
279
+ With two quick `search` emits and a `latest` handler, the default list is `search`, `search`. With `policies: true` it adds the `latest` stages of each call, for example `search/latest/cancel` for the first one.
280
+
225
281
  ### Write your own logger
226
282
 
227
283
  ```ts
package/llms-full.txt CHANGED
@@ -4564,7 +4564,7 @@ Each `TraceEvent` has:
4564
4564
  | `parentId` | Id of the task that started this one (absent for handler tasks) |
4565
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 |
4566
4566
  | `type` | Event type the task works on |
4567
- | `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, … |
4567
+ | `effect` | `latest`, `debounce`, `throttle`, `leading`, `run`, `all`, `race`, …; `emit` for the emit event of `tracePlugin` |
4568
4568
  | `phase` | `start`, `done`, `error`, `cancel`, or `drop` |
4569
4569
  | `payload` | Event payload |
4570
4570
  | `error` | Thrown value, only for `error` |
@@ -4593,6 +4593,32 @@ bus.on("load", async (id, ctx) => {
4593
4593
  });
4594
4594
  ```
4595
4595
 
4596
+ ## Show every emit
4597
+
4598
+ **When.** You want the log to say what was emitted, also when nothing listens or the listener is a plain function. Without it the trace has only the tasks that report: a policy (`latest`, ...) or a `ctx.run`.
4599
+
4600
+ `tracePlugin` reports one event for each emit, with `effect: "emit"` and `phase: "done"`, before the handlers run. `type` is the event name and `payload` is what was emitted. A `ctx.emit` has the task that emits as its `parentId`.
4601
+
4602
+ ```js
4603
+ // bus.js
4604
+ import { createBus } from "nanoeffects";
4605
+ import { consoleLogger, createTracer, tracePlugin } from "nanoeffects/trace";
4606
+
4607
+ export const bus = createBus({ trace: createTracer(consoleLogger()) })
4608
+ .use(tracePlugin()); // tracePlugin: reports each emit as an event
4609
+ ```
4610
+
4611
+ ```js
4612
+ import { bus } from "./bus";
4613
+
4614
+ bus.emit("search", "nano"); // logs: search · emit done
4615
+ ```
4616
+
4617
+ The event changes nothing else: the other tasks keep their `parentId`, `rootId` and `traceGroupId`. Without the plugin the trace has no `emit` events.
4618
+
4619
+ - **What counts as an emit.** Every `bus.emit`, also the ones a library makes for you: a job emits its `pending` and `success` (or `fail`, `cancel`), so one call of a job is several `emit` events.
4620
+ - **Cost.** One small task per emit, about 60 ns on a bus that already has a tracer (a bus with a tracer and no plugin: about 250 ns more than a bus without one). `sample` and `filter` of `createTracer` cut it in the log, not in the work.
4621
+
4596
4622
  ## Show which subscription began a chain
4597
4623
 
4598
4624
  **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.
@@ -4726,7 +4752,7 @@ const bus = createBus({
4726
4752
 
4727
4753
  | Logger | What it does |
4728
4754
  | ------ | ------------ |
4729
- | `reduxDevtools()` | Sends tasks to Redux DevTools as `type/effect/phase`; state is `{ running }` |
4755
+ | `reduxDevtools()` | Sends the emits to Redux DevTools, and with `policies` the stages of the handlers as `type/effect/phase`; state is `{ running }` |
4730
4756
  | `consoleLogger()` | Prints one line per phase, indented by task depth |
4731
4757
  | `performanceLogger()` | User Timing measures for the browser Performance panel |
4732
4758
  | `createTraceRecorder()` | Keeps events in `recorder.events`, for tests |
@@ -4749,19 +4775,49 @@ createTracer(logger, {
4749
4775
 
4750
4776
  Read-only (the bus owns no state, so there is no time travel). Does nothing when the extension is not installed. Has its own instance in the DevTools picker, apart from your store.
4751
4777
 
4752
- Quiet by default:
4778
+ Quiet by default, so you see what happened before you see how:
4779
+
4780
+ - **One action per emit**, named after the event (`search`), with its payload. This needs `bus.use(tracePlugin())` (see [Show every emit](#show-every-emit)); without it the default log is empty, and the logger prints one `console.warn` when the first handler task ends with no emit before it.
4781
+ - **A failed handler** is sent as `search/latest/error`.
4782
+ - Nothing else: no start, no `done`, no policy stages, no effects.
4783
+
4784
+ Turn on more when you need it:
4753
4785
 
4754
- - **A task is one action**, sent when it ends (`done`, `error`, `cancel`, `drop`). Running tasks are in `{ running }`. `starts: true` also sends every start.
4755
- - **Only the top is shown**: handler tasks and the effects they start. Attempts and starters under them are hidden. `depth: 0` — handlers only; `depth: 3` — more; `Infinity` — everything.
4786
+ - `policies` — the stages of the handler tasks, as `event/policy/phase`. `true` shows every phase, a list shows those phases, a function gets `{ effect, phase }`.
4787
+ - `starts: true` — also send the start of each shown task. Running tasks are in the state `{ running }`.
4788
+ - `depth: 1` — also the effects a handler starts (`retry`, `timeout`); `depth: Infinity` — everything below.
4756
4789
  - `maxAge: 200` — how many actions DevTools keeps (its default is 50).
4757
4790
 
4791
+ ```js
4792
+ // bus.js
4793
+ import { createBus } from "nanoeffects";
4794
+ import { createTracer, reduxDevtools, tracePlugin } from "nanoeffects/trace";
4795
+
4796
+ // reduxDevtools: sends the trace to the extension; it has the emits and the failures only
4797
+ const logger = reduxDevtools({ name: "app" });
4798
+
4799
+ export const bus = createBus({ trace: createTracer(logger) })
4800
+ .use(tracePlugin()); // tracePlugin: makes the emit events
4801
+ ```
4802
+
4758
4803
  ```js
4759
4804
  import { reduxDevtools } from "nanoeffects/trace";
4760
4805
 
4761
- reduxDevtools({ name: "app", depth: 0 }); // one line per handler call
4762
- reduxDevtools({ name: "app", starts: true, depth: Infinity }); // everything
4806
+ // debugging a race: which calls did a policy cancel or drop?
4807
+ reduxDevtools({ name: "app", policies: ["cancel", "drop"] });
4808
+
4809
+ // every stage of every policy, one function for the choice
4810
+ reduxDevtools({
4811
+ name: "app",
4812
+ policies: ({ effect, phase }) => effect === "latest" && phase !== "start",
4813
+ });
4814
+
4815
+ // the old log: every stage, and the effects below the handlers
4816
+ reduxDevtools({ name: "app", policies: true, depth: 1 });
4763
4817
  ```
4764
4818
 
4819
+ With two quick `search` emits and a `latest` handler, the default list is `search`, `search`. With `policies: true` it adds the `latest` stages of each call, for example `search/latest/cancel` for the first one.
4820
+
4765
4821
  ### Write your own logger
4766
4822
 
4767
4823
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nanoeffects",
3
- "version": "0.11.0",
3
+ "version": "0.12.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",