@zubra/runtime 5.8.0 → 5.9.1

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/types/effect.d.ts CHANGED
@@ -1,15 +1,28 @@
1
+ /**
2
+ * `[Symbol.dispose]()`, so `using` and `DisposableStack` accept it, when the project's `lib`
3
+ * has Explicit Resource Management (`esnext.disposable`, or `esnext`); nothing otherwise, so
4
+ * older `lib` settings still type-check.
5
+ */
6
+ export type Disposal = SymbolConstructor extends { readonly dispose: infer K extends symbol }
7
+ ? { [P in K]: () => void }
8
+ : {};
9
+
1
10
  /** Something to run when an effect re-runs or is disposed. */
2
11
  export type Cleanup = () => void;
3
12
 
4
13
  /**
5
14
  * Run `fn` now and again whenever a signal it read changes (batched: several writes in one task
6
15
  * cause one re-run, in a microtask). `fn` may return a cleanup. Returns a function that disposes
7
- * the effect. Effects created inside another effect or scope belong to it.
16
+ * the effect, and is also disposable (`using stop = effect(…)`). Effects created inside another
17
+ * effect or scope belong to it.
8
18
  */
9
- export declare function effect(fn: () => void | Cleanup): () => void;
19
+ export declare function effect(fn: () => void | Cleanup): (() => void) & Disposal;
10
20
 
11
- /** An ownership root: disposing, pausing or resuming it applies to every effect inside. */
12
- export interface Scope {
21
+ /**
22
+ * An ownership root: disposing, pausing or resuming it applies to every effect inside. It's
23
+ * disposable too (`using s = scope(…)`); pausing isn't disposal, and a paused scope resumes.
24
+ */
25
+ export interface Scope extends Disposal {
13
26
  run<T>(fn: () => T): T;
14
27
  dispose(): void;
15
28
  pause(): void;
package/types/index.d.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  import type { Signal } from './signal.js';
3
3
 
4
4
  export { Signal, isNative } from './signal.js';
5
- export { effect, scope, flush, tick, type Cleanup, type Scope } from './effect.js';
5
+ export { effect, scope, flush, tick, type Cleanup, type Disposal, type Scope } from './effect.js';
6
+ import type { Disposal } from './effect.js';
6
7
 
7
8
  /** A value, or a signal holding one: what templates and most hooks accept. */
8
9
  export type MaybeSignal<T> = T | Signal.State<T> | Signal.Computed<T>;
@@ -23,7 +24,7 @@ export declare function computed<T>(fn: () => T, options?: Signal.Options<T>): S
23
24
  export declare function watch<T>(source: (() => T) | MaybeSignal<T>, callback: (next: T, previous: T) => void): void;
24
25
 
25
26
  /** Async data as signals; see `resource()`. Awaitable: resolves to the current run's value. */
26
- export interface Resource<T> extends PromiseLike<T> {
27
+ export interface Resource<T> extends PromiseLike<T>, Disposal {
27
28
  /** The latest run's value (or `initial`); kept while the next run is pending. */
28
29
  readonly value: T | undefined;
29
30
  readonly error: unknown;
@@ -31,6 +32,8 @@ export interface Resource<T> extends PromiseLike<T> {
31
32
  readonly loading: boolean;
32
33
  /** Run the fetcher again. */
33
34
  refresh(): void;
35
+ /** Stop: abort the run in flight and never run again. `using` does this through `Symbol.dispose`. */
36
+ dispose(): void;
34
37
  }
35
38
  /**
36
39
  * Run `fetcher` now and whenever a signal it read before its first `await` changes; a new run
@@ -55,7 +58,7 @@ export declare function host<E extends HTMLElement = HTMLElement>(): E;
55
58
  * declares every event it sees. Returns false if a listener called `preventDefault()` on a
56
59
  * cancelable (`before…`) event.
57
60
  */
58
- export declare function emit(name: string, detail?: unknown): boolean;
61
+ export declare function emit<Detail = unknown>(name: string, detail?: Detail): boolean;
59
62
 
60
63
  /**
61
64
  * The signal itself, where a script passes a signal `const` along: `watch(signal(count), …)`.
@@ -0,0 +1,19 @@
1
+ // @zubra/runtime/track: reads after an `await` in a resource's fetcher.
2
+
3
+ /** What a fetcher wrapped by `tracked()` is given for each run. */
4
+ export interface TrackedRun {
5
+ /** Aborted when a newer run starts or the resource is disposed. */
6
+ signal: AbortSignal;
7
+ /**
8
+ * Read signals and follow them, after an `await` too: when what `read` returns changes, the
9
+ * resource runs the fetcher again. A replaced run still reads, without subscribing.
10
+ */
11
+ track<V>(read: () => V): V;
12
+ }
13
+
14
+ /**
15
+ * Wraps a resource's fetcher so it is also given `track()`:
16
+ * `resource(tracked(async ({ signal, track }) => …))`. The compiler writes it for an async
17
+ * fetcher in a `.zb` script.
18
+ */
19
+ export declare function tracked<T>(fetcher: (run: TrackedRun) => T | PromiseLike<T>): (context: { signal: AbortSignal }) => T | PromiseLike<T>;