You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
* Make interactive examples framework-agnostic via a keyed DOM mount
Interactive examples were hard-wired to ascent: `Example.body` is a
`URIO[R & Scope, ascent.ast.UI[R]]`, and the docs client ran that body then
called `AscentApp.mount`. A library rendering through anything else could not
have a live example at all. The concrete casualty was preactile, whose docs
client is a deliberate `ZIO.never` stub.
An interactive example is now a keyed DOM mount. `SiteBuilder` stamps
`data-specular-mount="<key>"` on each interactive example's wrapper, and the
Scala.js `SpecularClient.mountAll` scans for exactly that attribute and
dispatches on the key. Ascent stops being the mechanism and becomes one
adapter (`Mounter.fromAscent`), wired automatically by `.interactive`, so
preact, laminar, slinky, tyrian and raw DOM are all first-class.
Source panels come from real files, not strings. The mount code lives in a
Scala.js project the JVM DocSpec cannot see, let alone typecheck, so
`exampleDom(key).fromSource(path[, marker])` names a file and the JVM-only
`DomSourceLoader` reads it at build time. Nothing a reader sees was hand
retyped, and a moved file fails the build instead of silently rotting.
Three client behaviors are load-bearing, each a bug in the obvious
implementation: mounters share the page's Scope (a per-mount `ZIO.scoped`
releases listeners at setup), each mount is `.exit`-guarded including defects
(one bad example must not blank the page), and mounts are forked (a
`ZIO.never` mounter must not starve the rest).
`DomExample` is a documented exception to the "only `.assert` makes a test"
asymmetry: it always emits a source-resolution test, because its correctness
depends on the filesystem and a deleted marker has to go red under a plain
test run.
Security posture matches the existing invariants. `DomSourceLoader` is the
read-side counterpart to `SiteBuilder.writeUnder`, one step stronger: reads
are confined to `specularSourceRoot` by real-path comparison, so an absolute
path, a `..` escape, a symlink out of the tree, and a case-only mismatch
(which would pass on macOS and break Linux CI) are all rejected, and files
over 64 KiB are refused rather than inlined. `MountKey` restricts keys to
`[A-Za-z0-9._-]+` and throws at construction, since a key reaches an HTML
attribute; the client error box uses `textContent`, never `innerHTML`.
`validatePages` fails the build on duplicate keys across the whole site,
because the client keys one Map.
Also fix the CI gate this rode in on: `ciVerify` ran plain `test`, which is
`testQuick` on sbt 2 and prints "No tests to run" while proving nothing. It
now runs `testFull`; .github/workflows/ci.yml is regenerated, not hand-edited.
Verified: scalafmtCheckAll green; 52/32/11/94/35 tests pass for
core/coreJS/zioTest/site/docs; docs/specularSite writes the site. In the
browser, the raw-DOM counter mounts and counts up, both pre-existing ascent
interactives still remount, all six mount points across five pages mount with
a clean console, and misspelling a key produces the visible error box plus a
console error.
* Give the live-server spec headroom on a cold CI runner
Switching Verify from `test` to `testFull` surfaced SiteServerSpec's live
server case, which `testQuick` had been skipping in CI. It boots real Netty
plus a ZClient under a 5 s timeout: ~2.5 s warm locally, over budget on a
cold runner. The timeout is a deadlock guard, not a performance bound, so
give it 10 s.