Nectar Runtime API

This document describes the complete runtime layer that bridges Nectar’s WASM modules to browser APIs. Understanding this architecture is critical for compiler development and debugging.

Architecture

Nectar’s runtime is a single JavaScript file (runtime/modules/core.js, ~900 lines) that provides browser API syscalls to WASM. All logic runs in WASM. JavaScript functions are 1-3 lines each — pure bridges with zero computation.

What Lives Where

Layer Runs in Examples
Application logic WASM Components, stores, routing, validation, crypto, formatting
Reactive system WASM Signals, effects, memos, dependency tracking, batching
String operations WASM Concat, fromI32, fromF64, fromBool — all in linear memory
Animation math WASM Spring physics, easing, interpolation
Browser syscalls JS (core.js) DOM, fetch, WebSocket, IndexedDB, timers, clipboard

Key Patterns

Command Buffer (mount/flush): - Initial render: WASM builds an HTML string in linear memory → single mount() call sets innerHTML - Updates: WASM writes opcodes into a command buffer in linear memory → single flush() call per animation frame reads and executes them all - This collapses ~50 individual WASM→JS boundary crossings into 1-2 per frame

__readOpts pattern: WASM writes flat (key_ptr, key_len, val_ptr, val_len) tuples terminated by (0,0) into linear memory. JS reads them to build option objects for browser APIs. Replaces all JSON.parse/stringify. Zero serialization overhead.

Typed setters: For complex APIs like fetch, WASM calls setMethod(), setBody(), addHeader() individually before triggering the call. No option object serialization.

Callbacks: WASM registers callback indices. JS calls R.__cb(cbIdx) when async operations complete. Data is written to WASM linear memory before the callback fires.

Element registry: DOM elements are registered by integer ID. WASM refers to elements by ID, never by reference. R.__registerElement(el) returns an integer handle.


Flush Opcodes

The command buffer uses these opcodes for batched DOM updates:

Opcode Value Operation
OP_SET_TEXT 1 Set textContent on element
OP_SET_ATTR 2 Set attribute (name, value)
OP_REMOVE_ATTR 3 Remove attribute
OP_APPEND_CHILD 4 Append child element
OP_REMOVE_CHILD 5 Remove child element
OP_INSERT_BEFORE 6 Insert element before reference
OP_SET_STYLE 7 Set inline style property
OP_CLASS_ADD 8 Add CSS class
OP_CLASS_REMOVE 9 Remove CSS class
OP_CLASS_TOGGLE 10 Toggle CSS class
OP_SET_INNER_HTML 11 Set innerHTML
OP_ADD_EVENT 12 Add event listener
OP_REMOVE_EVENT 13 Remove event listener
OP_FOCUS 14 Focus element
OP_BLUR 15 Blur element
OP_SET_PROPERTY 16 Set DOM property (value, checked, etc.)

Namespaces

core.js exports 16 namespaces via wasmImports. Each namespace contains only functions that call browser APIs WASM physically cannot invoke.

dom

DOM manipulation, element queries, and rendering.

Function Parameters Browser API
mount containerElId, htmlPtr, htmlLen el.innerHTML = html
hydrateRefs containerElId querySelectorAll('[data-nid]')
flush bufPtr, bufLen Reads opcode buffer, executes batched DOM ops
getElementById ptr, len document.getElementById()
querySelector ptr, len document.querySelector()
createElement ptr, len document.createElement()
createTextNode ptr, len document.createTextNode()
getBody document.body
getHead document.head
getRoot document.getElementById('app') or document.body
getDocumentElement document.documentElement
addEventListener elId, evtPtr, evtLen, cbIdx el.addEventListener() — marshals event data (clientX/Y, keyCode, modifiers, key, dataTransfer)
removeEventListener elId, evtPtr, evtLen, cbIdx el.removeEventListener()
lazyMount containerElId, urlPtr, urlLen, cbIdx import(url) dynamic module loading
setTitle ptr, len document.title = str
getScrollTop elId el.scrollTop
getScrollLeft elId el.scrollLeft
getClientHeight elId el.clientHeight
getClientWidth elId el.clientWidth
getWindowWidth window.innerWidth
getWindowHeight window.innerHeight
getOuterHtml document.documentElement.outerHTML
setDragData fmtPtr, fmtLen, dataPtr, dataLen dataTransfer.setData()
getDragData fmtPtr, fmtLen dataTransfer.getData()
preventDefault event.preventDefault()
loadScript urlPtr, urlLen, cbIdx Creates <script> element, sets src
loadChunk urlPtr, urlLen Creates <script>, returns promise ID
decodeImage srcPtr, srcLen, cbIdx new Image(), img.decode()
progressiveImage elId, lowPtr, lowLen, highPtr, highLen Sets low-res src, swaps to high-res on load
print elId contentWindow.print()
reloadModule urlPtr, urlLen, cbIdx fetch()WebAssembly.compile()WebAssembly.instantiate()
download dataPtr, dataLen, namePtr, nameLen BlobURL.createObjectURL()<a> click → URL.revokeObjectURL()

timer

Timing and animation frame scheduling.

Function Parameters Browser API
setTimeout cbIdx, ms setTimeout()
clearTimeout id clearTimeout()
setInterval cbIdx, ms setInterval()
clearInterval id clearInterval()
requestAnimationFrame cbIdx requestAnimationFrame()
cancelAnimationFrame id cancelAnimationFrame()
now performance.now()

webapi

General browser APIs — storage, clipboard, location, history, console, sharing, performance.

Function Parameters Browser API
localStorageGet keyPtr, keyLen localStorage.getItem()
localStorageSet keyPtr, keyLen, valPtr, valLen localStorage.setItem()
localStorageRemove keyPtr, keyLen localStorage.removeItem()
sessionStorageGet keyPtr, keyLen sessionStorage.getItem()
sessionStorageSet keyPtr, keyLen, valPtr, valLen sessionStorage.setItem()
clipboardWrite ptr, len navigator.clipboard.writeText()
clipboardRead cbIdx navigator.clipboard.readText()
getLocationHref location.href
getLocationSearch location.search
getLocationHash location.hash
getLocationPathname location.pathname
pushState urlPtr, urlLen history.pushState()
replaceState urlPtr, urlLen history.replaceState()
consoleLog ptr, len console.log()
consoleWarn ptr, len console.warn()
consoleError ptr, len console.error()
onPopState cbIdx addEventListener('popstate')
envGet namePtr, nameLen window.__env[name]
canShare !!navigator.share
nativeShare titlePtr, titleLen, textPtr, textLen, urlPtr, urlLen navigator.share()
perfMark namePtr, nameLen performance.mark()
perfMeasure namePtr, nameLen, startPtr, startLen, endPtr, endLen performance.measure()

http

HTTP communication via typed setters — no serialization.

Function Parameters Browser API
setMethod ptr, len Stores method string for next fetch
setBody ptr, len Stores body string for next fetch
addHeader keyPtr, keyLen, valPtr, valLen Adds to headers for next fetch
fetch urlPtr, urlLen fetch() with stored method/body/headers, returns promise ID

Pattern: WASM calls setMethod("POST"), addHeader("Content-Type", "application/json"), setBody(json), then fetch(url). Each call is a simple string store — zero serialization overhead.

observe

Intersection, resize, and mutation observers.

Function Parameters Browser API
matchMedia queryPtr, queryLen matchMedia() — returns boolean
intersectionObserver cbIdx, optsPtr new IntersectionObserver() — uses __readOpts for options
observe obsId, elId observer.observe(el)
unobserve obsId, elId observer.unobserve(el)
disconnect obsId observer.disconnect()

ws

WebSocket connections.

Function Parameters Browser API
connect urlPtr, urlLen new WebSocket(url)
send wsId, dataPtr, dataLen ws.send(str)
sendBinary wsId, ptr, len ws.send(Uint8Array)
close wsId ws.close()
closeWithCode wsId, code, reasonPtr, reasonLen ws.close(code, reason)
onOpen wsId, cbIdx ws.addEventListener('open')
onMessage wsId, cbIdx ws.addEventListener('message') — writes data to WASM memory
onClose wsId, cbIdx ws.addEventListener('close')
onError wsId, cbIdx ws.addEventListener('error')
getReadyState wsId ws.readyState

db

IndexedDB operations.

Function Parameters Browser API
open namePtr, nameLen, version, cbIdx indexedDB.open() — creates ‘default’ store on upgrade
put dbId, storePtr, storeLen, keyPtr, keyLen, valPtr, valLen objectStore.put()
get dbId, storePtr, storeLen, keyPtr, keyLen, cbIdx objectStore.get()
delete dbId, storePtr, storeLen, keyPtr, keyLen objectStore.delete()
getAll dbId, storePtr, storeLen, cbIdx objectStore.getAll()

worker

Web Workers and message channels.

Function Parameters Browser API
spawn codePtr, codeLen new Worker(Blob URL) from code string
channelCreate new MessageChannel() — returns port1 ID
channelSend portId, dataPtr, dataLen port.postMessage()
channelRecv portId, cbIdx port.onmessage, port.start()
parallel fnPtrsPtr, fnCount, cbIdx Callback dispatch (stub)
await promiseId Returns promise object
postMessage workerId, dataPtr, dataLen worker.postMessage()
onMessage workerId, cbIdx worker.addEventListener('message')
terminate workerId worker.terminate()

pwa

Service workers, push notifications, and caching.

Function Parameters Browser API
cachePrecache namePtr, nameLen caches.open()
registerPush optsPtr pushManager.subscribe() — uses __readOpts for options
registerServiceWorker pathPtr, pathLen, cbIdx navigator.serviceWorker.register()

hardware

Device hardware access.

Function Parameters Browser API
haptic pattern navigator.vibrate()
biometricAuth optsPtr, successCb, failCb navigator.credentials.get() — uses __readOpts
cameraCapture constraintsPtr, cbIdx navigator.mediaDevices.getUserMedia() — uses __readOpts
geolocationCurrent cbIdx navigator.geolocation.getCurrentPosition() — writes lat/lon to WASM memory

payment

Payment processing via sandboxed iframes.

Function Parameters Browser API
processPayment elId, msgPtr, msgLen, cbIdx contentWindow.postMessage(), listens for response via window.addEventListener('message')

auth

OAuth and credential management.

Function Parameters Browser API
login urlPtr, urlLen location.href = url (redirect to OAuth provider)
logout urlPtr, urlLen location.href = url (redirect to logout endpoint)
getRawCookies document.cookie
setCookie ptr, len document.cookie = str

upload

File input and transfer.

Function Parameters Browser API
init acceptPtr, acceptLen, multiple, cbIdx Creates <input type="file">, calls click()
start urlPtr, urlLen, cbIdx new XMLHttpRequest(), opens POST, tracks progress/load events
cancel xhrId xhr.abort()

time

Locale-aware date/time formatting (the only valid use of Intl from JS).

Function Parameters Browser API
now Date.now()
format ms, localePtr, localeLen new Intl.DateTimeFormat(locale).format()
getTimezoneOffset new Date().getTimezoneOffset()
formatDate ms, localePtr, localeLen, optsPtr new Intl.DateTimeFormat(locale, opts).format() — uses __readOpts

streaming

Server-Sent Events and streaming fetch.

Function Parameters Browser API
streamFetch urlPtr, urlLen, cbIdx fetch()body.getReader() → pumps chunks with read()
sseConnect urlPtr, urlLen, cbIdx new EventSource(url), listens for message events

rtc

WebRTC peer connections, data channels, and media tracks.

Function Parameters Browser API
createPeer optsPtr new RTCPeerConnection(opts)
createPeerWithIce urlsPtr, urlsLen new RTCPeerConnection({iceServers:[{urls}]})
createOffer pcId, cbIdx pc.createOffer()
createAnswer pcId, cbIdx pc.createAnswer()
setLocalDescription pcId, typePtr, typeLen, sdpPtr, sdpLen, cbIdx pc.setLocalDescription()
setRemoteDescription pcId, typePtr, typeLen, sdpPtr, sdpLen, cbIdx pc.setRemoteDescription()
addIceCandidate pcId, candPtr, candLen, midPtr, midLen, cbIdx pc.addIceCandidate()
createDataChannel pcId, labelPtr, labelLen, optsPtr pc.createDataChannel()
dataChannelSend dcId, dataPtr, dataLen dc.send(string)
dataChannelSendBinary dcId, ptr, len dc.send(Uint8Array)
dataChannelClose dcId dc.close()
dataChannelGetState dcId dc.readyState
onDataChannelMessage dcId, cbIdx dc.onmessage
onDataChannelOpen dcId, cbIdx dc.onopen
onDataChannelClose dcId, cbIdx dc.onclose
addTrack pcId, trackId, streamId pc.addTrack()
removeTrack pcId, senderId pc.removeTrack()
getStats pcId, cbIdx pc.getStats()
close pcId pc.close()
onIceCandidate pcId, cbIdx pc.onicecandidate
onIceCandidateFull pcId, cbIdx pc.onicecandidate (full candidate object)
onTrack pcId, cbIdx pc.ontrack
onDataChannel pcId, cbIdx pc.ondatachannel
onConnectionStateChange pcId, cbIdx pc.onconnectionstatechange
onIceConnectionStateChange pcId, cbIdx pc.oniceconnectionstatechange
onIceGatheringStateChange pcId, cbIdx pc.onicegatheringstatechange
onSignalingStateChange pcId, cbIdx pc.onsignalingstate
onNegotiationNeeded pcId, cbIdx pc.onnegotiationneeded
getConnectionState pcId pc.connectionState
getIceConnectionState pcId pc.iceConnectionState
getSignalingState pcId pc.signalingState
attachStream elId, streamId el.srcObject = stream
getUserMedia optsPtr, cbIdx navigator.mediaDevices.getUserMedia()
getDisplayMedia optsPtr, cbIdx navigator.mediaDevices.getDisplayMedia()
stopTrack trackId track.stop()
setTrackEnabled trackId, enabled track.enabled = bool
getTrackKind trackId track.kind

WASM-Internal Features (No JS)

These features run entirely in WASM with zero JavaScript involvement:


Exported Functions

core.js also exports:

export function instantiate(wasmUrl)  // Load and initialize a WASM module
export function hydrate(wasmInstance, rootElement)  // Attach WASM to server-rendered DOM

instantiate() is the main entry point — fetches the WASM file, compiles it, creates the runtime bridge, and calls the module’s main() export.

hydrate() is the SSR entry point — registers the root element and calls the module’s __hydrate() export if present.