nanoeffects 0.11.0 → 0.13.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,23 +110,54 @@ 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);
123
+ /**
124
+ * Which actions carry the payload of the event: `"all"` of them, the
125
+ * `"essential"` ones (the emits, and the stages that end a call without a result:
126
+ * `cancel`, `drop`, `error`), or `"none"`.
127
+ */
128
+ type PayloadMode = "all" | "essential" | "none";
111
129
  type ReduxDevtoolsOptions = {
112
130
  /** Instance name in the Redux DevTools picker. */
113
131
  name?: string;
114
132
  /** Defaults to `globalThis.__REDUX_DEVTOOLS_EXTENSION__`. */
115
133
  extension?: ReduxExtension;
116
134
  /**
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.
135
+ * Also show the stages of the handler tasks, the tasks that a policy
136
+ * (`latest`, `debounce`, ...) or `ctx.run` starts: `true` for every phase, a
137
+ * list of phases, or a function of `{ effect, phase }`. Default: none, the
138
+ * log has the emits only. The error of a handler task is always shown.
139
+ */
140
+ policies?: PolicyStages;
141
+ /**
142
+ * Also send the start of every shown task as an action. Default `false`: a
143
+ * task is one action, sent when it ends, and the tasks that are running are
144
+ * in the state.
120
145
  */
121
146
  starts?: boolean;
122
147
  /**
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`.
148
+ * How deep below the handler tasks to show: `0` is none, `1` also the
149
+ * effects they start (`retry`, `timeout`, ...), and so on. Default `0`.
150
+ * The handler tasks themselves are chosen by `policies`.
126
151
  */
127
152
  depth?: number;
153
+ /**
154
+ * Which actions carry the payload of their event. Default `"essential"`: the
155
+ * emits, and the `cancel`, `drop` and `error` of a task, where the payload is
156
+ * the only way to tell which call it was; `start` and `done` repeat the emit
157
+ * and have none. `"all"` puts it in every action, `"none"` in no action, for
158
+ * size or for data that must not reach the extension.
159
+ */
160
+ payload?: PayloadMode;
128
161
  /** The number of actions that DevTools keeps; its own default is 50. */
129
162
  maxAge?: number;
130
163
  };
@@ -137,18 +170,21 @@ type ReduxDevtoolsState = {
137
170
  }>;
138
171
  };
139
172
  /**
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.
173
+ * Trace logger that sends the trace to Redux DevTools. By default it is quiet:
174
+ * one action for each emit (`type` is the event name, it needs `tracePlugin`),
175
+ * and the failures of handlers. `policies` adds the stages of the handler tasks
176
+ * as actions `event/policy/phase`, and `depth` the effects below them; the
177
+ * running tasks are in the state. Read-only: there is no time travel. Does
178
+ * nothing when the extension is not installed.
146
179
  *
147
180
  * @example
148
181
  * ```ts
149
182
  * const bus = createBus<Events>({
150
183
  * trace: createTracer(reduxDevtools({ name: "app" })),
151
- * });
184
+ * }).use(tracePlugin());
185
+ *
186
+ * // and the cancelled and dropped calls of the policies
187
+ * reduxDevtools({ name: "app", policies: ["cancel", "drop"] });
152
188
  * ```
153
189
  */
154
190
  export declare const reduxDevtools: (options?: ReduxDevtoolsOptions) => TraceLogger;
@@ -188,4 +224,4 @@ type TracerOptions = {
188
224
  */
189
225
  export declare const createTracer: <E = Record<string, unknown>>(trace: Trace<E>, options?: TracerOptions) => Tracer;
190
226
  //#endregion
191
- export type { ConsoleLoggerOptions, PerformanceLoggerOptions, Recorder, ReduxDevtoolsOptions, ReduxDevtoolsState, ReduxExtension, TracerOptions };
227
+ export type { ConsoleLoggerOptions, PayloadMode, PerformanceLoggerOptions, PolicyStage, PolicyStages, Recorder, ReduxDevtoolsOptions, ReduxDevtoolsState, ReduxExtension, TracerOptions };
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=/* @__PURE__ */ new Set([`cancel`,`drop`,`error`]),C=()=>!1,w=()=>!0,T=e=>{if(typeof e==`function`)return e;if(Array.isArray(e)){let t=new Set(e);return({phase:e})=>t.has(e)}return e?w:C},E=(e={})=>{let t=e.extension??globalThis.__REDUX_DEVTOOLS_EXTENSION__;if(!t)return x;let{starts:n=!1,depth:r=0,maxAge:i}=e,a=T(e.policies),o=e.payload??`essential`,s=t.connect({name:e.name??`nanoeffects`,...i!==void 0&&{maxAge:i}}),c=/* @__PURE__ */ new Map,l=/* @__PURE__ */ new Map,u=()=>({running:Object.fromEntries(c)});s.init(u());let d=(e,t,n)=>n===0?a({effect:e,phase:t}):n<=r,f=(e,t)=>{e.phase===`start`?(l.set(e.id,t),d(e.effect,`start`,t)&&c.set(e.id,{type:e.type,effect:e.effect,...e.traceGroupId!==void 0&&{traceGroupId:e.traceGroupId}})):(l.delete(e.id),c.delete(e.id))},p=!1,m=!1,h=(e,t)=>{p||m||t!==0||e===`start`||(m=!0,globalThis.console?.warn(`[nanoeffects] reduxDevtools got a task but no emit event: add bus.use(tracePlugin()) to see the emits.`))},g=e=>{let{payload:t,...n}=e;return o===`all`||o===`essential`&&(e.effect===`emit`||S.has(e.phase))?{...n,payload:t}:n},_=(e,t)=>{s.send({...g(e),type:`${e.type}/${e.effect}/${e.phase}`,event:e.type,...e.phase===`error`&&{error:t instanceof Error?t.message:t}},u())};return({error:e,...t})=>{if(t.effect===`emit`){p=!0,s.send({...g(t),event:t.type},u());return}let r=(t.parentId===void 0?-1:l.get(t.parentId)??-1)+1;f(t,r),h(t.phase,r),(t.phase===`error`&&r===0||d(t.effect,t.phase,r))&&(t.phase!==`start`||n)&&_(t,e)}},D=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)}}},O=()=>{},k=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},A=(e,t={})=>{let n=D(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:k(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 O;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,A as createTracer,f as performanceLogger,E 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,56 @@ 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.
249
+ - `payload` — which actions carry the payload of their event:
250
+ - `"essential"` (default) — the emit actions, and the `cancel`, `drop` and `error` of a task, where the payload is the only way to tell which call it was. `start` and `done` repeat the emit and have none, so a large payload is sent once.
251
+ - `"all"` — every action, as in 0.12.
252
+ - `"none"` — no action. Use it for a payload that is large, or holds data that must not reach the browser extension (tokens, personal data). You can then no longer tell which call a policy stopped.
216
253
  - `maxAge: 200` — how many actions DevTools keeps (its default is 50).
217
254
 
255
+ ```js
256
+ // bus.js
257
+ import { createBus } from "nanoeffects";
258
+ import { createTracer, reduxDevtools, tracePlugin } from "nanoeffects/trace";
259
+
260
+ // reduxDevtools: sends the trace to the extension; it has the emits and the failures only
261
+ const logger = reduxDevtools({ name: "app" });
262
+
263
+ export const bus = createBus({ trace: createTracer(logger) })
264
+ .use(tracePlugin()); // tracePlugin: makes the emit events
265
+ ```
266
+
218
267
  ```js
219
268
  import { reduxDevtools } from "nanoeffects/trace";
220
269
 
221
- reduxDevtools({ name: "app", depth: 0 }); // one line per handler call
222
- reduxDevtools({ name: "app", starts: true, depth: Infinity }); // everything
270
+ // debugging a race: which calls did a policy cancel or drop?
271
+ reduxDevtools({ name: "app", policies: ["cancel", "drop"] });
272
+
273
+ // every stage of every policy, one function for the choice
274
+ reduxDevtools({
275
+ name: "app",
276
+ policies: ({ effect, phase }) => effect === "latest" && phase !== "start",
277
+ });
278
+
279
+ // the old log: every stage, and the effects below the handlers
280
+ reduxDevtools({ name: "app", policies: true, depth: 1, payload: "all" });
281
+
282
+ // keep the payloads out of the extension
283
+ reduxDevtools({ name: "app", payload: "none" });
223
284
  ```
224
285
 
286
+ 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.
287
+
225
288
  ### Write your own logger
226
289
 
227
290
  ```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,56 @@ 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.
4753
4783
 
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.
4784
+ Turn on more when you need it:
4785
+
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.
4789
+ - `payload` — which actions carry the payload of their event:
4790
+ - `"essential"` (default) — the emit actions, and the `cancel`, `drop` and `error` of a task, where the payload is the only way to tell which call it was. `start` and `done` repeat the emit and have none, so a large payload is sent once.
4791
+ - `"all"` — every action, as in 0.12.
4792
+ - `"none"` — no action. Use it for a payload that is large, or holds data that must not reach the browser extension (tokens, personal data). You can then no longer tell which call a policy stopped.
4756
4793
  - `maxAge: 200` — how many actions DevTools keeps (its default is 50).
4757
4794
 
4795
+ ```js
4796
+ // bus.js
4797
+ import { createBus } from "nanoeffects";
4798
+ import { createTracer, reduxDevtools, tracePlugin } from "nanoeffects/trace";
4799
+
4800
+ // reduxDevtools: sends the trace to the extension; it has the emits and the failures only
4801
+ const logger = reduxDevtools({ name: "app" });
4802
+
4803
+ export const bus = createBus({ trace: createTracer(logger) })
4804
+ .use(tracePlugin()); // tracePlugin: makes the emit events
4805
+ ```
4806
+
4758
4807
  ```js
4759
4808
  import { reduxDevtools } from "nanoeffects/trace";
4760
4809
 
4761
- reduxDevtools({ name: "app", depth: 0 }); // one line per handler call
4762
- reduxDevtools({ name: "app", starts: true, depth: Infinity }); // everything
4810
+ // debugging a race: which calls did a policy cancel or drop?
4811
+ reduxDevtools({ name: "app", policies: ["cancel", "drop"] });
4812
+
4813
+ // every stage of every policy, one function for the choice
4814
+ reduxDevtools({
4815
+ name: "app",
4816
+ policies: ({ effect, phase }) => effect === "latest" && phase !== "start",
4817
+ });
4818
+
4819
+ // the old log: every stage, and the effects below the handlers
4820
+ reduxDevtools({ name: "app", policies: true, depth: 1, payload: "all" });
4821
+
4822
+ // keep the payloads out of the extension
4823
+ reduxDevtools({ name: "app", payload: "none" });
4763
4824
  ```
4764
4825
 
4826
+ 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.
4827
+
4765
4828
  ### Write your own logger
4766
4829
 
4767
4830
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nanoeffects",
3
- "version": "0.11.0",
3
+ "version": "0.13.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",
@@ -165,6 +165,7 @@
165
165
  "lefthook": "2.2.1",
166
166
  "lodash": "4.17.21",
167
167
  "mobx": "^7.0.6",
168
+ "playwright-core": "1.64.1",
168
169
  "publint": "^0.3.25",
169
170
  "react": "^19.3.1",
170
171
  "react-dom": "^19.3.1",