Add isomorphic SX architecture migration plan
Documents the 5-phase plan for making the sx s-expression layer a universal view language that renders on either client or server, with pages as cached components and data-only navigation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -358,3 +358,41 @@ Each service migrates independently, no coordination needed:
|
||||
3. Enable SSR for bots (Phase 2) — per-page opt-in
|
||||
4. Client data primitives (Phase 4) — global once sx.js updated
|
||||
5. Data-only navigation (Phase 5) — automatic for any `defpage` route
|
||||
|
||||
---
|
||||
|
||||
## Why: Architectural Rationale
|
||||
|
||||
The end state is: **sx.js is the only JavaScript in the browser.** All application code — components, pages, routing, event handling, data fetching — is expressed in sx, evaluated by the interpreter, with behavior mediated through bound primitives.
|
||||
|
||||
### Benefits
|
||||
|
||||
**Single language everywhere.** Components, pages, routing, event handling, data fetching — all sx. No context-switching between JS idioms and template syntax. One language for the entire frontend and the server rendering path.
|
||||
|
||||
**Portability.** The same source runs on any VM that implements the ~50-primitive interface. Today: Python + JS. Tomorrow: WASM, edge workers, native mobile, embedded devices. Coupled to a primitive contract, not to a specific runtime.
|
||||
|
||||
**Smaller wire transfer.** S-expressions are terser than equivalent JS. Combined with content-addressed caching (hash/localStorage), most navigations transfer zero code — just data.
|
||||
|
||||
**Inspectability.** The sx source is the running program — no build step, no source maps, no minification. View source shows exactly what executes. The AST is the structure the evaluator walks. Debugging is tracing a tree.
|
||||
|
||||
**Controlled surface area.** The only JS that runs is sx.js. Everything else is mediated through defined primitives. No npm supply chain. No third-party scripts with ambient DOM access. Components can only do what primitives allow — the capability surface is fully controlled.
|
||||
|
||||
**Hot-reloadable everything.** Components are data (cached AST). Swapping a definition is replacing a dict entry. No module system, no import graph, no HMR machinery. Already works for .sx file changes in dev mode — extends to behaviors too.
|
||||
|
||||
**AI-friendly.** S-expressions are trivially parseable and generatable. An LLM produces correct sx far more reliably than JS/JSX — fewer syntax edge cases, no semicolons/braces/arrow-function ambiguities. The codebase becomes more amenable to automated generation and transformation.
|
||||
|
||||
**Security boundary.** No `eval()`, no dynamic `<script>` injection, no prototype pollution. The sx evaluator is a sandbox — it only resolves symbols against the primitive table and component env. Auditing what any sx expression can do means auditing the primitive bindings.
|
||||
|
||||
### Performance and WASM
|
||||
|
||||
The tradeoff is interpreter overhead — a tree-walking interpreter is slower than native JS execution. For UI rendering (building DOM, handling events, fetching data), this is not the bottleneck — DOM operations dominate, and those are the same speed regardless of initiator.
|
||||
|
||||
If performance ever becomes a concern, WASM is the escape hatch at three levels:
|
||||
|
||||
1. **Evaluator in WASM.** Rewrite `sxEval` in Rust/Zig → WASM. The tight inner loop (symbol lookup, env traversal, function application) runs ~10-50x faster. DOM rendering stays in JS (it calls browser APIs regardless).
|
||||
|
||||
2. **Compile sx to WASM.** Ahead-of-time compiler: `.sx` → WASM modules. Each `defcomp` becomes a WASM function returning DOM instructions. Eliminates the interpreter entirely. The content-addressed cache stores compiled WASM blobs instead of sx source.
|
||||
|
||||
3. **Compute-heavy primitives in WASM.** Keep the sx interpreter in JS, bind specific primitives to WASM (image processing, crypto, data transformation). Most pragmatic and least disruptive — additive, no architecture change.
|
||||
|
||||
The primitive-binding model means the evaluator doesn't care what's behind a primitive. `(blur-image data radius)` could be a JS Canvas call today and a WASM JAX kernel tomorrow. The sx source doesn't change.
|
||||
|
||||
Reference in New Issue
Block a user