Targets · JavaScript · Entry point

Entry point

Every rendered Program has a single generated entry point: a main() function that builds the native drivers from the driver spec, hands the run body to the reactive system, and starts the cycle. It is where a page of otherwise static JavaScript becomes a running, reactive program.

Contents

How a Program becomes JavaScript, in parts. Start with the introduction, then follow each part into the rendered output and the runtime it links against.

One call to start

A whole program starts with a single call. It takes the root component — the run body, written as a callback — and the drivers the embed should use, and returns a cancel function that tears the program down again.

const cancel = Program.Application(
  () => Counter(),                 // the root component — the program itself
  [
    Ui.Dom.Driver('app'),          // render the interface into <div id="app">
  ]
);

That one call builds the drivers, creates the reactive program from the root component, runs the first cycle, and reconciles the result into the page. Everything after is the runtime reacting to change.

The cycle

That first call does not start a loop that spins. The runtime is demand-driven: it runs one cycle, then waits. A cycle is a single Reactive.Run — write-backs first, then the forward body, which emits the interface and effects into the drivers' channels. Then nothing happens until a driver has news.

When something outside changes — a click, a network reply, an animation frame — the driver calls the addUpdates it was handed at startup. That schedules exactly one more cycle (on a microtask, so a burst of updates coalesces into a single render), and Reactive.Run receives those updates as it runs again. A program at rest costs nothing; continuous motion, like animation, comes only from a driver that re-schedules itself every frame.

The friendly Program.Application(root, [drivers]) is a thin wrapper over the reserved runtime entry Program._Runtime.Run(root, …drivers) — the _ marking it as glue the program can never name, the same convention that walls off a record's generated helpers. It builds the reactive program from the root, initialises each driver with addUpdates, runs the first cycle, and returns the cancel that stops it.

Attaching to the page

The interface is attached to one root element in the DOM. The DOM user-interface driver takes that element — by id or as an element — and owns everything inside it, patching it in place on each cycle rather than rebuilding it.

<div id="app"></div>

<script src="program-runtime.js"></script>
<script>
  Program.Application(() => Counter(), [ Ui.Dom.Driver('app') ]);
</script>

Because the root is just an element, one page can host many independent programs at once, each mounted into its own node. That is exactly how a live example is embedded in these pages: a small program in an isolated <div>.

The run body is the root component

The callback passed first is the program. Its calls emit the interface, its state lives across cycles, and its nesting becomes nested closures in the rendered code — see the example. There is no separate template: the same reactive body that computes values also lays out the page.

Stopping

Program.Application returns a cancel function. Calling it stops the cycle, tears down the drivers, and releases the root element — the clean end of a program's life. Wire it to beforeunload for a whole-page app, or call it by hand for an embed that comes and goes.

const cancel = Program.Application(() => Counter(), [ Ui.Dom.Driver('app') ]);
window.addEventListener('beforeunload', cancel);
// … later, to remove an embedded program:
cancel();