rebar3_asobi_console (asobi v0.84.0)
View Sourcerebar3 asobi console - compose an operator console out of core's screens and
every installed extension's.
rebar3 asobi console # build the composed bundle
rebar3 asobi console --dev # Vite dev server, proxied at a running node
An extension owns its operator UI the way it owns its migrations and its RPC
handlers: as source in its own application, under priv/console, with
index.jsx as the entry. Nothing is registered and nothing is declared -
discovery is the file being there, the same way migrations and schemas are
discovered rather than declared.
Why this is a build step and not a runtime load
The console's CSP is script-src 'nonce-<per-response>' with no host source
(asobi_console_csp). A nonce does not propagate to a module's static
imports, so a second chunk fetched at runtime is refused by the browser -
which is why console/vite.config.js pins a single chunk in the first place.
Every runtime plugin mechanism there is (dynamic import(), module
federation, an import map) needs either 'strict-dynamic' or a host source in
script-src, and the console's strongest property is that even an injected
<script src> pointing at its own origin is refused.
So the composition happens where it costs nothing: at build time, producing one chunk exactly as before, just a larger one. The policy is not touched.
What it does
- Runs
asobi_extensions:check/0- the same validationrebar3 asobi checkruns - so a broken extension set fails here rather than after an npm install. - Copies asobi's
console/into_build/asobi_console, which is where everything generated lives. Neither the asobi dependency nor, when asobi is the project itself, its source tree is written to. - Symlinks each extension's
priv/consoleunderextensions/<name>, and generatessrc/registry.generated.jsimporting each one. npm cifrom asobi's own committed lockfile. Extensions have nopackage.json, nonode_modulesand no React of their own: they ship JSX source, so the whole composed bundle resolves one copy of everything.vite buildinto theconsole_bundle_app'spriv/console.
Symlinks rather than absolute paths in the generated imports, with
resolve.preserveSymlinks on: it keeps every source file under Vite's root,
which is what makes the dev server able to serve and hot-reload an extension's
JSX. Without preserveSymlinks Vite resolves through to the real path, the
file is outside the root again, and the dev server refuses it.
What the host has to set
In rebar.config, the application the built bundle is written into:
{asobi, [{console_bundle_app, game_console}]}.and the same name in sys.config, which is what makes the node serve it:
{asobi, [{console, true}, {console_bundle_app, game_console}]}.Two places because they answer two questions - where the build writes, and
what the node reads - and a release can be built on one machine and configured
on another. asobi_console refuses a console_bundle_app that is not in
the release rather than falling back to asobi's own bundle, so the two
disagreeing is loud.
See guides/console-extensions.md.