Skip to content

Lightweight render.types route; drop double serializeCard+searchDoc#4928

Open
habdelra wants to merge 1 commit into
cs-11208-computed-performance-improvementsfrom
worktree-cs-11237-types-only-endpoint
Open

Lightweight render.types route; drop double serializeCard+searchDoc#4928
habdelra wants to merge 1 commit into
cs-11208-computed-performance-improvementsfrom
worktree-cs-11237-types-only-endpoint

Conversation

@habdelra
Copy link
Copy Markdown
Contributor

Stacked on #4926 (parent ticket CS-11208). Linear: CS-11237.

Summary

  • The prerender runner used to call /render/.../meta twice per card. The first call existed only to read the ancestor type chain so the fitted/embedded format renders could be driven; the second captured the real serialized + searchDoc + deps + displayNames after those renders marked linked fields as "used".
  • The first call paid for a full serializeCard({includeComputeds:true}) + searchDoc(instance) walk every time, even though the runner consumed only meta.types.
  • This PR introduces a sibling host route /render/:id/:nonce/:options/types that returns only the type chain (getClass + getTypes). The runner calls it for the first pass and keeps /meta for the final pass.
  • The final render.meta still runs after the fitted/embedded ancestor renders, preserving the isUsed-via-non-isolated-render contract that the non-isolated formats render linked fields and those links appear in search doc test in packages/realm-server/tests/prerendering-test.ts covers.

Why this is safe

The previous attempt in the parent PR collapsed both calls into one and broke that test: a single render.meta before the embedded/fitted renders meant linksTo / linksToMany fields had not yet been marked "used", so the search doc walk missed them. This change does the opposite — it keeps the second render.meta exactly where it was (after the format renders) and replaces only the cheap first call with a route that returns the type list and nothing else.

No new module-state in card-api. No changes required in user realms.

Expected impact

renderElapsedMs per card prerender drops by roughly the wall-clock of one serializeCard + searchDoc on that card type — ~60–80ms for the heaviest cards measured under the parent ticket. Compounds across from-scratch reindex of a multi-file realm.

Test plan

  • CI green; in particular non-isolated formats render linked fields and those links appear in search doc in packages/realm-server/tests/prerendering-test.ts passes.
  • Local: trigger a from-scratch reindex against a representative realm and check renderElapsedMs distribution shifts down vs. parent branch.

🤖 Generated with Claude Code

The prerender runner needed the ancestor type chain before driving the
fitted/embedded format renders, but the only way to obtain it was a
full /render/.../meta call that also paid for serializeCard +
searchDoc — work the runner threw away. The fitted/embedded renders
are what mark linksTo / linksToMany fields as "used", so a single
render.meta after them produces the correct search doc; the earlier
call was structurally wasted per-card wall-clock on the host's main
thread.

Add a sibling /render/.../types route that returns only the type
chain (getClass + getTypes), and call it for the first pass. The
final render.meta still runs after the format renders, preserving
the isUsed-via-non-isolated-render contract that
`non-isolated formats render linked fields and those links appear in
search doc` covers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

Preview deployments

Host Test Results

    1 files      1 suites   1h 48m 38s ⏱️
2 712 tests 2 697 ✅ 15 💤 0 ❌
2 731 runs  2 716 ✅ 15 💤 0 ❌

Results for commit 4910004.

Realm Server Test Results

    1 files      1 suites   11m 16s ⏱️
1 480 tests 1 480 ✅ 0 💤 0 ❌
1 571 runs  1 571 ✅ 0 💤 0 ❌

Results for commit 4910004.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces per-card prerender overhead by introducing a lightweight host route that returns only a card’s ancestor type chain, avoiding an extra serializeCard + searchDoc traversal that was previously done solely to obtain meta.types.

Changes:

  • Add /render/:id/:nonce/:options/types (host) to return only the ancestor type chain.
  • Update the realm-server prerender runner to call render.types for the first pass and keep a single render.meta call for the final (post-ancestor-render) metadata capture.
  • Introduce a shared PrerenderTypes payload type in @cardstack/runtime-common.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/runtime-common/index.ts Adds the PrerenderTypes interface for the new lightweight prerender payload.
packages/realm-server/prerender/utils.ts Adds renderTypes() capture helper to drive the new /types route.
packages/realm-server/prerender/render-runner.ts Switches the runner’s first pass from render.meta (types) to render.types.
packages/host/app/templates/render/types.gts Adds a template that JSON-prints the render.types model.
packages/host/app/routes/render/types.ts Adds the new host route that computes and returns the ancestor type chain only.
packages/host/app/router.ts Registers the new render.types route.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/realm-server/prerender/render-runner.ts
Comment thread packages/realm-server/prerender/utils.ts
@habdelra habdelra requested a review from a team May 21, 2026 21:00
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.

3 participants