Skip to content

Runtime

Edgar Mesquita edited this page Sep 23, 2026 · 12 revisions

Runtime (TypeScript)

🌐 This page in: English · Português

The eQuantic.UI Runtime is the library that brings the application to life in the browser. It is responsible for transforming the virtual tree generated by the compiled code into real DOM elements and reacting to state changes.

📦 Runtime Distribution

The runtime is distributed as a single bundled file (runtime.js; its size is not quoted here, because its gzip size is a budget that ServedRuntimeBudgetTests enforces, see Build Flow) that includes:

  • Core Runtime: Virtual DOM reconciler, component lifecycle
  • State Management: Reactive state system
  • Event System: WeakMap-based event tracking
  • Server Actions Bridge: Client-server RPC communication
  • Development Tools: Logger and error overlay (dev-only)
  • Service Provider: Dependency injection container

Packaging & Deployment

The runtime ships inside the package that serves it, eQuantic.UI.Server: the Server embeds the bundle and answers /_equantic/runtime.js with it, and carries the same bytes as a file at tools/runtime/runtime.js. The SDK copies that file into the app's wwwroot/_equantic/ during the build (the CopyEQuanticRuntime MSBuild target) for the tools that load an app's modules outside a server, such as the VS Code preview; a running app is always answered with the embedded bundle.

Build Flow:

TypeScript source (src/eQuantic.UI.Runtime/src) + boot.ts (src/eQuantic.UI.Sdk/Resources)
    ↓ BundleRuntime target of eQuantic.UI.Server (embedded bun), before every build
src/eQuantic.UI.Server/wwwroot/runtime.js (ONE bundle, and its one writer)
    ├─ embedded in eQuantic.UI.Server.dll → served at /_equantic/runtime.js
    ↓ CollectServedRuntime target, at pack time
eQuantic.UI.Server.nupkg/tools/runtime/runtime.js
    ↓ SDK resolves via $(PkgeQuantic_UI_Server)
    ↓ MSBuild CopyEQuanticRuntime target
Consumer's wwwroot/_equantic/runtime.js (the same bytes)

Architecture Benefits:

  • ✅ One Writer: the bundle is written once, by the package that serves it; the SDK only references it
  • ✅ The Copy Is the Served Runtime: what an app finds in wwwroot/_equantic/runtime.js is byte for byte what /_equantic/runtime.js answers
  • ✅ No Duplication: Single source of truth for runtime.js
  • ✅ Zero Dependencies: Consumers get the runtime automatically without Node.js/npm

🔄 The Reconciler

Unlike frameworks that recreate the entire DOM, the eQuantic.UI Reconciler compares the current page with the desired new version and applies only the minimum necessary changes.

Diffing Algorithm:

  1. Type Comparison: If a node has changed its tag (e.g., div to span), it is replaced entirely.
  2. Attribute Update: Only modified attributes are changed in the DOM.
  3. Child Management: The reconciler recursively traverses the list of children.

🗝️ Keyed Identity

The reconciler supports Keyed Diffing through the key property.

  • If two nodes in different positions have the same key, the framework understands that the element has been moved, preserving the browser's internal state (such as the cursor position in an input or the scroll state).

🧠 Event and Memory Management

To prevent memory leaks, the runtime uses an event tracking system based on WeakMap.

Advantages of WeakMap:

  • Event listeners are mapped directly to the HTMLElement.
  • When an element is removed from the DOM and there are no more references to it, the browser's Garbage Collector can automatically clear the event metadata, ensuring that the application's memory consumption remains stable even in long sessions.

🎯 Two event contracts worth knowing

Since 0.2.0-preview.24

The HtmlElement mirror lowers on* properties to DOM event names, with the DOM's own spelling where lowercasing alone is wrong: OnDoubleClick registers dblclick (the C# EventNameMap's one divergence: a doubleclick listener attaches fine and fires never). And setting OnSubmit takes OWNERSHIP of submission: the runtime calls preventDefault() before invoking, so the browser's own navigate-away submission never runs, so the handler validates and calls a server action instead. A form that wants native submission simply sets no handler. click keeps the browser defaults it always had: a label click must still toggle its checkbox.

🔒 Modal focus management

Since 0.2.0-preview.24

Modal overlay layers arrive from both producers carrying role="dialog", aria-modal, tabindex="-1" and a data-eq-trap marker. The client controller reconciles traps against the DOM at the same moment the Shortcut set commits after each reconciler pass: a layer that appeared records the invoker and takes focus after the next frame (a layer fading out of visibility:hidden is not focusable until the style lands); Tab/Shift+Tab cycle inside, a focusin guard pulls back what escapes; a layer that stopped being marked (removed, closed keep-mounted, or reparented for its exit animation) restores focus to the invoker. Discovery by marker instead of by path is what keeps the two producers byte-identical.

💧 Hydration (SSR)

The runtime supports the "Hydration" process, where it takes control of HTML already rendered by the server (SSR). Instead of destroying and recreating, the runtime only attaches the necessary event listeners to the existing elements, ensuring an instantaneous initial load.

🔧 Development Mode

The runtime detects the environment via the window.__EQ_DEV__ flag (set by the server based on IWebHostEnvironment.IsDevelopment()).

Development-Only Features

Logger System (utils/logger.ts)

Professional logging system that only outputs in development mode:

import { logger } from './utils/logger';

logger.debug('Boot process started');  // Only in dev
logger.info('Component rendered');     // Only in dev
logger.warn('Deprecated API used');    // Always logs
logger.error('Failed to load data');   // Always logs

All logs are prefixed with [eQuantic.UI] for easy filtering.

Error Overlay (dev/error-overlay.ts)

Next.js-style error overlay that displays runtime errors in a full-screen UI (development only):

  • Automatic capture: Unhandled errors and promise rejections
  • Stack traces: Full error context with source information
  • Keyboard support: Press Esc to close
  • Clean UX: Similar to Next.js error overlay

The error overlay is automatically imported and activated when window.__EQ_DEV__ === true.

Production Mode

In production builds:

  • logger.debug() and logger.info() are silenced
  • Error overlay is never loaded
  • Only logger.warn() and logger.error() output to console
  • Minimal runtime overhead — one bundle, whose gzip size is a budget the suite enforces rather than a number quoted here

🎨 Theme Bridge

The app's theme is typed C# on the server (IAppTheme, selected via UseTheme, see DesignSystem). The server serializes the selected theme into window.__EQ_THEME__ next to the boot config, and the runtime's theme bridge (shared/theme-bridge.ts) adopts it at boot, so SSR pixels and client lowering resolve the same tokens, and a runtime light/dark switch flips one color-scheme declaration.

Clone this wiki locally