Skip to content

@lastshotlabs/slingshot-ssr

npm install @lastshotlabs/slingshot-ssr

Build the after() scheduler for a single request.

Returns a function that, when called from within a load function, enqueues a callback in the current request’s after-queue. Pass the returned function as shell._after so that buildLoadContext() in the renderer can expose it as ctx.after().

On edge runtimes, returns a no-op function with a console warning.

function buildAfterFn(): (callback: () => void | Promise<void>) => void

Source: packages/slingshot-ssr/src/after/index.ts

Build a synthetic route chain for a manifest-backed page declaration.

function buildPageChain(declaration: ResolvedPageDeclaration, params: Record<string, string>, url: URL, query: Record<string, string>,): SsrRouteChain

Source: packages/slingshot-ssr/src/pageResolver.ts

Compile manifest page declarations into a route table.

The returned table is sorted by specificity so static routes win over dynamic ones, mirroring the file-system SSR resolver.

function buildPageRouteTable(pages: Readonly<Record<string, PageDeclaration>>, entityConfigs: ReadonlyMap<string, ResolvedEntityConfig>,): readonly ResolvedPageDeclaration[]

Source: packages/slingshot-ssr/src/pageResolver.ts

Clear the route-module import cache. Intended for tests and dev-mode watchers that need to pick up a freshly-edited route file.

function clearRouteModuleCache(): void

Source: packages/slingshot-ssr/src/routeExecution.ts

Create a circuit breaker wrapping an external dependency.

function createCircuitBreaker(options: Partial<CircuitBreakerOptions> = {}): CircuitBreaker

Source: packages/slingshot-ssr/src/circuitBreaker.ts

Build the default file-based route source.

This is what createSsrPackage uses when no explicit routeSource is configured. Existing apps that pass serverRoutesDir to createSsrPackage get this source behind the scenes — no migration needed.

function createFileBasedRouteSource(config: FileBasedRouteSourceConfig): SsrRouteSource

Source: packages/slingshot-ssr/src/routeSource/fileBased.ts

Create the Slingshot SSR package.

Registers SSR middleware, server action routes, metadata routes, and optional entity-driven page support when config.pages is supplied.

The package itself owns no entities — page routes are driven imperatively from setupMiddleware/setupRoutes/setupPost lifecycle hooks, so the definePackage input has empty entities: [].

function createSsrPackage(rawConfig: SsrPluginConfig): SlingshotPackageDefinition

Source: packages/slingshot-ssr/src/plugin.ts

Default maximum byte length of a single decoded route param.

Per spec — pluggable through SsrPluginConfig.maxRouteParamBytes. Requests whose decoded params exceed this cap are rejected with HTTP 414.

Source: packages/slingshot-ssr/src/resolver.ts

The name of the HTTP cookie used to signal draft mode.

The cookie is HttpOnly, Secure, SameSite=Lax, Path=/. Its presence (with any non-empty value) indicates draft mode is active for the request.

Source: packages/slingshot-ssr/src/draft/index.ts

Access draft mode state for the current request.

Returns an object with:

  • isEnabled — whether the request carries the draft mode cookie
  • enable() — sets the draft cookie on the response (HttpOnly, Secure, SameSite=Lax, Path=/)
  • disable() — clears the draft cookie by expiring it in the past

Must be called from within a withDraftContext() wrapper. Calling outside of a draft context (e.g. from a non-SSR route) throws a descriptive error. In edge runtimes where AsyncLocalStorage is unavailable, draftMode() returns a permanent no-op (isEnabled: false) instead of throwing.

function draftMode(): void

Source: packages/slingshot-ssr/src/draft/index.ts

Run all after-callbacks registered for the current request.

Called by the SSR middleware after the response body stream has been flushed. Callbacks are executed in registration order. Errors in individual callbacks are caught and logged — they never propagate to the caller or affect other callbacks in the queue.

Safe to call outside an after context (returns immediately).

async function drainAfterCallbacks(): Promise<void>

Source: packages/slingshot-ssr/src/after/index.ts

Generate static param sets for entity-driven page declarations.

Static list, create-form, and dashboard pages emit a single empty param set when their path contains no dynamic segments. Detail and edit-form pages enumerate entity records and map route param names to record field names.

async function generatePageStaticParams(declaration: ResolvedPageDeclaration, adapters: Readonly<Record<string, PageEntityAdapter>>,): Promise<StaticParamSet[]>

Source: packages/slingshot-ssr/src/static-params/pageStaticParams.ts

Initialise the route tree for a given server routes directory.

Scans the directory and caches the result. Safe to call multiple times — returns the cached result if already initialised.

Called automatically:

  • by the SSR plugin’s setupMiddleware at request time
  • by slingshot-ssg’s renderer before resolveRouteChain at build time

Tooling that calls resolveRouteChain directly (e.g. custom build scripts, dev tooling, tests) must call this first to populate the cache.

function initRouteTree(serverRoutesDir: string): void

Source: packages/slingshot-ssr/src/resolver.ts

Invalidate the cached route tree for a directory.

Called by the dev-mode file watcher when files are added, changed, or removed. After invalidation, the next call to initRouteTree will re-scan.

Test code can call this between cases to ensure a fresh resolution.

function invalidateRouteTree(serverRoutesDir: string): void

Source: packages/slingshot-ssr/src/resolver.ts

Returns true when the incoming request carries the draft mode cookie.

Used by the ISR middleware to decide whether to bypass the cache. Does not require a draft context to be active — reads directly from the Hono context.

function isDraftRequest(c: HonoContext): boolean

Source: packages/slingshot-ssr/src/draft/index.ts

Returns true when the loader result signals forbidden (403). @internal

function isForbidden(result: SsrLoaderReturn): result is SsrForbiddenResult

Source: packages/slingshot-ssr/src/types.ts

Returns true when the loader result is a successful load with a data field.

function isLoadResult(result: SsrLoaderReturn): result is SsrLoadResult

Source: packages/slingshot-ssr/src/types.ts

Returns true when the loader result signals not-found.

function isNotFound(result: SsrLoaderReturn): result is SsrNotFoundResult

Source: packages/slingshot-ssr/src/types.ts

Returns true when the loader result is a redirect.

function isRedirect(result: SsrLoaderReturn): result is SsrRedirectResult

Source: packages/slingshot-ssr/src/types.ts

Returns true when err is a RouteParamTooLargeError. Uses a name check so the guard works across module boundaries / duplicate class loads.

function isRouteParamTooLargeError(err: unknown): err is RouteParamTooLargeError

Source: packages/slingshot-ssr/src/resolver.ts

Returns true when the loader result signals unauthorized (401). @internal

function isUnauthorized(result: SsrLoaderReturn): result is SsrUnauthorizedResult

Source: packages/slingshot-ssr/src/types.ts

PPR build-time pre-rendering.

Call this after the Vite build to pre-compute static shells for all PPR routes. Each route’s element is rendered via extractPprShell() (from snapshot/ssr); successful shells are stored in cache keyed by route path.

Usage pattern:

import { prerenderPprShells } from '@lastshotlabs/slingshot-ssr/ppr'
import { createPprCache, extractPprShell } from '@lastshotlabs/snapshot/ssr'
const pprCache = createPprCache()
await prerenderPprShells(
[
{ path: '/dashboard', element: <DashboardPage loaderData={...} /> },
{ path: '/home', element: <HomePage loaderData={...} /> },
],
pprCache,
// Pass extractPprShell from snapshot/ssr — avoids circular dep
extractPprShell,
)

Why pass extractPprShell as a parameter? slingshot-ssr must not import from @lastshotlabs/snapshot to avoid a circular package dependency. The consumer app imports both packages and passes extractPprShell as a callback — structural typing ensures compatibility.

async function prerenderPprShells(routes: readonly PprRouteDescriptor[], cache: PprCacheShape, extractShell: (element: ReactElement) => Promise<PprShellShape>,): Promise<void>

Source: packages/slingshot-ssr/src/ppr/index.ts

Register metadata file routes on a Hono application.

Checks for convention files in the server/ directory (the parent of serverRoutesDir) and registers GET handlers for each that exists:

  • server/sitemap.tsGET /sitemap.xml
  • server/robots.tsGET /robots.txt
  • server/manifest.tsGET /manifest.webmanifest and GET /manifest.json

Handlers dynamically import the convention file and call its default export. Each handler must be registered before SSR middleware so it takes priority.

Routes are only registered when the corresponding file exists — if the file does not exist, the route is not registered and the request falls through to SSR or the SPA.

function registerMetadataRoutes(app: unknown, serverRoutesDir: string): void

Source: packages/slingshot-ssr/src/metadata/index.ts

Register metadata file routes on a Hono application, scanning a directory directly for the convention files (sitemap.ts, robots.ts, manifest.ts).

Unlike registerMetadataRoutes, which derives the scan directory from a server/routes path, this takes the metadata directory itself — the right entry point when route discovery doesn’t use the file-based layout (e.g. a TanStack routeSource) but metadata files still live in server/.

Registered routes are logged at info level for boot-time visibility; when no convention file exists, nothing is registered and requests fall through to SSR or the SPA.

function registerMetadataRoutesFromDir(app: unknown, metadataDir: string): void

Source: packages/slingshot-ssr/src/metadata/index.ts

Resolve the global server middleware file path for a given routes directory.

Looks for middleware.ts adjacent to serverRoutesDir (i.e. in the parent server/ directory). This is the same check performed inside resolveRouteChain, but exposed separately so the middleware handler can run global middleware even when no page route matched — enabling redirects, rewrites, and auth guards for unmatched URLs without requiring a file-based page.

function resolveGlobalMiddlewarePath(serverRoutesDir: string): string | null

Source: packages/slingshot-ssr/src/resolver.ts

Resolve a pathname against a compiled page route table.

function resolvePageDeclaration(pathname: string, routeTable: readonly ResolvedPageDeclaration[],): void

Source: packages/slingshot-ssr/src/pageResolver.ts

Resolve and execute the generated loader for a page declaration.

async function resolvePageLoader(declaration: ResolvedPageDeclaration, params: Readonly<Record<string, string>>, query: Readonly<Record<string, string>>, adapters: Readonly<Record<string, PageEntityAdapter>>, entityConfigs: ReadonlyMap<string, ResolvedEntityConfig>, navigation?: NavigationConfig,): Promise<PageLoaderResult>

Source: packages/slingshot-ssr/src/pageLoaders.ts

Resolve a URL pathname to a full route chain including all ancestor layout.ts files, parallel @slot directories, interception routes, and middleware.

Returns null if no matching page route is found.

Layout detection (Phase 25): Walks up from the matched file’s directory toward serverRoutesDir, checking for layout.ts or layout/index.ts at each level. Layouts are collected in root-first order.

Parallel routes (Phase 26): Scans the leaf directory for @-prefixed subdirectories and attempts to resolve the pathname within each slot’s tree.

Intercepting routes (Phase 27): When fromPath is provided, checks for (.), (..), and (...) interception directories relative to fromPath’s level and tries to match pathname within them before the direct match.

Middleware (Phase 29): Checks for middleware.ts adjacent to serverRoutesDir (i.e., server/middleware.ts when serverRoutesDir is server/routes).

function resolveRouteChain(pathname: string, serverRoutesDir: string, fromPath?: string, options: ResolveRouteOptions = {},): SsrRouteChain | null

Source: packages/slingshot-ssr/src/resolver.ts

Execute fn with retry on failure.

Implements exponential backoff with full jitter between retries. The initial call counts as attempt 1; subsequent retries use the formula:

delay = random(0, baseDelayMs * 2^(attempt-1))
async function retry<T>(fn: () => Promise<T>, options: Partial<RetryOptions> = {},): Promise<T>

Source: packages/slingshot-ssr/src/retry.ts

P-SSR-3: Validate at plugin setup time that every entity referenced by a page declaration has a registered adapter. Without this, missing adapters surface only when the page is requested, returning a 500. Catching at setup fails the plugin init with a descriptive message that includes the offending page route.

function validatePageAdapters(pages: Readonly<Record<string, PageDeclaration>>, adapters: Readonly<Record<string, PageEntityAdapter | undefined>>,): void

Source: packages/slingshot-ssr/src/pageLoaders.ts

Wrap a request handler with a fresh per-request after-callback queue.

All after() calls within fn (including those inside nested load functions) are captured in the same queue and drained when drainAfterCallbacks() is called after the response stream flushes.

On edge runtimes where ALS is unavailable, fn is called directly and after() callbacks are silently discarded.

function withAfterContext<T>(fn: () => Promise<T>): Promise<T>

Source: packages/slingshot-ssr/src/after/index.ts

Wraps a request handler so that draftMode() is available within it.

Called by the SSR middleware around every render invocation so that load functions and route handlers can call draftMode() without receiving the Hono context explicitly.

All code executing within fn() — including transitively called functions — has access to the draft context via the ambient draftMode() function.

function withDraftContext<T>(c: HonoContext, fn: () => T | Promise<T>): Promise<T>

Source: packages/slingshot-ssr/src/draft/index.ts

Capability handle for the ISR invalidators (path and tag invalidation).

Cross-package consumers (server actions, route handlers, peer plugins) resolve it through ctx.capabilities.require(IsrInvalidatorsCap) to invalidate paths and tags on the active ISR cache adapter.

Source: packages/slingshot-ssr/src/public.ts

Provider-owned package contract for slingshot-ssr.

Source: packages/slingshot-ssr/src/public.ts

Error thrown when a page loader cannot resolve the requested entity record.

Source: packages/slingshot-ssr/src/pageLoaders.ts

Thrown by route resolution helpers when a decoded route param exceeds the configured byte cap. The SSR middleware catches this and returns a 414 URI Too Long response without invoking the renderer.

Source: packages/slingshot-ssr/src/resolver.ts

Error thrown when the asset manifest file cannot be read or parsed.

In production mode, createSsrPackage() treats this as a startup error — the server will not start without a valid manifest. Run bun run build before starting the server in production.

Source: packages/slingshot-ssr/src/assets.ts

Circuit breaker contract.

Implementations wrap an external dependency call with failure counting, automatic circuit opening, and recovery probing.

Source: packages/slingshot-ssr/src/circuitBreaker.ts

Options for createCircuitBreaker.

Source: packages/slingshot-ssr/src/circuitBreaker.ts

Escape hatch for renderer-specific custom page rendering.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Options accepted by defineRoute().

The TData generic threads from load through to Page and meta, so TypeScript checks prop types automatically without manual annotations.

Source: packages/slingshot-ssr/src/types.ts

Read-only snapshot of draft mode status for the current request.

Returned by draftMode() and exposed as ctx.draftMode() in load functions.

Source: packages/slingshot-ssr/src/draft/index.ts

Aggregate/stats dashboard page.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Single-record page resolved by primary key or lookup operation.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Entity field metadata passed to renderers.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Create or edit form bound to an entity.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Paginated list/table of entity records.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Entity metadata passed to renderers alongside loaded page data.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Configuration for createFileBasedRouteSource.

Source: packages/slingshot-ssr/src/routeSource/fileBased.ts

Interface for ISR cache backends.

Implement this interface to provide a custom cache store. Two built-in adapters are provided: createMemoryIsrCache() (single-instance) and createRedisIsrCache() (multi-instance/distributed).

All methods are async to allow network-backed implementations (Redis, KV stores).

Source: packages/slingshot-ssr/src/isr/types.ts

A single cached ISR page entry.

Stores the rendered HTML, response headers, and timing metadata needed to implement stale-while-revalidate logic. Both memory and Redis adapters use this shape — Redis serializes it as JSON.

Source: packages/slingshot-ssr/src/isr/types.ts

ISR (Incremental Static Regeneration) configuration for createSsrPackage().

When set, any loader returning revalidate: N causes the rendered HTML to be stored in the configured adapter. Subsequent requests are served from cache until the entry is stale, at which point it is regenerated in the background (stale-while-revalidate).

Source: packages/slingshot-ssr/src/isr/types.ts

Mutable sink for ISR metadata from the renderer back to the middleware.

The middleware creates a plain object {} satisfying this interface, attaches it to SsrShell._isr, and reads revalidate/tags/noStore from it after the renderer returns. The renderer writes to this object after calling load().

Source: packages/slingshot-ssr/src/types.ts

Navigation badge declaration.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Renderer-agnostic shell/navigation configuration.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Single navigation item in the app shell.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Dashboard chart declaration.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Base fields shared by all page declarations.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Section declaration for detail pages.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Per-field form override.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Filter control declaration for entity-list pages.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Result of executing an entity-driven page loader.

Contains the resolved declaration, loaded data, derived entity metadata, and optional navigation/ISR hints for renderer implementations.

Source: packages/slingshot-ssr/src/types.ts

Permission requirement declared directly on a page.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Related-entity section on a detail page.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Dashboard stat-card declaration.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Field-reference page title.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Template-based page title.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Structural equivalent of PprCache from @lastshotlabs/snapshot/ssr.

Passed by the consumer app (which imports from both packages) so that slingshot-ssr never imports from snapshot directly.

Source: packages/slingshot-ssr/src/ppr/index.ts

A PPR-enabled route descriptor passed to prerenderPprShells().

The caller is responsible for constructing the element (the full React tree for this route, including all providers) before calling the build step.

Source: packages/slingshot-ssr/src/ppr/index.ts

A page declaration after route-table compilation and entity resolution.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Per-call options for SsrRouteSource.resolveChain.

Source: packages/slingshot-ssr/src/routeSource/types.ts

Per-call options for SsrRouteSource.resolve.

Source: packages/slingshot-ssr/src/routeSource/types.ts

Options for retry.

Source: packages/slingshot-ssr/src/retry.ts

Configuration for robots.txt generation.

Source: packages/slingshot-ssr/src/metadata/index.ts

Result of executing a file-based route module’s loader and meta.

Source: packages/slingshot-ssr/src/routeExecution.ts

Serializable handler reference used in manifest-backed page declarations.

This mirrors the root manifest handler-ref shape without introducing a runtime dependency from slingshot-ssr back to the root package.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

A single entry in a sitemap response.

Serialised to a <url> element in the sitemap XML output. All fields except url are optional and omitted from the XML when undefined.

Source: packages/slingshot-ssr/src/metadata/index.ts

The renderer contract for slingshot-ssr.

Framework-agnostic. snapshot/ssr provides createReactRenderer() for React apps. Any object with resolve, render, and renderChain matching these signatures is valid — TypeScript verifies compatibility structurally at the consumer’s compile time.

Cross-repo coupling: snapshot/ssr does NOT import this type. The consumer app imports from both packages and TypeScript verifies structural compatibility. This is intentional — no forced dependency between the two packages.

Source: packages/slingshot-ssr/src/types.ts

Cache-control configuration for SSR responses.

Source: packages/slingshot-ssr/src/types.ts

Signal from a server route’s load() that the user lacks permission.

slingshot-ssr responds with 403 Forbidden. Co-locate a forbidden.ts convention file to render a custom UI instead of a plain-text fallback.

Source: packages/slingshot-ssr/src/types.ts

The context object passed to every server route load() and meta() function.

Provides request data and access to the slingshot instance. All data fetching in load() should go through bsCtx — no HTTP round trips needed since the loader runs in the same process as the database.

Source: packages/slingshot-ssr/src/types.ts

Successful load result from a server route’s load() function.

The generic parameter TData types the data field, connecting loader return type to component props via defineRoute(). Defaults to Record<string, unknown> for backwards-compatible untyped usage.

Both data and queryCache must be JSON-serializable — they are embedded in the HTML as dehydrated state for client hydration.

Source: packages/slingshot-ssr/src/types.ts

Head/meta tag configuration returned by a server route’s meta() function.

All string values are HTML-escaped before injection into the document.

Source: packages/slingshot-ssr/src/types.ts

Signal from a server route’s load() that the resource was not found.

slingshot-ssr falls through to the SPA, which renders its own 404 page. The HTTP response will be 200 (the SPA handles the 404 UI).

Source: packages/slingshot-ssr/src/types.ts

Configuration for createSsrPackage().

Source: packages/slingshot-ssr/src/types.ts

A TanStack Query cache entry to pre-seed during SSR.

The queryKey array must match exactly the key used by the corresponding client-side useQuery() hook. On hydration, the client reads this entry from the dehydrated state and skips the network request.

Source: packages/slingshot-ssr/src/types.ts

Signal from a server route’s load() that the client should be redirected.

slingshot-ssr responds with the appropriate HTTP redirect status.

Source: packages/slingshot-ssr/src/types.ts

A resolved layout chain from root to leaf page.

Used by SlingshotSsrRenderer.renderChain() to render nested layouts. When no layout.ts files are found, layouts is empty and the middleware calls renderer.render() instead of renderer.renderChain().

Source: packages/slingshot-ssr/src/types.ts

A resolved server route — the output of the file-based route resolver.

Passed from the resolver to the renderer’s render() method. Contains everything the renderer needs to load data and produce HTML.

Source: packages/slingshot-ssr/src/types.ts

Pluggable route discovery + resolution.

Implementations must be safe to call concurrently. State changes (init, invalidate) happen at well-defined moments — boot and dev-mode file-watcher notifications. Resolution (resolve, resolveChain, resolveGlobalMiddleware) is called on every SSR request and must be sync and side-effect-free.

Source: packages/slingshot-ssr/src/routeSource/types.ts

The HTML tag strings injected into the document head by slingshot-ssr.

Passed to the renderer’s render() method. The renderer is responsible for embedding these in the correct positions in the HTML output.

Standard injection order in <head>:

  1. headTags — title, meta, OG tags
  2. assetTags — hashed <link> and <script> from Vite manifest
  3. Renderer-specific dehydrated state scripts

Source: packages/slingshot-ssr/src/types.ts

Signal from a server route’s load() that the user is not authenticated.

slingshot-ssr responds with 401 Unauthorized. Co-locate an unauthorized.ts convention file to render a custom UI instead of a plain-text fallback.

Source: packages/slingshot-ssr/src/types.ts

A single route that exports generateStaticParams, plus the pre-computed param sets returned by calling it at build time.

routePath uses the file-system-derived URL pattern (e.g. /players/[id]). paramSets is the array returned by the route’s generateStaticParams export.

Source: packages/slingshot-ssr/src/static-params/index.ts

Result returned by CircuitBreaker.execute.

Source: packages/slingshot-ssr/src/circuitBreaker.ts

Circuit breaker state machine states.

Source: packages/slingshot-ssr/src/circuitBreaker.ts

Function exported from a route file to enumerate all static paths at build time.

Called during the static-params build phase. Receives an empty SsrLoadContext (no live request data) — only use it to access database handles via ctx.bsCtx when running inside the SSG crawler that injects a real context.

Returning an empty array or not exporting this function causes the route to be rendered on-demand (no pre-rendering for that route).

Source: packages/slingshot-ssr/src/types.ts

Result shape returned by page loaders.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Discriminated union of all supported manifest page declarations.

Source: packages/slingshot-ssr/src/pageDeclarations.ts

Exported from dynamic route files to enumerate all static paths. Required when load() returns revalidate: false on a dynamic route. Called during slingshot ssg — inject a DB context via globalThis.__ssgDb before calling this from the SSG crawler.

Source: packages/slingshot-ssr/src/types.ts

All possible return types from a server route’s load() function.

Source: packages/slingshot-ssr/src/types.ts

Result returned from the server/middleware.ts function.

Return one of four shapes:

  • { redirect, status? } — redirect the request to a new URL
  • { rewrite } — internally rewrite the route resolution to a different path
  • { headers } — add headers to the final rendered response
  • {} — pass through unchanged

Source: packages/slingshot-ssr/src/types.ts

Type-safe route definition helper. Connects the loader’s return type to the page component’s loaderData prop type, eliminating manual type annotations.

The returned object contains load, Page, and optionally meta and generateStaticParams. Spread the named exports for the module exports and use route.Page as the default export.

Rule 9: defineRoute does not redefine types — it constrains the existing SsrLoadResult<TData> generic at the call site.

Source: packages/slingshot-ssr/src/types.ts

Execute a file-based route module’s load() and (if present) meta(), returning the loader result, the meta object, and the page component.

This is the canonical helper for SlingshotSsrRenderer implementations. Without it, every renderer has to hand-roll the dynamic-import + load + meta dance and risks subtle drift across consumers (request-time renderer, SSG renderer, test renderers).

The helper does not invoke the page component — it only returns it. The renderer decides whether to call renderToString (React), renderToReadableStream (RSC), or any other output strategy.

Loader signals ({ redirect: ... }, { notFound: true }, etc.) are passed through on loaderResult unchanged. Callers should check via the isRedirect / isNotFound / etc. helpers before rendering.

Source: packages/slingshot-ssr/src/routeExecution.ts

Dynamically import a file-based route module, with module-level caching.

Test code can clear the cache between cases via clearRouteModuleCache(). Production callers should not need to clear the cache — module identity is keyed by absolute file path, which is stable for the lifetime of the build.

Source: packages/slingshot-ssr/src/routeExecution.ts