nanoeffects 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/actions.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { H as PayloadOf, T as EventKey, W as Task, c as WithScope, h as Bus, n as Free, o as Scope, r as FreeActionCreator, t as ActionCreator, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { r as Scoped, t as FieldsOfScope } from "./types-DWjyeii7.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-B5V47MZ0.js";
2
+ import { r as Scoped, t as FieldsOfScope } from "./types-Bkuhnkq6.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-B6rg5NIb.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>;
package/dist/dev.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { J as UnsubscribeCallback, h as Bus, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { i as WithApp, n as ModuleApi } from "./module-B5V47MZ0.js";
2
+ import { i as WithApp, n as ModuleApi } from "./module-B6rg5NIb.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
1
  import { P as WithSignal, W as Task, b as Context, u as Effect, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { a as RaceStarter, c as all, d as GroupOptions, f as Starter, i as take, l as ChildOf, m as delay, n as effectsPlugin, o as race, p as StarterOf, r as TakeOptions, s as allSettled, u as EffectContext } from "./plugin-B6UQyoVd.js";
2
+ import { a as RaceStarter, c as all, d as GroupOptions, f as Starter, i as take, l as ChildOf, m as delay, n as effectsPlugin, o as race, p as StarterOf, r as TakeOptions, s as allSettled, u as EffectContext } from "./plugin-D-j1DarG.js";
3
3
  //#region src/utils/onAbort.d.ts
4
4
  export declare const onAbort: (ctx: {
5
5
  signal?: AbortSignal | undefined;
@@ -1,5 +1,5 @@
1
1
  import { H as PayloadOf, J as UnsubscribeCallback, W as Task, _ as Command, b as Context, c as WithScope, h as Bus, o as Scope, v as CommandCall, y as Commands, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { n as ScopeSlots, r as Scoped, t as FieldsOfScope } from "./types-DWjyeii7.js";
2
+ import { n as ScopeSlots, r as Scoped, t as FieldsOfScope } from "./types-Bkuhnkq6.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> & {
@@ -1,5 +1,5 @@
1
1
  import { C as EventKey, F as EventMap, L as PayloadOf, R as Task, _ 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, z as UnsubscribeCallback } from "./action-creator-D2CotugT.js";
2
- import { t as BoundOf } from "./plugin-ks-oNq5n.js";
2
+ import { t as BoundOf } from "./plugin-Dz6hq8Jg.js";
3
3
  import { t as OriginOptions } from "./origin-key-CxK0SkeG.js";
4
4
  //#region src/scope/types.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { F as EventMap, M as WithSignal, R as Task, l as Effect, v as Context } from "./action-creator-D2CotugT.js";
2
- import { a as RaceStarter, c as all, d as GroupOptions, f as Starter, i as take, l as ChildOf, m as delay, n as effectsPlugin, o as race, p as StarterOf, r as TakeOptions, s as allSettled, u as EffectContext } from "./plugin-ks-oNq5n.js";
2
+ import { a as RaceStarter, c as all, d as GroupOptions, f as Starter, i as take, l as ChildOf, m as delay, n as effectsPlugin, o as race, p as StarterOf, r as TakeOptions, s as allSettled, u as EffectContext } from "./plugin-Dz6hq8Jg.js";
3
3
  //#region src/utils/onAbort.d.ts
4
4
  export declare const onAbort: (ctx: {
5
5
  signal?: AbortSignal | undefined;
@@ -71,7 +71,7 @@ type Starter<T, Child = EffectContext> = (child: Child) => T | PromiseLike<T>;
71
71
  * bus.on("open", async (_, ctx) => race(ctx, { load, user }));
72
72
  * ```
73
73
  */
74
- type StarterOf$1<E extends EventMap, R = unknown> = Starter<R, Context<E>>;
74
+ type StarterOf<E extends EventMap, R = unknown> = Starter<R, Context<E>>;
75
75
  /** Options of `all` and `allSettled`. */
76
76
  type GroupOptions = {
77
77
  /** At most this many starters run at once; the next starts when one settles. Default: no limit. */
@@ -132,6 +132,7 @@ declare function allSettled<Child, T extends Record<string, Starter<unknown, Chi
132
132
  declare function allSettled<Child, const T extends readonly Starter<unknown, Child>[]>(effects: T): (child: Child) => Promise<{ -readonly [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
133
133
  //#endregion
134
134
  //#region src/effects/race.d.ts
135
+ /** @deprecated Use `StarterOf<Events, Result>`; this name goes in 1.0.0. */
135
136
  type RaceStarter<T, Child = EffectContext> = Starter<T, Child>;
136
137
  /**
137
138
  * Runs all starters; first to settle wins, losers are cancelled via `ctx.abort`.
@@ -152,12 +153,12 @@ type RaceStarter<T, Child = EffectContext> = Starter<T, Child>;
152
153
  */
153
154
  declare function race<C extends {
154
155
  signal?: AbortSignal;
155
- }, T extends Record<string, RaceStarter<unknown, ChildOf<C>>>>(ctx: C, effects: T): Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
156
+ }, T extends Record<string, Starter<unknown, ChildOf<C>>>>(ctx: C, effects: T): Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
156
157
  declare function race<C extends {
157
158
  signal?: AbortSignal;
158
- }, const T extends readonly RaceStarter<unknown, ChildOf<C>>[]>(ctx: C, effects: T): Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
159
- declare function race<Child, T extends Record<string, RaceStarter<unknown, Child>>>(effects: T): (child: Child) => Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
160
- declare function race<Child, const T extends readonly RaceStarter<unknown, Child>[]>(effects: T): (child: Child) => Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
159
+ }, const T extends readonly Starter<unknown, ChildOf<C>>[]>(ctx: C, effects: T): Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
160
+ declare function race<Child, T extends Record<string, Starter<unknown, Child>>>(effects: T): (child: Child) => Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
161
+ declare function race<Child, const T extends readonly Starter<unknown, Child>[]>(effects: T): (child: Child) => Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
161
162
  //#endregion
162
163
  //#region src/effects/take.d.ts
163
164
  type TakeOptions<P> = {
@@ -259,9 +260,9 @@ type Helpers = Effects;
259
260
  * The effects as a handler sees them: the built-in ones keep their hand-written
260
261
  * types, any other loses its first parameter, or has the type `Effect<F>` says.
261
262
  */
262
- type BoundOf<H extends Helpers, E extends EventMap> = { [K in keyof H]: K extends keyof Bound<E, StarterOf<H, E>> ? H[K] extends Supported[K] ? Bound<E, StarterOf<H, E>>[K] : BoundForm<H[K]> : BoundForm<H[K]>; };
263
+ type BoundOf<H extends Helpers, E extends EventMap> = { [K in keyof H]: K extends keyof Bound<E, BoundStarter<H, E>> ? H[K] extends Supported[K] ? Bound<E, BoundStarter<H, E>>[K] : BoundForm<H[K]> : BoundForm<H[K]>; };
263
264
  /** What a starter of a bound `race` / `all` / `allSettled` gets: the context of its child, with the same bound effects. */
264
- type StarterOf<H extends Helpers, E extends EventMap> = Context<E> & BoundOf<H, E>;
265
+ type BoundStarter<H extends Helpers, E extends EventMap> = Context<E> & BoundOf<H, E>;
265
266
  /**
266
267
  * Puts the effects you pass on the context of every task of the bus, bound to
267
268
  * that task, so a handler can write `({ race })` instead of `race(ctx, …)`.
@@ -285,4 +286,4 @@ type StarterOf<H extends Helpers, E extends EventMap> = Context<E> & BoundOf<H,
285
286
  */
286
287
  declare const effectsPlugin: <H extends Helpers>(helpers: H) => <E extends EventMap>(bus: Bus<E> & NoClash<BoundOf<H, E>, E>) => Bus<E & WithContext<BoundOf<H, E>>>;
287
288
  //#endregion
288
- export { RaceStarter as a, all as c, GroupOptions as d, Starter as f, take as i, ChildOf as l, delay as m, effectsPlugin as n, race as o, StarterOf$1 as p, TakeOptions as r, allSettled as s, BoundOf as t, EffectContext as u };
289
+ export { RaceStarter as a, all as c, GroupOptions as d, Starter as f, take as i, ChildOf as l, delay as m, effectsPlugin as n, race as o, StarterOf as p, TakeOptions as r, allSettled as s, BoundOf as t, EffectContext as u };
@@ -65,7 +65,7 @@ type Starter<T, Child = EffectContext> = (child: Child) => T | PromiseLike<T>;
65
65
  * bus.on("open", async (_, ctx) => race(ctx, { load, user }));
66
66
  * ```
67
67
  */
68
- type StarterOf$1<E extends EventMap, R = unknown> = Starter<R, Context<E>>;
68
+ type StarterOf<E extends EventMap, R = unknown> = Starter<R, Context<E>>;
69
69
  /** Options of `all` and `allSettled`. */
70
70
  type GroupOptions = {
71
71
  /** At most this many starters run at once; the next starts when one settles. Default: no limit. */
@@ -126,6 +126,7 @@ declare function allSettled<Child, T extends Record<string, Starter<unknown, Chi
126
126
  declare function allSettled<Child, const T extends readonly Starter<unknown, Child>[]>(effects: T): (child: Child) => Promise<{ -readonly [K in keyof T]: PromiseSettledResult<Awaited<ReturnType<T[K]>>>; }>;
127
127
  //#endregion
128
128
  //#region src/effects/race.d.ts
129
+ /** @deprecated Use `StarterOf<Events, Result>`; this name goes in 1.0.0. */
129
130
  type RaceStarter<T, Child = EffectContext> = Starter<T, Child>;
130
131
  /**
131
132
  * Runs all starters; first to settle wins, losers are cancelled via `ctx.abort`.
@@ -146,12 +147,12 @@ type RaceStarter<T, Child = EffectContext> = Starter<T, Child>;
146
147
  */
147
148
  declare function race<C extends {
148
149
  signal?: AbortSignal;
149
- }, T extends Record<string, RaceStarter<unknown, ChildOf<C>>>>(ctx: C, effects: T): Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
150
+ }, T extends Record<string, Starter<unknown, ChildOf<C>>>>(ctx: C, effects: T): Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
150
151
  declare function race<C extends {
151
152
  signal?: AbortSignal;
152
- }, const T extends readonly RaceStarter<unknown, ChildOf<C>>[]>(ctx: C, effects: T): Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
153
- declare function race<Child, T extends Record<string, RaceStarter<unknown, Child>>>(effects: T): (child: Child) => Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
154
- declare function race<Child, const T extends readonly RaceStarter<unknown, Child>[]>(effects: T): (child: Child) => Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
153
+ }, const T extends readonly Starter<unknown, ChildOf<C>>[]>(ctx: C, effects: T): Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
154
+ declare function race<Child, T extends Record<string, Starter<unknown, Child>>>(effects: T): (child: Child) => Promise<{ [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
155
+ declare function race<Child, const T extends readonly Starter<unknown, Child>[]>(effects: T): (child: Child) => Promise<{ -readonly [K in keyof T]?: Awaited<ReturnType<T[K]>>; }>;
155
156
  //#endregion
156
157
  //#region src/effects/take.d.ts
157
158
  type TakeOptions<P> = {
@@ -253,9 +254,9 @@ type Helpers = Effects;
253
254
  * The effects as a handler sees them: the built-in ones keep their hand-written
254
255
  * types, any other loses its first parameter, or has the type `Effect<F>` says.
255
256
  */
256
- type BoundOf<H extends Helpers, E extends EventMap> = { [K in keyof H]: K extends keyof Bound<E, StarterOf<H, E>> ? H[K] extends Supported[K] ? Bound<E, StarterOf<H, E>>[K] : BoundForm<H[K]> : BoundForm<H[K]>; };
257
+ type BoundOf<H extends Helpers, E extends EventMap> = { [K in keyof H]: K extends keyof Bound<E, BoundStarter<H, E>> ? H[K] extends Supported[K] ? Bound<E, BoundStarter<H, E>>[K] : BoundForm<H[K]> : BoundForm<H[K]>; };
257
258
  /** What a starter of a bound `race` / `all` / `allSettled` gets: the context of its child, with the same bound effects. */
258
- type StarterOf<H extends Helpers, E extends EventMap> = Context<E> & BoundOf<H, E>;
259
+ type BoundStarter<H extends Helpers, E extends EventMap> = Context<E> & BoundOf<H, E>;
259
260
  /**
260
261
  * Puts the effects you pass on the context of every task of the bus, bound to
261
262
  * that task, so a handler can write `({ race })` instead of `race(ctx, …)`.
@@ -279,4 +280,4 @@ type StarterOf<H extends Helpers, E extends EventMap> = Context<E> & BoundOf<H,
279
280
  */
280
281
  declare const effectsPlugin: <H extends Helpers>(helpers: H) => <E extends EventMap>(bus: Bus<E> & NoClash<BoundOf<H, E>, E>) => Bus<E & WithContext<BoundOf<H, E>>>;
281
282
  //#endregion
282
- export { RaceStarter as a, all as c, GroupOptions as d, Starter as f, take as i, ChildOf as l, delay as m, effectsPlugin as n, race as o, StarterOf$1 as p, TakeOptions as r, allSettled as s, BoundOf as t, EffectContext as u };
283
+ export { RaceStarter as a, all as c, GroupOptions as d, Starter as f, take as i, ChildOf as l, delay as m, effectsPlugin as n, race as o, StarterOf as p, TakeOptions as r, allSettled as s, BoundOf as t, EffectContext as u };
package/dist/scope.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { P as WithSignal, a as EmitScope, d as Effects, f as Signaled, h as Bus, o as Scope, u as Effect, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { n as ScopeSlots, r as Scoped } from "./types-DWjyeii7.js";
2
+ import { n as ScopeSlots, r as Scoped } from "./types-Bkuhnkq6.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
@@ -1,5 +1,5 @@
1
1
  import { d as Effects, o as Scope, s as WithFields, z as EventMap } from "./action-creator-Bpa8BUS9.js";
2
- import { t as BoundOf } from "./plugin-B6UQyoVd.js";
2
+ import { t as BoundOf } from "./plugin-D-j1DarG.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
package/docs/README.md CHANGED
@@ -42,6 +42,7 @@ Code for every guide step: [`examples/guide.ts`](../examples/guide.ts) (type-che
42
42
  - [TypeScript in many files](typescript.md) — keep the types of the bus in commands and modules
43
43
  - [Errors and unheard events](errors.md)
44
44
  - [Package entries](package.md) — which import holds what
45
+ - [Stability](stability.md) — which entries are stable, what is breaking, supported versions
45
46
  - [Plugins](plugins.md) — every plugin of a bus, what it returns, and the order to use them in
46
47
  - [Tracing](tracing.md) — loggers, Redux DevTools, `notrace`
47
48
  - [Observable](observable.md) — events as TC39 Observables
package/docs/effects.md CHANGED
@@ -347,7 +347,7 @@ bus.on("open", async (id, ctx) => {
347
347
  });
348
348
  ```
349
349
 
350
- The same call with a payload and a context, `(payload, ctx)`, is the Task that `bus.on` and `ctx.run` use, and it does not change.
350
+ The same call with a payload and a context, `(payload, ctx)`, is the Task that `bus.on` and `ctx.run` use, and it does not change. `retry`, `timeout` and `poll` therefore have three call forms: with `ctx`, as a Task, and as a starter. All three stay; [Stability](stability.md#rely-on-these-call-forms) says how they are told apart.
351
351
 
352
352
  **`all`, `allSettled` and `race` without `ctx` are starters too**, so a group nests in another group with no wrapper. Load two things, but give up after five seconds:
353
353
 
@@ -396,7 +396,7 @@ bus.on("sync", async (ids, ctx) => {
396
396
  });
397
397
  ```
398
398
 
399
- The result has the same order and shape as without the option. If one starter of `all` fails, the running ones are cancelled and the waiting ones are never called. `allSettled` goes on after a failure. When `ctx` aborts, both stop and start nothing more. A value that is not a positive integer (`0`, `-1`, `NaN`, `1.5`) throws a `RangeError` before anything starts; `Infinity` means no limit. `race` has no `concurrency`: it ends at the first result, so a starter that waited for a free slot would never run. A group starter inside another group takes no options; write `(child) => all(child, effects, { concurrency: 5 })` there.
399
+ The result has the same order and shape as without the option. If one starter of `all` fails, the running ones are cancelled and the waiting ones are never called. `allSettled` goes on after a failure. When `ctx` aborts, both stop and start nothing more. A value that is not a positive integer (`0`, `-1`, `NaN`, `1.5`) throws a `RangeError` before anything starts; `Infinity` means no limit. `race` has no `concurrency`: it ends at the first result, so a starter that waited for a free slot would never run. The form without `ctx` takes no options, because a second argument would look like the form with `ctx`, so a group starter inside another group takes none; write `(child) => all(child, effects, { concurrency: 5 })` there.
400
400
 
401
401
 
402
402
  **A starter gets the context of its own child task.** Pass `child` as the first argument to any effect, as a handler passes `ctx`: when the race ends, `child` is aborted and stops exactly this effect. The outer `ctx` is only for `race` itself. Your own API takes the signal in its options: `({ signal }) => fetchUser(id, { signal })`. The same holds for `all`, `allSettled` and `timeout`.
package/docs/oop.md CHANGED
@@ -261,6 +261,8 @@ export const Cache = extendBus(bus, CacheBase);
261
261
 
262
262
  ## Subscribe a method with `@On` and `@Once`
263
263
 
264
+ `On` here is a decorator. The main entry also exports a type named `On`, the type of `bus.on`; they are different things in different imports, and a file that needs both can rename one: `import { On as OnDecorator } from "nanoeffects/oop"`.
265
+
264
266
  A decorator turns a method into a handler of an event for each instance, with `this` bound. You write no `this.on` in the constructor, and `dispose()` ends the subscription too.
265
267
 
266
268
  The bus and the events are the same as above, in TypeScript:
@@ -0,0 +1,90 @@
1
+ # Know what is stable
2
+
3
+ Use this page to decide what you can rely on. It says which imports are stable, what counts as a breaking change, which versions are supported, and which call forms stay.
4
+
5
+ ## See which entries are stable
6
+
7
+ A stable entry follows the rules below. An unstable entry can change in any minor release; every change is written in the [CHANGELOG](../CHANGELOG.md), and nothing promises a deprecation period first.
8
+
9
+ | Import | Status | Note |
10
+ | ------ | ------ | ---- |
11
+ | `nanoeffects` | Stable | `createBus` and the types of a bus |
12
+ | `nanoeffects/effects` | Stable | `retry`, `timeout`, `poll`, `take`, `delay`, `all`, `allSettled`, `race`, `backoff`, `cancellable`, `effectsPlugin` |
13
+ | `nanoeffects/policies` | Stable | `latest`, `leading`, `debounce`, `throttle`, `queue`, `withPolicy` |
14
+ | `nanoeffects/actions` | Stable | `createAction`, `createCommand`, `createJob`, `createModule` and their plugins |
15
+ | `nanoeffects/trace` | Stable: the contract | `Tracer`, `Trace`, `TraceEvent`, `TraceLogger`, `Phase`, `EffectName` and `tracePlugin` |
16
+ | `nanoeffects/trace` | Unstable: the tools | `createTracer`, `consoleLogger`, `performanceLogger`, `createTraceRecorder`, `reduxDevtools` and their option types |
17
+ | `nanoeffects/scope` | Unstable | `scopePlugin`, `effect` |
18
+ | `nanoeffects/match` | Unstable | `matchPlugin` |
19
+ | `nanoeffects/signal` | Unstable | `signalPlugin` |
20
+ | `nanoeffects/context` | Unstable | `contextPlugin` |
21
+ | `nanoeffects/oop` | Unstable | `Emitter`, `extendBus`, `On`, `Once`, `WithBus`, `disposeBus` |
22
+ | `nanoeffects/observable` | Unstable | `createObserve`, `Subject` |
23
+ | `nanoeffects/redux` | Unstable | `createEffectsMiddleware` |
24
+ | `nanoeffects/react` | Unstable | `reactPlugin`, `usePolicy` |
25
+ | `nanoeffects/svelte` | Unstable | `sveltePlugin` |
26
+ | `nanoeffects/vue` | Unstable | `vuePlugin` |
27
+ | `nanoeffects/dev` | Unstable | `devPlugin` |
28
+
29
+ The adapters (`react`, `vue`, `svelte`, `redux`) are unstable because they follow the APIs of other libraries and have had little use. They can become stable after the library has been in use for a while.
30
+
31
+ ## Know what is a breaking change
32
+
33
+ For a stable entry, these changes are breaking:
34
+
35
+ - A change in what a call does at run time.
36
+ - A change in a type that stops code that compiled before from compiling.
37
+ - A removed or renamed export.
38
+
39
+ A stricter type that only finds mistakes in code that was already wrong is not breaking. It ships in a minor release with a note in the CHANGELOG.
40
+
41
+ These are not covered by the promise:
42
+
43
+ - Any unstable entry.
44
+ - The text of the messages of `nanoeffects/dev`.
45
+ - The payload details of trace events. The shape of an event (`id`, `parentId`, `rootId`, `type`, `effect`, `phase`) is covered; the contents of `payload` are not.
46
+
47
+ Before version 1.0.0, a breaking change ships in a minor release under `### Breaking` in the CHANGELOG. From 1.0.0 it ships only in a major release.
48
+
49
+ A deprecated export keeps working and is removed only in a major release. `RaceStarter` is deprecated: use `StarterOf` instead.
50
+
51
+ ## Check the supported versions
52
+
53
+ | What | Supported |
54
+ | ---- | --------- |
55
+ | Node.js, the library | 18, 20, 22 and 24 (a smoke test runs the built package on each) |
56
+ | Node.js, the dev tools and the trace in a browser | 22 and newer |
57
+ | TypeScript | The types are written and checked with TypeScript 7. TypeScript 5.9 is used for the Vue tools and for the test of the exports. Older versions are not tested. |
58
+ | React | 18 or newer (`nanoeffects/react`) |
59
+ | Vue | 3.3 or newer (`nanoeffects/vue`) |
60
+ | Svelte | 5 or newer (`nanoeffects/svelte`) |
61
+
62
+ The library has no dependencies. React, Vue and Svelte are optional peer dependencies of their own entries only.
63
+
64
+ ## Rely on these call forms
65
+
66
+ `retry`, `timeout` and `poll` have three call forms, and all three stay:
67
+
68
+ ```js
69
+ import { createBus } from "nanoeffects";
70
+ import { race, retry } from "nanoeffects/effects";
71
+
72
+ const bus = createBus();
73
+
74
+ bus.on("open", async (id, ctx) => {
75
+ // with ctx: you pass the context, the function gets the signal
76
+ const user = await retry(ctx, ({ signal }) => fetchUser(id, { signal }), { times: 3 });
77
+
78
+ // as a starter: a group (race, all, allSettled, timeout) calls it with its child
79
+ const result = await race(ctx, {
80
+ user: retry((_, { signal }) => fetchUser(id, { signal }), { times: 3 }),
81
+ });
82
+ });
83
+
84
+ // as a Task: a function of (payload, ctx), which bus.on and ctx.run call
85
+ bus.on("reload", retry((id, { signal }) => fetchUser(id, { signal }), { times: 3 }));
86
+ ```
87
+
88
+ The forms are told apart by the arguments: a context first is the form with `ctx`; a function called with a payload and a context is the Task; the same function called by a group with one argument, its child, is the starter. The rule is part of the contract and does not depend on how many parameters your function declares.
89
+
90
+ `all` and `allSettled` take options only in the form with `ctx`, `all(ctx, effects, { concurrency })`. The form without `ctx` has one argument, so a second one would look like the `ctx` form. `race` takes no `concurrency`: it ends at the first result, so a starter that waited for a free slot would never run. See [Effects](effects.md#run-several-starters-together).
package/llms-full.txt CHANGED
@@ -721,6 +721,100 @@ The package ships plain-text files for assistants that write code with it:
721
721
 
722
722
  Find them in `node_modules/nanoeffects/`, or load `llms-full.txt` from a CDN such as `https://unpkg.com/nanoeffects/llms-full.txt`. All match the installed version.
723
723
 
724
+ ---
725
+ Source: docs/stability.md
726
+
727
+ # Know what is stable
728
+
729
+ Use this page to decide what you can rely on. It says which imports are stable, what counts as a breaking change, which versions are supported, and which call forms stay.
730
+
731
+ ## See which entries are stable
732
+
733
+ A stable entry follows the rules below. An unstable entry can change in any minor release; every change is written in the [CHANGELOG](../CHANGELOG.md), and nothing promises a deprecation period first.
734
+
735
+ | Import | Status | Note |
736
+ | ------ | ------ | ---- |
737
+ | `nanoeffects` | Stable | `createBus` and the types of a bus |
738
+ | `nanoeffects/effects` | Stable | `retry`, `timeout`, `poll`, `take`, `delay`, `all`, `allSettled`, `race`, `backoff`, `cancellable`, `effectsPlugin` |
739
+ | `nanoeffects/policies` | Stable | `latest`, `leading`, `debounce`, `throttle`, `queue`, `withPolicy` |
740
+ | `nanoeffects/actions` | Stable | `createAction`, `createCommand`, `createJob`, `createModule` and their plugins |
741
+ | `nanoeffects/trace` | Stable: the contract | `Tracer`, `Trace`, `TraceEvent`, `TraceLogger`, `Phase`, `EffectName` and `tracePlugin` |
742
+ | `nanoeffects/trace` | Unstable: the tools | `createTracer`, `consoleLogger`, `performanceLogger`, `createTraceRecorder`, `reduxDevtools` and their option types |
743
+ | `nanoeffects/scope` | Unstable | `scopePlugin`, `effect` |
744
+ | `nanoeffects/match` | Unstable | `matchPlugin` |
745
+ | `nanoeffects/signal` | Unstable | `signalPlugin` |
746
+ | `nanoeffects/context` | Unstable | `contextPlugin` |
747
+ | `nanoeffects/oop` | Unstable | `Emitter`, `extendBus`, `On`, `Once`, `WithBus`, `disposeBus` |
748
+ | `nanoeffects/observable` | Unstable | `createObserve`, `Subject` |
749
+ | `nanoeffects/redux` | Unstable | `createEffectsMiddleware` |
750
+ | `nanoeffects/react` | Unstable | `reactPlugin`, `usePolicy` |
751
+ | `nanoeffects/svelte` | Unstable | `sveltePlugin` |
752
+ | `nanoeffects/vue` | Unstable | `vuePlugin` |
753
+ | `nanoeffects/dev` | Unstable | `devPlugin` |
754
+
755
+ The adapters (`react`, `vue`, `svelte`, `redux`) are unstable because they follow the APIs of other libraries and have had little use. They can become stable after the library has been in use for a while.
756
+
757
+ ## Know what is a breaking change
758
+
759
+ For a stable entry, these changes are breaking:
760
+
761
+ - A change in what a call does at run time.
762
+ - A change in a type that stops code that compiled before from compiling.
763
+ - A removed or renamed export.
764
+
765
+ A stricter type that only finds mistakes in code that was already wrong is not breaking. It ships in a minor release with a note in the CHANGELOG.
766
+
767
+ These are not covered by the promise:
768
+
769
+ - Any unstable entry.
770
+ - The text of the messages of `nanoeffects/dev`.
771
+ - The payload details of trace events. The shape of an event (`id`, `parentId`, `rootId`, `type`, `effect`, `phase`) is covered; the contents of `payload` are not.
772
+
773
+ Before version 1.0.0, a breaking change ships in a minor release under `### Breaking` in the CHANGELOG. From 1.0.0 it ships only in a major release.
774
+
775
+ A deprecated export keeps working and is removed only in a major release. `RaceStarter` is deprecated: use `StarterOf` instead.
776
+
777
+ ## Check the supported versions
778
+
779
+ | What | Supported |
780
+ | ---- | --------- |
781
+ | Node.js, the library | 18, 20, 22 and 24 (a smoke test runs the built package on each) |
782
+ | Node.js, the dev tools and the trace in a browser | 22 and newer |
783
+ | TypeScript | The types are written and checked with TypeScript 7. TypeScript 5.9 is used for the Vue tools and for the test of the exports. Older versions are not tested. |
784
+ | React | 18 or newer (`nanoeffects/react`) |
785
+ | Vue | 3.3 or newer (`nanoeffects/vue`) |
786
+ | Svelte | 5 or newer (`nanoeffects/svelte`) |
787
+
788
+ The library has no dependencies. React, Vue and Svelte are optional peer dependencies of their own entries only.
789
+
790
+ ## Rely on these call forms
791
+
792
+ `retry`, `timeout` and `poll` have three call forms, and all three stay:
793
+
794
+ ```js
795
+ import { createBus } from "nanoeffects";
796
+ import { race, retry } from "nanoeffects/effects";
797
+
798
+ const bus = createBus();
799
+
800
+ bus.on("open", async (id, ctx) => {
801
+ // with ctx: you pass the context, the function gets the signal
802
+ const user = await retry(ctx, ({ signal }) => fetchUser(id, { signal }), { times: 3 });
803
+
804
+ // as a starter: a group (race, all, allSettled, timeout) calls it with its child
805
+ const result = await race(ctx, {
806
+ user: retry((_, { signal }) => fetchUser(id, { signal }), { times: 3 }),
807
+ });
808
+ });
809
+
810
+ // as a Task: a function of (payload, ctx), which bus.on and ctx.run call
811
+ bus.on("reload", retry((id, { signal }) => fetchUser(id, { signal }), { times: 3 }));
812
+ ```
813
+
814
+ The forms are told apart by the arguments: a context first is the form with `ctx`; a function called with a payload and a context is the Task; the same function called by a group with one argument, its child, is the starter. The rule is part of the contract and does not depend on how many parameters your function declares.
815
+
816
+ `all` and `allSettled` take options only in the form with `ctx`, `all(ctx, effects, { concurrency })`. The form without `ctx` has one argument, so a second one would look like the `ctx` form. `race` takes no `concurrency`: it ends at the first result, so a starter that waited for a free slot would never run. See [Effects](effects.md#run-several-starters-together).
817
+
724
818
  ---
725
819
  Source: docs/plugins.md
726
820
 
@@ -1943,7 +2037,7 @@ bus.on("open", async (id, ctx) => {
1943
2037
  });
1944
2038
  ```
1945
2039
 
1946
- The same call with a payload and a context, `(payload, ctx)`, is the Task that `bus.on` and `ctx.run` use, and it does not change.
2040
+ The same call with a payload and a context, `(payload, ctx)`, is the Task that `bus.on` and `ctx.run` use, and it does not change. `retry`, `timeout` and `poll` therefore have three call forms: with `ctx`, as a Task, and as a starter. All three stay; [Stability](stability.md#rely-on-these-call-forms) says how they are told apart.
1947
2041
 
1948
2042
  **`all`, `allSettled` and `race` without `ctx` are starters too**, so a group nests in another group with no wrapper. Load two things, but give up after five seconds:
1949
2043
 
@@ -1992,7 +2086,7 @@ bus.on("sync", async (ids, ctx) => {
1992
2086
  });
1993
2087
  ```
1994
2088
 
1995
- The result has the same order and shape as without the option. If one starter of `all` fails, the running ones are cancelled and the waiting ones are never called. `allSettled` goes on after a failure. When `ctx` aborts, both stop and start nothing more. A value that is not a positive integer (`0`, `-1`, `NaN`, `1.5`) throws a `RangeError` before anything starts; `Infinity` means no limit. `race` has no `concurrency`: it ends at the first result, so a starter that waited for a free slot would never run. A group starter inside another group takes no options; write `(child) => all(child, effects, { concurrency: 5 })` there.
2089
+ The result has the same order and shape as without the option. If one starter of `all` fails, the running ones are cancelled and the waiting ones are never called. `allSettled` goes on after a failure. When `ctx` aborts, both stop and start nothing more. A value that is not a positive integer (`0`, `-1`, `NaN`, `1.5`) throws a `RangeError` before anything starts; `Infinity` means no limit. `race` has no `concurrency`: it ends at the first result, so a starter that waited for a free slot would never run. The form without `ctx` takes no options, because a second argument would look like the form with `ctx`, so a group starter inside another group takes none; write `(child) => all(child, effects, { concurrency: 5 })` there.
1996
2090
 
1997
2091
 
1998
2092
  **A starter gets the context of its own child task.** Pass `child` as the first argument to any effect, as a handler passes `ctx`: when the race ends, `child` is aborted and stops exactly this effect. The outer `ctx` is only for `race` itself. Your own API takes the signal in its options: `({ signal }) => fetchUser(id, { signal })`. The same holds for `all`, `allSettled` and `timeout`.
@@ -4713,6 +4807,8 @@ export const Cache = extendBus(bus, CacheBase);
4713
4807
 
4714
4808
  ## Subscribe a method with `@On` and `@Once`
4715
4809
 
4810
+ `On` here is a decorator. The main entry also exports a type named `On`, the type of `bus.on`; they are different things in different imports, and a file that needs both can rename one: `import { On as OnDecorator } from "nanoeffects/oop"`.
4811
+
4716
4812
  A decorator turns a method into a handler of an event for each instance, with `this` bound. You write no `this.on` in the constructor, and `dispose()` ends the subscription too.
4717
4813
 
4718
4814
  The bus and the events are the same as above, in TypeScript:
package/llms.txt CHANGED
@@ -18,6 +18,7 @@ Rules that generated code often gets wrong:
18
18
  - [Start a project from a template](docs/starter.md): `npm create nanoeffects` makes a Vite project (`vanilla`, `react`, `vue-ts`, `svelte-ts` and others, in JavaScript or TypeScript) with a bus, `debounce` and `latest`
19
19
  - [Why nanoeffects exists](docs/motivation.md): no dependencies and no global state for SDKs and Module Federation, and why not Redux, Effector or RxJS
20
20
  - [Package entries](docs/package.md): which import holds what
21
+ - [Stability](docs/stability.md): which entries are stable or unstable, what a breaking change is, the supported Node, TypeScript and framework versions, the call forms that stay
21
22
  - [Plugins](docs/plugins.md): every plugin of a bus, what `bus.use` returns, and the order to use them in
22
23
 
23
24
  ## Async UI and unreliable networks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nanoeffects",
3
- "version": "0.25.0",
3
+ "version": "0.26.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",