microhost.d.ts
Editor declarations for the SDK. Download microhost.d.ts.
// MicroHost JavaScript SDK, API version 1 (SDK_API_VERSION = 1 in crates/engine-api).
//
// Ambient declarations for site code. Anything not declared here is not part of the SDK:
// engines do not provide it, and conformance scenarios check that it stays absent.
// See sdk/README.md for the supported feature set and the explicit exclusions.
//
// Every host call (database, mail, storage, env, fetch) returns a Promise. Failures are thrown
// with a stable `code` property; messages are generic and carry no paths or SQL text.
/** Header input accepted by Headers, Response and fetch. Names are matched case-insensitively. */
type HeadersInit = Headers | Record<string, string> | [string, string][];
/** Body input accepted by Response and fetch. */
type BodyInit = string | Uint8Array | null;
/** A SQL parameter. Mapping is defined in sdk/README.md (section "ctx.db"). */
type SqlParam =
| null
| undefined
| boolean
| number
| bigint
| string
| Uint8Array
| ArrayBuffer;
/** Error thrown by host calls. `code` is stable; `message` is generic. */
interface MicroHostError extends Error {
readonly code:
| "capability_denied"
| "CAPABILITY_DENIED"
| "QUOTA_EXCEEDED"
| "INVALID_INPUT"
| "HOST_UNAVAILABLE"
| "HOST_INTERNAL"
| "invalid_admin_request"
// Lowercase codes raised by the SDK modules (microhost/auth, /db, /session, /sql).
| "invalid_input"
| "quota_exceeded"
| "host_unavailable"
| "db_use_sql_template"
| "db_multiple_rows"
| "db_duplicate_column"
| "db_no_such_column"
| "db_where_required"
| "db_invalid_direction"
| "sdk_module_disabled";
}
/** Returned only after host verification of a private admin invocation. */
interface VerifiedAdminRequest {
readonly userId: string;
readonly role: "viewer" | "editor" | "developer" | "owner";
readonly workspace: string;
readonly site: string;
readonly extension: string;
readonly name: string;
readonly operation: string;
readonly issuedAt: number;
}
/** A safe error with code and message invalid_admin_request. */
interface AdminRequestError extends Error {
readonly code: "invalid_admin_request";
}
/** Verifies the eight signed admin headers through the host; signing keys never reach JavaScript. */
declare function verifyAdminRequest(request: Request, ctx: SiteContext): VerifiedAdminRequest;
/** Request headers have lowercase names. Only the methods listed here exist in v1. */
declare class Headers {
constructor(init?: HeadersInit);
get(name: string): string | null;
has(name: string): boolean;
set(name: string, value: string): void;
append(name: string, value: string): void;
delete(name: string): void;
}
/** Absolute https URL. Only the properties listed here exist in v1; URLSearchParams does not. */
declare class URL {
constructor(input: string, base?: string | URL);
readonly href: string;
readonly origin: string;
readonly protocol: string;
readonly host: string;
readonly hostname: string;
readonly pathname: string;
readonly search: string;
readonly hash: string;
toString(): string;
}
/** The request handed to the handler. The body is fully buffered and size limited. */
declare class Request {
/** Not constructible in v1; instances are provided to the handler. */
private constructor();
/** Uppercase HTTP method. */
readonly method: string;
/** Absolute URL, `https://host/path?query`. */
readonly url: string;
/** Header names are lowercase. */
readonly headers: Headers;
/** Decoded route captures. This is an immutable empty object for exact and convention routes. */
readonly params: Readonly<Record<string, string>>;
text(): Promise<string>;
json(): Promise<unknown>;
arrayBuffer(): Promise<ArrayBuffer>;
}
interface ResponseInit {
/** 200 to 599 inclusive. Other values throw a RangeError. */
status?: number;
headers?: HeadersInit;
}
/** The response the handler must return. Hop-by-hop headers and content-length are stripped by the host. */
declare class Response {
constructor(body?: BodyInit, init?: ResponseInit);
/** Serializes `value` with JSON.stringify and sets content-type: application/json. */
static json(value: unknown, init?: ResponseInit): Response;
/** Sets content-type: text/html; charset=utf-8 and x-content-type-options: nosniff. */
static html(html: string, init?: ResponseInit): Response;
/** Redirect with a Location header. Only 301, 302, 303, 307 and 308 are accepted. */
static redirect(url: string | URL, status: 301 | 302 | 303 | 307 | 308): Response;
}
interface SiteDbResult {
columns: string[];
rows: unknown[][];
rowsAffected: number;
/** Number for safe integers, bigint otherwise, null when not applicable. */
lastInsertRowid: number | bigint | null;
}
interface SiteDbStatement {
sql: string;
params?: SqlParam[];
}
/** Immutable SQL fragment produced by the `microhost/sql` tagged template. */
interface SqlFragment {
readonly text: string;
readonly parameters: readonly SqlParam[];
}
/** Build parameterized SQL. Interpolated fragments compose; arrays expand to placeholders. */
declare function sql(strings: TemplateStringsArray, ...values: (SqlParam | SqlFragment | readonly SqlParam[])[]): SqlFragment;
interface DbRows<T extends object = Record<string, unknown>> extends Array<T> {
readonly hasMore: boolean;
first(): T | null;
next(): Promise<DbRows<T>>;
}
type DbFilter<T extends object> = Partial<{ [K in keyof T]: T[K] | readonly T[K][] | null }>;
interface DbQuery<T extends object> {
orderBy(key: keyof T & string | SqlFragment, direction?: "asc" | "desc"): DbQuery<T>;
limit(count?: number): DbQuery<T>;
offset(count: number): DbQuery<T>;
all(): Promise<DbRows<T>>;
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface DbTable<T extends object, K extends keyof T = keyof T> {
readonly name: string;
readonly columns: readonly (keyof T & string)[];
readonly primaryKey: K | null;
get(key: T[K]): Promise<T | null>;
find(filter?: DbFilter<T>): Promise<T | null>;
where(filter?: DbFilter<T>): DbQuery<T>;
count(filter?: DbFilter<T>): Promise<number | bigint>;
insert(values: Partial<T>): Promise<T | null>;
update(keyOrFilter: T[K] | DbFilter<T>, values: Partial<T>): Promise<T[]>;
delete(keyOrFilter: T[K] | DbFilter<T>): Promise<T[]>;
stmt: {
insert(values: Partial<T>): SqlFragment;
insertOrIgnore(values: Partial<T>): SqlFragment;
};
}
interface MicroHostDbModule {
all<T extends object = Record<string, unknown>>(statement: SqlFragment): Promise<T[]>;
one<T extends object = Record<string, unknown>>(statement: SqlFragment): Promise<T | null>;
run(statement: SqlFragment): Promise<{ rowsAffected: number; lastInsertRowid: number | bigint | null }>;
tx(statements: readonly SqlFragment[]): Promise<SiteDbResult[]>;
id(): string;
now(): string;
}
/** Present on ctx only when the site was granted database access. */
interface SiteDb {
query(sql: string, params?: SqlParam[]): Promise<SiteDbResult>;
execute(sql: string, params?: SqlParam[]): Promise<SiteDbResult>;
/** All-or-nothing. Rejects on the first failing statement after rollback. No interactive transactions in v1. */
transaction(statements: SiteDbStatement[]): Promise<SiteDbResult[]>;
}
/** A named workspace database handle. Same shapes as `ctx.db`; governed by a per-database grant. */
interface SiteWorkspaceDb {
query(sql: string, params?: SqlParam[]): Promise<SiteDbResult>;
execute(sql: string, params?: SqlParam[]): Promise<SiteDbResult>;
transaction(statements: SiteDbStatement[]): Promise<SiteDbResult[]>;
}
/** Present on ctx only when the release requested workspace databases and a grant exists. */
interface SiteWorkspaceDbs {
/** `name` is 1 to 64 letters, digits, underscores or hyphens; anything else throws INVALID_INPUT. */
get(name: string): SiteWorkspaceDb;
}
interface SiteMailMessage {
to: string | string[];
subject: string;
text: string;
html?: string;
}
/** Present on ctx only when the site was granted mail. The sender is the site identity, not chosen by tenant code. */
interface SiteMail {
/** Resolves when accepted for delivery; it does not mean the provider has delivered it. */
send(message: SiteMailMessage): Promise<{ messageId: string }>;
}
/** Present on ctx only when the site was granted storage. Keys are safe relative paths. */
interface SiteStorage {
get(key: string): Promise<Uint8Array | null>;
/** Strings are stored as UTF-8. */
put(key: string, value: string | Uint8Array): Promise<void>;
/**
* Atomically appends to the object, creating it when missing. Concurrent appends never lose or
* interleave bytes inside one call. The object may not grow past 16 MiB and counts against the storage quota.
*/
append(key: string, value: string | Uint8Array): Promise<void>;
delete(key: string): Promise<boolean>;
/**
* Returns a response that makes the platform stream the object to the visitor, with `Range`,
* `ETag` and `304` handled for you. Your code decides who may download (check the session, then
* call this); the bytes never pass through your handler, so an object of any size up to the
* store limit works. `init.headers` such as `Content-Type`, `Content-Disposition` and
* `Cache-Control` are kept; framing and validators come from the object. A missing object answers
* 404. Only the site's own objects can be named.
*/
sendFile(key: string, init?: { headers?: HeadersInit }): Response;
/** Sorted names under `prefix`, at most `limit` (default and maximum 1000). No cursors in v1. */
list(prefix: string, options?: { limit?: number }): Promise<string[]>;
}
/** Present on ctx only when the site declares jobs and was granted jobs. */
interface SiteJobs {
/**
* Queues a run of a job declared in the manifest and resolves with its durable run id. `payload`
* is any JSON value up to 64 KiB, read by the job with `request.json()`. While a run with the same
* `key` is queued or running, that run is returned instead of a new one. `delaySeconds` is 0 to
* 604800. Rejects with `INVALID_INPUT` for an unknown job, `QUOTA_EXCEEDED` when the queue is full.
*/
enqueue(
name: string,
payload?: unknown,
options?: { delaySeconds?: number; key?: string },
): Promise<{ runId: number }>;
}
/** Present on ctx only when the site was granted secrets access. Unknown or reserved names resolve to undefined. */
interface SiteEnv {
get(name: string): Promise<string | undefined>;
}
/** The context passed to the handler. Optional members are absent (undefined) unless granted. */
interface SiteContext {
db?: SiteDb;
dbs?: SiteWorkspaceDbs;
mail?: SiteMail;
storage?: SiteStorage;
jobs?: SiteJobs;
env?: SiteEnv;
/** Present only on private /_admin/ invocations. */
admin?: { verify(request: Request): VerifiedAdminRequest };
}
/** A requested capability was not granted or is unavailable for this execution. */
interface CapabilityUnavailableError extends Error {
readonly code: "capability_denied";
}
interface FetchInit {
method?: string;
headers?: HeadersInit;
body?: string | Uint8Array;
}
interface FetchResponse {
readonly status: number;
readonly ok: boolean;
readonly headers: Headers;
text(): Promise<string>;
json(): Promise<unknown>;
arrayBuffer(): Promise<ArrayBuffer>;
}
/** Outbound HTTPS through the egress broker. Host, redirects and size are policy-controlled. No AbortSignal, streams or cookies. */
declare function fetch(input: string | URL, init?: FetchInit): Promise<FetchResponse>;
declare class TextEncoder {
readonly encoding: "utf-8";
encode(input?: string): Uint8Array;
}
declare class TextDecoder {
readonly encoding: "utf-8";
decode(input?: Uint8Array | ArrayBuffer): string;
}
declare const crypto: {
/** Fills `array` with random bytes. At most 65536 bytes per call. */
getRandomValues<T extends Uint8Array | Uint16Array | Uint32Array | Int8Array | Int16Array | Int32Array>(array: T): T;
};
/** Output is bounded (64 KiB per execution) and goes to the site log, never to the visitor. */
declare const console: {
log(...args: unknown[]): void;
info(...args: unknown[]): void;
warn(...args: unknown[]): void;
error(...args: unknown[]): void;
};
/**
* Handler signature. The module's default export must be an async function that resolves to a
* Response. Any other resolved value is an error (InvalidResponse, HTTP 502).
*
* @example
* export default async function handle(request: Request, ctx: SiteContext): Promise<Response> {
* return Response.json({ ok: true });
* }
*/
type SiteHandler = (request: Request, ctx: SiteContext) => Promise<Response>;
declare module "microhost/sql" {
export interface SqlFragment { readonly text: string; readonly parameters: readonly SqlParam[]; }
export function sql(strings: TemplateStringsArray, ...values: (SqlParam | SqlFragment | readonly SqlParam[])[]): SqlFragment;
}
declare module "microhost/db" {
export const all: MicroHostDbModule["all"];
export const one: MicroHostDbModule["one"];
export const run: MicroHostDbModule["run"];
export const tx: MicroHostDbModule["tx"];
export const id: MicroHostDbModule["id"];
export const now: MicroHostDbModule["now"];
}
declare module "microhost/auth" {
export function hashPassword(password: string): Promise<string>;
export function verifyPassword(password: string, storedHash: string): Promise<boolean>;
}
declare module "microhost/session" {
export interface Session { readonly userId: string; readonly data: Readonly<Record<string, unknown>>; }
export interface SessionOptions { readonly maxAgeSeconds?: number; readonly data?: Record<string, unknown>; }
export function create(userId: string, options?: SessionOptions): Promise<string>;
export function get(request: Request): Promise<Session | null>;
export function destroy(request: Request): Promise<string>;
export function destroyAll(userId: string): Promise<void>;
}
declare module "db" {
export * from "microhost/db";
}
// Build-profile declarations. These names are exported by the server-shipped `microhost` module
// while resolving microhost.config.js; they are not available to request handlers.
interface BuildRoute {
readonly path: string;
readonly script: string;
readonly methods: readonly string[];
}
/** Names accepted in the manifest `sdk` list. `db` implies `sql`. */
type BuildSdkModule = "auth" | "db" | "session" | "sql";
/** Value returned by `defineExtension`; passed in the `admin` list of `defineSite`. */
interface BuildExtension {
readonly [key: string]: unknown;
}
/** Value returned by `field.<type>(...)`, used in extension input and output descriptors. */
interface BuildField {
readonly [key: string]: unknown;
}
interface BuildFieldOptions {
/** Author-friendly spelling of the schema's `enum`. */
readonly values?: readonly string[];
readonly [key: string]: unknown;
}
type BuildFieldFactory = (options?: BuildFieldOptions) => BuildField;
interface BuildSite {
readonly schemaVersion: 1;
readonly schemaCompat?: { readonly min: number; readonly max: number };
readonly static?: { readonly root?: string; readonly spaFallback?: boolean };
/**
* Custom pages for errors the platform produces. Keys: a status from 400 to 599, "4xx", "5xx" or
* "_" (everything else). A value ending in ".js" is a script that reads the status from
* `request.params.status`; any other value is a file inside `public/`. The visitor always gets the
* original status.
*/
readonly errorPages?: { readonly [statusOrClass: string]: string };
/**
* The path the post-activation health check requests (default "/"). It must start with "/", be
* at most 256 bytes of visible ASCII, contain no "..", "?" or "#", and not start with "/_admin".
* It may name a static file, a route or a script route; the check needs a 2xx answer.
*/
readonly healthCheck?: { readonly path: string };
/**
* Scheduled and background jobs, by name. A job is a script under `jobs/`; it runs on a cron
* `schedule` (read in `timezone`, default UTC) or on demand, and needs the jobs capability.
*/
readonly jobs?: {
readonly [name: string]: {
readonly script: string;
readonly schedule?: string;
readonly timezone?: string;
readonly overlap?: "skip" | "queue";
readonly catchUp?: "none" | "latest";
readonly timeoutMs?: number;
readonly retries?: number;
};
};
readonly routes?: readonly BuildRoute[];
readonly permissionsRequested?: {
readonly database?: boolean;
readonly workspaceDatabases?: readonly string[];
readonly email?: boolean;
readonly storage?: boolean;
readonly secrets?: boolean;
readonly externalHttp?: readonly string[];
};
readonly limitsRequested?: { readonly timeoutMs?: number; readonly memoryMb?: number };
/** SDK modules the release imports. Absent: every module is importable. */
readonly sdk?: readonly BuildSdkModule[];
readonly admin?: readonly BuildExtension[] | unknown;
}
declare function defineSite(site?: Omit<BuildSite, "schemaVersion">): BuildSite;
declare function route(path: string, script: string, options?: { methods?: readonly string[] }): BuildRoute;
/** Declares an admin extension. Queries default to GET/viewer/read, actions to POST/editor/write. */
declare function defineExtension(extension: { readonly [key: string]: unknown }): BuildExtension;
declare const field: {
string: BuildFieldFactory;
number: BuildFieldFactory;
status: BuildFieldFactory;
date: BuildFieldFactory;
boolean: BuildFieldFactory;
text: BuildFieldFactory;
textarea: BuildFieldFactory;
};
declare const files: {
list(prefix?: string): readonly string[];
read(path: string): string | Uint8Array;
};