Esc

App Runtime

Streak's client runtime is a tiny inline <script> injected at the end of every generated page's <body>, right after the w-m registry element. It runs immediately as the page parses — no bundler, no separate runtime file, no Web Worker. Its entire job is to register the four gDom helpers on window and then load any lazy widgets in order.


What Gets Injected

Two elements are appended just before </body>:

<script type="application/json" id="w-m">["HelloMessage"]</script>
<script>(function (window, opts) { /* ...runtime body, see below... */ })(window, {
  endpoint: "/__streak/content",
  widgetMetaId: "w-m"
});</script>

The runtime function is written as a normal, readable function and stringified into the page — the same technique the Script component uses for widget-authored scripts.


What the Runtime Registers on window

FunctionSignatureDescription
addResourceToBody(src, options?, callback?)Appends a real <script src>/<link rel=stylesheet> tag to document.body; caches by src so repeat calls don't re-fetch
loadPackage(name)addResourceToBody(/assets/$name, { async: true }) — loads a file from public/assets/
loadDynamicComponent(id, callback?)Fetches a Dynamic block's content and injects it into its placeholder
addWidgetToBody(id, callback?)Fetches a lazy widget's content and injects it into its placeholder

These are exactly the four methods documented in the gDom API — gDom inside a Script function is literally window after this runtime has run.


Startup Sequence

1. Register the four helper functions on window

addResourceToBody, loadPackage, loadDynamicComponent, and addWidgetToBody are all assigned directly onto the window object passed in.

2. Read the w-m widget registry

The runtime looks up the <script id="w-m"> element by id and JSON.parses its text content into an array of widget ids. If the element isn't present, it treats the list as empty.

const metaEl = window.document.getElementById("w-m");
const lazyWidgetIds = metaEl ? JSON.parse(metaEl.textContent || "[]") : [];

3. Load lazy widgets sequentially

The runtime walks the id list one at a time, calling addWidgetToBody(id, callback) for the first id and only moving to the next once that widget's fetch-and-inject has completed:

const loadNext = (ids) => {
  const [first, ...rest] = ids;
  if (!first) return;
  window.addWidgetToBody(first, () => loadNext(rest));
};
loadNext(lazyWidgetIds);

That's the whole startup sequence. There's no timing/session bookkeeping, no cookies, and nothing runs on a separate thread — widgets without loadingStrategy: "lazy" are never touched by this runtime at all, because their HTML and <script> tags are already inline in the page by the time it runs.


Fetching Content

loadDynamicComponent and addWidgetToBody both fetch from the same content endpoint (/__streak/content), passing the current page path, a resource type (c for Dynamic, w for widgets), and the id as query parameters. The response is JSON: { html: string, scripts?: { id: string; content: string }[] }. The runtime swaps html into the matching placeholder element ([component-id][component-type]) via outerHTML, then appends any scripts to the document, skipping anything already injected.