Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Observable Runtime Spike

Date: 2026-05-09

Outcome

Do not adopt @observablehq/runtime as the core Lapis notebook runtime for the current refactor.

Lapis should keep its own explicit notebook execution runtime and continue borrowing Observable-compatible API shapes selectively, especially for viewof, Inputs, html, svg, FileAttachment, generators, and helper naming. A future compatibility layer may still use Observable concepts or selected packages, but the core runtime should remain Lapis-owned until there is a concrete design that preserves Lapis execution, persistence, worker, and renderer contracts without forcing an Observable notebook execution model into the product plugin.

Spike Inputs

The upstream @observablehq/runtime package exposes a reactive module/variable runtime:

  • new Runtime(builtins, global) creates a runtime with builtins and unresolved-global resolution.
  • runtime.module(define, observer) creates modules and observer factories.
  • module.variable(observer).define(name, inputs, definition) defines reactive variables.
  • module.import(...), module.derive(...), and module.redefine(...) support imports and replacement variables.
  • module.value(name) resolves the next value of a named variable.
  • observers receive pending, fulfilled, and rejected notifications.
  • variables compute when observed, and generator variables are pulled by the runtime.

Those mechanics are well-suited to Observable notebooks, where the runtime owns reactive variables and observers are the primary output surface.

Compatibility Findings

Observable runtime can model dependency-driven JavaScript values, but it does not directly match the Lapis notebook execution contract.

RequirementFinding
Explicit note-level, stale-cell, current-cell, and inline execution commandsObservable variables recompute through runtime dependency invalidation and observers. Lapis commands need deterministic command-scoped execution, cancellation, progress, and selective downstream rerun policy.
Generated-state persistence keyed by markdown cell ids and source fingerprintsObservable variables are runtime entities whose names can change or be anonymous. Lapis persists output and cell state by stable parsed markdown cell ids and source fingerprints.
TypeScript cellsObservable runtime accepts JavaScript definitions; Lapis still needs TypeScript transform and error mapping before execution.
SQL and dynamic markdown cellsObservable runtime does not provide Lapis SQL interpolation, DuckDB table registration, or dynamic markdown output semantics. These remain Lapis runtime responsibilities.
Worker/main execution selectionLapis chooses worker versus in-process execution from DOM, DuckDB, dependency-loader, and host capability facts. Observable runtime does not solve the product-specific worker and host bridge split.
Notebook-owned typed output renderersObservable observers/inspectors are output-oriented, while Lapis needs typed table, chart, markdown, DOM, image, and error outputs routed through notebook-owned renderers and generated-state persistence.

Decision

Keep the Lapis runtime as the core execution engine.

Use Observable as an API-shape reference rather than as the execution substrate:

  • keep constrained viewof name = expression support through @lapis-notes/notebook-input
  • keep Observable Inputs and htl loading lazy and DOM-scoped
  • keep lapis.FileAttachment vault-backed rather than Observable-host-backed
  • keep user dependencies as validated bare globals rather than Observable module imports for now
  • evaluate specific Observable concepts later only when they can be adapted behind the existing Lapis runtime contracts

This closes the current compatibility spike. Fuller Observable JavaScript syntax, imports, mutable values, generators, and invalidation should not be adopted wholesale or tracked as one standing implementation task. Future compatibility work may propose a specific Observable concept only when it preserves the existing Lapis execution, persistence, worker, and renderer contracts.