muddle/contracts/index.ts
2026-10-23 08:53:07 -04:00

229 lines
7.1 KiB
TypeScript

import type { RequestContext } from "./typegen/http/request.ts";
export * from "./typegen/http/index.ts";
export * from "./typegen/orm/index.ts";
export * from "./typegen/minify.ts";
export type { ViewPath, ViewProps, ViewPropsOf } from "./typegen/views.ts";
/** A file handled by a loader. */
export interface LoaderFile {
/** Absolute path of the file. */
path: string;
/** Path relative to the project root. */
relativePath: string;
/** Raw content of the file. */
source: string;
}
/** Result of a loader. */
export interface LoadedFile {
/** Data exposed to the application, e.g. frontmatter and rendered html. */
data: Record<string, unknown>;
/** Rendered representation, e.g. the compiled css or the markdown html. */
content?: string;
}
export interface Loader {
/** Name of the loader, used in error messages. */
name: string;
/** File extensions handled by the loader, e.g. `['.md']`. */
extensions: string[];
/** Loads a single file. */
load(file: LoaderFile): Promise<LoadedFile> | LoadedFile;
}
/** Options shared by all loaders. */
export interface LoaderOptions {
/** Directory the loader scans, relative to the project root. */
dir?: string;
}
/** Configuration of the content collections. */
export interface CollectionConfig {
/** Name of the collection, e.g. `notes`. */
name: string;
/** Directory of the collection, relative to the project root. */
dir: string;
/** Loader used to read the files of the collection, by name or inline. */
loader: Loader | string;
/** Schema of the frontmatter, validated at load time. */
schema?: ContentSchema;
}
/** Schema of a content collection. */
export interface ContentSchema {
/** Required frontmatter fields. */
required?: string[];
/** Allowed values of a field. */
enum?: Record<string, string[]>;
/** Maximum length of a string field. */
maxLength?: Record<string, number>;
}
/** A single entry of a content collection. */
export interface CollectionEntry<
Frontmatter extends Record<string, unknown> = Record<string, unknown>,
> {
/** Identifier of the entry, derived from the file name. */
id: string;
/** Path of the file, relative to the project root. */
path: string;
/** Parsed frontmatter. */
frontmatter: Frontmatter;
/** Rendered body. */
body: string;
/** Raw markdown source. */
content: string;
}
/** Minimal view registry handed to integration mounts. */
export interface MountableViews {
/** Registers a named view component. */
component(
name: string,
component: { render: (props: unknown, context: unknown) => string | Promise<string> },
): unknown;
}
/** A self-contained unit an integration mounts into the application. */
export interface Mounts {
/** Registers view components, e.g. the primitives of a theme. */
views?(views: MountableViews): void | Promise<void>;
/** Adds routes, e.g. assets, fonts and endpoints. */
routes?(
route: (method: string, path: string, handler: (request: Request) => Response | Promise<Response>) => void,
): void | Promise<void>;
/** Served inline stylesheets by route. */
styles?: Record<string, string | (() => string | Promise<string>)>;
}
/** Integration hook, called while the application is created. */
export interface Integration {
/** Transforms a staged build asset. Promises run in configured order. */
transform?(asset: BuildAsset): BuildAsset | Promise<BuildAsset>;
/** Analyzes a fully rendered page before the output is published. */
analyze?(page: { route: string; html: string }): unknown | Promise<unknown>;
/** Runs before routing, including raw integration endpoints. */
request?(context: RequestContext): void | Promise<void>;
/** Name of the integration. */
name: string;
/** Called with the resolved configuration. */
setup?(context: IntegrationContext): void | Promise<void>;
/** Full-stack units mounted while the application boots. */
mounts?: Mounts[];
/** Called before the server starts serving requests. */
ready?(): void | Promise<void>;
}
export interface BuildAsset {
path: string;
contentType: string;
content: string | Uint8Array;
}
/** Context handed to an integration. */
export interface IntegrationContext {
/** Resolved project configuration. */
config: ResolvedConfig;
/** Adds a route to the application. */
route(
method: string,
path: string,
handler: (
request: Request,
context: RequestContext,
) => Promise<Response> | Response,
): void;
/** Adds a loader to the project. */
loader(name: string, loader: Loader): void;
}
/** Adapter configuration, e.g. the port of the http server. */
export interface AdapterOptions {
/** Port the server listens on. */
port?: number;
/** Hostname the server binds to. */
hostname?: string;
/** Name of the adapter, e.g. `server-bun`. */
name?: string;
}
/** Database configuration. */
export interface DatabaseOptions {
/** Dialect of the database. */
dialect: "sqlite" | "postgres" | "d1";
/** Path of the sqlite file or the connection string of postgres. */
url?: string;
/** Cloudflare D1 binding configuration. */
d1?: { accountId: string; databaseId: string; apiToken: string };
}
/** Prerendering configuration. */
export interface PrerenderOptions {
/** Routes rendered at build time. */
routes: string[];
/** Directory the html files are written to. */
outDir?: string;
/** Writes every route, even when the handler answers with a redirect. */
fallback?: boolean;
}
/** Project configuration, as written in `ct.config.ts`. */
export interface MuddleConfig {
/** Root directory of the project. */
root?: string;
/** Server entry module, relative to the project root. Defaults to `index.ts`. */
entry?: string;
/** Directory with the templates. */
views?: string;
/** Directory with the components. */
components?: string;
/** Directory with the markdown collections. */
content?: string;
/** Directory with the structured css files. */
styles?: string;
/** Directory with static assets. */
publicDir?: string;
/** Directories scanned for templates, relative to the project root. */
viewRoots?: string[];
/** Loaders available to the content collections. */
loaders?: Record<string, Loader>;
/** Content collections of the project. */
collections?: CollectionConfig[];
/** Integrations of the project. */
integrations?: Integration[];
/** Prerendering configuration. */
prerender?: PrerenderOptions;
/** Adapter used to serve the application. */
adapter?: AdapterOptions;
/** Database configuration. */
database?: DatabaseOptions;
/** Model files of the project, relative to the project root. */
tables?: string[];
/** Where the generated contracts are written to. */
typegen?: { out?: string };
}
/** Configuration with every default applied. */
export interface ResolvedConfig extends MuddleConfig {
root: string;
views: string;
components: string;
content: string;
styles: string;
publicDir: string;
viewRoots: string[];
loaders: Record<string, Loader>;
collections: CollectionConfig[];
integrations: Integration[];
}
/**
* Declares the configuration of a project.
*
* The helper only exists for the editor: it checks the configuration while
* it is written in `ct.config.ts`.
*/
export function defineConfig(config: MuddleConfig): MuddleConfig {
return config;
}