Try bundling base realm - #5653
Draft
backspace wants to merge 9 commits into
Draft
Conversation
Base-realm modules are compiled into the host bundle (eager
import.meta.glob over packages/base) and registered as loader shims at
loader creation, so host imports of @cardstack/base/* resolve to
compiled-in modules instead of fetching realm-server-transpiled source
per loader generation.
Supporting changes the bundling surfaces:
- base modules imported several type-only names as value imports
(babel's TS transform elides them; rolldown resolves them and fails
on the missing runtime exports) — converted to `import type` /
inline `type` in 15 base files
- color.gts imported siblings via @cardstack/base/* specifiers;
converted to relative imports like the rest of the package
- date-fns is a real dependency of base source, declared it (it was
previously satisfied only by the host's runtime shim)
- vite resolve.alias maps @cardstack/boxel-host/{tools,commands}/* to
app/tools/* — the same 1:1 mapping shimHostTools provides at runtime
- https:// imports (currency.gts's esm.run dependency) are externalized
so the browser fetches them at chunk load, as the loader-served
module does
Known semantic shifts, to evaluate on this branch: base modules become
singletons shared across loader generations (loader resets no longer
re-evaluate base module state), and editing base source in a running
realm no longer affects the host runtime without a rebuild.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Preview deploymentsHost Test Results 1 files ± 0 1 suites ±0 1h 26m 14s ⏱️ - 5m 48s Results for commit 54f2640. ± Comparison against earlier commit b654ec1. For more details on these errors, see this check. Realm Server Test Results 1 files ± 0 1 suites ±0 10m 0s ⏱️ - 2m 10s Results for commit 54f2640. ± Comparison against earlier commit b654ec1. For more details on these errors, see this check. |
Base modules discover their loader via import.meta.loader, which only exists when a Loader evaluates the module source. Modules compiled into the host bundle are evaluated by the platform's module system, so every myLoader() there returned undefined and loadCardDef threw "Cannot read properties of undefined (reading 'getVirtualNetwork')" on each base-realm instance deserialize. The prerender renders with the host dist, so base indexing failed, realms never reached readiness, and every stack-booting CI job timed out. - Loader gains setForBundledModules/forBundledModules statics; the host loader-service publishes each loader that becomes its active one (initial, clones, resets) - the seven myLoader() copies in base fall back to Loader.forBundledModules() when import.meta.loader is absent, and throw a descriptive error when neither is available - workspace.gts derived its own module URL from import.meta.url, which a bundler reports as the compiled chunk's URL; it now states its canonical base-realm URL directly - the bundled-base header no longer claims prerender/indexing are unaffected — they render with the host dist, so they see the bundled base too Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bundled base modules were registered per loader by the loader service,
so loaders constructed anywhere else — the test-realm adapter's loader,
in-browser indexing loaders — fetched base modules from the realm server
and evaluated a second copy of card-api. Card instances built by one
copy fail field lookups performed by the other ("the card Object does
not have a field ..."), which is what most of the host test failures
reduced to.
Registration now happens once, on the virtual network (network service,
right after the @cardstack/base realm mapping it resolves against). The
loader's module-fetch path consults the network's shim registry after
its own per-loader shims, so every loader sharing the network serves
the same compiled-in modules. The lookup lives in the module-fetch path
rather than the network's fetch handlers deliberately: base-realm URLs
also serve card-instance documents at extensionless URLs, and only the
module path knows a request is for a module.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
base declared ^0.7.0 while the host uses ^0.8.0, so pnpm installed both versions and the bundled host build resolved base's imports to its own 0.7.1 copy. Two copies mean two context registries: providers rendered from base components registered with the copy whose glimmer integration the app never booted, so consumers found no provided value and fell back to defaults. Concretely, DefaultFormatsConsumer read 'isolated' instead of the provided child format, and an unset linksTo then rendered DefaultCardDefTemplate with an undefined @model, crashing every indexing render of a card with links ("Cannot read properties of undefined (reading 'constructor')"). Under loader-served operation the mismatch was invisible — the runtime shim always supplied the host's copy regardless of what base declared. Bundling makes build-time resolution the binding one, so base's declared range now matches what the shim provides at runtime. With the ranges aligned pnpm dedupes to a single 0.8.0 install. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
card-serialization listed Loader in its import-type block, so the transpiled module had no runtime binding for it and myLoader's Loader.forBundledModules() fallback threw "ReferenceError: Loader is not defined" — every bundled deserialize through cardClassFromResource failed, which broke indexing of any card with linked fixtures (the operator-mode links/basics/ui failures). base has no type-check step, so nothing caught the type-only import being used as a value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base card components fire a _types fetch against the mock test realm as soon as they render; with base compiled into the host bundle that render can precede the test-realm service worker's interception, so the fetch escapes to the real network and fails the shard as "Global error: Uncaught TypeError: Failed to fetch". Match that form in the shard retry pattern — the race is rare per shard, so the second pass lands. The durable fix is queuing test-realm fetches in the harness until realm registration completes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…orms Two fixes for the remaining bundled-base test failures: The test fetch wrapper now answers requests to registered test realms via realm.maybeHandle instead of dispatching them to the network. The service-worker relay that otherwise serves these URLs only intercepts once the worker controls the page and its per-module activation is acked; a card component that renders before that window closes (which bundled base modules do — no module-fetch latency) fired fetches that escaped to the real network and failed shards as "Global error: Uncaught TypeError: Failed to fetch". The registry is populated at realm construction, so the in-page path has no such window. Bundled base modules now register under the canonical https://cardstack.com/base/ form as well as the RRI form. resolveImport passes URL-form identifiers through unchanged (URL-to-URL mapping happens at the network's fetch boundary, which shim lookup precedes), so canonical-form imports missed the shim and fell through to a network fetch that evaluated a second copy of the module, splitting def identity — the serializeFileDef adoptsFrom mismatch and skill-state failures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Registering each bundled module under the canonical https://cardstack.com/base/ form as well made a def's identified module URL depend on import order: loaders capture export identities under whichever identifier form they fetched first, so flows that compare or serialize refs (skill and command attachment, file choosers) saw mismatched identities. The ai-assistant-panel, mini-file-chooser, and card-basics failures bisect to exactly that dual registration. Canonical-form imports therefore fall through to a network fetch that evaluates a second module copy — a known, narrower gap (a handful of commands/* imports) documented at the registration site. The durable fix is normalizing identifiers through the network's URL mappings in the loader's module-fetch path so both forms converge on one module state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three fixes for the def-identity failures that remained with base compiled into the host bundle: - The loader folds virtual-alias URL identifiers (e.g. https://cardstack.com/base/…) onto their mapped real URL when resolving imports, so both spellings of a module converge on one module-state entry, one shim lookup, and one captured export identity. resolveImport alone passes full URLs through, so a virtual-alias import previously keyed separate module state — and, since virtual forms miss the shim registry, fetched and evaluated a second copy. Non-URL identifiers pass through untouched (mapURL constructs a URL). - Loaders replay the virtual network's sync-shim inventory through identity capture, in registration order, before any individual shim's own capture. Loader-evaluated modules got declaring-module-first identity from dependency-first evaluation; shims carry no dependency chain, so a loader whose first shim load was a re-exporter (file-api re-exports FileDef from card-api) captured identities under the re-exporter. Registration order stands in for dependency order: externals (runtime-common, boxel-ui) register before base, and within base card-api and cards-grid — the declaring modules for the def classes serialization identifies — register ahead of the rest. - code-ref.gts validated code refs with a bare dynamic import(module). Inside loader-evaluated modules the AMD transpile rewrites that to a loader-mediated import; compiled-in modules must route through the loader explicitly, since the specifier is a runtime realm URL only a Loader can resolve. Verified locally against the full service stack: serializeFileDef 7/7, update-room-skills 9/9, card-basics 101/101, host command schema generation 92/92, ai-assistant-panel skills clean, markdown skill search 3/3; Store, file-attachment, and serialization stay green. Remaining known residue is the dependency/reference-shape class in realm-indexing assertions (shimmed modules carry no consumed-module chains). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.