Architecture

01. Trace a page load through core

An implementation map from application definitions to pages displayed in the browser.

A page load connects a reusable application definition, request-owned services, and data sent to the browser. These have different lifetimes: compiling Routes does not acquire services, and rendering Flight does not transfer the server's Context to the client.

Find the definition, request, and browser entrypoints

  1. Definition: the default entry, src/entry.effront.tsx, provides the application as its default export. Vite exposes it as @effront/core/application-entry. makeApplication in application/definition.tsx compiles Routes and stores the service Layer without building it.
  2. Request: the host connects the definition to @effront/core/http. workers.ts adapts this native Effect HTTP boundary to Fetch. The portable Vite integration defaults its RSC entry to src/entry.workers.ts, unless the host integration selects another entry.
  3. Browser: client/entry.ts runs browserMain through BrowserRuntime.runMain. It hydrates the document from Flight, not by importing the application's server services.

Follow a request into a rendered document

createFetchHandler reuses a Web handler but supplies a fresh WorkersRequestContext on every call:

Excerpt from packages/core/src/workers.ts
  const handler = HttpEffect.toWebHandler(toHttpEffect(application));
  return (request, env, executionContext) =>
    handler(request, Context.make(WorkersRequestContext, { env, executionContext, request }));

In http.ts, each evaluation of toHttpEffect creates a fresh Layer memo map and builds ServerApplication.httpLayer(application) in the current request Context. The application Layer can read that request's host values during acquisition. Handler reuse therefore does not share application service instances across requests.

  1. Match: server/application.ts registers compiled destinations with Effect HTTP and attaches their services and middleware.
  2. Validate: a GET to a parameterized Page decodes its matched parameters before rendering. A Schema decoding failure returns 404. POST instead prepares and executes a Server Function.
  3. Render: renderRouteTree builds the route tree and FlightRenderer produces its React Flight stream.
  4. Respond: an Accept header exactly equal to FlightMediaType selects Flight. Otherwise, HtmlRenderer loads the SSR entry to turn the same stream into HTML.
  5. Hydrate: client/application.ts loads the initial Flight payload, hydrates the document, and installs refresh and Server Function handling. It installs the client router only when navigationMode is "Client".

Response headers can arrive before rendering finishes. Effect HTTP's Web handler keeps the request Scope alive until a streaming body ends, fails, or is cancelled. Core removes HEAD bodies before that transfer so unread streams cannot retain the Scope. Services borrowed through makeHttpEffect remain owned by the host, which must keep them alive through all response bodies that use them. See request processing for these ownership boundaries.

Locate the code that owns each stage

Paths below are relative to packages/core/src:

  • application/: definition identity, service contracts, and route compilation.
  • http.ts and server/application.ts: request acquisition, HTTP dispatch, middleware, and response selection.
  • rsc/ and the renderers in server/: route-tree contracts, Flight production, and HTML rendering.
  • client/: hydration, navigation, refresh, and Server Function calls.

Execution environments belong to the integrations. packages/vite/src/index.ts configures React/RSC plugins, the application alias, and browser, RSC, and SSR entries. The Cloudflare adapter runs RSC and its child SSR environment in workerd. @effront/server hosts separate RSC and SSR graphs in Node.js or Bun.

Choose the next part of the trace