Build · server capability boundary
Loaders and server actions
Presolve can execute two compiler-proven server capabilities in its generated Node host: route-owned loaders and Form-bound server actions. Both begin in ordinary application source, but only the exact package export selected by the compiler enters the server bundle.
The compiler proves component ownership, TypeScript symbol identity, package capability, request shape, response codec, cancellation, and resume behavior. The Node adapter bundles that coordinate with Vite and verifies the generated registry digest before import. It never evaluates a route component on the server.
Load route data
A loader is a route-owned Resource<Data, Error>. Its handler must be one direct call to a named package import with canonical RouteParameters and AbortSignal parameters.
import { Component, loader, type Resource, type RouteParameters } from "presolve";import { loadPost } from "post-service";type PostRecord = { slug: string; title: string };type NotFound = { code: "not_found" };export class Post extends Component {post: Resource<PostRecord, NotFound> = loader<PostRecord, NotFound>(async (params: RouteParameters, signal: AbortSignal) => loadPost(params, signal),);render() {return <article>{this.post.data?.title ?? "Loading"}</article>;}}The compiler publishes exact data and error codecs, decoded route-parameter indexes, cache-key facts, and the Resource activation that receives the first value. The Node host validates the result and injects one script-safe generation-one bootstrap before the route runtime. Server package code never enters the browser.
Submit to a server action
A server action is selected by a canonical Form submission. Use serialization: "form-data" and make the submit body one direct call to the admitted named import.
import { Component, defineForm, field, required } from "presolve";import { saveContact } from "contact-service";export class Contact extends Component {contact = defineForm({serialization: "form-data",fields: { email: field({ initial: "", validate: [required()] }) },submit: async ({ formData, signal }) => saveContact(formData, signal),});}The compiler gives every admitted action one immutable /_presolve/actions/… coordinate. The browser Form host owns validation, duplicate suppression, serialization, reset cancellation, and submission state. The Node host accepts only POST, same-origin requests, supported form media types, and bodies up to 8 MiB, then invokes exactly (formData, signal).
Declare the package capability
TypeScript shape alone does not grant server authority. A package must publish an integrity-qualified Presolve capability naming the runtime module, exact export, execution boundary, cancellation, result family, and cache or failure policy. See third-party package contracts for the manifest format and trust model.
route_loader(RouteParameters, AbortSignal) -> Promise<RouteLoaderResult>, server/shared Resource endpoint, typed failure, reload resume, and explicit public, private, or no-store caching.server_action(FormData, AbortSignal) -> Promise<ServerActionResult>, cold fallback, typed failure, and one declared JSON or redirect response family.What the generated Node host guarantees
- Both Vite-built named-export registries are SHA-256 inventoried and verified before import.
- Loader parameters are strictly decoded from compiler-issued route indexes; malformed or foreign routes cannot reach package code.
- Loader success and typed failure pass the compiler-issued data/error codecs before they enter a Resource.
- Public, private, and no-store loader policies use distinct exact cache keys; private responses vary on authorization and cookies.
- Request disconnect and host shutdown abort active work. Pending server work is never serialized or replayed during resume.
- Unknown exceptions and response mismatches become stable executor errors without exposing package internals.
Cloudflare Static Assets boundary
The Cloudflare static adapter rejects routes that require either capability. Use presolve deploy node --prepare for these routes. This is a provider boundary, not a source workaround: both adapters consume the same compiler products, but only the Node projection currently carries the proven executor.
Inspect the plan
pnpm exec presolve checkpnpm exec presolve buildpnpm exec presolve explain --capabilities --format humanpnpm exec presolve deploy node --prepare