Skip to content

The app-mount seam: visor/frame/mount.ts, every caller moved onto it - #147

Merged
lannbot merged 1 commit into
mainfrom
frame/mount-seam
Sep 5, 2026
Merged

The app-mount seam: visor/frame/mount.ts, every caller moved onto it#147
lannbot merged 1 commit into
mainfrom
frame/mount-seam

Conversation

@lannbot

@lannbot lannbot commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Step A of #142's design: one function, mountApp, stands a component up on a sandboxed surface and returns the frame, the exports (every method a Promise, serialized through a runner the caller no longer sees), setRoute, suspend/resume, destroy. Implemented over today's internals — a pure refactor. Step B swaps the internals (instance inside the frame, imports over ports) without touching callers.

Callers moved: demo.ts mountApp + mountPanel, solo.ts mountApp, the todomvc spike's frame kind. createFrameBackend has one caller left (mount.ts).

Two options beyond the minimal seam, each because a caller already had the semantic (reasoning at the declaration): signalmountPanel tears down in-flight mounts and awaits completion; afterEvent — the panel's live binding re-reads destination() after each event (#22 rule 2).

Gates: deno check both consumers; check-invariants.sh 9/9 (now scanning mount.ts); spikes/todomvc e2e 5/5; demo e2e 36/36, zero scenario edits (run locally with a linux-arm64 MinIO in engine/.deps — the minio recipe fetches amd64 only, a papercut for another PR).

Automerge armed.

One function stands a component up on a sandboxed surface and hands
back what a visor needs to drive it — the frame element, the exports
(every method a Promise, serialized through the runner the caller no
longer sees), setRoute, suspend/resume, destroy. Implemented over
today's internals, semantics unchanged: createFrameBackend,
createSurface, instantiate and createRunner are now named in exactly
one place.

Why now: #142 validated moving the wasm instance INTO the frame
(surface bound to the frame's real DOM; every other import a
MessagePort proxy). That is a wholesale change of internals and no
change to what a mount provides, so the internals go behind a seam
first, and the swap that follows touches no caller. It is also the
seam a visor-as-component embedder would call.

Callers: demo.ts mountApp and mountPanel, solo.ts mountApp, and the
todomvc spike's `frame` kind (the three same-realm harness kinds are
untouched). Two options exist because callers already had the
semantics and the plain seam could not express them, each recorded at
its declaration: `signal` — mountPanel tears a mount down while it is
still mounting and awaits the completion, so an in-flight mount must
be abortable and settle (the rejection is the frame backend's own,
which the generation counter already distinguishes from failure);
`afterEvent` — the panel's live binding re-reads destination() after
every pumped event (#22 rule 2). Every generation check in mountPanel
keeps its comment.

check-invariants.sh's frame-seam scans now cover mount.ts. The todomvc
spike's import map gains @polyengine/runtime/embedder as a second name
for the same sibling file, so shared code and the demo spell the
embedder the same way.

Gates: deno check (demo, spikes/todomvc); check-invariants 9/9;
spikes/todomvc e2e 5/5; demo e2e 36/36 with zero scenario edits, run
locally on arm64 with a linux-arm64 MinIO in engine/.deps.
@lannbot
lannbot enabled auto-merge September 5, 2026 19:30
@lannbot
lannbot merged commit 162b93b into main Sep 5, 2026
3 checks passed
@lannbot
lannbot deleted the frame/mount-seam branch September 5, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant