import type { Context, HonoRequest, Next } from "hono"; import type { InferMethodInput, InferMethodMessage, InferMethodOutput, InferMethodParams, Procedure, Query, Subscription, } from "@atp/lex"; import { z } from "zod"; import type { ErrorResult, XRPCError } from "./errors.ts"; import type { CalcKeyFn, CalcPointsFn } from "./rate-limiter.ts"; import type { RateLimiterI } from "./rate-limiter.ts"; /** * Represents a value that can be either synchronous or asynchronous. * @template T - The type of the value */ export type Awaitable = T | Promise; /** * Handler function for catching all unmatched routes. * @param c - The Hono context object * @param next - The next middleware function * @returns A promise that resolves to void or a Response */ export type CatchallHandler = ( c: Context, next: Next, ) => Promise; /** * Configuration options for the XRPC server. */ export type Options = { /** Whether to validate response schemas */ validateResponse?: boolean; /** Handler for catching all unmatched routes */ catchall?: CatchallHandler; /** Payload size limits for different content types */ payload?: RouteOptions; /** Rate limiting configuration */ rateLimits?: { /** Factory function for creating rate limiters */ creator: RateLimiterCreator; /** Global rate limits applied to all routes */ global?: ServerRateLimitDescription[]; /** Shared rate limits that can be referenced by name */ shared?: ServerRateLimitDescription[]; /** Function to determine if rate limits should be bypassed for a request */ bypass?: (ctx: HandlerContext) => boolean; }; /** * By default, errors are converted to {@link XRPCError} using * {@link XRPCError.fromError} before being rendered. If method handlers throw * error objects that are not properly rendered in the HTTP response, this * function can be used to properly convert them to {@link XRPCError}. The * provided function will typically fallback to the default error conversion * (`return XRPCError.fromError(err)`) if the error is not recognized. * * @note This function should not throw errors. */ errorParser?: (err: unknown) => XRPCError; }; /** * Raw query parameters from the HTTP request before type conversion. */ export type UndecodedParams = HonoRequest["query"]; /** * Basic primitive types supported in XRPC parameters. */ export type Primitive = string | number | boolean; /** * Type-safe parameter object with optional primitive values or arrays. */ export type Params = { [P in string]?: undefined | Primitive | Primitive[] }; /** * Input data for XRPC method handlers. */ export type HandlerInput = { /** Content encoding of the request body */ encoding: string; /** Parsed request body */ body: unknown; }; /** Result of successful authentication. */ export type AuthResult = { /** Authentication credentials (e.g., user info, tokens) */ credentials: unknown; /** Optional authentication artifacts (e.g., session data) */ artifacts?: unknown; }; /** Zod Schema for request headers. */ export const headersSchema: z.ZodRecord = z.record( z.string(), z.string(), ); /** HTTP headers as a record of string key-value pairs. */ export type Headers = z.infer; export const handlerSuccess: z.ZodObject<{ encoding: z.ZodString; body: z.ZodAny; headers: z.ZodOptional>; }> = z.object({ encoding: z.string(), body: z.any(), headers: headersSchema.optional(), }); /** Successful response from a method handler. */ export type HandlerSuccess = z.infer; /** Handler response that pipes through a buffer. */ export type HandlerPipeThroughBuffer = { /** Content encoding of the response */ encoding: string; /** Response data as a buffer */ buffer: Uint8Array; /** Optional HTTP headers */ headers?: Headers; }; /** Handler response that pipes through a stream. */ export type HandlerPipeThroughStream = { /** Content encoding of the response */ encoding: string; /** Response data as a readable stream */ stream: ReadableStream; /** Optional HTTP headers */ headers?: Headers; }; /** Union type for handler responses that pipe data through either a buffer or stream. */ export type HandlerPipeThrough = | HandlerPipeThroughBuffer | HandlerPipeThroughStream; /** Authentication state for a handler context. */ export type Auth = void | AuthResult; /** Input data for a handler context. */ export type Input = void | HandlerInput; /** Output data from a handler. */ export type Output = void | HandlerSuccess | ErrorResult; /** * Function that verifies authentication for a request. * @template C - The context type * @template A - The authentication result type */ export type AuthVerifier = | ((ctx: C) => Awaitable) | ((ctx: C) => Awaitable); // Handler context that combines Hono Context with XRPC-specific properties /** * Context object provided to XRPC method handlers containing request data and utilities. * @template A - Authentication type * @template P - Parameters type * @template I - Input type */ export type HandlerContext< A extends Auth = Auth, P extends Params = Params, I extends Input = Input, > = MethodAuthContext

& { /** Authentication result */ auth: A; /** Request input data */ input: I; /** Function to reset rate limits for this route */ resetRouteRateLimits: () => Promise; }; /** * Handler function for XRPC methods. * @template A - Authentication type * @template P - Parameters type * @template I - Input type * @template O - Output type */ export type MethodHandler< A extends Auth = Auth, P extends Params = Params, I extends Input = Input, O extends Output = Output, > = (ctx: HandlerContext) => Awaitable; /** * Factory function for creating rate limiter instances. * @template T - The handler context type */ export type RateLimiterCreator = < C extends T = T, >(opts: { /** Prefix for rate limiter keys */ keyPrefix: string; /** Duration window in milliseconds */ durationMs: number; /** Number of points allowed in the duration window */ points: number; /** Function to calculate the rate limit key */ calcKey: CalcKeyFn; /** Function to calculate points consumed */ calcPoints: CalcPointsFn; /** Whether to fail closed (deny) when rate limiter is unavailable */ failClosed?: boolean; }) => RateLimiterI; /** * Context object for method authentication containing request data. * @template P - Parameters type * @template I - Input type */ export type MethodAuthContext

= { params: P; req: Request; res: Response; }; /** * Authentication verifier function for XRPC methods. * @template A - Authentication result type * @template P - Parameters type * @template I - Input type */ export type MethodAuthVerifier< A extends AuthResult = AuthResult, P extends Params = Params, > = (ctx: MethodAuthContext

) => Awaitable; /** * Context object for streaming handlers. * @template A - Authentication type * @template P - Parameters type */ export type StreamContext< A extends Auth = Auth, P extends Params = Params, > = StreamAuthContext

& { /** Authentication result */ auth: A; /** Abort signal for cancelling the stream */ signal: AbortSignal; }; /** * Handler function for streaming XRPC endpoints. * @template A - Authentication type * @template P - Parameters type * @template O - Output item type */ export type StreamHandler< A extends Auth = Auth, P extends Params = Params, O = unknown, > = (ctx: StreamContext) => AsyncIterable; /** * Context object for stream authentication. * @template P - Parameters type */ export type StreamAuthContext

= { /** Parsed request parameters */ params: P; /** HTTP request object */ req: Request; }; /** * Authentication verifier function for streaming endpoints. * @template A - Authentication result type * @template P - Parameters type */ export type StreamAuthVerifier< A extends AuthResult = AuthResult, P extends Params = Params, > = AuthVerifier, A>; export type LexMethod = Procedure | Query | Subscription; export type LexMethodNamespace = | { readonly main: M } | { readonly Main: M }; export type LexMethodLike = | M | LexMethodNamespace; export type LexMethodParams = InferMethodParams; export type LexMethodInput = InferMethodInput< M, ReadableStream >; export type LexMethodOutput = InferMethodOutput> extends undefined ? InferMethodOutput> | void : InferMethodOutput>; export type LexMethodMessage = InferMethodMessage; export type LexMethodHandler< M extends Procedure | Query, A extends Auth = Auth, > = MethodHandler, LexMethodInput, LexMethodOutput>; export type LexMethodConfig< M extends Procedure | Query, A extends Auth = Auth, > = MethodConfig, LexMethodInput, LexMethodOutput>; export type LexSubscriptionHandler< M extends Subscription, A extends Auth = Auth, > = StreamHandler< A, LexMethodParams, LexMethodMessage >; export type LexSubscriptionConfig< M extends Subscription, A extends Auth = Auth, > = StreamConfig, LexMethodMessage>; /** * Configuration for server-level rate limits. * @template C - Handler context type */ export type ServerRateLimitDescription< C extends HandlerContext = HandlerContext, > = { /** Unique name for this rate limit */ name: string; /** Duration window in milliseconds */ durationMs: number; /** Number of points allowed in the duration window */ points: number; /** Optional function to calculate the rate limit key */ calcKey?: CalcKeyFn; /** Optional function to calculate points consumed */ calcPoints?: CalcPointsFn; /** Whether to fail closed when rate limiter is unavailable */ failClosed?: boolean; }; /** * Options for referencing a shared rate limit by name. * @template C - Handler context type */ export type SharedRateLimitOpts = { /** Name of the shared rate limit to use */ name: string; /** Optional function to calculate the rate limit key */ calcKey?: CalcKeyFn; /** Optional function to calculate points consumed */ calcPoints?: CalcPointsFn; }; /** * Options for defining a route-specific rate limit. * @template C - Handler context type */ export type RouteRateLimitOpts = { /** Duration window in milliseconds */ durationMs: number; /** Number of points allowed in the duration window */ points: number; /** Optional function to calculate the rate limit key */ calcKey?: CalcKeyFn; /** Optional function to calculate points consumed */ calcPoints?: CalcPointsFn; }; /** * Union type for rate limit options - either shared or route-specific. * @template C - Handler context type */ export type RateLimitOpts = | SharedRateLimitOpts | RouteRateLimitOpts; /** * Type guard to check if rate limit options are for a shared rate limit. * @template C - Handler context type * @param opts Rate limit options to check * @returns True if the options reference a shared rate limit */ export function isSharedRateLimitOpts< C extends HandlerContext = HandlerContext, >(opts: RateLimitOpts): opts is SharedRateLimitOpts { return "name" in opts && typeof opts.name === "string"; } /** Options for configuring payload size limits by content type. */ export type RouteOptions = { /** Maximum size for binary/blob payloads in bytes */ blobLimit?: number; /** Maximum size for JSON payloads in bytes */ jsonLimit?: number; /** Maximum size for text payloads in bytes */ textLimit?: number; }; /** Simplified route options with only blob limit configuration. */ export type RouteOpts = { /** Maximum size for binary/blob payloads in bytes */ blobLimit?: number; }; /** * Configuration object for an XRPC method including handler, auth, and options. * @template A - Authentication type * @template P - Parameters type * @template I - Input type * @template O - Output type */ export type MethodConfig< A extends Auth = Auth, P extends Params = Params, I extends Input = Input, O extends Output = Output, > = { /** The method handler function */ handler: MethodHandler; /** Optional authentication verifier */ auth?: MethodAuthVerifier, P>; /** Optional route configuration */ opts?: RouteOptions; /** Optional rate limiting configuration */ rateLimit?: | RateLimitOpts> | RateLimitOpts>[]; }; /** * Union type allowing either a simple handler function or full method configuration. * @template A - Authentication type * @template P - Parameters type * @template I - Input type * @template O - Output type */ export type MethodConfigOrHandler< A extends Auth = Auth, P extends Params = Params, I extends Input = Input, O extends Output = Output, > = MethodHandler | MethodConfig; /** * Configuration object for a streaming XRPC endpoint. * @template A - Authentication type * @template P - Parameters type * @template O - Output item type */ export type StreamConfig< A extends Auth = Auth, P extends Params = Params, O = unknown, > = { /** Optional authentication verifier for the stream */ auth?: StreamAuthVerifier, P>; /** The stream handler function */ handler: StreamHandler; }; /** * Union type allowing either a simple stream handler or full stream configuration. * @template A - Authentication type * @template P - Parameters type * @template O - Output item type */ export type StreamConfigOrHandler< A extends Auth = Auth, P extends Params = Params, O = unknown, > = StreamHandler | StreamConfig; /** * Type guard to check if handler output is a pipe-through buffer response. * @param output - The handler output to check * @returns True if the output is a buffer pipe-through response */ export function isHandlerPipeThroughBuffer( output: Output | HandlerPipeThrough, ): output is HandlerPipeThroughBuffer { // We only need to discriminate between possible Output values return output != null && "buffer" in output && output["buffer"] !== undefined; } /** * Type guard to check if handler output is a pipe-through stream response. * @param output - The handler output to check * @returns True if the output is a stream pipe-through response */ export function isHandlerPipeThroughStream( output: Output | HandlerPipeThrough, ): output is HandlerPipeThroughStream { // We only need to discriminate between possible Output values return output != null && "stream" in output && output["stream"] !== undefined; }