@zubra/runtime 5.7.0 → 5.9.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/README.md +24 -0
- package/dist/production/bridge.js +97 -0
- package/dist/production/context.js +101 -0
- package/dist/production/effect.js +18 -1
- package/dist/production/element.js +94 -16
- package/dist/production/forms.js +126 -0
- package/dist/production/navigation.js +53 -0
- package/dist/production/outlet.js +80 -0
- package/dist/production/react.js +136 -2
- package/dist/production/resource.js +6 -2
- package/dist/production/testing.js +154 -0
- package/dist/production/track.js +78 -0
- package/dist/production/vue.js +67 -1
- package/package.json +48 -3
- package/src/bridge.js +96 -0
- package/src/context.js +100 -0
- package/src/effect.js +18 -1
- package/src/element.js +94 -16
- package/src/forms.js +125 -0
- package/src/navigation.js +52 -0
- package/src/outlet.js +79 -0
- package/src/react.js +136 -2
- package/src/resource.js +6 -2
- package/src/testing.js +153 -0
- package/src/track.js +77 -0
- package/src/vue.js +67 -1
- package/types/bridge.d.ts +44 -0
- package/types/context.d.ts +17 -0
- package/types/effect.d.ts +17 -4
- package/types/forms.d.ts +37 -0
- package/types/index.d.ts +6 -3
- package/types/navigation.d.ts +22 -0
- package/types/outlet.d.ts +23 -0
- package/types/react.d.ts +17 -1
- package/types/testing.d.ts +32 -0
- package/types/track.d.ts +19 -0
- package/types/vue.d.ts +15 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Signal } from './signal.js';
|
|
2
|
+
|
|
3
|
+
/** A signal, or a function that reads signals (tracked like a computed). */
|
|
4
|
+
export type SignalSource<T> = Signal.State<T> | Signal.Computed<T> | (() => T);
|
|
5
|
+
|
|
6
|
+
export interface BridgeOptions {
|
|
7
|
+
/** Also notify when something inside an array or plain object changes (`items.push(x)`). */
|
|
8
|
+
deep?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** An unsubscribe function that is also an RxJS-style `Unsubscribable`. */
|
|
12
|
+
export interface Unsubscribe {
|
|
13
|
+
(): void;
|
|
14
|
+
unsubscribe(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The Svelte store contract and an RxJS-style Subscribable at once: Svelte's `$store`, Solid's
|
|
19
|
+
* `from`, Angular's `toSignal` and RxJS's `from` take it as it is.
|
|
20
|
+
*/
|
|
21
|
+
export interface SignalStore<T> {
|
|
22
|
+
/** The current value. */
|
|
23
|
+
get(): T;
|
|
24
|
+
/** Call `run` now and on each change. */
|
|
25
|
+
subscribe(run: ((value: T) => void) | { next?(value: T): void }): Unsubscribe;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A store over a writable signal: also a Svelte writable store. */
|
|
29
|
+
export interface WritableSignalStore<T> extends SignalStore<T> {
|
|
30
|
+
set(value: T): void;
|
|
31
|
+
update(fn: (value: T) => T): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Call `callback(value)` now and on each change (batched per microtask) until unsubscribed. */
|
|
35
|
+
export declare function subscribe<T>(source: SignalSource<T>, callback: (value: T) => void, options?: BridgeOptions): () => void;
|
|
36
|
+
|
|
37
|
+
/** The source as a store. Writable (`set`, `update`) when the source is a `Signal.State` or has `set`. */
|
|
38
|
+
export declare function toStore<T>(source: Signal.State<T>, options?: BridgeOptions): WritableSignalStore<T>;
|
|
39
|
+
export declare function toStore<T>(source: SignalSource<T>, options?: BridgeOptions): SignalStore<T>;
|
|
40
|
+
|
|
41
|
+
/** The source's current value. */
|
|
42
|
+
export declare function read<T>(source: SignalSource<T>): T;
|
|
43
|
+
/** Whether the source can be written. */
|
|
44
|
+
export declare function writable(source: unknown): boolean;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<zb-context>`: answers the Context Protocol for the elements inside it. Set `context` (a key
|
|
3
|
+
* from `createContext()`, or any protocol key) and `value` (a value or a signal).
|
|
4
|
+
*/
|
|
5
|
+
export declare class ZubraContextElement extends HTMLElement {
|
|
6
|
+
context: unknown;
|
|
7
|
+
value: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Define `<zb-context>` (done on import; a no-op without `customElements`). */
|
|
11
|
+
export declare function defineContextElement(): void;
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
'zb-context': ZubraContextElement;
|
|
16
|
+
}
|
|
17
|
+
}
|
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
|
|
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
|
-
/**
|
|
12
|
-
|
|
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/forms.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** The model prop of a Zubra control (`value` by default), with its `<prop>change` event. */
|
|
2
|
+
export interface FieldOptions {
|
|
3
|
+
prop?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** React Hook Form's `register(name)` result (the parts used). */
|
|
7
|
+
export interface Registration {
|
|
8
|
+
name: string;
|
|
9
|
+
ref: unknown;
|
|
10
|
+
onChange(event: { type: string; target: { name: string; value: unknown } }): unknown;
|
|
11
|
+
onBlur(event: { type: string; target: { name: string; value: unknown } }): unknown;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** React Hook Form's `register(name)` as props for a wrapped Zubra control (model: `value`). */
|
|
16
|
+
export declare function registerProps(registration: Registration): Record<string, unknown>;
|
|
17
|
+
|
|
18
|
+
/** A React Hook Form `<Controller>` field, or a TanStack Form field API. */
|
|
19
|
+
export type ReactField =
|
|
20
|
+
| { name: string; value: unknown; onChange(value: unknown): void; onBlur(): void; ref?: unknown }
|
|
21
|
+
| { name: string; state: { value: unknown }; handleChange(value: any): void; handleBlur(): void };
|
|
22
|
+
|
|
23
|
+
/** A field's props for a wrapped Zubra control in React. */
|
|
24
|
+
export declare function fieldProps(field: ReactField, options?: FieldOptions): Record<string, unknown>;
|
|
25
|
+
|
|
26
|
+
/** A field's props for a wrapped Zubra control in Vue, from VeeValidate's `useField()` or a `<Field>` slot's `field`. */
|
|
27
|
+
export declare function vueFieldProps(field: object, options?: FieldOptions): Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
/** Felte's `createField(name)` result (the parts used). */
|
|
30
|
+
export interface FelteField {
|
|
31
|
+
field(node: HTMLElement): { destroy?(): void } | void;
|
|
32
|
+
onInput(value: unknown): void;
|
|
33
|
+
onBlur(): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Connect a Zubra control to a Felte field: a Svelte action, or call it with the element. */
|
|
37
|
+
export declare function felteControl(node: HTMLElement, field: FelteField, options?: FieldOptions): { destroy(): 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?:
|
|
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,22 @@
|
|
|
1
|
+
/** What a router is told about a routed link. */
|
|
2
|
+
export interface RoutedLink {
|
|
3
|
+
event: MouseEvent;
|
|
4
|
+
anchor: HTMLAnchorElement;
|
|
5
|
+
url: URL;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface RouteLinksOptions {
|
|
9
|
+
/** Which ancestors make a link routed (default: a Zubra component's host). */
|
|
10
|
+
within?(node: Element): boolean;
|
|
11
|
+
/** Where to listen (default: the document). */
|
|
12
|
+
root?: Document | Element;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Whether `node` is a Zubra component's host. */
|
|
16
|
+
export declare function isComponent(node: unknown): boolean;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Route clicks on links inside Zubra components through `navigate(to)`, where `to` is the path,
|
|
20
|
+
* query and hash. Returns a function that stops it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function routeLinks(navigate: (to: string, link: RoutedLink) => unknown, options?: RouteLinksOptions): () => void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<zb-outlet name="…" item={…}>` (experimental): a place in a component where the app's framework
|
|
3
|
+
* renders its own content for `item`, through a portal. The React adapter fills it from a
|
|
4
|
+
* `render<Name>` prop.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ZubraOutletElement extends HTMLElement {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
item: unknown;
|
|
9
|
+
/** Who renders into this outlet, once claimed. */
|
|
10
|
+
owner: { change(outlet: ZubraOutletElement): void; disconnect(outlet: ZubraOutletElement): void } | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** The unclaimed outlets inside `host` (light DOM and shadow root). */
|
|
14
|
+
export declare function outletsOf(host: Element): ZubraOutletElement[];
|
|
15
|
+
|
|
16
|
+
/** Define `<zb-outlet>` (done on import; a no-op without `customElements`). */
|
|
17
|
+
export declare function defineOutletElement(): void;
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface HTMLElementTagNameMap {
|
|
21
|
+
'zb-outlet': ZubraOutletElement;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/types/react.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { ZubraElementClass } from './index.js';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A zubra element as a React component (React 18 and 19): props as properties, `on<Event>`
|
|
6
|
-
* handlers for its events, other props and children passed to the element
|
|
6
|
+
* handlers for its events, other props and children passed to the element, and `render<Name>`
|
|
7
|
+
* props that fill the element's `<zb-outlet name>`s (experimental). The build plugin's
|
|
7
8
|
* generated typings give exact prop types; this one is general.
|
|
8
9
|
*/
|
|
9
10
|
export declare function wrap<E extends HTMLElement>(
|
|
@@ -11,3 +12,18 @@ export declare function wrap<E extends HTMLElement>(
|
|
|
11
12
|
): React.ForwardRefExoticComponent<
|
|
12
13
|
React.HTMLAttributes<E> & Record<string, unknown> & { children?: React.ReactNode } & React.RefAttributes<E>
|
|
13
14
|
> & { readonly element: ZubraElementClass<E> };
|
|
15
|
+
|
|
16
|
+
/** Route links inside Zubra components through the app's router while mounted. */
|
|
17
|
+
export declare function useRouteLinks(
|
|
18
|
+
navigate: (to: string, link: import('./navigation.js').RoutedLink) => unknown,
|
|
19
|
+
options?: import('./navigation.js').RouteLinksOptions,
|
|
20
|
+
): void;
|
|
21
|
+
|
|
22
|
+
/** Provide a Context Protocol value (or a signal) to every element inside; renders `<zb-context>`. */
|
|
23
|
+
export declare function ZubraContext(props: { context: unknown; value: unknown; children?: React.ReactNode }): React.ReactElement;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read a Zubra signal (or a function of signals) in a React component, re-rendering when it
|
|
27
|
+
* changes. `deep: true` re-renders on mutations inside an array or object too.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useSignal<T>(source: import('./bridge.js').SignalSource<T>, options?: import('./bridge.js').BridgeOptions): T;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ZubraElementClass } from './index.js';
|
|
2
|
+
|
|
3
|
+
export type Framework = 'html' | 'react' | 'vue';
|
|
4
|
+
|
|
5
|
+
/** The frameworks that can be mounted here: `html` always, `react` and `vue` when installed. */
|
|
6
|
+
export declare function frameworks(): Promise<Framework[]>;
|
|
7
|
+
|
|
8
|
+
export interface MountOptions {
|
|
9
|
+
framework?: Framework;
|
|
10
|
+
props?: Record<string, unknown>;
|
|
11
|
+
/** Text for each slot: `{ default: 'Body', title: 'Heading' }`. */
|
|
12
|
+
slots?: Record<string, string>;
|
|
13
|
+
/** Where to mount (default: a new <div> in <body>, removed on unmount). */
|
|
14
|
+
container?: HTMLElement;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Mounted<E extends HTMLElement = HTMLElement> {
|
|
18
|
+
readonly framework: Framework;
|
|
19
|
+
readonly host: HTMLElement;
|
|
20
|
+
/** The component's host element. */
|
|
21
|
+
readonly element: E;
|
|
22
|
+
/** The `detail` of each `name` event so far, as the framework's handler got it; with no name, every event as `[name, detail]`. */
|
|
23
|
+
events(name: string): unknown[];
|
|
24
|
+
events(): [string, unknown][];
|
|
25
|
+
/** Change props through the framework (merged with the current ones), and settle. */
|
|
26
|
+
set(props: Record<string, unknown>): Promise<void>;
|
|
27
|
+
/** Remove it the framework's way. */
|
|
28
|
+
unmount(): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Mount a compiled component through plain HTML, the React adapter or the Vue adapter. */
|
|
32
|
+
export declare function mount<E extends HTMLElement>(Element: ZubraElementClass<E>, options?: MountOptions): Promise<Mounted<E>>;
|
package/types/track.d.ts
ADDED
|
@@ -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>;
|
package/types/vue.d.ts
CHANGED
|
@@ -6,3 +6,18 @@ export declare function wrap(Element: ZubraElementClass): DefineComponent<Record
|
|
|
6
6
|
|
|
7
7
|
/** The component's two-way bindings: `<prop>` + `<prop>change` pairs. */
|
|
8
8
|
export declare function models(meta: Pick<ElementMeta, 'props' | 'events'>): { prop: string; event: string }[];
|
|
9
|
+
|
|
10
|
+
/** Route links inside Zubra components through Vue Router (or any push) until the scope ends. */
|
|
11
|
+
export declare function useRouteLinks(
|
|
12
|
+
navigate: (to: string, link: import('./navigation.js').RoutedLink) => unknown,
|
|
13
|
+
options?: import('./navigation.js').RouteLinksOptions,
|
|
14
|
+
): () => void;
|
|
15
|
+
|
|
16
|
+
/** Provide a Context Protocol value (or a signal) to every element inside; renders `<zb-context>`. */
|
|
17
|
+
export declare const ZubraContext: DefineComponent<{ context: unknown; value: unknown }>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A Zubra signal (or a function of signals) as a Vue ref; assigning `.value` writes a writable
|
|
21
|
+
* signal. Stops with the current effect scope, or with `stop()`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useSignal<T>(source: import('./bridge.js').SignalSource<T>, options?: import('./bridge.js').BridgeOptions): import('vue').Ref<T> & { stop(): void };
|