VSN.jsGitHub ↗

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.