Two bugs caused code blocks to render empty across the site: 1. ~docs/code component had parameter named `code` which collided with the HTML <code> tag name. Renamed to `src` and updated all 57 callers. Added font-mono class for explicit monospace. 2. Batched IO dispatch in ocaml_bridge.py only skipped one leading number (batch ID) but the format has two (epoch + ID): (io-request EPOCH ID "name" args...). Changed to skip all leading numbers so the string name is correctly found. This fixes highlight and other batchable helpers returning empty results. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
334 lines
28 KiB
Plaintext
334 lines
28 KiB
Plaintext
;; ---------------------------------------------------------------------------
|
|
;; Isolated Evaluator — Shared platform layer, isolated JS, Rust WASM
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~plans/isolated-evaluator/plan-isolated-evaluator-content ()
|
|
(~docs/page :title "Isolated Evaluator"
|
|
|
|
(~docs/section :title "Context" :id "context"
|
|
(p "The SX spec is already split into three layers:")
|
|
(ul :class "list-disc list-inside space-y-1 mt-2"
|
|
(li (code "spec/") " \u2014 Core language (19 files): evaluator, parser, primitives, CEK, types, continuations. Host-independent.")
|
|
(li (code "web/") " \u2014 Web framework (20 files): signals, adapters, engine, orchestration, boot, router, deps. Built on core spec.")
|
|
(li (code "sx/") " \u2014 Application (sx-docs website). Built on web framework."))
|
|
(p "Bootstrappers search " (code "spec/ \u2192 web/ \u2192 shared/sx/ref/") " for " (code ".sx") " files. The separation is clean.")
|
|
(p "This plan takes the next step: " (strong "isolate the evaluator from the real world") ". The JS evaluator should run in the same sandbox as Rust/WASM \u2014 unable to touch DOM, fetch, timers, or storage directly. Both evaluators call into a shared " (code "sx-platform.js") " for all browser access.")
|
|
(p "This also involves sorting out the JavaScript: eliminating hand-coded JS that duplicates specced " (code ".sx") " logic, and moving web framework " (code ".sx") " from compiled-into-evaluator to runtime-evaluated."))
|
|
|
|
(~docs/section :title "Existing Architecture" :id "existing"
|
|
(h4 :class "font-semibold mt-4 mb-2" "Three-layer spec split (DONE)")
|
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Layer")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Directory")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Files")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Content")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Core spec")
|
|
(td :class "px-3 py-2 text-stone-700" (code "spec/"))
|
|
(td :class "px-3 py-2 text-stone-700" "19")
|
|
(td :class "px-3 py-2 text-stone-600" "eval, parser, primitives, render, types, CEK, continuations, boundary-core"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Web framework")
|
|
(td :class "px-3 py-2 text-stone-700" (code "web/"))
|
|
(td :class "px-3 py-2 text-stone-700" "20")
|
|
(td :class "px-3 py-2 text-stone-600" "adapters, signals, engine, orchestration, boot, router, deps, forms, boundary-web"))
|
|
(tr
|
|
(td :class "px-3 py-2 text-stone-700" "Application")
|
|
(td :class "px-3 py-2 text-stone-700" (code "sx/"))
|
|
(td :class "px-3 py-2 text-stone-700" "\u2014")
|
|
(td :class "px-3 py-2 text-stone-600" "sx-docs website, page components, content")))))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Rust/WASM evaluator (DONE)")
|
|
(p (code "sx-rust/") " has a working parser + evaluator + render-to-html in WASM: 9,823 lines generated Rust, 75 real primitives, 154 stubs, 92 tests passing. Currently pure computation \u2014 no DOM interaction.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "What needs to change")
|
|
(p "Currently " (strong "everything") " (core + web framework) gets compiled into one monolithic " (code "sx-browser.js") ". The web framework " (code ".sx") " files (signals, engine, orchestration, boot, etc.) are baked into the evaluator output by the bootstrapper. They should instead be " (strong "evaluated at runtime") " by the core evaluator, like any other " (code ".sx") " code.")
|
|
(p "The JavaScript platform primitives (DOM, fetch, timers, storage) are also inlined into the bootstrapped output. They need to be extracted into a standalone " (code "sx-platform.js") " module that both JS and WASM evaluators share."))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; What's core vs web (bootstrapped vs runtime)
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Bootstrapped vs Runtime-Evaluated" :id "bootstrap-vs-runtime"
|
|
(p "The key question: what MUST be compiled into the evaluator vs what can be loaded as " (code ".sx") " at runtime?")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Must be bootstrapped (core spec)")
|
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "File")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Dir")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Why")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "eval.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "spec/")
|
|
(td :class "px-3 py-2 text-stone-600" "IS the language \u2014 can't evaluate without it"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "parser.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "spec/")
|
|
(td :class "px-3 py-2 text-stone-600" "Can't read .sx source without a parser"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "primitives.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "spec/")
|
|
(td :class "px-3 py-2 text-stone-600" "80+ built-in pure functions \u2014 must be native"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "render.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "spec/")
|
|
(td :class "px-3 py-2 text-stone-600" "HTML_TAGS registry, parse-element-args"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "adapter-html.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "render-to-html \u2014 co-recursive with eval-expr"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "adapter-sx.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "aser (wire format) \u2014 co-recursive with eval-expr"))
|
|
(tr
|
|
(td :class "px-3 py-2 text-stone-700" (code "adapter-dom.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "render-to-dom \u2014 co-recursive with eval-expr")))))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Runtime-evaluated (web framework)")
|
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "File")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Dir")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Why it can be runtime")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "signals.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Pure computation \u2014 dicts with markers, no new types"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "engine.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Pure logic \u2014 trigger parsing, swap specs, morph"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "orchestration.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Event binding + fetch \u2014 calls platform primitives"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "boot.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Page lifecycle \u2014 calls platform primitives"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "router.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "URL pattern matching \u2014 pure computation"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "deps.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Component dependency analysis \u2014 pure AST walking"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" (code "page-helpers.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Data transformation helpers"))
|
|
(tr
|
|
(td :class "px-3 py-2 text-stone-700" (code "forms.sx"))
|
|
(td :class "px-3 py-2 text-stone-700" "web/")
|
|
(td :class "px-3 py-2 text-stone-600" "Server-only definition forms")))))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "adapter-dom.sx reactive split")
|
|
(p (code "adapter-dom.sx") " contains reactive-aware code (" (code "reactive-text") ", " (code "reactive-attr") ", " (code "render-dom-island") ", " (code "render-dom-lake") ") interleaved with core DOM rendering. These call " (code "signal?") " and " (code "deref") " from " (code "signals.sx") " via environment lookup \u2014 no compile-time dependency. Option: split reactive DOM functions into " (code "adapter-dom-reactive.sx") " (web/ layer), keeping base " (code "adapter-dom.sx") " purely about elements/text/fragments/components.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Hand-coded JS to clean up")
|
|
(ul :class "list-disc list-inside space-y-2 mt-2"
|
|
(li (code "CONTINUATIONS_JS") " in " (code "platform_js.py") " \u2014 hand-coded shift/reset. Should use specced " (code "continuations.sx") " or be eliminated if continuations are application-level.")
|
|
(li (code "ASYNC_IO_JS") " in " (code "platform_js.py") " \u2014 hand-coded async rendering dispatch. Already replaced by " (code "adapter-async.sx") " for Python. JS version should also be bootstrapped or eliminated.")
|
|
(li "Various wrapper functions in " (code "PLATFORM_BOOT_JS") " that duplicate logic from " (code "boot.sx") ".")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Phase 1: Extract sx-platform.js
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Phase 1: Extract sx-platform.js" :id "phase-1"
|
|
(p (strong "Goal:") " All real-world-touching JavaScript lives in one standalone module. The evaluator never directly accesses " (code "document") ", " (code "window") ", " (code "fetch") ", " (code "localStorage") ", " (code "history") ", etc.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Architecture")
|
|
(~docs/code :src (highlight " \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sx-platform.js \u2502 \u2190 DOM, fetch, timers, storage\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 \u2502\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sx-evaluator.js \u2502 \u2502 sx-wasm-shim.js \u2502\n \u2502 (isolated JS) \u2502 \u2502 (WASM instance \u2502\n \u2502 \u2502 \u2502 + handle table) \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" "text"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "What moves into sx-platform.js")
|
|
(p "Extracted from " (code "platform_js.py") " string constants:")
|
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Category")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Source")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "~Functions")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "DOM primitives")
|
|
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_DOM_JS"))
|
|
(td :class "px-3 py-2 text-stone-600" "~50 (createElement, setAttribute, appendChild...)"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Engine platform")
|
|
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_ENGINE_PURE_JS"))
|
|
(td :class "px-3 py-2 text-stone-600" "~6 (locationHref, pushState, nowMs...)"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Orchestration platform")
|
|
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_ORCHESTRATION_JS"))
|
|
(td :class "px-3 py-2 text-stone-600" "~80 (fetch, abort, timers, SSE, scroll, media...)"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Boot platform")
|
|
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_BOOT_JS"))
|
|
(td :class "px-3 py-2 text-stone-600" "~20 (mount target, localStorage, cookies, logging...)"))
|
|
(tr
|
|
(td :class "px-3 py-2 text-stone-700" "Parser helpers")
|
|
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_PARSER_JS"))
|
|
(td :class "px-3 py-2 text-stone-600" "~4 (isIdentStart, parseNumber...)")))))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Isolation rule")
|
|
(p "After extraction, searching " (code "sx-evaluator.js") " for " (code "document") ", " (code "window") ", " (code "fetch") ", " (code "localStorage") ", " (code "history") ", " (code "setTimeout") ", " (code "console") " should find " (strong "zero") " direct references.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "The callSxFunction bridge")
|
|
(p "Platform code (event listeners, timers) needs to invoke SX lambdas. The evaluator provides a single " (code "callSxFunction(fn, args) \u2192 result") " bridge to the platform at registration time. This is the ONE evaluator-to-platform callback.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Implementation")
|
|
(ul :class "list-disc list-inside space-y-2 mt-2"
|
|
(li "Modify " (code "platform_js.py") " to emit platform functions as a separate output section")
|
|
(li "Create " (code "sx-platform.js") " as an IIFE that sets " (code "globalThis.SxPlatform = {...}"))
|
|
(li "The evaluator IIFE reads " (code "globalThis.SxPlatform") " at init, registers each function as a PRIMITIVE")
|
|
(li "Clean up " (code "CONTINUATIONS_JS") " and " (code "ASYNC_IO_JS") " \u2014 eliminate or bootstrap")
|
|
(li "Test that existing pages work identically")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Phase 2: Isolate the JS Evaluator
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Phase 2: Isolate the JS Evaluator" :id "phase-2"
|
|
(p (strong "Goal:") " " (code "sx-evaluator.js") " contains ONLY core spec + render adapters. Web framework " (code ".sx") " is evaluated at runtime.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Core-only build")
|
|
(p "The bootstrapper already supports selecting which modules to compile. A core-only build:")
|
|
(~docs/code :src (highlight "# In run_js_sx.py \u2014 core-only build\ncompile_ref_to_js(\n adapters=[\"parser\", \"html\", \"sx\", \"dom\"], # core spec + adapters\n modules=None, # no signals, engine, orchestration, boot\n extensions=None, # no continuations\n spec_modules=None # no deps, router, cek, frames, page-helpers\n)" "python"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Web framework loading")
|
|
(p "Web framework " (code ".sx") " files ship as " (code "<script type=\"text/sx-lib\">") " blocks. The platform boot shim evaluates them before component scripts:")
|
|
(~docs/code :src (highlight "<script src=\"/static/scripts/sx-platform.js\"></script>\n<script src=\"/static/scripts/sx-evaluator.js\"></script>\n<script type=\"text/sx-lib\">\n ;; concatenated web/ framework: signals, deps, router,\n ;; engine, orchestration, boot\n</script>\n<script type=\"text/sx\" data-components>\n ;; page component definitions\n</script>" "html"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Boot chicken-and-egg")
|
|
(p (code "boot.sx") " orchestrates the boot sequence but is itself web framework code. Solution: thin native boot shim (~30 lines) in " (code "sx-platform.js") ":")
|
|
(~docs/code :src (highlight "SxPlatform.boot = function(evaluator) {\n // 1. Evaluate web framework .sx libraries\n var libs = document.querySelectorAll('script[type=\"text/sx-lib\"]');\n for (var i = 0; i < libs.length; i++) {\n evaluator.evalSource(libs[i].textContent);\n }\n // 2. Call boot-init (defined in boot.sx)\n evaluator.callFunction('boot-init');\n};" "javascript"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Performance")
|
|
(p "Parsing + evaluating ~5,000 lines of web framework " (code ".sx") " at startup takes ~10\u201350ms. After " (code "define") ", functions are Lambda objects dispatched identically to compiled functions. " (strong "Zero ongoing performance difference.")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Phase 3: Wire Up Rust/WASM
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Phase 3: Wire Up Rust/WASM" :id "phase-3"
|
|
(p (strong "Goal:") " Rust evaluator calls " (code "sx-platform.js") " via wasm-bindgen imports. Handle table bridges DOM references.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Handle table (JS-side)")
|
|
(~docs/code :src (highlight "// In sx-wasm-shim.js\nconst handles = [null]; // index 0 = null handle\nfunction allocHandle(obj) { handles.push(obj); return handles.length - 1; }\nfunction getHandle(id) { return handles[id]; }\nfunction freeHandle(id) { handles[id] = null; }" "javascript"))
|
|
(p "DOM nodes are JS objects. The handle table maps " (code "u32") " IDs to JS objects. Rust stores " (code "Value::Handle(u32)") " and passes the " (code "u32") " to imported JS functions.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Value::Handle in Rust")
|
|
(~docs/code :src (highlight "// In platform.rs\npub enum Value {\n // ... existing variants ...\n Handle(u32), // opaque reference to JS-side object\n}" "rust"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "WASM imports from platform")
|
|
(~docs/code :src (highlight "#[wasm_bindgen(module = \"/sx-platform-wasm.js\")]\nextern \"C\" {\n fn platform_create_element(tag: &str) -> u32;\n fn platform_create_text_node(text: &str) -> u32;\n fn platform_set_attr(handle: u32, name: &str, value: &str);\n fn platform_append_child(parent: u32, child: u32);\n fn platform_add_event_listener(handle: u32, event: &str, callback_id: u32);\n // ... ~50 DOM primitives\n}" "rust"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Callback table for events")
|
|
(p "When Rust creates an event handler (a Lambda), it stores it in a callback table and gets a " (code "u32") " ID. JS " (code "addEventListener") " wraps it: when the event fires, JS calls into WASM with the callback ID. Rust looks up the Lambda and evaluates it.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "sx-wasm-shim.js")
|
|
(p "Thin glue (~100 lines):")
|
|
(ul :class "list-disc list-inside space-y-1 mt-2"
|
|
(li "Instantiate WASM module")
|
|
(li "Wire handle table")
|
|
(li "Delegate all platform calls to " (code "sx-platform.js"))
|
|
(li "Provide " (code "invoke_callback") " \u2192 Rust for event dispatch")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Phase 4: Web Framework Loading
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Phase 4: Web Framework Loading" :id "phase-4"
|
|
(p (strong "Goal:") " Both JS and WASM evaluators load the same web framework " (code ".sx") " files at runtime.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Boot sequence (identical for both evaluators)")
|
|
(ol :class "list-decimal list-inside space-y-2 mt-2"
|
|
(li "Load " (code "sx-platform.js") " + evaluator (" (code "sx-evaluator.js") " or " (code "sx-wasm-shim.js") ")")
|
|
(li "Platform registers primitives with evaluator")
|
|
(li "Platform boot shim evaluates " (code "<script type=\"text/sx-lib\">") " blocks")
|
|
(li "Dependency order: signals \u2192 deps \u2192 frames \u2192 router \u2192 page-helpers \u2192 engine \u2192 orchestration \u2192 boot")
|
|
(li (code "boot-init") " called \u2014 processes component scripts, hydrates, initializes engine")
|
|
(li "Page is interactive"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Library dependency order")
|
|
(~docs/code :src (highlight "signals.sx \u2190 no SX deps (uses only core primitives)\ndeps.sx \u2190 no SX deps\nframes.sx \u2190 no SX deps\nrouter.sx \u2190 no SX deps\npage-helpers.sx \u2190 no SX deps\nengine.sx \u2190 uses render.sx (core), adapter-dom.sx (core)\norchestration.sx \u2190 depends on engine.sx\nboot.sx \u2190 depends on orchestration.sx" "text")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Phase 5: Verification + Rollout
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Phase 5: Verification + Rollout" :id "phase-5"
|
|
(h4 :class "font-semibold mt-4 mb-2" "Shadow comparison")
|
|
(p "Run both JS and WASM evaluators on same input, compare outputs:")
|
|
(ol :class "list-decimal list-inside space-y-1 mt-2"
|
|
(li (strong "Parse") " \u2014 same AST (already testable with current WASM exports)")
|
|
(li (strong "Eval") " \u2014 same values (already testable)")
|
|
(li (strong "Render") " \u2014 same DOM structure (requires Phase 3)"))
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Feature flag")
|
|
(p "Server sets " (code "data-sx-runtime=\"wasm\"") " or " (code "\"js\"") " on root element. Boot shim loads appropriate evaluator. Progressive enhancement: try WASM, fall back to JS.")
|
|
|
|
(h4 :class "font-semibold mt-4 mb-2" "Test matrix")
|
|
(p "All " (code "test-*.sx") " files from both " (code "spec/") " and " (code "web/") " run on BOTH evaluators."))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Principles
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Principles" :id "principles"
|
|
(ul :class "list-disc list-inside space-y-2"
|
|
(li (strong "Core is what defines the language.") " Parser, evaluator, primitives, render modes. If you can't run SX without it, it's core (" (code "spec/") "). If you can write it " (em "in") " SX, it's web framework (" (code "web/") ").")
|
|
(li (strong "Web framework runs ON the evaluator.") " Signals, engine, orchestration, boot, router, deps \u2014 these are SX programs. They're evaluated at runtime, not compiled into the evaluator.")
|
|
(li (strong "Isolation is the boundary.") " The evaluator can't touch the real world. Platform primitives are the only bridge. JS and WASM evaluators have identical isolation.")
|
|
(li (strong "Shared platform, not duplicated.") " One implementation of every browser primitive in " (code "sx-platform.js") ". Both evaluators use it. Fix a bug once, both get the fix.")
|
|
(li (strong "Handle table is the WASM boundary.") " Rust holds " (code "u32") " IDs. JavaScript holds real objects. Swap the handle table for a different host and the Rust code doesn't change.")
|
|
(li (strong "Progressive, not all-or-nothing.") " WASM is an enhancement. JS remains the fallback. Feature-flagged per page. Gradual and reversible.")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Interactions
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Interaction with Other Plans" :id "interactions"
|
|
(ul :class "list-disc list-inside space-y-2"
|
|
(li (strong (a :href "/sx/(etc.(plan.rust-wasm-host))" "Rust/WASM Host")) " \u2014 this plan supersedes it. That plan didn't distinguish core from web framework or propose evaluator isolation. The shared platform layer is the key architectural difference.")
|
|
(li (strong (a :href "/sx/(etc.(plan.wasm-bytecode-vm))" "WASM Bytecode VM")) " \u2014 complementary. This plan bootstraps the tree-walking evaluator. A future bytecode VM compiles SX to bytecodes. The platform layer and handle table are shared.")
|
|
(li (strong (a :href "/sx/(etc.(plan.runtime-slicing))" "Runtime Slicing")) " \u2014 simplified. With web framework as runtime-evaluated " (code ".sx") ", slicing becomes: ship core + only the framework files you need. L0 pages skip signals/engine entirely.")
|
|
(li (strong (a :href "/sx/(etc.(plan.reactive-runtime))" "Reactive Runtime")) " \u2014 signals are web framework code, confirming they layer on top of core without special treatment.")
|
|
(li (strong (a :href "/sx/(etc.(plan.foundations))" "Foundations")) " \u2014 the core/web split is the same principle: small kernel, everything else built on top.")))
|
|
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Outcome
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Outcome" :id "outcome"
|
|
(p "After completion:")
|
|
(ul :class "list-disc list-inside space-y-2 mt-2"
|
|
(li "Core evaluator is ~3,200 lines of bootstrapped spec (" (code "spec/") " + adapters from " (code "web/") ").")
|
|
(li "JS evaluator is isolated \u2014 can't touch the real world, same sandbox as WASM.")
|
|
(li "Shared " (code "sx-platform.js") " provides all browser primitives to both evaluators.")
|
|
(li "Rust/WASM evaluator runs in the browser with full DOM rendering via handle table.")
|
|
(li "Web framework " (code ".sx") " files (signals, engine, orchestration, boot) are runtime-evaluated by whichever evaluator is active.")
|
|
(li "Hand-coded JS (" (code "CONTINUATIONS_JS") ", " (code "ASYNC_IO_JS") ") eliminated or bootstrapped from spec.")
|
|
(li "The architecture proof is complete: one spec, isolated evaluator, shared platform, deployment-time target selection.")))))
|