API reference
Exports of @sigx/runtime-core v0.15.3.
All examples assume a named import from the package root:
import { component, defineApp } from '@sigx/runtime-core';
The package also exposes ./jsx-runtime and ./jsx-dev-runtime subpaths (both aliasing the main build) for TSX transpilation via jsxImportSource, a separate ./internals entry for renderer authors, and an ./inspect entry with the inspection-only topic registry for tooling (see Inspection).
Component system
component
Define a component from a setup function that returns a render (view) function. setup runs once; the returned function is the reactive render. Setup may be sync or async — async setup is awaited during SSR and errors on the client (SIGX102).
function component<
TCombined extends Record<string, any> = {},
TRef = ExtractExposed<TCombined>,
TSlots = ExtractSlots<TCombined>
>(
setup: (ctx: ComponentSetupContext<PlatformElement, StripInternalMarkers<TCombined>, TCombined, TRef, TSlots>) => ViewFn | Promise<ViewFn>,
options?: ComponentOptions
): ComponentFactory<TCombined, TRef, TSlots>;
setup— the setup function receiving aComponentSetupContext; returns aViewFn(or a promise of one).options— optionalComponentOptions(currently{ name? }).- Returns a
ComponentFactoryusable as a JSX element.
getCurrentInstance
Return the setup context of the currently executing component, or null outside setup. Used to build composables.
function getCurrentInstance(): ComponentSetupContext<any, any, any> | null;
- Returns the active
ComponentSetupContext, ornullif called outside a component's setup.
getComponentMeta
Return the registered metadata (name and setup) for a component factory. Used by DevTools.
function getComponentMeta(factory: Function): { name?: string; setup: SetupFn<any, any, any, any> } | undefined;
factory— a component factory.- Returns
{ name?, setup }, orundefinedif the factory is not registered.
isComponent
Type guard that returns true for SignalX components (functions carrying a __setup property), and false for plain function components or strings.
function isComponent(type: unknown): type is ComponentLike;
type— the value to test.- Returns
trueif it is a SignalX component factory.
compound
Create a compound component by attaching sub-components as static properties. Enables the Parent.Child pattern (e.g. Menu.Item, Card.Body) while preserving full TypeScript inference for both the parent and its children.
function compound<
TMain extends AnyComponentFactory,
TSub extends Record<string, AnyComponentFactory>
>(main: TMain, sub: TSub): TMain & TSub;
main— the main/parent component factory.sub— an object whose values are the sub-components to attach.- Returns the main component with the sub-components attached as static properties.
Lifecycle hooks
Each hook must be called synchronously during setup. Called outside a component they warn and no-op (no throw).
onMounted
Register a callback to run after the component mounts. Receives a MountContext with the root element.
function onMounted(fn: (ctx: MountContext) => void): void;
fn— callback invoked post-mount with{ el }.
onUnmounted
Register a cleanup callback to run when the component unmounts. The place to clear timers and unsubscribe.
function onUnmounted(fn: (ctx: MountContext) => void): void;
fn— cleanup callback invoked on unmount with{ el }.
onCreated
Register a callback that runs immediately after setup completes, before the first render.
function onCreated(fn: () => void): void;
fn— callback invoked once after setup, before first render.
onUpdated
Register a callback that runs after every reactive re-render of the component.
function onUpdated(fn: () => void): void;
fn— callback invoked after each re-render.
Application
defineApp
Create a renderer-agnostic application instance with a chainable use / hook / directive / defineProvide / mount / unmount API.
function defineApp<TContainer = any>(rootComponent: JSXElement): App<TContainer>;
rootComponent— the root JSX element to render.- Returns an
App<TContainer>instance.
JSX runtime
jsx
The core JSX factory called by TSX transpilation. Builds VNodes, resolves components, and processes model bindings.
function jsx(type: string | Function | typeof Fragment, props: JSXProps | null, key?: string): JSXElement;
type— an intrinsic tag, component factory, orFragment.props— element props, ornull.key— optional reconciliation key.- Returns a
JSXElement. The fast path skips props cloning unless amodel/model:*prop is present.
jsxs
JSX factory variant for elements with multiple children; delegates to jsx.
function jsxs(type: string | typeof Fragment | Function, props?: Record<string, unknown>, key?: string): JSXElement;
type— an intrinsic tag, component factory, orFragment.props— element props.key— optional reconciliation key.- Returns a
JSXElement.
jsxDEV
Dev-mode JSX factory; an alias of jsx.
const jsxDEV: typeof jsx;
Fragment
VNode type marker for fragments — grouping children without a wrapper element.
const Fragment: unique symbol; // Symbol.for('sigx.Fragment')
Text
VNode type marker for text nodes.
const Text: unique symbol; // Symbol.for('sigx.Text')
Comment
VNode type marker for comment / placeholder nodes. Falsy children become Comment placeholders so positional diffing does not shift sibling indices when conditionals toggle.
const Comment: unique symbol; // Symbol.for('sigx.Comment')
Lazy loading & async
lazy
Wrap a dynamic-import loader into a lazy component loaded on first render. Adds preload() and isLoaded(). While the chunk loads the wrapper renders null; wrap it in <Defer> for a fallback. A load rejection throws from render (routes to the nearest errorScope, then app.onError).
function lazy<T extends AnyComponentFactory>(loader: ComponentLoader<T>): LazyComponentFactory<T>;
loader— a function returning a dynamic import of the component.- Returns a
LazyComponentFactory<T>(the component pluspreload/isLoaded/__lazy).
Defer
Boundary component that shows a fallback while lazy chunks beneath it load (a pending useData read renders through its own component's match(), not a wrapper). On SSR the fallback streams with the shell and one replacement arrives when everything pending below it — chunks and keyed data — resolves.
const Defer: ComponentFactory<DeferProps, void, { default?: () => JSXElement | JSXElement[] | null }>;
- Accepts a
fallbackprop (a node or a function returning a node) and a default slot.
isLazyComponent
Type guard for lazy-loaded components (checks the __lazy marker).
function isLazyComponent(component: any): component is LazyComponentFactory<any>;
component— the value to test.- Returns
trueif it is a lazy component factory.
useData
The keyed async read. Auto-runs its fetcher and returns a reactive AsyncState<T>; the render re-runs as the state changes. Every read has a key — the reactive trigger, the cache / SSR identity, and the fetcher's input. A static string key is SSR-transferable: it runs during SSR, serializes its value under the key into window.__SIGX_ASYNC__, restores on hydration without refetching, and dedupes per key. A getter key may return a string or a tuple (serialized to canonical JSON); a falsy result holds the read in idle.
// Static key: runs on the server, serialized under `key`, restored on hydration.
function useData<T>(
key: string,
fetcher: (arg: string, ctx: AsyncFetcherContext) => Promise<T>,
opts?: AsyncOptions
): AsyncState<T>;
// Reactive key: string or tuple; a falsy result skips the fetch (state 'idle').
function useData<T, const K extends KeyValue>(
key: () => K | Falsy,
fetcher: (arg: K, ctx: AsyncFetcherContext) => Promise<T>,
opts?: AsyncOptions
): AsyncState<T>;
key— a static string, or a getter returning a string / tuple / falsy. A static tuple is rejected (parameters that change belong in a getter).fetcher— an async function producing the value; receives the resolved key and a context whosesignalis anAbortSignal. Runs untracked.opts.server— run the fetcher during SSR. Defaulttrue;server: falserenders the pending arm on the server and fetches after hydration (still keyed).- Returns an
AsyncState<T>— see AsyncState.
useAction
The async write. The manual counterpart to useData: never auto-runs, triggered by .run(input). run never rejects — it resolves a settled RunResult<T>. In-flight runs are never aborted; a newer run() or reset() supersedes the observation (the older run resolves { ok: false, error: SupersededError } and never writes state).
function useAction<T, In = void>(
fn: (input: In, ctx: AsyncFetcherContext) => Promise<T>,
opts?: ActionOptions
): AsyncAction<T, In>;
fn— the write; receives theruninput and a context whosesignalis anAbortSignal.- Returns an
AsyncAction<T, In>withstate(idle | pending | ready | errored),value,error,loading(state === 'pending'),match(),run(input): Promise<RunResult<T>>, andreset().
all
Combine several AsyncStates into one all-or-nothing state (one match for a whole view). A pure derived view — no fetching. Object form gives named value/errors records; the rest-tuple form gives positional ones.
function all<S extends Record<string, AsyncState<unknown>>>(sources: S): AllState<ValuesOf<S>, ErrorsOf<S>>;
function all<S extends readonly AsyncState<unknown>[]>(...sources: S): AllState<ValuesOf<S>, ErrorsOf<S>>;
Derived state: any member idle ⇒ idle; else any errored ⇒ errored; else any pending ⇒ pending; else any refreshing ⇒ refreshing; else ready. AllState<T, E> extends AsyncState<T> with errors: E (collect-all; .error stays first-error-wins).
useStream
Composable for progressive text (LLM-token-style). Accumulates a streamed AsyncIterable<string> into a reactive string; SSR-aware via the key (streams on the server, restores on hydration without re-running the source, live on client navigation).
function useStream(
key: string,
source: () => AsyncIterable<string>
): { readonly value: string };
key— the serialization/restore key.source— a function returning an async iterable of text chunks.- Returns a
{ value }string signal that grows as chunks arrive.
Models & two-way binding
registerModelProcessor
Register an extension model processor for intrinsic-element model handling. Extension processors run before the platform processor (DOM/Lynx/Terminal), in registration order, and the first one returning true wins — so packs and apps can add model behavior for custom elements or widget libraries without replacing the platform's. Registering the same function twice is a no-op.
function registerModelProcessor(fn: ModelProcessor): void;
fn— aModelProcessor:(type, props, modelBinding, originalProps) => boolean. Returntrueto claim the binding (skip the fallback),falseto defer to later processors.
setPlatformModelProcessor is the separate, platform-identity seam used by renderer packages and is unchanged — registerModelProcessor is the additive extension tier.
createModel
Create a two-way binding Model<T> from a [sourceObject, key] tuple and an update handler.
function createModel<T>(tuple: [object, string], updateHandler: (value: T) => void): Model<T>;
tuple— a[sourceObject, key]pair identifying the bound value.updateHandler— called with the new value on write.- Returns a
Model<T>.
createModelFromBinding
Create a Model<T> from a full binding tuple [obj, key, handler] for forwarding scenarios.
function createModelFromBinding<T>(binding: ModelBindingTuple<T>): Model<T>;
binding— a[obj, key, handler]tuple.- Returns a
Model<T>.
isModel
Type guard that detects Model<T> objects via the sigx.model symbol. Used by the JSX runtime.
function isModel(value: unknown): value is Model<unknown>;
value— the value to test.- Returns
trueif it is aModel.
Error handling
errorScope
Setup-time error boundary for the calling component's own subtree — a function, not a wrapper component. Catches setup / render / reactive-re-render throws beneath it and data errors bubbled by a match() with no error arm. Does not catch fetcher rejections (those are values on .error) or DOM event-handler throws (those go to app.onError). retry is a real remount.
function errorScope(options: ErrorScopeOptions): void;
interface ErrorScopeOptions {
fallback?: (error: Error, retry: () => void) => JSXElement;
onError?: (error: Error, instance: ComponentInstance | null, info: string) => void;
}
fallback— rendered in place of the subtree while errored; omitted ⇒ renders nothing (the scope still stops propagation).onError— observer called before the fallback renders; its own throws are swallowed.
SupersededError
Error a superseded useAction run resolves with ({ ok: false, error: SupersededError }); never written to .error.
class SupersededError extends Error {
readonly name = 'SupersededError';
}
SigxError
Base error class for all runtime errors; carries a stable code (SIGX###) and an optional suggestion.
class SigxError extends Error {
readonly code: string;
readonly suggestion?: string;
declare readonly cause?: Error;
constructor(message: string, options: { code: string; suggestion?: string; cause?: Error });
}
code— the stable error code.suggestion— optional remediation hint.cause— optional underlying error.
SigxErrorCode
Frozen map of error code constants grouped by category (app / render / DI).
const SigxErrorCode: {
readonly NO_MOUNT_FUNCTION: 'SIGX001';
readonly RENDER_TARGET_NOT_FOUND: 'SIGX100';
readonly MOUNT_TARGET_NOT_FOUND: 'SIGX101';
readonly ASYNC_SETUP_CLIENT: 'SIGX102';
readonly PROVIDE_OUTSIDE_SETUP: 'SIGX200';
readonly PROVIDE_INVALID_INJECTABLE: 'SIGX201';
};
noMountFunctionError
Construct the SIGX001 error — no mount function provided and no platform default.
function noMountFunctionError(): SigxError;
- Returns the
SIGX001SigxError.
renderTargetNotFoundError
Construct the SIGX100 error — render target selector not found.
function renderTargetNotFoundError(selector: string): SigxError;
selector— the selector that could not be resolved.- Returns the
SIGX100SigxError.
mountTargetNotFoundError
Construct the SIGX101 error — mount target selector not found.
function mountTargetNotFoundError(selector: string): SigxError;
selector— the selector that could not be resolved.- Returns the
SIGX101SigxError.
asyncSetupClientError
Construct the SIGX102 error — async setup attempted on the client (only supported during SSR).
function asyncSetupClientError(componentName: string): SigxError;
componentName— the offending component's name.- Returns the
SIGX102SigxError.
provideOutsideSetupError
Construct the SIGX200 error — defineProvide called outside component setup.
function provideOutsideSetupError(): SigxError;
- Returns the
SIGX200SigxError.
provideInvalidInjectableError
Construct the SIGX201 error — defineProvide called with a non-injectable.
function provideInvalidInjectableError(): SigxError;
- Returns the
SIGX201SigxError.
Dependency injection
defineInjectable
Define an injectable service or value. The returned callable token resolves the nearest provided instance up the component tree, or lazily creates a global singleton if none was provided.
function defineInjectable<T>(factory: () => T): InjectableFunction<T>;
factory— produces the value when an instance must be created.- Returns an
InjectableFunction<T>(callable token).
defineProvide
Provide a new injectable instance at the current component level, visible to descendants. Must run during setup; otherwise throws SIGX200 / SIGX201.
function defineProvide<T>(useFn: InjectableFunction<T>, factory?: () => T): T;
useFn— the injectable token to provide.factory— optional override factory for the provided instance.- Returns the provided instance.
useAppContext
Return the current AppContext from the component tree (provided at root during mount / hydrate / SSR), or null.
function useAppContext(): AppContext | null;
- Returns the active
AppContext, ornull.
defineFactory
Create a factory with managed subscriptions, disposal, and a real instance lifetime. Parameterless factories become injectables; parameterized ones return FactoryFunction creators (still usable with defineProvide).
function defineFactory<R>(
setup: (ctx: SetupFactoryContext, ...args: unknown[]) => R,
lifetime: Lifetime,
typeIdentifier?: guid
): InjectableFunction<R & { dispose: () => void }>;
function defineFactory<R, T1>(setup: (ctx: SetupFactoryContext, p1: T1) => R, lifetime: Lifetime, id?: guid): FactoryFunction<[T1], R & { dispose: () => void }>;
// ...overloads up to 5 parameters
setup— receives aSetupFactoryContextand any creator arguments. Must return an object or function (primitives throw).lifetime— aLifetimestring literal; honored since core0.5.0:'singleton'— one instance perAppContext, created on first resolution and disposed onapp.unmount(). Outside any app context, one instance per JS realm.'scoped'— the nearest instance provided viadefineProvidein the component tree; falls back to the app-context instance when no provider exists.'transient'— a new instance per call, auto-disposed with the calling component (or manually viadispose()).
typeIdentifier— optionalguididentity.- Returns an
InjectableFunction(parameterless) or aFactoryFunctioncreator (parameterized).
dispose is attached as a non-enumerable property (a setup-returned dispose is delegated to) and is idempotent. Parameterized non-transient factories honor arguments at first creation only — later calls resolve the existing shared instance.
SubscriptionHandler
Collects unsubscribe callbacks and disposes them together. Used by defineFactory contexts.
class SubscriptionHandler {
add(unsub: () => void): void;
unsubscribe(): void;
}
add(unsub)— register an unsubscribe callback.unsubscribe()— run and clear all registered callbacks.
Directives
defineDirective
Identity function that marks a directive definition (with the sigx.directive symbol) for the use:name prop syntax.
function defineDirective<T = any, El = any>(definition: DirectiveDefinition<T, El>): DirectiveDefinition<T, El>;
definition— the directive's lifecycle hooks (created/mounted/updated/unmounted).- Returns the same definition, branded.
isDirective
Type guard for directive definitions created by defineDirective.
function isDirective(value: any): value is DirectiveDefinition;
value— the value to test.- Returns
trueif it is a directive definition.
Messaging
createTopic
Create an in-memory pub/sub Topic<T>. subscribe() auto-unsubscribes via onUnmounted when called inside a component.
function createTopic<T>(options?: CreateTopicOptions): Topic<T>;
interface CreateTopicOptions {
/** Tooling metadata; topics WITH a namespace register in the inspection registry. */
namespace?: string;
/** Tooling metadata. */
name?: string;
/** Called when subscriberCount transitions 0 → 1 (refCount pattern). */
onActivate?(): void;
/** Called when subscriberCount transitions back to 0 (last unsubscribe or destroy). */
onDeactivate?(): void;
}
options— metadata plus refCount lifecycle hooks.- Returns a
Topic<T>.
Behavior contract (Topic v2):
publishisolates subscriber errors — a throwing handler is logged viaconsole.errorand neither skips later subscribers nor propagates into the publisher. Publishing to a destroyed topic is a no-op.subscribethrows on a destroyed topic — a handler attached afterdestroy()could never be cleaned up, so it is rejected loudly instead of leaking.onActivate/onDeactivatefire on the 0 → 1 and → 0 subscriber transitions, letting producers pay for work only while observed (e.g.@sigx/storeruns its per-key state watchers only while the key's event has subscribers). Hook errors are isolated.- Topics created with a
namespaceregister in the inspection registry (see Inspection);destroy()unregisters.
createTopicGroup
Create a typed group of topics keyed by an event map — mitt-level DX on the Topic primitive. Topics are created lazily per key on first access and are namespaced/registered like any other topic.
function createTopicGroup<EventMap extends Record<string, any>>(options?: { namespace?: string }): {
topics: { [K in keyof EventMap]: Topic<EventMap[K]> };
destroy(): void;
};
options— optional{ namespace? }applied to every created topic.- Returns
{ topics, destroy };destroy()destroys all created topics. Accessing a key on a destroyed group throws.
const group = createTopicGroup<{ loggedIn: User; loggedOut: void }>({ namespace: 'auth#1.events' });
group.topics.loggedIn.publish(user); // payload type-checked
group.topics.loggedIn.subscribe(u => {}); // u: User
group.destroy();
toSubscriber
Wrap a Topic as a subscribe-only view, hiding publish and destroy.
function toSubscriber<T>(topic: Topic<T>): { subscribe: (handler: (data: T) => void) => Subscription };
topic— the topic to wrap.- Returns an object exposing only
subscribe.
Inspection (@sigx/runtime-core/inspect)
A separate ./inspect package entry exposing the inspection-only topic registry for tooling (devtools, diagnostics). The DX contract: typed application code holds Topic<T> references; strings are tooling metadata. The registry is deliberately Topic<unknown>-typed and realm-global — do not use it as an app-level lookup mechanism; share typed topic references instead. Only topics created with a namespace register; destroy() unregisters.
import { getTopic, listTopics, subscribeTopics, onTopicCreated } from '@sigx/runtime-core/inspect';
Patterns use * wildcards matched over ${namespace}.${name} (e.g. 'todos#1.*', '*.actions.*').
getTopic
function getTopic(namespace: string, name: string): Topic<unknown> | undefined;
Look up a live registered topic by exact namespace and name.
listTopics
function listTopics(pattern?: string): Topic<unknown>[];
List live registered topics, optionally filtered by a *-wildcard pattern.
subscribeTopics
function subscribeTopics(
pattern: string,
handler: (data: unknown, meta: { namespace: string; name: string }) => void
): Subscription;
Subscribe to every registered topic matching a pattern — both topics that already exist and topics created later. One Subscription tears everything down. Auto-unsubscribes on component unmount when called inside a setup.
onTopicCreated
function onTopicCreated(handler: (topic: Topic<unknown>) => void): Subscription;
Register a handler for every topic that registers from now on. Handler errors are isolated. Auto-unsubscribes on component unmount when called inside a setup.
Utilities
signal
Re-export of signal() from @sigx/reactivity for convenience. The ComponentSetupContext also exposes it as ctx.signal.
export { signal } from '@sigx/reactivity';
Utils
Static utility helpers; currently exposes isPromise.
class Utils {
static isPromise(value: any): boolean;
}
isPromise(value)— thenable detection; returnstruefor promise-like values.
guid
Generate a v4-style GUID string. guid is also a string type alias of the same name (both exported from models/index).
const guid: () => string;
type guid = string;
- Returns a new GUID string.
Lifetimes
Lifetime
Lifetime options for defineFactory (and consumers like @sigx/store's defineStore). A string-literal union; the lifetime is enforced (see defineFactory for the semantics of each value).
type Lifetime = 'singleton' | 'scoped' | 'transient';
Internal
__registerComponentPlugin
Internal HMR hook (registerComponentPlugin re-exported under a __-prefixed name). Not part of the stable public API — listed for completeness only.
function __registerComponentPlugin(plugin: ComponentPlugin): void;
plugin— aComponentPluginwith an optionalonDefinehook.
Types
Define (namespace)
The preferred, discoverable API for typing component props, events, models, slots, and expose. Combine members with the & intersection.
namespace Define {
type Prop<TName extends string, TType, Required extends boolean = false> =
Required extends false ? { [K in TName]?: TType } : { [K in TName]: TType };
type Event<TName extends string, TDetail = void> = { [K in TName]?: EventDefinition<TDetail> };
type Model<TNameOrType, TType = void> = /* default: model + update:modelValue; named: model:name + update:name */;
type Slot<TName extends string, TProps = void> = {
__slots?: { [K in TName]: TProps extends void
? () => JSXElement | JSXElement[] | null
: (props: TProps) => JSXElement | JSXElement[] | null };
};
type Expose<T> = { __exposed?: { __type: T } };
}
ComponentSetupContext
The context object passed to a component's setup function. renderFn / update() are HMR escape hatches.
interface ComponentSetupContext<TElement = PlatformElement, TProps extends Record<string, any> = {}, TEvents extends Record<string, any> = {}, TRef = any, TSlots = {}> extends SetupContext {
el: TElement;
signal: typeof signal;
props: PropsAccessor<TProps>;
slots: SlotsObject<TSlots>;
emit: EmitFn<TEvents>;
parent: ComponentSetupContext | null;
onMounted(fn: (ctx: MountContext<TElement>) => void): void;
onUnmounted(fn: (ctx: MountContext<TElement>) => void): void;
onCreated(fn: () => void): void;
onUpdated(fn: () => void): void;
expose(exposed: TRef): void;
renderFn: ViewFn | null;
update(): void;
}
ComponentFactory
Return type of component(). Callable as JSX, plus internal __-branded props used by the renderer.
type ComponentFactory<TCombined extends Record<string, any>, TRef, TSlots> =
((props: StripForJSX<Omit<TCombined, EventNames<TCombined>>>
& EventHandlers<TCombined>
& SlotProps<TSlots>
& SyncProps<TCombined>
& ExternalModelProps<TCombined>
& JSX.IntrinsicAttributes
& ComponentAttributeExtensions
& { ref?: Ref<TRef>; children?: any }) => JSXElement)
& { __setup: SetupFn<any, any, any, any>; __name?: string; __islandId?: string; __props: any; __events: any; __ref: TRef; __slots: TSlots };
AnyComponentFactory
Structural constraint accepting any ComponentFactory (used by lazy / compound) without contravariance issues.
type AnyComponentFactory = {
(...args: any[]): any;
__setup: SetupFn<any, any, any, any>;
__props: any;
__events: any;
__ref: any;
__slots: any;
};
Model
Two-way binding object: .value reads/writes, .binding forwards.
interface Model<T> {
value: T;
readonly binding: ModelBindingTuple<T>;
readonly [MODEL_SYMBOL]: true;
}
// ModelBindingTuple<T> = readonly [object, string, (value: T) => void]
App
Chainable app instance returned by defineApp(). The _-prefixed members are @internal for renderers.
interface App<TContainer = any> {
config: AppConfig;
use<Options>(plugin: Plugin<Options> | PluginInstallFn<Options>, options?: Options): App<TContainer>;
defineProvide<T>(useFn: InjectableFunction<T>, factory?: () => T): T;
hook(hooks: AppLifecycleHooks): App<TContainer>;
directive(name: string, definition: DirectiveDefinition): App<TContainer>;
directive(name: string): DirectiveDefinition | undefined;
mount(container: TContainer, mountFn?: MountFn<TContainer>): App<TContainer>;
unmount(): void;
_context: AppContext;
_isMounted: boolean;
_container: TContainer | null;
_rootComponent: JSXElement;
}
App / plugin type cluster
The supporting types for the app and plugin system (from app-types.ts).
interface AppConfig {
onError?(err: Error, instance: ComponentInstance | null, info: string): boolean | void; // usually set via app.onError(fn)
warnHandler?(...args: any[]): void;
performance?: boolean;
}
interface AppLifecycleHooks {
onComponentCreated?(instance: ComponentInstance): void;
onComponentMounted?(instance: ComponentInstance): void;
onComponentUnmounted?(instance: ComponentInstance): void;
onComponentUpdated?(instance: ComponentInstance): void;
onComponentError?(err: unknown, instance: ComponentInstance, info: string): boolean | void;
}
interface AppContext {
app: App;
provides: Map<symbol, unknown>;
config: AppConfig;
hooks: AppLifecycleHooks[];
directives: Map<string, DirectiveDefinition>;
}
interface Plugin<Options = any> { name?: string; install(app: App, options?: Options): void; }
type PluginInstallFn<Options = any> = (app: App, options?: Options) => void;
type MountFn<TContainer = any> = (element: JSXElement, container: TContainer, appContext: AppContext) => (() => void) | void;
type UnmountFn<TContainer = any> = (container: TContainer) => void;
interface ComponentInstance { name?: string; ctx: ComponentSetupContext; vnode: VNode; }
VNode / JSXElement / JSXChild / JSXChildren
Core virtual-node and JSX value types.
type VNode = {
type: string | typeof Fragment | typeof Text | typeof Comment | Function;
props: Record<string, any>;
key: string | number | null;
children: VNode[];
dom: any | null;
text?: string | number;
parent?: VNode | null;
cleanup?: () => void;
};
type JSXChild = VNode | string | number | boolean | null | undefined | JSXChild[];
type JSXChildren = JSXChild;
type JSXElement = VNode | string | number | boolean | null;
Component-system helper types
Helper types from component-types.ts. SetupContext and MountContext are augmentation points.
type ViewFn = () => JSXElement | JSXElement[] | undefined;
type SetupFn<TProps, TEvents, TRef, TSlots> =
(ctx: ComponentSetupContext<PlatformElement, TProps, TEvents, TRef, TSlots>) => ViewFn | Promise<ViewFn>;
interface ComponentOptions { name?: string; }
type PropsAccessor<TProps> = { readonly [K in keyof TProps]: TProps[K] };
type SlotsObject<TSlots = {}> = { default: () => JSXElement[] } & TSlots;
type EmitFn<TEvents> = <TName extends keyof TEvents>(eventName: TName, ...args: any[]) => void;
interface MountContext<TElement = PlatformElement> { el: TElement; }
interface SetupContext {}
type PropsWithDefaults<TProps, D> = { readonly [K in keyof TProps]-?: K extends keyof D ? NonNullable<TProps[K]> : TProps[K] };
type Ref<T> = { current: T | null } | ((instance: T | null) => void);
type Exposed<T extends { __ref: any }> = T['__ref'];
type ComponentRef<T extends { __ref: any }> = Ref<T['__ref']>;
Platform types
Module-augmentation hooks: platforms set the element type; packages add global component attributes.
interface PlatformTypes {} // platforms declaration-merge `element: HTMLElement`
type PlatformElement = PlatformTypes extends { element: infer E } ? E : any;
interface ComponentAttributeExtensions {} // augmented by SSR to add 'client:load', etc.
Host namespaces (RendererOptions)
createRenderer(nodeOps) takes a RendererOptions object — the host operations a
platform implements. Three of them are optional and concern namespaces.
Core is namespace-agnostic: it knows nothing about SVG. It threads an opaque
boolean flag through the tree walk, caches it on each vnode, and hands it back to
createElement and patchProp as the ns parameter. What that flag means is the
host's business — in @sigx/runtime-dom it means "in the SVG namespace".
interface RendererOptions<HostNode = any, HostElement = any> {
createElement(type: string, ns?: boolean, isCustomizedBuiltIn?: string): HostElement;
patchProp(el: HostElement, key: string, prev: any, next: any, ns?: boolean, appContext?: AppContext | null): void;
// …
/** The flag for an element, given its parent's. `undefined` parentNS means the
* surrounding context is unknown (a hydrated subtree patched from the top) —
* hosts typically fall back to a tag-name heuristic there. */
getElementNamespace?(tag: string, parentNS: boolean | undefined): boolean;
/** The flag children inherit, given the element's own. Lets a host RESET the
* context at a boundary element (in the DOM, `foreignObject`). */
getChildNamespace?(tag: string, ns: boolean): boolean;
/** The flag of an element's CONTAINER, given its tag and cached flag. Used when
* replacing a node whose surrounding context is unknown. */
getContainerNamespace?(tag: string, ns: boolean): boolean;
}
Defaults when a hook is absent:
| Hook | Absent behaviour |
|---|---|
getElementNamespace | Every element resolves to the default namespace (false) |
getChildNamespace | Children inherit the element's flag unchanged |
getContainerNamespace | The container resolves to the default namespace (false) |
For renderer authors: a DOM-like custom renderer must implement these to keep
SVG working — core no longer detects SVG tags itself, so omitting them resolves
everything to the default namespace. @sigx/runtime-dom's
nodeOps is the reference
implementation. Renderers with no namespace concept (terminal, lynx) simply omit
all three.
The ns parameter of createElement/patchProp was previously named isSVG;
positions are unchanged, so only the name differs.
Directive types
Directive lifecycle hook types. DirectiveDefinitionExtensions is an augmentation point (e.g. SSR getSSRProps).
interface DirectiveBinding<T = any> { value: T; oldValue?: T; }
interface DirectiveDefinitionExtensions<T = any> {}
interface DirectiveDefinition<T = any, El = any> extends DirectiveDefinitionExtensions<T> {
created?(el: El, binding: DirectiveBinding<T>): void;
mounted?(el: El, binding: DirectiveBinding<T>): void;
updated?(el: El, binding: DirectiveBinding<T>): void;
unmounted?(el: El, binding: DirectiveBinding<T>): void;
}
type ResolvedDirective<T = any, El = any> = [DirectiveDefinition<T, El>, T];
AsyncState
The reactive result of useData (and, via all, the combined state), plus the match arm table, the shared fetcher shape, and the read options. value is the best data the cell has — kept across a same-key refresh() and across a failed fetch, cleared on key change; loading is state === 'pending' only; refresh() never rejects (failures land on .error).
AsyncState<T> is a discriminated union, so state/hasValue checks narrow value without a cast. Extending it with interface X extends AsyncState<T> no longer compiles — intersect instead (type X<T> = AsyncState<T> & { … }).
type AsyncStateName = 'idle' | 'pending' | 'ready' | 'refreshing' | 'errored';
type AsyncState<T> =
| AsyncIdle<T> | AsyncPending<T> | AsyncReady<T> | AsyncRefreshing<T> | AsyncErrored<T>;
interface AsyncStateBase<T> { // the shared methods
match<R>(arms: MatchArms<T, R>): R | undefined;
refresh(): Promise<void>; // never rejects
}
// Each member narrows state / value / hasValue / error / loading:
interface AsyncReady<T> extends AsyncStateBase<T> {
readonly state: 'ready';
readonly value: T; // narrowed — no cast
readonly hasValue: true;
readonly error: null;
readonly loading: false;
}
interface AsyncErrored<T> extends AsyncStateBase<T> {
readonly state: 'errored';
readonly error: Error; // splits on hasValue: a cell that
// once succeeded still holds its value
readonly loading: false;
}
interface MatchArms<T, R> {
idle?: () => R; // defaults to `pending`
pending?: () => R; // omitted ⇒ renders nothing
error?: (e: Error, ctx: ErrorArmContext<T>) => R; // omitted ⇒ bubbles to errorScope / app onError
ready: (v: T) => R; // required — the only route to a non-null T
}
type ErrorArmContext<T> =
& { retry: () => void }
& ({ value: T; hasValue: true } | { value: null; hasValue: false });
type Fetcher<T, Arg> = (arg: Arg, ctx: AsyncFetcherContext) => Promise<T>;
interface AsyncFetcherContext { signal: AbortSignal; } // pass to fetch(url, { signal })
interface AsyncOptions { server?: boolean; } // default true; false ⇒ client-only keyed read
AsyncAction / RunResult / AllState
The useAction handle, its settled result, and the combined all() state.
interface AsyncAction<T, In> {
readonly state: 'idle' | 'pending' | 'ready' | 'errored';
readonly value: T | null;
readonly error: Error | null;
readonly loading: boolean; // state === 'pending'
match<R>(arms: MatchArms<T, R>): R | undefined;
run(input: In): Promise<RunResult<T>>; // never rejects
reset(): void;
}
type RunResult<T> = { ok: true; value: T } | { ok: false; error: Error };
interface AllState<T, E> extends AsyncState<T> {
readonly errors: E; // collect-all counterpart to first-error-wins `.error`
}
interface ActionOptions {} // open interface — empty in core; packs augment it
LazyComponentFactory / DeferProps
Lazy-loading types.
type LazyComponentFactory<T extends AnyComponentFactory> = T & {
preload: () => Promise<T>;
isLoaded: () => boolean;
__lazy: true;
};
type DeferProps = Define.Prop<'fallback', JSXElement | (() => JSXElement)> & Define.Slot<'default'>;
InjectableFunction / SetupFactoryContext
Dependency-injection types. InjectableFunction is what defineInjectable returns and what defineProvide / app.defineProvide accept.
interface InjectableFunction<T> {
(): T;
_factory: () => T;
_token: symbol;
}
type FactoryFunction<TArgs extends unknown[], TInstance> =
((...args: TArgs) => TInstance) & { _factory: () => TInstance; _token: symbol };
interface SetupFactoryContext {
onDeactivated(fn: () => void): void;
subscriptions: SubscriptionHandler;
overrideDispose(onDispose: (fn: () => void) => void): void;
}
FactoryFunction is what a parameterized defineFactory returns — callable with the setup's params and carrying the provide metadata so it works with defineProvide / app.defineProvide.
Subscription / Topic / ModelProcessor / ComponentPlugin
Messaging, model-processor, and plugin types. ModelProcessor is the signature of an intrinsic-element model handler — register one with registerModelProcessor. ComponentPlugin is a type-only public export (its setter is internal).
interface Subscription { unsubscribe(): void; }
interface Topic<T> {
/** Tooling metadata (set via createTopic options). */
readonly namespace?: string;
readonly name?: string;
readonly subscriberCount: number;
readonly hasSubscribers: boolean;
readonly disposed: boolean;
publish(data: T): void;
subscribe(handler: (data: T) => void): Subscription;
destroy(): void;
}
type ModelProcessor = (type: string, props: Record<string, any>, modelBinding: [Record<string, any>, string], originalProps: Record<string, any>) => boolean;
type ComponentPlugin = { onDefine?: (name: string | undefined, factory: Function, setup: Function) => void };
ModelBinding / EventDefinition
type ModelBinding<_T> = [object, string];
type EventDefinition<T> = { __eventDetail: T };
See also
- Usage guide — practical patterns and examples.
- Overview — package summary and installation.
