@stacksjs/rpx
Version:
A modern and smart reverse proxy.
107 lines (106 loc) • 4.52 kB
TypeScript
import type { OnDemandSitesConfig, SiteRouteTemplate } from './types';
/** Expand a leading `~` (or `~/…`) to the resolved home directory. */
export declare function expandHome(p: string, home: string): string;
/** A registry-safe id derived from a host (`a.localhost` → `a.localhost`). */
export declare function siteIdForHost(host: string): string;
/**
* The host's single project label for convention discovery: `<name>.<tld>` →
* `name`. Multi-label hosts (`docs.app.localhost`) and bare TLDs don't discover —
* point those at an explicit `sites` entry. Returns `null` when no TLD matches.
*/
export declare function projectNameFromHost(host: string, tlds: string[]): string | null;
/**
* Read a per-project {@link SiteManifest} so users can define the dev startup
* manually. Checked sources, in order: a `rpx.site.json` file in the project,
* then a `"rpx"` key in its `package.json`. Returns `null` when neither exists
* (or both are malformed).
*/
export declare function readSiteManifest(dir: string, deps: ResolverProbes): SiteManifest | null;
/*` dependency in
* `package.json`. Boots frontend (`/`), API (`/api`) and docs (`/docs`) with
* the conventional `PORT`/`PORT_API`/`PORT_DOCS` env, deferring proxy + TLS to
* rpx (`STACKS_PROXY_MANAGED=1`) and taking its public origin via `APP_URL`.
* - **Generic** — any `package.json` with a `dev` script: a single `bun run dev`
* backend on `PORT`.
* - Otherwise `null`.
*/
export declare function detectProjectPreset(dir: string, deps: ResolverProbes): SitePreset | null;
/**
* Build a {@link SiteResolver} over an {@link OnDemandSitesConfig}. Explicit
* `sites` are matched first (exact host, then wildcard), then convention
* discovery under `roots`.
*/
export declare function createSiteResolver(config: OnDemandSitesConfig, deps?: SiteResolverDeps): SiteResolver;
/**
* Enumerate the sites rpx can currently boot — explicit non-wildcard
* {@link SiteConfig}s plus every project discovered by scanning the configured
* roots. Powers `rpx sites`. Each entry is a fully-resolved {@link ResolvedSite}
* (so the caller sees the dir, command, and host it would serve).
*/
export declare function listDiscoverableSites(config: OnDemandSitesConfig, deps?: SiteResolverDeps): ResolvedSite[];
/** A fully-resolved site, ready for the supervisor to boot and route. */
export declare interface ResolvedSite {
host: string
id: string
dir: string
command: string
env: Record<string, string>
routes: SiteRouteTemplate[]
selfRegisters: boolean
idleTimeoutMs: number
source: 'config' | 'discovered'
}
/**
* The dev-command shape for a recognized project kind. Returned by a
* {@link SiteDetector}; the resolver folds it into a {@link ResolvedSite}.
*/
export declare interface SitePreset {
command: string
env?: Record<string, string>
routes?: SiteRouteTemplate[]
selfRegisters?: boolean
urlEnv?: string[]
}
/**
* A per-project override of how rpx boots a site — so the dev startup can be
* defined **manually** instead of relying on auto-detection. Read from a
* `rpx.site.json` file in the project, or a `"rpx"` key in its `package.json`.
* A manifest with a `command` fully defines the preset (and makes even a
* directory that isn't otherwise a recognized project bootable).
*
* ```jsonc
* // rpx.site.json
* {
* "command": "pnpm dev",
* "env": { "NODE_ENV": "development" },
* "routes": [
* { "path": "/", "portEnv": "PORT", "defaultPort": 5173, "readyGate": true },
* { "path": "/api", "portEnv": "API_PORT", "defaultPort": 4000 }
* ]
* }
* ```
*/
export declare interface SiteManifest {
command?: string
env?: Record<string, string>
routes?: SiteRouteTemplate[]
selfRegisters?: boolean
urlEnv?: string[]
}
/** Filesystem probes the resolver and detector use — injected for tests. */
export declare interface ResolverProbes {
dirExists: (p: string) => boolean
fileExists: (p: string) => boolean
readText: (p: string) => string | null
}
export declare interface SiteResolverDeps extends Partial<ResolverProbes> {
detect?: SiteDetector
homeDir?: string
readdir?: (p: string) => string[]
}
/** Resolve a host to a site (or `null`). Construct once, call per request. */
export declare interface SiteResolver {
resolve: (host: string) => ResolvedSite | null
}
/** Detect a project kind from its directory. Returns `null` for "not a dev project". */
export type SiteDetector = (dir: string, deps: ResolverProbes) => SitePreset | null;