@mochabug/adapt-plugin-toolkit
Version:
The API toolkit to facilitate mochabug adapt plugin development
503 lines • 24.9 kB
TypeScript
import { ConfiguratorApi, ExecutorApi, SessionExecutorApi, StopExecutorApi } from './api';
import { SignalData } from './genproto/mochabugapis/adapt/graph/signal_data_pb';
import { CronTriggerRequest, ExchangeResultRequest, StartExecutionRequest, StopExecutionRequest } from './genproto/mochabugapis/adapt/runtime/v1/incoming_pb';
import { ConfiguratorEnvironment, ExecutionContext, ExecutorEnvironment } from './types';
export interface ExchangeResultWrapper extends ExchangeResultRequest {
getSignal<T>(key: string): T;
getSignalBinary(key: string): SignalData;
}
/**
* Takes a sequence of path segments and joins them together into a single path.
* This function follows Linux-style path semantics, and behaves similarly to the `path.join()` method in Node.js.
*
* It can handle relative path components (e.g., '..') and will correctly ascend directories when they are encountered.
* Note that it will not resolve '..' that ascend beyond the root directory.
* Leading and trailing slashes on individual path segments are ignored.
* However, if the original first path starts with a slash, the final result will also start with a slash.
*
* @example
* joinPaths('/foo', 'bar', 'baz/asdf', 'quux', '..'); // Returns: "/foo/bar/baz"
*
* @param {...string[]} paths - The path segments to join. Each entry should be a string representing a path segment.
* These can include both directories and filenames, and can be absolute or relative paths.
* Each argument can also include multiple path segments separated by slashes.
* @returns {string} A string representing the joined path. The resulting path is normalized
* (i.e., '..' and '.' path segments are resolved), and any redundant slashes are removed.
* If the original first path starts with a slash, the final result will also start with a slash.
*/
export declare function joinPaths(...paths: string[]): string;
/**
* Represents a middleware function in the routing system.
*
* Middleware functions are designed to:
* - Perform operations on the incoming request.
* - Modify the execution context with additional data.
* - Short-circuit the request handling by directly returning a response.
* They are executed in the order they are added to the router.
*
* Common use cases for middleware include:
* - Logging incoming requests.
* - Authentication and authorization checks.
* - Request validation.
* - Error handling.
* - Modifying the execution context.
*
* @template T
* The type of the API that this middleware can handle. This can be:
* - `ConfiguratorApi`: An API tailored for configuration tasks.
* - `ExecutorApi`: An API tailored for execution tasks.
*
* @template C
* The type of the execution context passed through the middleware during the request's lifecycle.
* This context can be used to share data or state across different parts of the request processing pipeline.
*
* @template R
* The return type of the middleware. By default, it can be `void` or `Response`, but can be constrained
* to just `void` if needed.
*
* @param req
* The incoming web request, providing details such as headers, body content, method, URL, etc.
* Middleware can inspect or modify the request before it reaches the route handler.
*
* @param api
* The API-specific functionalities, which could be an instance of `ConfiguratorApi` or `ExecutorApi`.
* Middleware can utilize this API for tasks or checks related to the API's capabilities.
*
* @param ctx
* The execution context for the current request. Middleware can read or modify this context to share
* data with subsequent middleware or route handlers. For instance, an authentication middleware might
* add a user object to the context after verifying a user's credentials.
*
* @param next
* A function that passes control to the next middleware in the chain. If the current middleware is the
* last in the chain, calling `next` will pass control to the route handler. Unless returning a response,
* middleware should call this function to continue the request handling process.
*
* @returns
* A promise that resolves to either `void` or a `Response` object. If the middleware returns `void`, the
* next middleware or route handler is executed. If it returns a `Response`, the middleware chain is
* short-circuited, and the response is sent to the client, bypassing subsequent middleware or route handlers.
*/
export type Middleware<T extends ConfiguratorApi | ExecutorApi, C extends ExecutionContext = ExecutionContext, R = void | Response> = (req: Request, api: T, ctx: C, next: () => Promise<R>) => Promise<R>;
/**
* Represents an HTTP request method.
*/
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
/**
* Represent the method HTTP request options
*/
export type RequestMethodOption = RequestMethod | RequestMethod[] | '*';
/**
* Represents a set of key-value pairs for parameters.
*/
export type Params = Record<string, string>;
/**
* Represents the structure of the route information for a request in a server environment.
* This is similar to the routing structure used in frameworks like Express.js.
*
* It provides a comprehensive view of the route which a request is being handled by,
* encompassing the actual path, URL path parameters, and search (query) parameters.
*
* - `path`: Holds the route path string that the server listens on. This defines the endpoint
* of your API and is used to match incoming requests.
*
* - `params`: Represents dynamic parameters within the request path. Useful for routes
* with path parameters, e.g., `/users/:userId`, where `:userId` is a dynamic segment
* and its actual value is captured as a parameter.
*
* - `searchParams`: Reflects the search (query) parameters from the request's URL.
* Useful for filtering or altering the server's response based on these parameters.
*
* @property path - The path of the route to match incoming requests.
* @property params - An object representing the path parameters.
* @property searchParams - An object representing search (query) parameters.
*/
export type RouteInfo = {
path: string;
params: Params;
searchParams: Params;
};
/**
* Represents a route handler function for processing web requests.
*
* The handler receives the incoming request and the associated route information.
* It's expected to return a Promise that resolves to the appropriate response.
*
* - `req`: Contains detailed information about the incoming request, including headers,
* body content, method, etc.
*
* - `route`: Provides structured information about the matched route, including the path,
* any dynamic path parameters, and search (query) parameters.
*
* @typedef RouteHandler
* @param req - The incoming web request.
* @param route - Information about the matched route.
* @returns A Promise that resolves to the appropriate server response.
*/
export type RouteHandler = (req: Request, route: RouteInfo) => Promise<Response>;
/**
* Represents a route handler function tailored for APIs of type `ConfiguratorApi`
* or `ExecutorApi`. This handler processes incoming requests by leveraging specific
* API functionalities and returns an appropriate response.
*
* @template T
* The type of the API being used in the handler. This can be either `ConfiguratorApi` or `ExecutorApi`.
* @template C
* The type of the execution context that provides additional information about the ongoing process or operation.
*
* @param req
* The incoming web request, containing details like headers, body content, and method.
* @param api
* The specific API instance, representing either `ConfiguratorApi` or `ExecutorApi` functionalities.
* @param route
* Structured information about the matched route, including path, dynamic path parameters, and query parameters.
* @param ctx
* The execution context providing additional data or state for the route handler.
*
* @returns {Promise<Response>}
* A Promise that resolves to the appropriate server response. Errors during processing should be caught and
* reflected in the resulting `Response`.
*/
export type ApiRouteHandler<T extends ConfiguratorApi | ExecutorApi, C extends ExecutionContext = ExecutionContext> = (req: Request, api: T, route: RouteInfo, ctx: C) => Promise<Response>;
/**
* Represents a route handler function designed for APIs of type `StopExecutorApi`
* or `SessionExecutorApi`. This handler processes incoming requests using specific
* API functionalities.
*
* @template T
* The type of the API being used in the handler, either `StopExecutorApi` or `SessionExecutorApi`.
* @template C
* The type of the execution context providing additional information about the ongoing process or operation.
*
* @param req
* The incoming web request.
* @param api
* The specific API instance, representing functionalities of either `StopExecutorApi` or `SessionExecutorApi`.
* @param ctx
* The execution context providing additional data or state for the route handler.
*
* @returns {Promise<void>}
* A Promise that resolves once the handler has processed the request. Errors should be caught and handled appropriately.
*/
export type InternalRouteHandler<R extends StartExecutionRequest | StopExecutionRequest | CronTriggerRequest, T extends StopExecutorApi | SessionExecutorApi, C extends ExecutionContext = ExecutionContext> = (req: R, api: T, ctx: C) => Promise<void>;
/**
* Represents a route handler function tailored for the `SessionExecutorApi`. This handler
* processes incoming requests using the functionalities of `SessionExecutorApi` and responds
* based on the specific exchange that triggered the handler.
*
* @template T
* The type of the API being used in the handler, specifically `SessionExecutorApi`.
* @template C
* The type of the execution context providing additional information about the ongoing process or operation.
*
* @param req
* The incoming web request.
* @param api
* The specific API instance, representing `SessionExecutorApi` functionalities.
* @param name
* The name of the exchange that triggered the handler.
* @param ctx
* The execution context providing additional data or state for the route handler.
*
* @returns
* A Promise that resolves once the handler has processed the request. Errors should be caught and handled appropriately.
*/
export type ExchangeRouteHandler<R extends ExchangeResultWrapper, T extends SessionExecutorApi, C extends ExecutionContext = ExecutionContext> = (req: R, api: T, name: string, ctx: C) => Promise<void>;
/**
* Represents an individual route, providing functionalities to match HTTP methods
* and URL paths against pre-defined templates.
*/
export declare class Route {
private method;
private matcher;
/**
* Constructs a new `Route` instance.
*
* @param {RequestMethodOption} method - The HTTP method or methods the route responds to.
* @param {string} template - The URL path template, which may include parameters, that the route matches against.
*
* @throws {Error} Throws an error if the provided template is invalid.
*/
constructor(method: RequestMethodOption, template: string);
/**
* Determines if the provided HTTP method and URL path match the route.
*
* If a match is found, it returns the dynamic parameters extracted from the URL.
* If no match is found, it returns null.
*
* @param {RequestMethod} method - The HTTP method to check against the route's method.
* @param {string} path - The URL path to check against the route's template.
*
* @return {Params | null} Returns an object with matching parameters if a match is found, otherwise null.
*/
match(method: RequestMethod, path: string): Params | null;
}
interface RouterEntrypoint<T extends ConfiguratorEnvironment | ExecutorEnvironment, C extends ExecutionContext = ExecutionContext> {
entrypoint: (req: Request, env: T, ctx: C) => Promise<Response>;
}
/**
* `BaseRouter` serves as an abstract foundation for building routers that can process
* web requests by matching them to predefined routes and subsequently delegating the
* request handling to the appropriate handler.
*
* This base class provides core functionalities such as:
* - Storing a collection of routes and their associated handlers.
* - Mechanisms to add new routes.
* - Logic to match incoming requests to the correct route.
* - Delegating the request to the matched route's handler.
* - Registering and executing middleware functions.
*
* Derived classes can extend this base class to introduce more specific behaviors or
* to support additional types of route handlers.
*
* @template T
* Represents the type of route handler that this router can handle. This can be:
* - A basic route handler (`RouteHandler`), which processes the request and returns a response.
* - An API-specific route handler (`ApiRouteHandler`), tailored for specific API types like
* `ConfiguratorApi` or `ExecutorApi`.
*
* @template C extends ExecutionContext
* Represents the type of the execution context that will be passed through the middleware
* and route handlers during the request's lifecycle. This context can be used to share
* data or state across different parts of the request processing pipeline.
* By default, it uses the base `ExecutionContext`, but users can extend this to add
* custom properties that are relevant to their application's logic.
*
* @template A
* Represents the type of the API that the middleware and route handlers can handle. This ensures
* that the middleware's API type matches the route handler's API type. Depending on the context,
* this could be an instance of `ConfiguratorApi` or `ExecutorApi`.
*
* @example
* // Creating a new router that handles basic route handlers and uses a custom execution context.
* class MyRouter extends BaseRouter<RouteHandler, MyExecutionContext> { ... }
*
* // Creating a new router that handles API-specific route handlers for `ConfiguratorApi`.
* class ConfiguratorRouter extends BaseRouter<ApiRouteHandler<ConfiguratorApi>, MyExecutionContext> { ... }
*/
declare abstract class BaseRouter<T extends RouteHandler | ApiRouteHandler<any, C>, C extends ExecutionContext = ExecutionContext, A extends ConfiguratorApi | ExecutorApi = T extends ApiRouteHandler<infer U, C> ? U : never> {
protected routes: {
route: Route;
handler: T;
}[];
protected middlewares: Middleware<A, C, Response>[];
constructor();
/**
* Registers a middleware function to log details of incoming requests.
*
* This middleware captures and logs essential details about the incoming request,
* including the URL, time of the request, and certain headers that provide information
* about the request's origin and protocol. The logged headers include `X-Forwarded-Host`,
* `X-Forwarded-Proto`, and `X-Forwarded-For`, which are commonly used in scenarios where
* the application is behind a proxy or load balancer.
*
* The log is structured for easy reading, with separators to distinguish between individual
* log entries. This middleware is particularly useful for monitoring and debugging purposes.
*
* @returns
* Returns the current router instance, allowing for method chaining.
*
* @example
* const router = new MyRouter();
*
* // Add request logging middleware to the router
* router.useRequestLogging();
*/
useRequestLogging(): this;
useErrorHandling(handler: (e: unknown) => Promise<Response>): this;
/**
* Registers a middleware function to handle Bearer token authorization for specific paths.
*
* This middleware checks the incoming request's `Authorization` header for a Bearer token.
* If the token is present, it attempts to authorize the token using the provided API.
* If the token is invalid or missing, the middleware returns a 401 Unauthorized response.
*
* The middleware can be configured to protect specific paths (those that require authorization)
* and to exclude certain paths (those that should skip authorization).
*
* @param protectedPaths
* An optional array of path prefixes that should be protected by Bearer token authorization.
* If a request's path starts with any of the prefixes in this array, it will be subject to
* authorization checks. If this parameter is omitted or empty, all paths will be protected by default.
*
* @param unprotectedPaths
* An optional array of path prefixes that should be excluded from Bearer token authorization.
* If a request's path starts with any of the prefixes in this array, it will skip the authorization checks.
*
* @returns
* Returns the current router instance, allowing for method chaining.
*
* @example
* const router = new MyRouter();
*
* // Protect all paths under '/api' and exclude paths under '/public'
* router.useBearerAuthorization(['/api'], ['/public']);
*
* // Protect all paths under '/api' without any exclusions
* router.useBearerAuthorization(['/api']);
*
* // Protect all paths by default, excluding paths under '/public'
* router.useBearerAuthorization([], ['/public']);
*/
useBearerAuthorization(protectedPaths?: string[], unprotectedPaths?: string[]): this;
/**
* Registers a middleware function to be executed before reaching the route handlers.
*
* Middleware functions are designed to perform operations on the incoming request,
* potentially modify the execution context, or even short-circuit the request handling
* by directly returning a response. They are executed in the order they are added to the router.
*
* This method allows for the chaining of middleware additions, enabling a fluent API style.
*
* @param middleware
* The middleware function to be added. This function will have access to the request,
* the execution context, and a `next` function. It can either modify the request or context
* and call `next` to continue the middleware chain, or return a `Response` to short-circuit
* the request handling.
*
* @returns
* Returns the current router instance, allowing for method chaining.
*
* @example
* // Creating a new router instance
* const router = new MyRouter();
*
* // Using middleware for Bearer token authentication
* router.use(async (req, ctx, next) => {
* const authHeader = req.headers['authorization'];
* if (authHeader && authHeader.startsWith('Bearer ')) {
* const token = authHeader.split(' ')[1];
* // Validate the token and set user in context (pseudo-code)
* ctx.user = await validateToken(token);
* await next();
* } else {
* return new Response('Unauthorized', { status: 401 });
* }
* });
*
* // Using middleware to check for a specific query parameter
* router.use((req, ctx, next) => {
* if (req.searchParams.has('myParam')) {
* await next();
* } else {
* return new Response('Bad Request', { status: 400 });
* }
* });
*
* // Using middleware to check for a specific cookie
* router.use((req, ctx, next) => {
* const cookies = parseCookies(req.headers.cookie); // Assuming a function to parse cookies
* if (cookies['myCookie']) {
* ctx.myCookieValue = cookies['myCookie'];
* await next();
* } else {
* return new Response('Cookie not found', { status: 400 });
* }
* });
*/
use(middleware: Middleware<A, C, Response>): this;
/**
* Adds a new route to the router.
*
* @param method - The HTTP method(s) for the route.
* @param template - The URL template for the route.
* @param handler - The handler that should be invoked when the route is matched.
* @returns - Returns the router instance for chaining.
*/
add(method: RequestMethodOption, template: string, handler: T): this;
/**
* Matches an incoming request to a route and invokes the appropriate handler.
*
* @param req - The incoming request.
* @param handlerCallback - The callback function that
* handles the request once a matching route is found.
* @returns - Returns the response from the handler or appropriate error response.
*/
protected routeRequest(req: Request, handlerCallback: (handler: T, routeInfo: RouteInfo) => Promise<Response>): Promise<Response>;
}
/**
* Router specialized in handling routes for the `ExecutorApi`.
*/
export declare class ExternalExecutorRouter<C extends ExecutionContext = ExecutionContext> extends BaseRouter<ApiRouteHandler<ExecutorApi, C>, C> implements RouterEntrypoint<ExecutorEnvironment, C> {
constructor();
entrypoint(req: Request, env: ExecutorEnvironment, ctx: C): Promise<Response>;
}
/**
* `InternalExecutorRouter` is a specialized router designed to handle routes specifically for the `SessionExecutorApi`.
*
* This router provides functionalities to:
* - Register routes with associated handlers.
* - Register exchange routes with associated handlers.
* - Add middleware functions that can process and potentially modify incoming requests before they reach the route handlers.
*
* @template C
* Represents the type of the execution context that will be passed through the middleware and route handlers during the request's lifecycle.
* This context can be used to share data or state across different parts of the request processing pipeline.
* By default, it uses the base `ExecutionContext`, but users can extend this to add custom properties that are relevant to their application's logic.
*/
export declare class InternalExecutorRouter<C extends ExecutionContext = ExecutionContext> implements RouterEntrypoint<ExecutorEnvironment, C> {
private startRoute?;
private stopRoute?;
private exchangeRoutes;
protected cronRoute?: {
route: Route;
handler: InternalRouteHandler<CronTriggerRequest, any, C>;
};
/**
* Creates a new instance of InternalExecutorRouter.
*/
constructor();
entrypoint(req: Request, env: ExecutorEnvironment, ctx: C): Promise<Response>;
/**
* Registers a start handler for the vertex start endpoint
*
* @public
* @param handle - The route handler for the start event.
* @returns - Returns the instance of the router for method chaining.
*/
onStart(handle: InternalRouteHandler<StartExecutionRequest, SessionExecutorApi, C>): this;
/**
* Registers a stop handler in case the vertex execution is stopped. Typically used for triggers and
* vertices that requires cleanup
*
* @public
* @param handle - The route handler for the stop event.
* @returns - Returns the instance of the router for method chaining.
*/
onStop(handle: InternalRouteHandler<StopExecutionRequest, StopExecutorApi, C>): this;
/**
* Registers a stream handler for a stream callback.
*
* @public
* @param handle - The route handler for the stream event.
* @returns - Returns the instance of the router for method chaining.
*/
onExchange(handle: ExchangeRouteHandler<ExchangeResultWrapper, SessionExecutorApi, C>): this;
}
export declare class CronExecutorRouter<C extends ExecutionContext = ExecutionContext> extends InternalExecutorRouter<C> {
constructor();
/**
* Registers a cron handler for the cron events.
*
* @public
* @param handle - The route handler for the stop event.
* @returns - Returns the instance of the router for method chaining.
*/
onCron(handle: InternalRouteHandler<CronTriggerRequest, SessionExecutorApi, C>): this;
}
/**
* Router specialized in handling routes for the `ConfiguratorApi`.
*/
export declare class ExternalConfiguratorRouter<C extends ExecutionContext = ExecutionContext> extends BaseRouter<ApiRouteHandler<ConfiguratorApi, C>, C> implements RouterEntrypoint<ConfiguratorEnvironment, C> {
constructor();
entrypoint(req: Request, env: ConfiguratorEnvironment, ctx: C): Promise<Response>;
}
/**
* Router specialized in handling routes for the `ConfiguratorApi`.
*/
export declare class InternalConfiguratorRouter implements RouterEntrypoint<ConfiguratorEnvironment, ExecutionContext> {
constructor();
entrypoint(_req: Request, _env: ConfiguratorEnvironment, _ctx: ExecutionContext): Promise<Response>;
}
export {};
//# sourceMappingURL=router.d.ts.map