Skip to content

Store prerendered calls as the live answer, so server components prerender - #11

Merged
ryansolid merged 5 commits into
mainfrom
claude/project-thread-4achkk
Oct 20, 2026
Merged

ryansolid merged 5 commits into
mainfrom
claude/project-thread-4achkk

Conversation

@ryansolid

@ryansolid ryansolid commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Requested by Ryan · project thread

Before: a server component wrapped in prerendered() (return props => <jsx/>) was saved as an Internal Server Error artifact, because the integration encoded the returned render function itself. Client navigation to it crashed in both static and hybrid builds. Hybrid never fell back to the server, since the bad artifact (or a host's 200 fallback page) didn't look like a miss. Page load also refetched every prerendered read the server had already rendered.

After: the artifact is exactly what the app's server-function endpoint answers for the call's GET address, status and headers included. Server components, codec values and plain JSON all round-trip. Page load makes no request, and navigation fetches only the artifact. In hybrid mode, a missing artifact falls back to the live server through the app's own transport fetch, even when the host answers with a 200 page. Artifacts are versioned by build, so a CDN never serves the previous deploy's answer. The app's transport fetch is never displaced.

How:

  • prerender-crawler
    • PrerenderContext.fetch(input) sends a request through the crawl's own transport, paced like a page, and returns the raw response.
    • The new buildId option reaches integrations as context.buildId. The Vite plugin mints one per process and defines it as import.meta.env.PRERENDER_BUILD. It is per process rather than per plugin instance because a build can evaluate the config more than once.
  • @solidjs/prerender integration
    • The sink records each call's GET address (serverFunctionUrl, resolved in the app's realm).
    • After the crawl, it replays each address through context.fetch and stores the response as _static/<build>/<label>.<key>.txt: a marker line with status and headers, followed by the body bytes.
    • Each captured call therefore runs once more at the end of the build.
  • @solidjs/prerender client
    • prerendered() returns the runtime's own GET reference and declares its own send: withMeta(fn, { fetch }), from feat(web): a server function reference can declare its own client send solid#4170 (released in rc.16). The send maps a prerendered call's GET address to its artifact and rebuilds the Response, and passes everything else to next, the app's send.
    • Keys are derived synchronously (a small SHA-256, checked against crypto.subtle), so the lookup reaches fetch in the same tick and hydration adoption keeps working.
  • Breaking (0.x):
    • New artifact format, extension and directory.
    • The codec option on serverFunctions() is removed.
    • @solidjs/web must be >=2.0.0-rc.16.
    • The SSG example moves to rc.16 and router next.41.

Verified:

  • Unit tests on rc.16: crawler 62, Solid 39, all passing.
  • The SSG example on rc.16, with nothing wired by hand, in a browser:
    • A direct load of a post or the posts list makes no request beyond the page.
    • Client navigation fetches only _static/<build>/ artifacts, with no errors.
  • A server-component route app built on solid next, checked earlier in this PR:
    • In static mode, the load makes no requests and navigation renders the baked frame.
    • In hybrid mode, navigation falls back to /_server/data when the artifact is missing.

Not covered: the hybrid server still runs prerendered() functions live during SSR rather than reading the artifacts.


Generated by Claude Code

…s prerender

The integration now replays each captured call's GET address through the
crawl (new PrerenderContext.fetch) and stores the endpoint's response,
headers included, instead of encoding the returned value itself. The
client keeps the runtime's own reference and answers the send from the
artifact through a transport fetch (staticFetch), so hydration adoption,
server-component interception and decoding stay the runtime's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkjGmX4hAoiU9VhXNt7j3E
@ryansolid ryansolid self-assigned this Oct 20, 2026
@socket-security

socket-security Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​solidjs/​web@​2.0.0-rc.6 ⏵ 2.0.0-rc.16100 +110083 +197100
Updatedfilesystem-routing@​0.2.1 ⏵ 0.4.091 +14100100 +192 +2100
Updatedsolid-js@​2.0.0-rc.6 ⏵ 2.0.0-rc.16100 +110095 +195 -1100
Updated@​solidjs/​vite-plugin@​3.0.0-next.39 ⏵ 3.0.0-next.5098 +1100100 +197 +2100
Updated@​solidjs/​router@​2.0.0-next.21 ⏵ 2.0.0-next.41100 +1100100 +197 +1100

View full report

@socket-security

socket-security Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Priority Alert  (click "▶" to expand/collapse) Action
Low priority
Low adoption: npm @solidjs/compiler-darwin-x64

Location: Package overview

From: pnpm-lock.yaml → npm/@solidjs/vite-plugin@3.0.0-next.50 → npm/@solidjs/compiler-darwin-x64@2.0.0-rc.15

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@solidjs/compiler-darwin-x64@2.0.0-rc.15. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn

View full report

Artifacts lived at the same url in every build, so a CDN or browser cache
could serve the previous deploy's answer. The crawl now takes a buildId
(the Vite plugin mints one per process and defines it as
import.meta.env.PRERENDER_BUILD); integrations get it as context.buildId and
@solidjs/prerender writes _static/<build>/<label>.<key>.txt.

prerendered() installed staticFetch as the transport fetch, so it and an
app's own configureServerFunctionsClient({ fetch }) replaced each other by
order. On a runtime with wrapServerFunctionsFetch (solidjs/solid#4170) it now
installs a send layer instead, which composes in either order; older
runtimes keep the previous behavior.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkjGmX4hAoiU9VhXNt7j3E
Instead of a global send layer, prerendered() declares the artifact send on
its reference (withMeta(fn, { fetch }), solidjs/solid#4170). The runtime
calls it for that function's requests with the app's transport fetch as
next, so the app's fetch is never displaced and carries the hybrid
fallback. Runtimes without reference sends wire staticFetch() by hand; on
current ones a wired staticFetch passes through, so artifacts are looked up
once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkjGmX4hAoiU9VhXNt7j3E
rc.16 ships reference sends (solidjs/solid#4170), so prerendered()
serves artifacts on its own. The hand-wired staticFetch transport only
existed for older runtimes; remove it along with its server stub and
the test probe. Bump the SSG example and dev deps to rc.16 / router
next.41.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkjGmX4hAoiU9VhXNt7j3E
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkjGmX4hAoiU9VhXNt7j3E
@ryansolid
ryansolid merged commit 4954460 into main Oct 20, 2026
4 checks passed
@ryansolid
ryansolid deleted the claude/project-thread-4achkk branch October 20, 2026 22:29
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.

2 participants