Auto-opt-in published realms with default CardsGrid index#4770
Auto-opt-in published realms with default CardsGrid index#4770habdelra wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an automatic opt-in for published realms whose index.json is the default CardsGrid so the publish flow writes includePrerenderedDefaultRealmIndex: true into the published realm.json before triggering the post-publish reindex, enabling real prerendered isolated HTML instead of a boilerplate placeholder.
Changes:
- Detects whether the published realm’s
index.jsonis an adopted default CardsGrid. - Writes
includePrerenderedDefaultRealmIndex: trueinto the published realm’srealm.jsonon disk when applicable. - Invokes the opt-in write after the directory swap and before enqueueing the reindex job.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7cde395 to
5a2fa61
Compare
…rendered isolated HTML
When publishing a source realm whose `index.json` is the default
CardsGrid (no custom landing card), the publish handler now writes
`includePrerenderedDefaultRealmIndex: true` into the published
realm's `realm.json` before kicking off the post-publish reindex.
Why: anonymous visitors of a published realm's homepage hit the SSR
injection path (`retrieveIsolatedHTML` → `injectIsolatedHTML` in
`server.ts`). Without the opt-in, the host's render route substitutes
a boilerplate placeholder for the realm-index card's isolated HTML
and the SSR shell would inject the placeholder instead of a real
grid view. Anonymous visitors would see the placeholder until JS
boots and re-renders.
By setting the field on the published snapshot specifically (not the
source realm), we keep two properties:
1. Source realms continue to skip the (expensive) realm-index
isolated render — owners and editors who view the source-realm
homepage already have JS loaded and don't need SSR.
2. Published realms get the full isolated render automatically,
without operators having to remember to flip the realm.json field
by hand.
Detection is a structural read of the published realm's
`index.json`: if its `data.meta.adoptsFrom` is exactly
`{ module: 'https://cardstack.com/base/cards-grid', name: 'CardsGrid' }`
the helper sets the field. A published realm that has customised its
index to a different CardDef is left alone (its isolated render is
presumably the bespoke landing page the publisher curated).
The write happens after the directory swap (so it lands on the
already-canonical `publishedRealmPath`) and before
`enqueueReindexRealmJob` so the same reindex pass picks up the new
flag through `parseRealmInfo`'s realm.json overlay. No cleanup is
needed on unpublish: `handle-unpublish-realm.ts:159`
(`removeRealmFiles(publishedRealmPath)`) deletes the entire published
directory, including the realm.json carrying the field.
Existing published realms (already on disk before this PR lands) are
unaffected; they keep serving the boilerplate placeholder for the
realm index until someone republishes them from source. No
migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The detection in `ensureRealmIndexBoilerplateOptIn` originally only matched the absolute base-realm URL form (`https://cardstack.com/base/cards-grid`). Cardstack's RRI scheme also supports `@cardstack/base/cards-grid` as a valid module reference once the `@cardstack/base/` prefix is wired as a virtual-network mapping. That prefix isn't registered in production today — only test fixtures register it — so the absolute URL is the canonical form on disk for now, but listing both keeps the detection future-proof. Also tighten the adoptsFrom validation: use `isResolvedCodeRef` from runtime-common instead of an inline structural cast, so the parsed JSON has to actually present a `{ module, name }` shape before we even read its fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per Copilot review: `${e}` on a non-Error throw can produce
`[object Object]` and elides `.message` / `.stack`. Normalize to
`e instanceof Error ? e.message : String(e)` across the three warn
sites in `ensureRealmIndexBoilerplateOptIn` so the log output is
consistent regardless of what was thrown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two tests in publish-unpublish-realm-test.ts: 1. Publishing a realm with the default CardsGrid index (the shape handle-create-realm seeds) → asserts the published realm's realm.json on disk has `includePrerenderedDefaultRealmIndex: true` set by the publish handler. 2. Publishing a realm whose index.json has been replaced with a non-CardsGrid CardDef → asserts the published realm.json does NOT carry the field. Confirms the detection only fires for the default CardsGrid case and leaves bespoke landing pages alone. Both tests reuse the existing `with a publishable source realm` hooks scaffold, so they share the freshly-created source realm setup and the cleanup path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45a6787 to
9be90bf
Compare
|
[Claude Code] Rebased this PR onto the new PR #4769 (which itself now stacks directly on This PR is unchanged in scope — publish handler writes the realm-index opt-in flag for published default-CardsGrid realms so SSR injection works. Files changed: Fresh CI run on |
Host Test Results 1 files ±0 1 suites ±0 1h 46m 37s ⏱️ - 1m 58s Results for commit 18c1e08. ± Comparison against earlier commit e0185da. Realm Server Test Results 1 files ±0 1 suites ±0 13m 13s ⏱️ +36s Results for commit 18c1e08. ± Comparison against earlier commit e0185da. |
…ish-default-index-isolated
… into indexer-perf/publish-default-index-isolated
Summary
Stacked on top of #4769. When publishing a source realm whose
index.jsonis the default CardsGrid (no custom landing card), the publish handler writesincludePrerenderedDefaultRealmIndex: trueinto the published realm'srealm.jsonbefore triggering the post-publish reindex. The reindex picks up the new flag throughparseRealmInfo's existing disk overlay and the host's render route produces a real isolated HTML for the index card instead of the boilerplate placeholder.The write is bound to the published snapshot specifically — source realms are left alone (their owners have JS loaded and don't need SSR for the homepage).
What this depends on
includePrerenderedDefaultRealmIndexfield on RealmConfig /RealmInfoand the boilerplate-substitution logic in the host's render route.Why a structural index.json check
The detection reads
published-realm/index.jsonafter the directory swap and checksdata.meta.adoptsFromfor exactly:{ "module": "https://cardstack.com/base/cards-grid", "name": "CardsGrid" }(or the equivalent
@cardstack/base/cards-gridRRI form once that prefix is registered)If the source realm has customised its index to a different CardDef (a bespoke landing card), we don't set the field. Custom landing pages were curated for a reason — render them, don't replace with a placeholder. The publisher who put work into a custom homepage almost certainly wants the SSR injection to show real content, and there's no upside to skipping that render anyway since fan-out doesn't apply.
Timing
The write happens:
moveSyncintopublishedRealmPath) so it lands on the canonical published path, not the.tmpstaging dir.enqueueReindexRealmJobso the same reindex pass picks up the new flag viaparseRealmInforeadingrealm.jsonfrom disk.Unpublish
No cleanup needed.
packages/realm-server/handlers/handle-unpublish-realm.ts:159runsremoveRealmFiles(publishedRealmPath)which deletes the entire published directory including the realm.json carrying the flag. The DB-side state is tombstoned in the same handler and the registry row is deleted. There's no orphan state to worry about.Backfill
Existing published realms (already on disk before this PR lands) keep serving the boilerplate placeholder for their index until someone republishes them from source. No migration script. Republishing from source after this PR lands picks up the new behaviour automatically.
Test plan
pnpm lint:typesclean in@cardstack/realm-server.pnpm eslintclean forpackages/realm-server/handlers/handle-publish-realm.ts.index.jsonis default CardsGrid → published realm'srealm.jsonon disk containsincludePrerenderedDefaultRealmIndex: true; behaviour matches pre-this-PR.realm.jsondoes NOT contain the field; behaviour matches pre-this-PR.🤖 Generated with Claude Code