- orchestration.sx: post-swap calls sx-hydrate-islands for new islands
in swapped content, plus process-emit-elements for data-sx-emit
- Regenerate sx-ref.js
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- adapter-dom.sx: detect :on-click/:on-submit etc. in render-dom-element
— if attr starts with "on-" and value is callable, wire via dom-listen
- orchestration.sx: add process-emit-elements for data-sx-emit attrs
— auto-dispatch custom events on click with optional JSON detail
- bootstrap_js.py: add processEmitElements RENAME
- Regenerate sx-ref.js with all changes
- Update reactive-islands status table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7c: Client data cache management via element attributes
(sx-cache-invalidate) and response headers (SX-Cache-Invalidate,
SX-Cache-Update). Programmatic API: invalidate-page-cache,
invalidate-all-page-cache, update-page-cache.
7d: Service Worker (sx-sw.js) with IndexedDB for offline-capable
data caching. Network-first for /sx/data/ and /sx/io/, stale-while-
revalidate for /static/. Cache invalidation propagates from
in-memory cache to SW via postMessage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
defpage is already portable: server executes via execute_page(),
client via try-client-route. Add render plan logging to client
routing so console shows boundary decisions on navigation:
"sx:route plan pagename — N server, M client"
Mark Phase 7 (Full Isomorphism) as complete:
- 7a: affinity annotations + render-target
- 7b: page render plans (boundary optimizer)
- 7e: cross-host isomorphic testing (61 tests)
- 7f: universal page descriptor + visibility
7c (optimistic updates) and 7d (offline data) remain as future work.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client-side routing was only swapping #main-panel content without
updating OOB headers (nav rows, sub-rows). Now each page entry in the
registry includes a layout identity (e.g. "sx-section:Testing") and
try-client-route falls through to server when layout changes, so OOB
header updates are applied correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded IO primitive lists on both client and server with
data-driven registration. Page registry entries carry :io-deps (list
of IO primitive names) instead of :has-io boolean. Client registers
proxied IO on demand per page via registerIoDeps(). Server builds
allowlist from component analysis.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire async rendering into client-side routing: pages whose component
trees reference IO primitives (highlight, current-user, etc.) now
render client-side via Promise-aware asyncRenderToDom. IO calls proxy
through /sx/io/<name> endpoint, which falls back to page helpers.
- Add has-io flag to page registry entries (helpers.py)
- Remove IO purity filter — include IO-dependent components in bundles
- Extend try-client-route with 4 paths: pure, data, IO, data+IO
- Convert tryAsyncEvalContent to callback style, add platform mapping
- IO proxy falls back to page helpers (highlight works via proxy)
- Demo page: /isomorphism/async-io with inline highlight calls
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bindBoostForm re-reads method/action at submit time.
bind-preload-for re-reads verb-info and headers at preload time.
No closed-over stale values anywhere in the event binding system.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All click handlers (bind-event, bindBoostLink, bindClientRouteClick)
now re-read href/verb-info from the DOM element when the click fires,
instead of using values captured at bind time. This ensures correct
behavior when DOM is replaced or attributes are morphed after binding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pages whose components aren't loaded client-side now fall through
to server fetch instead of silently failing in the async callback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handle-popstate falls back to #main-panel when no [sx-boost] element
is found, fixing back button for apps using explicit sx-target attrs.
bindClientRouteClick also checks sx-target on the link itself.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Spec layer (orchestration.sx):
- try-client-route now handles :data pages instead of falling back to server
- New abstract primitive resolve-page-data(name, params, callback) — platform
decides transport (HTTP, IPC, cache, etc)
- Extracted swap-rendered-content and resolve-route-target helpers
Platform layer (bootstrap_js.py):
- resolvePageData() browser implementation: fetches /sx/data/<name>, parses
SX response, calls callback. Other hosts provide their own transport.
Server layer (pages.py):
- evaluate_page_data() evaluates :data expr, serializes result as SX
- auto_mount_page_data() mounts /sx/data/ endpoint with per-page auth
- _build_pages_sx now computes component deps for all pages (not just pure)
Test page at /isomorphism/data-test exercises the full pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove strict deps check — for case expressions like essay pages,
deps includes ALL branches but only one is taken. Instead, just
try to eval the content. If a component is missing, tryEvalContent
catches the error and we transparently fall back to server fetch.
deps field remains in registry for future prefetching use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each page entry now includes a deps list of component names needed.
Client checks all deps are loaded before attempting eval — if any
are missing, falls through to server fetch with a clear log message.
No bundle bloat: server sends components for the current page only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NIL is a frozen sentinel object ({_nil:true}) which is truthy in JS.
(not expr) compiled to !expr, so (not nil) returned false instead of
true. Fixed to compile as !isSxTruthy(expr) which correctly handles
NIL. This was preventing client-side routing from activating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SES lockdown may suppress console.error. Use logInfo for error
reporting since we know it works ([sx-ref] prefix visible).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Log "sx:route server fetch <url>" when falling back to network
- Use console.error for eval errors (not console.warn)
- Restructure bind-event to separate client route check from &&-chain
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows "pages: N routes loaded" at startup and
"sx:route no match (N routes) /path" when no route matches,
so we can see if routes loaded and why matching fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tryClientRoute now logs why it falls through: has-data, no content,
eval failed, or #main-panel not found. tryEvalContent logs the actual
error on catch. Added logWarn platform function (console.warn).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bind-event now checks tryClientRoute before executeRequest for GET
clicks on links. Previously only boost links (inside [sx-boost]
containers) attempted client routing — explicit sx-get links like
~nav-link always hit the network. Now essay/doc nav links render
client-side when possible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes for sx-browser.js (spec-compiled) vs sx.js (hand-written):
1. CSS meta tag mismatch: initCssTracking read meta[name="sx-css-hash"]
but the page template uses meta[name="sx-css-classes"]. This left
_cssHash empty, causing the server to send ALL CSS as "new" on every
navigation, appending duplicate rules that broke Tailwind responsive
ordering (e.g. menu bar layout).
2. Stale verb info after morph: execute-request used captured verbInfo
from bind time. After morph updated element attributes (e.g. during
OOB nav swap), click handlers still fired with old URLs. Now re-reads
verb info from the element first, matching sx.js behavior.
Also includes: render-expression dispatch in eval.sx, NIL guard for
preload cache in bootstrap_js.py, and helpers.py switched to
sx-browser.js.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
engine.sx now contains only pure logic: parsing, morph, swap, headers,
retry, target resolution, etc. orchestration.sx contains the browser
wiring: request execution, trigger binding, SSE, boost, post-swap
lifecycle, and init. Dependency is one-way: orchestration → engine.
Bootstrap compiler gains "orchestration" as a separate adapter with
deps on engine+dom. Engine-only builds get morph/swap without the
full browser runtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>