import { RouteMetadataCollector } from "../openapi/collector.js"; import type { VextInternalHooks } from "../../types/hooks.js"; /** * route-reloader.ts — 路由重载(Fresh Adapter 策略)(Phase 2B) * * 每次 soft reload 创建全新的 adapter 实例,走与生产环境完全一致的 * 路由注册 + 中间件注册 + 错误处理注册 + buildHandler 流程。 * * 为什么需要 Fresh Adapter: * * | 问题 | 影响 | * |-----------------------------------------|---------------------------------------------------| * | hot reload 用 Vext 自建 router,prod 用 | 路由匹配行为不一致(优先级、通配符、参数提取) | * | Hono trie router | | * | 绕过 adapter 层直接构建 handler | Adapter 注入的逻辑丢失(VextReq/Res 转换等) | * | Hono 不支持 clearRoutes/removeRoute | 无法在同一 Hono 实例上增量更新 | * * 解决方案:每次 soft reload 创建全新的 adapter 实例。 * 旧 adapter 实例由 GC 自动回收。 * * 核心流程: * * 1. resolveAdapter(config, app) → 创建全新 adapter 实例 * 2. 注册内置中间件(requestId / cors / body-parser / rate-limit / response-wrapper) * 3. 注册插件全局中间件 * 4. 注册路由(从 outDir/routes/ 重新加载路由文件并注册到新 adapter) * 5. 注册错误处理 + 404 兜底 * 6. adapter.buildHandler() → 返回新的 requestHandler * * 安全保证: * * - 新 adapter 走与生产环境完全一致的代码路径 * - 路由注册表是干净的(全新实例),无需清除旧路由 * - buildHandler() 返回的 handler 与 listen() 内部使用的完全一致 * - 如果重载过程中任何步骤失败,不调用 HotSwappableHandler.swap(), * 旧 handler 通过闭包继续服务 * * @module lib/dev/route-reloader * @see 11b-soft-reload.md §5(路由重载 — Fresh Adapter 策略) * @see 11e-edge-cases.md §1(Reload 失败回退) * @see 08-adapter.md(VextAdapter 接口) * @see IMPLEMENTATION-PLAN.md 任务 2.2b */ import type { IncomingMessage, ServerResponse } from "node:http"; /** * Node.js HTTP 请求处理函数类型 * * 与 http.createServer() 的回调签名一致。 * adapter.buildHandler() 返回此类型。 */ export type RequestHandler = (req: IncomingMessage, res: ServerResponse) => void; /** * 最小化的 VextMiddleware 类型 * * 与框架的 VextMiddleware 签名兼容。 * 使用局部类型避免循环依赖。 */ export type RouteReloaderMiddleware = (ctx: unknown, next: () => Promise) => unknown | Promise; /** * 最小化的 VextErrorMiddleware 类型 */ export type RouteReloaderErrorMiddleware = (error: Error, ctx: unknown, next: () => Promise) => unknown | Promise; /** * 最小化的 VextAdapter 接口(仅包含 route-reloader 需要的方法) */ export interface RouteReloaderAdapter { readonly name: string; registerMiddleware(middleware: RouteReloaderMiddleware): void; registerRoute(method: string, path: string, chain: RouteReloaderMiddleware[]): void; registerErrorHandler(handler: RouteReloaderErrorMiddleware): void; registerNotFound(handler: RouteReloaderMiddleware): void; buildHandler(): RequestHandler; } /** * 最小化的 VextApp 接口(仅包含 route-reloader 需要的字段) */ export interface RouteReloaderApp { config: Record; logger: { info(...args: unknown[]): void; warn(...args: unknown[]): void; debug(...args: unknown[]): void; error(...args: unknown[]): void; }; adapter: RouteReloaderAdapter; services: Record; hooks?: VextInternalHooks; cache?: { clear(): Promise; }; } /** * 中间件注册表条目 * * 与 middleware-loader 的 MiddlewareRegistryEntry 兼容。 */ export interface MiddlewareRegistryEntry { handler: RouteReloaderMiddleware | ((options?: Record) => RouteReloaderMiddleware); defaultOptions: Record | undefined; kind: "middleware" | "factory"; } /** * 中间件注册表 * * name → MiddlewareRegistryEntry 的映射。 */ export type MiddlewareRegistry = Record; /** * Adapter 解析函数类型 * * 与 adapter-resolver.ts 的 resolveAdapter 签名兼容。 * v2.4 变更:返回 Promise(resolveAdapter 改为异步动态 import)。 */ export type AdapterResolver = (config: Record, app: RouteReloaderApp) => Promise; /** * 路由加载函数类型 * * 与 router-loader.ts 的 loadRoutes 签名兼容。 */ export type RoutesLoader = (app: RouteReloaderApp, routesDir: string, options: { middlewareDefs: MiddlewareRegistry | Map; globalMiddlewares: RouteReloaderMiddleware[]; }, collector?: RouteMetadataCollector | null) => Promise; /** * 错误处理器工厂函数类型 */ export type ErrorHandlerFactory = (config: Record) => RouteReloaderErrorMiddleware; /** * 404 处理器工厂函数类型 */ export type NotFoundHandlerFactory = () => RouteReloaderMiddleware; /** * 内置中间件创建器集合 * * 用于在新 adapter 上注册所有内置中间件, * 保持与生产环境 bootstrap 完全一致的中间件栈。 */ export interface BuiltinMiddlewareCreators { /** * 创建 requestId 中间件 * * 需要从 config 中读取 requestId 配置, * 以及从 internals 获取 requestId 生成器。 */ createRequestIdMiddleware?: (config: Record) => RouteReloaderMiddleware; /** * 创建 cors 中间件 */ createCorsMiddleware?: (config: Record) => RouteReloaderMiddleware; /** * 创建 body-parser 中间件 */ createBodyParserMiddleware?: (config: Record) => RouteReloaderMiddleware; /** * 创建 rate-limit 中间件 */ createRateLimitMiddleware?: (config: Record) => RouteReloaderMiddleware; /** * response-wrapper 中间件(已创建好的实例) */ responseWrapper?: RouteReloaderMiddleware; /** * 创建 access-log 中间件 * * 需要从 config 中读取 accessLog 配置, * 以及使用 app.logger 输出日志。 */ createAccessLogMiddleware?: (config: Record) => RouteReloaderMiddleware; } /** * 路由重载选项 */ export interface ReloadRoutesOptions { /** VextApp 实例 */ app: RouteReloaderApp; /** 编译产物目录(.vext/dev/ 的绝对路径) */ outDir: string; /** 中间件注册表(从 middleware-loader 加载的) */ middlewareDefs: MiddlewareRegistry | Map; /** 插件注册的全局中间件列表 */ globalMiddlewares: RouteReloaderMiddleware[]; /** * Adapter 解析函数 * * 用于创建全新的 adapter 实例。 * 通常传入 adapter-resolver.ts 的 resolveAdapter 函数。 */ resolveAdapter: AdapterResolver; /** * 路由加载函数 * * 用于从 routesDir 扫描并注册所有路由到新 adapter。 * 通常传入 router-loader.ts 的 loadRoutes 函数。 */ loadRoutes: RoutesLoader; /** * 错误处理器工厂 * * 用于创建错误处理中间件。 * 通常传入 error-handler.ts 的 createErrorHandler 函数。 */ createErrorHandler: ErrorHandlerFactory; /** * 404 处理器工厂 * * 用于创建 404 兜底中间件。 * 通常传入 error-handler.ts 或 dev-bootstrap.ts 的 createNotFoundHandler 函数。 */ createNotFoundHandler: NotFoundHandlerFactory; /** * 内置中间件创建器(可选) * * 如果提供,将在新 adapter 上注册所有内置中间件。 * 如果不提供,假设内置中间件已包含在 globalMiddlewares 中, * 或者调用方自行处理内置中间件注册。 */ builtinMiddlewares?: BuiltinMiddlewareCreators; /** * OpenAPI 配置(可选) * * 如果提供且 enabled 不为 false,在路由重载后自动: * 1. 创建 RouteMetadataCollector 收集路由元信息 * 2. 使用 OpenAPIGenerator 生成新的 OpenAPI spec * 3. 在新 adapter 上注册 /docs 和 /openapi.json 端点 * * 确保热重载后文档端点仍然可用(修复 BUG-022)。 */ openapiConfig?: Record; } /** * 路由重载结果 */ export interface RouteReloadResult { /** 新的请求处理函数(供 HotSwappableHandler.swap 使用) */ handler: RequestHandler; /** 新创建的 adapter 实例 */ adapter: RouteReloaderAdapter; } /** * reloadRoutes — 重载路由并构建新的 requestHandler * * 核心策略:创建全新的 adapter 实例 * - Hono 等框架不支持清除路由 → 新建实例解决 * - 新 adapter 走与生产环境完全一致的注册 + 构建流程 * - 旧 adapter 实例由 GC 自动回收 * * 流程: * 1. 创建全新的 adapter 实例(不是复用旧的!) * 2. 注册内置中间件(如果提供了 builtinMiddlewares) * 3. 注册插件全局中间件 * 4. 注册错误处理 + 404 兜底 * 5. 加载路由文件并注册到新 adapter * 6. adapter.buildHandler() → 返回新 handler * * @param options 重载选项 * @returns 路由重载结果(新 handler + 新 adapter) * @throws 任何步骤失败时向上抛出(调用方不应调用 swap) */ export declare function reloadRoutes(options: ReloadRoutesOptions): Promise; /** * createSimpleRouteReloader — 创建一个预配置的路由重载函数 * * 将所有依赖注入点绑定后,返回一个只需 outDir 和 invalidationSet 的 * 简化重载函数。适合在 soft reload 主流程中使用。 * * @param deps 依赖注入 * @returns 简化的路由重载函数 * * @example * ```ts * // 初始化时 * const reloadRoutesFn = createSimpleRouteReloader({ * app, * resolveAdapter, * loadRoutes, * createErrorHandler, * createNotFoundHandler, * middlewareDefs, * globalMiddlewares, * }) * * // soft reload 时 * const { handler } = await reloadRoutesFn(outDir) * hotHandler.swap(handler) * ``` */ export declare function createSimpleRouteReloader(deps: { app: RouteReloaderApp; resolveAdapter: AdapterResolver; loadRoutes: RoutesLoader; createErrorHandler: ErrorHandlerFactory; createNotFoundHandler: NotFoundHandlerFactory; middlewareDefs: MiddlewareRegistry | Map; globalMiddlewares: RouteReloaderMiddleware[]; builtinMiddlewares?: BuiltinMiddlewareCreators; openapiConfig?: Record; }): (outDir: string) => Promise;