VSN.js runtime API
Use explicit JavaScript setup when you need control over registration, mounting, hydration, or extensions. CFS defines the behavior source; plugins add optional features.
Engine
Import public APIs from vsn in a bundled application:
import { Engine, parseCFS } from "vsn";
const source = "#counter { count: 0; }";
const program = parseCFS(source);
const engine = new Engine({ diagnostics: true });
engine.registerBehaviors(source);
await engine.mount(document.getElementById("counter"));
parseCFS(source: string) parses source into a program AST. It does not mount anything or inherit an engine's registered flags. For behavior source using runtime event flags or custom extensions, use engine.registerBehaviors() with the relevant registrations in place. Lexer, Parser, TokenType, AST exports, and VERSION are also exported for tooling.
Initialization and teardown
| API | Return / behavior |
|---|---|
new Engine(options?) |
Create an engine |
registerBehaviors(source: string) |
Parse and register CFS source |
mount(root: HTMLElement) |
Promise<void>; initialize and observe the root |
hydrate(root: HTMLElement, { state }?) |
Promise<void>; initialize with optional root state and preserve uninitialized SSR values |
unmount(element: Element) |
Tear down bindings for the subtree |
dispose() |
Tear down mounted roots and engine-owned resources |
signal |
Engine lifetime's AbortSignal |
getScope(element, parentScope?) |
Get or create an element's Scope |
getLifetime(element) |
Element inline-binding Lifetime |
evaluate(element) |
Re-evaluate the element's registered bindings |
getRegistryStats() |
{ behaviorCount, behaviorCacheSize } |
EngineOptions accepts diagnostics, logger (optional info and warn methods), htmlSanitizer, trustedTypesPolicy, and trustedTypesPolicyName. A sanitizer has the signature (html: string) => string. A supplied Trusted Types policy provides createHTML(value: string).
autoMount(root?: HTMLElement | Document): Engine | null sets up automatic source loading and mounting, with document as its browser default. It returns the engine before asynchronous mounting completes. It applies registered browser plugins, loads inline and external text/vsn scripts, and mounts the body or supplied element. Use explicit mount() when you need an awaitable initialization step.
Globals and extensions
| API | Purpose |
|---|---|
registerGlobal(name, value) |
Expose a value to CFS |
registerGlobals(values) |
Expose a record of values |
registerFlag(name, handler?) |
Register declaration/event flag hooks |
registerBehaviorModifier(name, handler?) |
Register behavior lifecycle hooks |
registerAttributeHandler(handler) |
Register a custom attribute handler |
registerHtmlTransformer(transform, { priority }?) |
Transform HTML values; returns a removal function |
registerHtmlSanitizer(sanitizer) |
Replace the sanitizer; returns a restoration function |
An attribute handler supplies id, match(name), and handle(element, name, value, scope, context?). A handler with the same ID replaces the previous handler. Its context includes lifetime, signal, hydrating, and onCleanup.
Behavior modifier hooks are onBind, onConstruct, onDestruct, and onUnbind. The names important and debounce are reserved for behavior modifier registration. Flag hooks include onApply, transformValue, onEventBind, onEventBefore, onEventAfter, and transformEventArgs. Register custom flags and modifiers before parsing behavior source that uses them.
HTML transformers receive (value, { element, trusted }); lower priority runs first, with registration order breaking ties. Use lifetime cleanup for resources created by extensions.
HTML and requests
setHtml(element, value, { trusted?, process? }?) transforms and inserts HTML. Untrusted output is sanitized. Processing is enabled unless process is false.
processHtml(root, { trusted? }?) processes dynamic HTML within a root. Only use trusted: true for application-controlled content. HTML safety guide.
request(element, config): Promise<RequestResult> sends a request and optionally applies its HTML response. RequestConfig accepts:
{
url?: string;
method?: string;
headers?: HeadersInit;
body?: unknown;
form?: HTMLFormElement;
submitter?: HTMLElement;
targetSelector?: string;
swap?: "inner" | "outer" | "none";
trusted?: boolean;
history?: "none" | "push" | "replace";
historyUrl?: string;
restoreFocus?: boolean | string;
signal?: AbortSignal;
}
The result contains response: Response, body: string, target: Element | null, and swapped: boolean. GetConfig is a backward-compatible alias for RequestConfig. HTTP failures use the exported RequestError, with response, status, statusText, and url fields.
Scope and reactivity
The core exports Scope, batch, computed, and effect for JavaScript integrations:
import { Scope, batch, computed, effect } from "vsn";
const scope = new Scope();
scope.set("count", 0);
const doubled = computed(scope, s => s.get("count") * 2);
const stop = effect(scope, s => {
console.log(s.get("count"), doubled.value);
});
batch(() => {
scope.set("count", 1);
scope.set("count", 2);
});
stop();
doubled.dispose();
| API | Purpose |
|---|---|
new Scope(parent?) |
Create a scope with an optional parent |
scope.createChild() |
Create an inheriting child |
scope.get(path) / getPath(path) |
Read a scope path |
scope.set(path, value) / setPath(path, value) |
Write a scope path |
scope.getLocal(name) / setLocal(name, value) |
Access local state |
scope.on(path, handler) / off(path, handler) |
Add/remove a path listener |
scope.onAny(handler) / offAny(handler) |
Add/remove a scope-wide listener |
batch(callback) |
Batch reactive updates; returns the callback's result |
computed(scope, getter, options?) |
Create a dependency-tracked computed reference |
effect(scope, callback, options?) |
Run a dependency-tracked effect; return a disposer |
A computed reference exposes read-only value, get(), subscribe(listener), and dispose(). subscribe returns a disposer. Computed options accept a lifetime.
scope.computed(getter, options?) is the scope-bound form. scope.computed(name, getter, options?) also exposes a named computed value on the scope; the name must be an unused, nonempty root key without dots. scope.effect(callback, options?) binds an effect to that scope.
An effect callback receives the scope and may return a cleanup function. Effect options accept lifetime and a scheduler(run) that may return a disposer. The engine also exposes batch(callback), computed(scope, getter, options?), and effect(scope, callback, options?).
Lifetime, isAbortError, throwIfAborted, and the Disposer type are exported for lifecycle-aware integrations. Tie external subscriptions and asynchronous work to the relevant lifetime rather than leaving them running after unmount.