import type { Actor, Target } from "@rotorsoft/act"; import type { IdempotencyStore } from "@rotorsoft/act-ops/idempotency"; import type { Context, MiddlewareHandler } from "hono"; import { Hono } from "hono"; import { type ActorExtractor, type SseOptions } from "../api/index.js"; /** * Hono context variables this subpath contributes. The generic on * the returned middleware threads it through so routes downstream * of {@link authenticated} see `c.get("actor")` typed without a * manual cast. */ export type ActMiddlewareVariables = { actor: Actor; }; /** * Per-call options for {@link hono}. The host supplies the four * seams the package can't make decisions on: * * - `actor` — auth resolver. Receives the Hono context, returns the * `Actor` that flows onto `c.get("actor")` for every generated * route (via an internal {@link authenticated}). * - `stream` — stream-id resolver. Receives the action name, the * validated body, and the Hono context; returns the target * stream. Singleton aggregates return a constant; per-tenant * aggregates pull from headers, path, or body. * - `expectedVersion` (optional) — optimistic-concurrency resolver. * Hosts typically read it from an `If-Match` header on the * request, or from the client's last-known snapshot. Returning * `undefined` skips the check for that call — handy when only * some actions are concurrency-sensitive. * - `idempotency` (optional) — when set, the route honors * `Idempotency-Key` via {@link withIdempotency}. The host * supplies the `IdempotencyStore`; `keyFrom` defaults to reading * the `Idempotency-Key` header. On a duplicate claim, the route * responds `409 Conflict` — the contract intentionally doesn't * cache the original handler's result, matching the * receiver-side "ack the duplicate" semantics. * - `basePath` (optional, default `"/api"`) — Hono basePath the * routes mount under. */ export type HonoOptions = { readonly actor: ActorExtractor; readonly stream: (action_name: string, input: unknown, c: Context) => string | Promise; readonly expectedVersion?: (action_name: string, input: unknown, c: Context) => number | undefined | Promise; readonly idempotency?: { readonly store: IdempotencyStore; readonly keyFrom?: (c: Context) => string | undefined; }; /** * Optional SSE wiring. When set, the generator emits one * `GET /sse/?stream=` per unique * state name in the registry. The endpoint runs the host * {@link actor} extractor, looks up the streamId from * `?stream=...`, opens a `text/event-stream`, yields the cached * state (if any), and forwards every patch published to the * shared {@link SseOptions.channel}. A heartbeat keeps proxies * from idling the connection out; the per-process connection cap * returns `503 Service Unavailable` (with `Retry-After: 1`) when * full so operators never see silent stalls. * * Off by default — most APIs don't expose live state to every * client, and opening one is widening both the auth and cost * surface. */ readonly sse?: SseOptions; readonly basePath?: string; }; /** * Build a Hono middleware that runs an {@link ActorExtractor} once * per call and stashes the resolved {@link Actor} under * `c.set("actor", ...)` so downstream routes can read it via * `c.get("actor")`. The generator at {@link hono} uses this * internally; it's also exported so hosts that want to compose * their own route chain (logging, tracing, custom auth flavors) * can wire it directly: * * ```ts * import { Hono } from "hono"; * import { authenticated, type ActMiddlewareVariables } from "@rotorsoft/act-http/hono"; * * const api = new Hono<{ Variables: ActMiddlewareVariables }>(); * api.use("*", authenticated((c) => resolveUserFromJwt(c))); * api.get("/me", (c) => c.json(c.get("actor"))); // typed * ``` * * Errors thrown by the extractor surface as `401 Unauthorized` with * the shared {@link ApiError} envelope. */ export declare function authenticated(extractor: ActorExtractor): MiddlewareHandler<{ Variables: ActMiddlewareVariables; }>; /** * Build a typed Hono REST surface from a built `Act` instance. * * Walks `app.registry.actions` once and emits one * `POST /actions/` per action under the configured * `basePath` (default `/api`). Each route: * * 1. Runs the internal {@link authenticated} middleware — * `options.actor` resolves the {@link Actor} once and stashes * it under `c.get("actor")`. * 2. Validates the JSON body against the action's Zod schema via * `@hono/zod-validator`. Failures short-circuit with `422`. * 3. Resolves the target stream via * `options.stream(name, input, c)`. * 4. (Optionally) resolves `expectedVersion` for optimistic * concurrency. * 5. (Optionally) claims the `Idempotency-Key` via * {@link withIdempotency} — responds `409` on duplicate. * 6. Calls * `app.do(name, { stream, actor, expectedVersion? }, input)`. * 7. Maps any thrown framework error onto the shared * {@link ApiError} envelope via {@link toApiError}, returning * the conventional HTTP status (`409` / `412` for concurrency, * `422` for validation, `400` for non-retryable, etc.). * * @param app A built `Act` orchestrator. * @param options Auth, stream, expected-version, idempotency, and * base-path seams. * @returns A Hono app covering every registered action under * `/actions/`. */ /** * Structural shape of the Act surface this generator walks at * runtime — the registry's action-name → owning-state map plus the * `do(...)` dispatch. Letting TApp infer to the caller's concrete * `Act<...>` against this structural bound keeps nested variance * out of the signature and avoids `any` in either direction. * * @internal */ type ActSurface = { readonly registry: { actions: Record; }>; }; do(action: string, target: Target, payload: unknown): Promise; }; export declare function hono(app: TApp, options: HonoOptions): Hono<{ Variables: ActMiddlewareVariables; }>; export {}; //# sourceMappingURL=index.d.ts.map