@faapi/faapi 4.3.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +14 -29
- package/dist/index.js.map +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/resolveAlias.ts","../../src/utils/prodPaths.ts","../../src/utils/readTsconfig.ts","../../src/cli/aliasPlugin.ts","../../src/cli/collectImports.ts","../../src/cli/compileConfig.ts","../../src/utils/atomicWrite.ts","../../src/cli/generateRoutes.ts","../../src/router/constants.ts","../../src/utils/normalizePath.ts","../../src/router/parseRouteFile.ts","../../src/utils/importWithCacheBust.ts","../../src/middleware/loadMiddlewares.ts","../../src/router/scanRoutes.ts","../../src/router/sortRoutes.ts","../../src/tools/scanTools.ts","../../src/ast/jsDocMetadata.ts","../../src/ast/extractToolMetadata.ts","../../src/ast/createProgram.ts","../../src/ast/resolveTypeNode.ts","../../src/ast/extractHandlerTypes.ts","../../src/runtime/inputType.ts","../../src/validator/schemaName.ts","../../src/injection/resolveInjection.ts","../../src/injection/analyzeInjection.ts","../../src/cli/collectRouteSchemaSources.ts","../../src/ast/generateZodSchema.ts","../../src/cli/generateSchemaFiles.ts","../../src/cli/generateToolArtifacts.ts","../../src/agents/scanAgents.ts","../../src/ast/extractAgentMetadata.ts","../../src/cli/generateAgentArtifacts.ts","../../src/config/loadConfig.ts","../../src/cli/loadEnv.ts","../../../../node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js","../../../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/handler.js","../../../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js","../../src/cli/compileSourceFiles.ts","../../src/cli/compileDevRoutes.ts","../../src/cli/rebuildScheduler.ts","../../src/cli/watcher.ts","../../src/router/detectRouteConflicts.ts","../../src/router/matchRoute.ts","../../src/loader/resolveExports.ts","../../src/loader/validateRouteModule.ts","../../src/cli/compileOnDemand.ts","../../src/loader/loadRouteModule.ts","../../src/runtime/sse.ts","../../src/errors/FaapiError.ts","../../src/errors/httpErrors.ts","../../src/response/responseFormatter.ts","../../src/runtime/createContext.ts","../../src/utils/queryToObject.ts","../../src/utils/parseJsonBody.ts","../../src/utils/parseMultipart.ts","../../src/runtime/resolveInput.ts","../../src/utils/isPlainObject.ts","../../src/response/pendingMeta.ts","../../src/response/toResponse.ts","../../src/injection/registries.ts","../../src/injection/agentRegistry.ts","../../src/injection/agentHandle.ts","../../src/injection/injectParams.ts","../../src/runtime/invokeHandler.ts","../../src/response/sendNodeResponse.ts","../../src/validator/validateInput.ts","../../src/utils/getClientIp.ts","../../src/middleware/cors.ts","../../src/middleware/helmet.ts","../../src/middleware/compression.ts","../../src/middleware/etag.ts","../../src/middleware/logger.ts","../../src/errors/formatErrorResponse.ts","../../src/server/serverUtils.ts","../../src/runtime/wsHandler.ts","../../src/server/handleWsUpgrade.ts","../../src/server/createServer.ts","../../src/server/startServer.ts","../../src/cli/loadPlugins.ts","../../src/cli/createAppCore.ts","../../src/cli/createDevApp.ts","../../src/cli/devCommand.ts","../../src/cli/compileBuildRoutes.ts","../../src/cli/buildCommand.ts","../../../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs","../../src/cli/index.ts"],"sourcesContent":["import type { TsconfigPathsConfig } from './readTsconfig';\n\n/**\n * 按 tsconfig paths 配置把 import specifier 解析为候选绝对路径\n *\n * 纯函数,不检查文件存在性。调用方需自行尝试 .ts/.js/index 等后缀。\n * 支持精确匹配(无 *)和单个 * 通配匹配。\n */\nexport function resolveAlias(specifier: string, config: TsconfigPathsConfig): string[] {\n const candidates: string[] = [];\n\n for (const [pattern, targets] of Object.entries(config.paths)) {\n const wildcardIndex = pattern.indexOf('*');\n\n if (wildcardIndex === -1) {\n // 精确匹配\n if (specifier === pattern) {\n candidates.push(...targets);\n }\n continue;\n }\n\n // 通配匹配:按 * 分割为 prefix / suffix\n const prefix = pattern.slice(0, wildcardIndex);\n const suffix = pattern.slice(wildcardIndex + 1);\n\n if (\n specifier.startsWith(prefix) &&\n specifier.endsWith(suffix) &&\n specifier.length >= prefix.length + suffix.length\n ) {\n const captured = specifier.slice(prefix.length, specifier.length - suffix.length);\n for (const target of targets) {\n candidates.push(target.replace('*', captured));\n }\n }\n }\n\n return candidates;\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\n\n/** 路由源码目录(写死为 src) */\nexport const APP_DIR = 'src';\n\n/** 路由文件扫描 patterns(devCommand / buildCommand / createAppCore 共用) */\nexport const ROUTE_PATTERNS = ['src/api/**/*.ts'];\n\n/**\n * 把源码 filePath(src/api/hello/handler.ts)转为产物路径(<dist>/api/hello/handler.js)\n *\n * 产物结构打平 src/ 前缀:去掉 `src/`,加 dist 前缀,.ts → .js。\n * 已带 `<dist>/` 前缀的输入保持不变(幂等)。\n */\nexport function toProdFilePath(filePath: string, dist: string): string {\n let rel = filePath.replace(/\\\\/g, '/');\n // 去掉 src/ 前缀(打平产物结构)\n if (rel.startsWith('src/')) {\n rel = rel.slice(4);\n }\n const jsPath = rel.replace(/\\.ts$/, '.js');\n return jsPath.startsWith(`${dist}/`) ? jsPath : `${dist}/${jsPath}`;\n}\n\n/**\n * 源文件后缀 → 产物后缀(.ts/.tsx/.jsx → .js,其余原样)\n */\nexport function toProdExtension(filePath: string): string {\n if (filePath.endsWith('.ts')) return filePath.slice(0, -3) + '.js';\n if (filePath.endsWith('.tsx')) return filePath.slice(0, -4) + '.js';\n if (filePath.endsWith('.jsx')) return filePath.slice(0, -4) + '.js';\n return filePath;\n}\n\n/**\n * 规范化路径为 realpath(处理 macOS /tmp → /private/tmp 等符号链接场景)\n *\n * 目录不存在时回退到原路径(不抛错),保证调用方逻辑稳定。\n */\nexport function toRealPath(p: string): string {\n try {\n return fs.realpathSync(p);\n } catch {\n return p;\n }\n}\n\n/**\n * 判断 filePath 是否位于 dir 目录下(不依赖路径前缀字符串比较,兼容符号链接规范化差异)\n *\n * 基于 `path.relative`:相对路径不以 `..` 开头且非绝对路径时视为位于 dir 下。\n * 调用前应先用 `toRealPath` 规范化两侧路径,确保前缀一致。\n */\nexport function isInsideDir(filePath: string, dir: string): boolean {\n const rel = path.relative(dir, filePath);\n return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}\n\n/**\n * 源文件相对路径剥离 `src/` 前缀(打平产物结构)\n *\n * schema/tool zod.js 输出路径计算的公共步骤:\n * `src/api/hello/handler.ts` → `api/hello/handler.ts`(取目录后 join dist + zod.js)\n */\nexport function stripSrcPrefix(sourceFile: string): string {\n const rel = sourceFile.replace(/\\\\/g, '/');\n return rel.startsWith('src/') ? rel.slice(4) : rel;\n}\n","import ts from 'typescript';\nimport path from 'node:path';\nimport fs from 'node:fs';\n\n/**\n * tsconfig paths 别名配置(已规范化为绝对路径)\n *\n * - baseUrl:绝对路径(无 baseUrl 时为 tsconfig 所在目录)\n * - paths:pattern -> 绝对路径数组(保留 * 通配符,供 resolveAlias 替换)\n */\nexport interface TsconfigPathsConfig {\n baseUrl: string;\n paths: Record<string, string[]>;\n}\n\n/**\n * 读取项目 tsconfig.json,提取 baseUrl + paths,规范化为绝对路径配置\n *\n * 用 TypeScript Compiler API 解析(处理注释、extends 合并)。\n * 仅读取 rootDir 下的 tsconfig.json,不向上递归查找。\n * 无 paths 时返回 null。\n *\n * 按 tsconfig mtime 缓存:每次编译(watcher 每轮重建、每次首请求按需编译)都会\n * 调本函数,Compiler API 的 readConfigFile + extends 链合并在长 extends 链项目上\n * 开销可观。tsconfig 变化时 mtime 变化 → 缓存自动失效,无需手动清理。\n */\ninterface TsconfigCacheEntry {\n mtimeMs: number;\n config: TsconfigPathsConfig | null;\n}\nconst tsconfigCache = new Map<string, TsconfigCacheEntry>();\n\nexport function readTsconfig(rootDir: string): TsconfigPathsConfig | null {\n const tsconfigPath = path.resolve(rootDir, 'tsconfig.json');\n if (!fs.existsSync(tsconfigPath)) return null;\n\n // mtime 缓存:tsconfig 未变化时直接复用解析结果\n let mtimeMs: number;\n try {\n mtimeMs = fs.statSync(tsconfigPath).mtimeMs;\n } catch {\n return null;\n }\n const cached = tsconfigCache.get(tsconfigPath);\n if (cached && cached.mtimeMs === mtimeMs) {\n return cached.config;\n }\n\n const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n if (configFile.error || !configFile.config) return null;\n\n // parseJsonConfigFileContent 处理 extends 合并、解析 baseUrl 为绝对路径\n const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, rootDir);\n\n // baseUrl:parseJsonConfigFileContent 已解析为绝对路径;未设置时回退到 rootDir\n const baseUrl = parsed.options.baseUrl ?? rootDir;\n\n const rawPaths = parsed.options.paths;\n const config: TsconfigPathsConfig | null = rawPaths\n ? (() => {\n // paths 目标:原始字符串相对 baseUrl,解析为绝对路径(保留 *)\n const paths: Record<string, string[]> = {};\n for (const [pattern, targets] of Object.entries(rawPaths)) {\n paths[pattern] = targets.map((t) => path.resolve(baseUrl, t));\n }\n return { baseUrl, paths };\n })()\n : null;\n\n tsconfigCache.set(tsconfigPath, { mtimeMs, config });\n return config;\n}\n","import type { Plugin } from 'esbuild';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { resolveAlias } from '../utils/resolveAlias';\nimport { APP_DIR, isInsideDir, toProdExtension, toRealPath } from '../utils/prodPaths';\nimport { readTsconfig, type TsconfigPathsConfig } from '../utils/readTsconfig';\n\n// 兼容 re-export:toProdExtension 此前由本模块导出\nexport { toProdExtension };\n\n/**\n * 把候选源文件路径转为产物 import 路径(相对 importer,POSIX 风格,带 .js 后缀)\n */\nfunction toProdImportPath(sourceFile: string, importer: string): string {\n const importerDir = path.dirname(importer);\n let rel = path.relative(importerDir, sourceFile);\n rel = rel.split(path.sep).join('/');\n if (!rel.startsWith('.')) rel = './' + rel;\n return toProdExtension(rel);\n}\n\n/**\n * 把 src 下的源文件路径转为剥离 src/ 前缀的产物路径(相对 dist 根,POSIX 风格,带 .js 后缀)\n *\n * 用于 src 外 importer(config 文件、本地插件等,位于 rootDir)引用 src 内模块的场景:\n * - 源文件 `<rootDir>/src/lib/errors.ts` → 产物 `dist/lib/errors.js` → 相对 dist 根 `./lib/errors.js`\n *\n * 与 `toProdImportPath` 的区别:后者相对 importer 目录(适用于 importer 也在 src 内的场景,\n * outbase 打平后相对结构不变);本函数相对 src 根(适用于 importer 在 src 外的场景,\n * 需要剥离 src/ 前缀以匹配 compileDevRoutes 的打平产物结构)。\n *\n * 内部用 `toRealPath` 规范化 appDirAbs,兼容 macOS 符号链接(esbuild onLoad 传入的\n * args.path 已是 realpath,未规范化的 appDirAbs 会导致前缀比较失败)。\n */\nfunction toStrippedProdImportPath(sourceFile: string, rootDir: string): string {\n const appDirAbs = toRealPath(path.resolve(rootDir, APP_DIR));\n const sourceReal = toRealPath(sourceFile);\n let rel = path.relative(appDirAbs, sourceReal);\n rel = rel.split(path.sep).join('/');\n if (!rel.startsWith('.')) rel = './' + rel;\n return toProdExtension(rel);\n}\n\n/**\n * 把「相对 dist 根」的产物路径改写为「相对 importer 产物位置」的 import 路径\n *\n * src 外 importer 的产物位置 = importer 源文件相对 rootDir 的路径(.js 后缀),\n * 因为 src 外编译(compileConfig 步骤 1a / compileProjectModules)以 rootDir 为\n * outbase,产物在 `<dist>/` 下保留相对结构。\n *\n * - config `faapi.config.ts` → 产物 `faapi.config.js`(dist 根)→ `./lib/errors.js` 保持不变\n * - 插件 `plugins/db-skills.ts` → 产物 `plugins/db-skills.js`(dist 子目录)\n * → 引用 `./lib/errors.js` 需改写为 `../lib/errors.js`(相对 dist 根的路径在\n * 子目录模块里会解析到 `plugins/lib/errors.js`,错位)\n */\nfunction toProdImportFromImporter(importer: string, rootDir: string, relFromDist: string): string {\n // 两侧 realpath 归一化,兼容 macOS 符号链接(esbuild onLoad 传入的 importer 已是\n // realpath,rootDir 可能仍是 /var 形式——不归一化则 relative 产生绕行路径)\n const importerRel = path\n .relative(toRealPath(path.resolve(rootDir)), toRealPath(importer))\n .split(path.sep)\n .join('/');\n const importerProdDir = path.posix.dirname(toProdExtension(importerRel));\n if (importerProdDir === '.') return relFromDist;\n const target = relFromDist.replace(/^\\.\\//, '');\n let rel = path.posix.relative(importerProdDir, target);\n if (!rel.startsWith('.')) rel = './' + rel;\n return rel;\n}\n\n/**\n * aliasPlugin 扩展选项\n *\n * - `rootDir`:启用 src/ 前缀剥离。当 importer 不在 src 下(如 config 文件)\n * 且引用的源文件在 src 下时,重写为剥离前缀的产物路径(相对 dist 根),以匹配\n * `compileDevRoutes`/`compileBuildRoutes` 的打平产物结构。\n */\nexport interface AliasPluginOptions {\n /** 项目根目录(启用剥离时必填) */\n rootDir?: string;\n}\n\n/**\n * 产物后缀集合(已带这些后缀的 specifier 视为产物路径,不重写)\n */\nconst PROD_EXTS = ['.js', '.mjs', '.cjs'];\n\n/**\n * 源文件后缀集合(重写时尝试这些后缀定位实际文件)\n */\nconst SOURCE_EXTS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];\n\n/**\n * index 文件后缀集合(specifier 指向目录时尝试)\n */\nconst INDEX_EXTS = [\n '/index.ts',\n '/index.tsx',\n '/index.js',\n '/index.jsx',\n '/index.mjs',\n '/index.cjs',\n];\n\n/**\n * 解析相对 specifier 到实际源文件绝对路径\n *\n * - 已带产物后缀(.js/.mjs/.cjs)且文件存在 → 返回该路径(不重写)\n * - 已带源后缀(.ts/.tsx/.jsx)且文件存在 → 返回该路径(重写为 .js)\n * - 无后缀 → 尝试各源后缀 + index 文件\n *\n * @param importer 导入文件绝对路径\n * @param specifier 相对 specifier(./xxx 或 ../xxx)\n * @returns 解析到的源文件绝对路径,或 null(未解析到)\n */\nexport function resolveRelativeSpecifier(importer: string, specifier: string): string | null {\n const importerDir = path.dirname(importer);\n const base = path.resolve(importerDir, specifier);\n\n // 已带产物后缀:文件存在则返回(视为产物路径,不重写)\n if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {\n return fs.existsSync(base) ? base : null;\n }\n\n // 已带源后缀:文件存在则返回(将重写为产物后缀)\n if (/\\.(ts|tsx|jsx)$/.test(specifier)) {\n return fs.existsSync(base) ? base : null;\n }\n\n // 无后缀:尝试各源后缀\n for (const ext of SOURCE_EXTS) {\n const file = base + ext;\n if (fs.existsSync(file)) return file;\n }\n\n // 无后缀:尝试 index 文件\n for (const indexExt of INDEX_EXTS) {\n const file = base + indexExt;\n if (fs.existsSync(file)) return file;\n }\n\n return null;\n}\n\n/**\n * esbuild specifier 重写插件(dev/build/config 编译共用)\n *\n * 在 `onLoad` 阶段把源码 import/export 中的 specifier 重写为产物相对路径(带 `.js` 后缀):\n *\n * 1. **相对 specifier**(`./xxx`、`../xxx`):解析到实际源文件,重写为产物相对路径。\n * - 无后缀 → 解析 + 加 `.js`\n * - `.ts`/`.tsx`/`.jsx` → 改为 `.js`\n * - `.js`/`.mjs`/`.cjs` → 不处理(视为产物路径)\n * - 解析失败(文件不存在)→ 原样保留\n *\n * 2. **别名 specifier**(tsconfig.paths,如 `@/xxx`):调 `resolveAlias` 解析候选路径,\n * 命中则重写为产物相对路径。\n *\n * `bundle: false` 模式下 esbuild 不递归解析依赖(`onResolve` 不触发),specifier 会原样\n * 保留到产物 `.js`,运行时 Node.js ESM loader 无法解析(无后缀推断)。本插件通过\n * `onLoad` 介入,确保产物中所有相对 import 都带 `.js` 后缀。\n *\n * **src/ 前缀剥离**(可选,通过 `options.rootDir` 启用):\n * 当 importer 不在 src 下(如 `faapi.config.ts` 位于 rootDir)且引用的源文件在 src 下时,\n * 重写为剥离 src/ 前缀的产物路径(相对 dist 根),以匹配 `compileDevRoutes`/`compileBuildRoutes`\n * 的打平产物结构。典型场景:config 引用项目模块(如 `./src/lib/errors` → `./lib/errors.js`),\n * 使 config 与 routes 共享同一份模块产物,`instanceof` 跨边界生效。\n *\n * 覆盖的 import 形式:\n * - `import { x } from './base'` / `import { x } from '@/base'`\n * - `export { x } from './base'`(含 `from`,被同一正则匹配)\n * - `import('./base')`(动态)\n *\n * 绝对路径(`/`)、`file:` URL、`node:` 协议不处理,交 esbuild 默认。\n */\nexport function createAliasPlugin(\n config: TsconfigPathsConfig,\n options?: AliasPluginOptions,\n): Plugin {\n // 匹配 from '...' / from \"...\" 和 import('...') / import(\"...\")\n const SPEC_RE = /(\\bfrom\\s*|import\\s*\\(\\s*)(['\"])([^'\"]+)\\2/g;\n // 预计算 src 绝对路径(realpath 规范化,用于判断 importer 和 resolved 是否在 src 内)\n // 规范化是为了兼容 macOS 符号链接:esbuild onLoad 传入的 args.path 是 realpath,\n // 未规范化的 appDirAbs 会导致 isInsideDir 比较失败(/var/folders vs /private/var/folders)\n const appDirAbs = options?.rootDir ? toRealPath(path.resolve(options.rootDir, APP_DIR)) : null;\n return {\n name: 'faapi-alias',\n setup(build) {\n build.onLoad({ filter: /\\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {\n let source: string;\n try {\n source = fs.readFileSync(args.path, 'utf8');\n } catch {\n return undefined;\n }\n const importer = args.path;\n // importer 是否在 src 外(启用剥离时才计算,用 isInsideDir 兼容符号链接)\n const importerOutsideAppDir = appDirAbs ? !isInsideDir(importer, appDirAbs) : false;\n let modified = false;\n const newSource = source.replace(SPEC_RE, (full, prefix, quote, specifier) => {\n // 绝对路径 / file: URL / node: 协议不处理\n if (\n specifier.startsWith('/') ||\n specifier.startsWith('file:') ||\n specifier.startsWith('node:')\n ) {\n return full;\n }\n\n // 相对 specifier(./ 或 ../):解析到实际文件并重写\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n const resolved = resolveRelativeSpecifier(importer, specifier);\n if (resolved) {\n // 已带产物后缀的 specifier 不重写(resolveRelativeSpecifier 返回原路径)\n if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {\n return full;\n }\n // src 前缀剥离:importer 在 src 外、resolved 在 src 内\n // → 重写为剥离前缀的产物路径(相对 importer 产物位置)\n if (appDirAbs && importerOutsideAppDir && isInsideDir(resolved, appDirAbs)) {\n modified = true;\n return `${prefix}${quote}${toProdImportFromImporter(\n importer,\n options!.rootDir!,\n toStrippedProdImportPath(resolved, options!.rootDir!),\n )}${quote}`;\n }\n modified = true;\n return `${prefix}${quote}${toProdImportPath(resolved, importer)}${quote}`;\n }\n return full;\n }\n\n // 别名 specifier:调 resolveAlias 解析\n const candidates = resolveAlias(specifier, config);\n for (const candidate of candidates) {\n for (const ext of SOURCE_EXTS) {\n const file = candidate + ext;\n if (fs.existsSync(file)) {\n modified = true;\n // 别名解析到的文件如果在 src 内且 importer 在 src 外,同样剥离前缀\n if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {\n return `${prefix}${quote}${toProdImportFromImporter(\n importer,\n options!.rootDir!,\n toStrippedProdImportPath(file, options!.rootDir!),\n )}${quote}`;\n }\n return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;\n }\n }\n for (const indexExt of INDEX_EXTS) {\n const file = candidate + indexExt;\n if (fs.existsSync(file)) {\n modified = true;\n if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {\n return `${prefix}${quote}${toProdImportFromImporter(\n importer,\n options!.rootDir!,\n toStrippedProdImportPath(file, options!.rootDir!),\n )}${quote}`;\n }\n return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;\n }\n }\n }\n return full;\n });\n if (!modified) return undefined;\n return { contents: newSource, loader: 'default' };\n });\n },\n };\n}\n\n/**\n * 读取 tsconfig paths 并构造 specifier 重写插件\n *\n * 始终返回含本插件的数组:相对路径重写不依赖 tsconfig(`bundle: false` 下 Node ESM 必需),\n * 别名重写依赖 tsconfig.paths(无 paths 时别名不重写)。\n *\n * @param rootDir 项目根目录。传入时启用 src/ 前缀剥离:importer 在 src 外\n * 且引用 src 内文件时,重写为剥离前缀的产物路径。主要用于 compileConfig\n * 编译 config 文件(位于 rootDir)引用项目模块(位于 src)的场景。\n */\nexport function buildAliasPlugins(rootDir: string): Plugin[] {\n const tsconfig = readTsconfig(rootDir);\n return [createAliasPlugin(tsconfig ?? { baseUrl: '.', paths: {} }, { rootDir })];\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { isInsideDir, toRealPath } from '../utils/prodPaths';\nimport { resolveRelativeSpecifier } from './aliasPlugin';\nimport { resolveAlias } from '../utils/resolveAlias';\nimport { readTsconfig } from '../utils/readTsconfig';\n\n/**\n * 匹配 from '...' / from \"...\" / import('...') / import(\"...\") 中的 specifier\n */\nconst SPEC_RE = /(\\bfrom\\s*|import\\s*\\(\\s*)(['\"])([^'\"]+)\\2/g;\n\n/**\n * 从源文件内容中提取所有 import specifier\n *\n * 覆盖相对 specifier(./xxx、../xxx)与 tsconfig paths 别名 specifier(如 @/xxx)。\n * 动态 import(import('...'))同样覆盖。\n */\nfunction extractImportSpecifiers(source: string): string[] {\n const specifiers: string[] = [];\n let match: RegExpExecArray | null;\n SPEC_RE.lastIndex = 0;\n while ((match = SPEC_RE.exec(source)) !== null) {\n specifiers.push(match[3]!);\n }\n return specifiers;\n}\n\n/**\n * 从目录解析 specifier 为源文件绝对路径(探测 .ts/.tsx 等源后缀与 index 文件)\n *\n * 复用 resolveRelativeSpecifier 的探测逻辑:它按 importer 文件所在目录解析,\n * 传入目录内的合成文件名即可从指定目录解析。\n */\nfunction resolveSpecifierFromDir(dir: string, specifier: string): string | null {\n return resolveRelativeSpecifier(path.join(dir, '__faapi_probe__.ts'), specifier);\n}\n\n/**\n * 递归收集入口文件的所有项目内 import 依赖(含传递依赖),按是否位于 appDir 内分组\n *\n * 供两类调用方使用:\n * - `compileConfig`:编译 config 源 + 其引用的项目模块,使 config 产物的 import\n * 在运行时能解析到实际文件(与 routes 编译的同一产物共享,instanceof 跨边界生效)\n * - `compileOnDemand.ensureCompiled`:dev 按需编译 handler 时补齐依赖闭包,\n * 否则 handler 引用的共享模块(如 `../../lib/db`)产物不存在,首次请求即\n * ERR_MODULE_NOT_FOUND\n *\n * 解析规则:\n * - 相对 specifier:`resolveRelativeSpecifier` 探测源后缀与 index 文件\n * - 别名 specifier(tsconfig paths,如 `@/lib/db`):`resolveAlias` 解析候选目标后\n * 同样探测源后缀(无 tsconfig/paths 时跳过)\n * - 已带产物后缀(.js/.mjs/.cjs)的 specifier 不递归(视为已编译产物,源码不在此处)\n * - 项目外依赖(node_modules、rootDir 外文件)不收集——按 external 语义运行时解析\n *\n * @param entryFiles 起始文件(绝对路径)\n * @param rootDir 项目根目录\n * @returns 收集到的文件,分为 src 内(打平前缀编译)和 src 外(保留结构编译)两组\n * (绝对路径,去重,不含入口文件本身)\n */\nexport async function collectRelativeImports(\n entryFiles: string[],\n rootDir: string,\n): Promise<{ insideFiles: string[]; outsideFiles: string[] }> {\n // 用 realpath 规范化 src 绝对路径,兼容 macOS 符号链接(esbuild 传入的路径已是 realpath)\n const appDirAbs = toRealPath(path.resolve(rootDir, 'src'));\n const tsconfig = readTsconfig(rootDir);\n const visited = new Set<string>();\n const insideFiles = new Set<string>();\n const outsideFiles = new Set<string>();\n\n async function collect(filePath: string): Promise<void> {\n if (visited.has(filePath)) return;\n visited.add(filePath);\n\n let source: string;\n try {\n source = await fs.promises.readFile(filePath, 'utf8');\n } catch {\n return;\n }\n\n for (const specifier of extractImportSpecifiers(source)) {\n // 已带产物后缀:运行时 import 的就是产物(或第三方 .js),源码不递归\n if (/\\.(js|mjs|cjs)$/.test(specifier)) continue;\n\n let resolved: string | null;\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n resolved = resolveRelativeSpecifier(filePath, specifier);\n } else if (tsconfig) {\n // 别名 specifier:解析候选目标(相对 rootDir)后探测源文件\n resolved = null;\n for (const candidate of resolveAlias(specifier, tsconfig)) {\n const probed = resolveSpecifierFromDir(rootDir, candidate);\n if (probed) {\n resolved = probed;\n break;\n }\n }\n } else {\n resolved = null;\n }\n\n if (!resolved) continue;\n // 项目外文件(node_modules / rootDir 外)不收集。\n // 归类判断用 realpath 归一化两侧(tmpdir 符号链接 /var → /private/var 会误判),\n // 但收集结果保留原始解析路径——esbuild 的 outbase 基于调用方传入的 rootDir 形式,\n // entry 路径必须与 outbase 字符串前缀一致,否则产物落入 _.._ 垃圾目录\n if (!isInsideDir(toRealPath(resolved), toRealPath(path.resolve(rootDir)))) continue;\n\n if (isInsideDir(toRealPath(resolved), appDirAbs)) {\n insideFiles.add(resolved);\n } else {\n outsideFiles.add(resolved);\n }\n await collect(resolved);\n }\n }\n\n for (const entry of entryFiles) {\n await collect(entry);\n }\n\n return {\n insideFiles: Array.from(insideFiles),\n outsideFiles: Array.from(outsideFiles),\n };\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport type { Plugin } from 'esbuild';\nimport { buildAliasPlugins } from './aliasPlugin';\nimport { collectRelativeImports } from './collectImports';\nimport { toProdExtension, toRealPath } from '../utils/prodPaths';\n\n/**\n * 基础配置文件查找顺序(与 loadConfig 保持一致)\n */\nconst BASE_CONFIG_FILES = ['faapi.config.ts', 'faapi.config.js'];\n\n/**\n * 查找基础配置文件(返回源文件名,含后缀)\n *\n * 返回相对 rootDir 的路径(如 'faapi.config.ts'),供 esbuild 作为 entryPoint。\n */\nfunction findBaseConfig(rootDir: string): string | null {\n for (const f of BASE_CONFIG_FILES) {\n if (fs.existsSync(path.join(rootDir, f))) {\n return f;\n }\n }\n return null;\n}\n\n/**\n * 配置文件名 → 产物 import 名(如 'faapi.config.ts' → 'faapi.config.js')\n */\nfunction toProdImport(filename: string): string {\n return toProdExtension(filename);\n}\n\nexport interface CompileConfigOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(如 dist) */\n dist: string;\n}\n\nexport interface CompileConfigResult {\n /** 是否生成了配置产物(无基础配置文件时不生成) */\n generated: boolean;\n /** 输出文件绝对路径(generated=false 时为空字符串) */\n outputFile: string;\n /** 是否命中 mtime 短路缓存跳过了编译(watcher 重建场景,输入无变化时省 3 次 esbuild build) */\n skipped: boolean;\n}\n\n/**\n * compileConfig 的 mtime 短路缓存\n *\n * watcher 每次重建都会调 compileConfig;若无短路,即使配置源毫无变化,\n * 也要付出 3 次 esbuild build + collectRelativeImports 递归读整个依赖图的开销。\n *\n * key 为 `rootDir::dist`,value 记录上次编译的全部输入文件 mtime。\n * 再次调用时输入文件集合 mtime 全部一致且产物仍存在 → 跳过编译;\n * 任一变化/缺失 → 全量重编译并更新缓存。编译抛错时不写缓存(下次可重试)。\n */\ninterface CompileConfigCacheEntry {\n /** 上次编译的输入文件(绝对路径 → mtimeMs) */\n inputs: Map<string, number>;\n /** 主产物绝对路径(faapi-config.js) */\n outputFile: string;\n}\n\nconst compileConfigCache = new Map<string, CompileConfigCacheEntry>();\n\n/** 检查缓存是否仍然新鲜:产物存在 + 全部输入文件存在且 mtime 一致 */\nasync function isCacheFresh(entry: CompileConfigCacheEntry): Promise<boolean> {\n try {\n await fs.promises.stat(entry.outputFile);\n } catch {\n return false;\n }\n for (const [filePath, mtimeMs] of entry.inputs) {\n try {\n const stat = await fs.promises.stat(filePath);\n if (stat.mtimeMs !== mtimeMs) return false;\n } catch {\n return false;\n }\n }\n return true;\n}\n\n/**\n * 匹配 from '...' / from \"...\" / import('...') / import(\"...\") 中的 specifier\n * 与递归收集逻辑见 collectImports.ts(compileConfig 与 compileOnDemand 共用)\n */\n\n/**\n * 创建 external 相对路径插件(步骤 2 用)\n *\n * 在 `bundle: true` 模式下,把所有相对路径 import(./xxx 或 ../xxx)标记为 external,\n * 阻止 esbuild 把已编译的 config 产物 inline 进 faapi-config.js。\n *\n * 这使 faapi-config.js 保留 `import base from './faapi.config.js'` 语句,运行时\n * Node.js ESM loader 加载 faapi.config.js,后者再 import 项目模块产物——\n * 与 routes 编译的同一文件共享,instanceof 生效。\n */\nfunction createExternalRelativePlugin(): Plugin {\n return {\n name: 'faapi-external-relative',\n setup(build) {\n // 相对路径(./ 或 ../开头)标记为 external\n build.onResolve({ filter: /^\\.{1,2}\\// }, (args) => ({\n path: args.path,\n external: true,\n }));\n },\n };\n}\n\n/**\n * 编译 src 外入口文件及其项目内依赖闭包(compileConfig / 本地 TS 插件编译共用)\n *\n * - 入口 + src 外依赖:outbase=rootDir,产物在 `<dist>/` 下保留相对结构\n * (如 `plugins/db-skills.ts` → `dist/plugins/db-skills.js`)\n * - src 内依赖:outbase=rootDir/src(打平前缀,与 compileDevRoutes 一致),\n * 使入口产物 import 的 src 内模块与 routes/config 共享同一份产物(instanceof 生效)\n * - aliasPlugin 重写 specifier:相对路径加 .js 后缀;src 外 importer 引用 src 内\n * 模块时剥离前缀(import 路径相对 importer 产物位置计算,见 aliasPlugin 的\n * toProdImportFromImporter)\n *\n * @param entryPoints src 外入口文件(绝对路径);src 内入口请走 compileDevRoutes /\n * compileBuildRoutes / ensureCompiled(打平产物 + 同一运行时对象)\n * @returns 收集到的 src 内/外依赖文件(供调用方记录 mtime 缓存)\n */\nexport async function compileProjectModules(\n entryPoints: string[],\n rootDir: string,\n dist: string,\n): Promise<{ insideFiles: string[]; outsideFiles: string[] }> {\n const { insideFiles, outsideFiles } = await collectRelativeImports(entryPoints, rootDir);\n\n const esbuild = await import('esbuild');\n const aliasPlugins = buildAliasPlugins(rootDir);\n const absDist = path.resolve(rootDir, dist);\n\n // src 外文件(outbase=rootDir):入口 + src 外依赖,产物保留相对结构\n await esbuild.build({\n entryPoints: [...entryPoints, ...outsideFiles],\n outdir: absDist,\n outbase: rootDir,\n bundle: false,\n platform: 'node',\n format: 'esm',\n sourcemap: true,\n packages: 'external',\n plugins: aliasPlugins,\n logLevel: 'silent',\n });\n\n // src 内依赖(outbase=rootDir/src,打平前缀,与 compileDevRoutes 一致)\n if (insideFiles.length > 0) {\n const appOutbase = path.resolve(rootDir, 'src');\n await esbuild.build({\n entryPoints: insideFiles,\n outdir: absDist,\n outbase: appOutbase,\n bundle: false,\n platform: 'node',\n format: 'esm',\n sourcemap: true,\n packages: 'external',\n plugins: aliasPlugins,\n logLevel: 'silent',\n });\n }\n\n return { insideFiles, outsideFiles };\n}\n\n/**\n * build 时编译配置文件,生成 `dist/faapi-config.js`\n *\n * 采用两步编译,使 config 引用的项目模块与 routes 共享同一份运行时对象(instanceof 跨边界生效):\n *\n * **步骤 1:逐文件编译 config 源文件(`bundle: false`)**\n * - 编译 `faapi.config.ts` → `dist/faapi.config.js`\n * - 递归收集 config 引用的项目模块,按 src 内/外分别编译:\n * - src 内:outbase=rootDir/src(打平前缀,与 compileDevRoutes 一致)→ `dist/lib/errors.js`\n * - src 外:outbase=rootDir → `dist/base.js`\n * - aliasPlugin 重写 specifier:相对路径加 .js 后缀;config 引用 src 内模块时剥离前缀\n *\n * **步骤 2:编译合并入口(`bundle: true` + external 相对路径)**\n * - 生成虚拟入口源码(import 已编译的 config 产物 + export base)\n * - 相对路径 import 标记为 external(不 inline config 产物)\n * - 第三方依赖(`packages: 'external'`)也保持 external\n * - 产物 `dist/faapi-config.js` 保留 `import base from './faapi.config.js'`\n *\n * 产物由 `loadConfig` 在运行时统一 import。环境变量通过 `.env` 文件加载(见 `loadEnv`),\n * 配置文件中通过 `process.env.XXX` 读取,运行时取值。不传 `define`,保留 `process.env` 表达式。\n */\nexport async function compileConfig(options: CompileConfigOptions): Promise<CompileConfigResult> {\n const { rootDir, dist } = options;\n\n const baseConfigName = findBaseConfig(rootDir);\n if (!baseConfigName) {\n // 无基础配置文件:不生成产物,loadConfig 会返回 null\n return { generated: false, outputFile: '', skipped: false };\n }\n\n const absDist = path.resolve(rootDir, dist);\n await fs.promises.mkdir(absDist, { recursive: true });\n\n const outputFile = path.resolve(absDist, 'faapi-config.js');\n const cacheKey = `${toRealPath(rootDir)}::${dist}`;\n\n // mtime 短路:上次编译的全部输入文件无变化且产物仍存在 → 跳过编译\n const cached = compileConfigCache.get(cacheKey);\n if (cached && (await isCacheFresh(cached))) {\n return { generated: true, outputFile: cached.outputFile, skipped: true };\n }\n compileConfigCache.delete(cacheKey);\n\n // 收集 config 入口文件(绝对路径)\n const configEntryPoints: string[] = [path.resolve(rootDir, baseConfigName)];\n\n // 步骤 1:逐文件编译 config 源 + 项目模块(共享实现见 compileProjectModules)\n const { insideFiles: appDirFiles, outsideFiles: nonAppDirFiles } = await compileProjectModules(\n configEntryPoints,\n rootDir,\n dist,\n );\n\n // 步骤 2:编译入口(bundle:true + external 相对路径)\n // 入口源码 import 已编译的 config 产物(带 .js 后缀)+ export base\n const baseImport = `import base from './${toProdImport(baseConfigName)}';`;\n const exportDefault = 'export default base;';\n\n const entryCode = [baseImport, exportDefault].join('\\n');\n\n const esbuild = await import('esbuild');\n await esbuild.build({\n stdin: { contents: entryCode, resolveDir: absDist, loader: 'ts' },\n outfile: outputFile,\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node20',\n sourcemap: true,\n packages: 'external',\n plugins: [createExternalRelativePlugin()],\n logLevel: 'silent',\n });\n\n // 编译成功:记录全部输入文件的 mtime(config 源 + src 内/外依赖模块 + tsconfig——\n // alias 重写依赖 tsconfig paths,tsconfig 变化必须触发重编译),\n // 供下次调用的 mtime 短路判断。编译抛错时不走此处(下次可重试)。\n const inputs = new Map<string, number>();\n const tsconfigPath = path.resolve(rootDir, 'tsconfig.json');\n const inputCandidates = [\n ...configEntryPoints,\n ...appDirFiles,\n ...nonAppDirFiles,\n ...(fs.existsSync(tsconfigPath) ? [tsconfigPath] : []),\n ];\n for (const filePath of inputCandidates) {\n try {\n const stat = await fs.promises.stat(filePath);\n inputs.set(filePath, stat.mtimeMs);\n } catch {\n // 收集阶段存在的文件理论上不会消失;消失则不缓存该文件,下次触发重编译\n }\n }\n compileConfigCache.set(cacheKey, { inputs, outputFile });\n\n return { generated: true, outputFile, skipped: false };\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\n\n/**\n * 原子写文件:先写临时文件再 rename 替换\n *\n * rename 在同一文件系统上是原子的(POSIX),并发读者要么看到旧文件要么看到\n * 新文件,不会读到截断的半成品。用于运行时会 import 的产物(zod.js、路由清单、\n * tool/agent 清单)——dev watch 重建与在途请求并发时,非原子写会让请求 import\n * 到半成品文件,报 SyntaxError/ENOENT 且错误信息不指向根因。\n *\n * 与 compileSourceFiles 的 esbuild 原子写(write:false + tmp + rename)同源语义。\n *\n * @param outputPath 目标绝对路径\n * @param content 文件内容\n */\nexport async function atomicWriteFile(outputPath: string, content: string): Promise<void> {\n await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });\n const tmp = `${outputPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;\n await fs.promises.writeFile(tmp, content, 'utf-8');\n await fs.promises.rename(tmp, outputPath);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type {\n RouteManifest,\n WsRouteManifest,\n RouteRecord,\n WsRouteRecord,\n} from '../router/routeTypes';\nimport type { HttpMethod } from '../router/constants';\nimport { toProdFilePath } from '../utils/prodPaths';\nimport { atomicWriteFile } from '../utils/atomicWrite';\n\n/**\n * 序列化路由记录(可写入 JS 模块,无函数引用)\n *\n * build 时生成,start 时读取。filePath/middlewarePaths 已转为 prd 形式\n * (打平 src/ 前缀 + dist 前缀 + .js)。\n */\nexport interface SerializedRouteRecord {\n method: HttpMethod;\n urlPath: string;\n filePath: string;\n paramNames: string[];\n isDynamic: boolean;\n isCatchAll?: boolean;\n /** 从根到路由目录的中间件文件绝对路径列表(已排序,根在前) */\n middlewarePaths: string[];\n}\n\nexport interface SerializedWsRouteRecord {\n urlPath: string;\n filePath: string;\n paramNames: string[];\n isDynamic: boolean;\n isCatchAll?: boolean;\n middlewarePaths: string[];\n}\n\nexport interface SerializedRouteManifest {\n routes: SerializedRouteRecord[];\n wsRoutes: SerializedWsRouteRecord[];\n}\n\n/**\n * 序列化路由清单为可写入 JS 模块的结构\n *\n * - filePath 转为产物路径(打平 src/ 前缀 + dist 前缀 + .js)\n * - middlewares/injectors 不序列化(函数无法序列化),改为 middlewarePaths(中间件文件绝对路径列表)\n * - middlewarePaths 已排序(根在前,路由目录在后),start 时按序加载即可还原洋葱模型\n *\n * @param rootDir 项目根目录\n * @param dist 产物目录(dist 或 .faapi),用于转换 filePath 和查找中间件\n */\nexport function serializeRoutes(\n routes: RouteManifest,\n wsRoutes: WsRouteManifest,\n rootDir: string,\n dist: string = 'dist',\n): SerializedRouteManifest {\n const serialize = <T extends RouteRecord | WsRouteRecord>(\n route: T,\n ): T extends RouteRecord ? SerializedRouteRecord : SerializedWsRouteRecord => {\n const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir, dist);\n const serialized = {\n urlPath: route.urlPath,\n filePath: toProdFilePath(route.filePath, dist),\n paramNames: route.paramNames,\n isDynamic: route.isDynamic,\n isCatchAll: route.isCatchAll,\n middlewarePaths,\n };\n if ('method' in route) {\n (serialized as SerializedRouteRecord).method = (route as RouteRecord).method;\n }\n return serialized as T extends RouteRecord ? SerializedRouteRecord : SerializedWsRouteRecord;\n };\n\n return {\n routes: routes.map(serialize) as SerializedRouteRecord[],\n wsRoutes: wsRoutes.map(serialize) as SerializedWsRouteRecord[],\n };\n}\n\n/**\n * 从路由文件路径,向上查找所有中间件文件绝对路径(根在前,路由目录在后)\n *\n * 与 scanRoutes.findMergedMiddlewares 的收集逻辑一致,但只返回路径不加载。\n * 检查源码形式(.ts)中间件文件是否存在,返回产物形式(.js)绝对路径。\n *\n * @param routeFilePath 源码相对路径(如 src/api/hello/handler.ts)\n * @param rootDir 项目根目录\n * @param dist 产物目录(dist 或 .faapi)\n */\nfunction extractMiddlewarePaths(routeFilePath: string, rootDir: string, dist: string): string[] {\n const routeDir = path.dirname(routeFilePath);\n const resolvedRoot = path.resolve(rootDir);\n\n const paths: string[] = [];\n let currentDir = path.resolve(rootDir, routeDir);\n while (true) {\n // 检查源码 middlewares.ts 是否存在,回退 middlewares.js(prd 残留场景)\n const mwTsPath = path.join(currentDir, 'middlewares.ts');\n const mwJsPath = path.join(currentDir, 'middlewares.js');\n const absTsPath = path.resolve(rootDir, mwTsPath);\n const absJsPath = path.resolve(rootDir, mwJsPath);\n const absMwPath = fs.existsSync(absTsPath)\n ? absTsPath\n : fs.existsSync(absJsPath)\n ? absJsPath\n : null;\n if (absMwPath) {\n // 转为产物形式绝对路径(打平 src/ 前缀)\n const relMwPath = path.relative(rootDir, absMwPath);\n const prodAbsPath = path.resolve(rootDir, toProdFilePath(relMwPath, dist));\n paths.push(prodAbsPath);\n }\n if (currentDir === resolvedRoot) break;\n // 不超出 src 目录(避免向上查到非源码目录)\n // 到达 src 的父目录(rootDir)时停止\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n\n // paths 是从路由目录向上收集的,需反转为根在前\n paths.reverse();\n return paths;\n}\n\n/**\n * 把序列化的路由清单写入 JS 模块\n *\n * 生成 ESM 模块,start 时通过 import() 加载。\n */\nexport async function writeRoutesModule(\n manifest: SerializedRouteManifest,\n outputPath: string,\n): Promise<void> {\n // 用 JSON.stringify 嵌入,保证字符串转义安全\n const content = `// 自动生成,请勿手动编辑(faapi build 产物)\nexport const routes = ${JSON.stringify(manifest.routes, null, 2)};\nexport const wsRoutes = ${JSON.stringify(manifest.wsRoutes, null, 2)};\n`;\n\n await atomicWriteFile(outputPath, content);\n}\n\n/**\n * 从序列化清单水合路由(还原 RouteRecord,不预加载中间件)\n *\n * 中间件改为按需加载:只把 `middlewarePaths` 存到 `route.middlewarePaths`,\n * 请求阶段由 `createServer` / `handleWsUpgrade` 调 `loadMergedMiddlewares` 按需加载。\n * 这样 dev/prod 启动时都不全量加载中间件模块(Vite 风格),首次请求时才加载。\n */\nexport async function hydrateRoutes(\n manifest: SerializedRouteManifest,\n): Promise<{ routes: RouteManifest; wsRoutes: WsRouteManifest }> {\n const hydrateRoute = (serialized: SerializedRouteRecord): RouteRecord => ({\n method: serialized.method,\n urlPath: serialized.urlPath,\n filePath: serialized.filePath,\n paramNames: serialized.paramNames,\n isDynamic: serialized.isDynamic,\n isCatchAll: serialized.isCatchAll,\n middlewarePaths: serialized.middlewarePaths,\n });\n\n const hydrateWsRoute = (serialized: SerializedWsRouteRecord): WsRouteRecord => ({\n urlPath: serialized.urlPath,\n filePath: serialized.filePath,\n paramNames: serialized.paramNames,\n isDynamic: serialized.isDynamic,\n isCatchAll: serialized.isCatchAll,\n middlewarePaths: serialized.middlewarePaths,\n });\n\n const routes = manifest.routes.map(hydrateRoute);\n const wsRoutes = manifest.wsRoutes.map(hydrateWsRoute);\n return { routes, wsRoutes };\n}\n","export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const;\n\nexport type HttpMethod = (typeof HTTP_METHODS)[number];\n\nconst HTTP_METHOD_SET: ReadonlySet<string> = new Set(HTTP_METHODS);\n\nexport function isHttpMethod(value: string): value is HttpMethod {\n return HTTP_METHOD_SET.has(value);\n}\n","/**\n * 标准化路径:\n * - 将反斜杠替换为正斜杠\n * - 去除重复的斜杠\n * - 去除尾部斜杠\n * - 确保以 / 开头(如果非空)\n */\nexport function normalizePath(path: string): string {\n if (!path) return '';\n\n // 将反斜杠替换为正斜杠\n let result = path.replace(/\\\\/g, '/');\n\n // 去除重复的斜杠\n result = result.replace(/\\/+/g, '/');\n\n // 去除尾部斜杠\n result = result.replace(/\\/+$/, '');\n\n // 确保以 / 开头(如果非空)\n if (result && !result.startsWith('/')) {\n result = '/' + result;\n }\n\n return result;\n}\n","import { normalizePath } from '../utils/normalizePath';\n\n/**\n * 将 [name] 格式的目录名转换为 :name 格式\n * '[id]' -> ':id'\n * '[...slug]' -> ':...slug'\n * 'login' -> 'login'\n */\nexport function dynamicSegmentToParam(segment: string): string {\n const match = segment.match(/^\\[(.+)\\]$/);\n if (match) {\n return ':' + match[1];\n }\n return segment;\n}\n\n/**\n * 从 URL 路径中提取动态参数名\n * '/user/:id' -> ['id']\n * '/auth/login' -> []\n * '/user/:id/posts/:postId' -> ['id', 'postId']\n * '/shop/:...slug' -> ['slug']\n */\nexport function extractParamNames(urlPath: string): string[] {\n const params: string[] = [];\n const segments = urlPath.split('/');\n for (const segment of segments) {\n if (segment.startsWith(':...')) {\n // catch-all 参数::...slug -> slug\n params.push(segment.slice(4));\n } else if (segment.startsWith(':')) {\n params.push(segment.slice(1));\n }\n }\n return params;\n}\n\n/**\n * 判断路径段是否为 catch-all 路由\n * '[...slug]' -> true\n * '[id]' -> false\n */\nexport function isCatchAllSegment(segment: string): boolean {\n return /^\\[\\.\\.\\..+\\]$/.test(segment);\n}\n\n/**\n * 判断路径段是否为路由分组\n * '(marketing)' -> true\n * 'login' -> false\n */\nexport function isRouteGroup(segment: string): boolean {\n return /^\\(.+\\)$/.test(segment);\n}\n\n/**\n * 将路由文件路径转换为 URL 路径\n * 输入: 相对于项目根目录的文件路径,如 'src/api/auth/login/handler.ts'\n * 输出: URL 路径,如 '/api/auth/login'\n *\n * 规则:\n * - 去掉 src/ 前缀(路由源码目录写死为 src)\n * - 去掉文件名(handler.ts 等)\n * - 将 [id] 转换为 :id\n * - 忽略路由分组 (groupName) — 不影响 URL\n * - 标准化路径\n *\n * 说明:路由源码目录写死为 src/api/,src/ 前缀剥离后 api/ 前缀保留在 URL 中,\n * 因此 URL 始终带 /api 前缀。\n */\nexport function filePathToUrlPath(filePath: string): string {\n // 去掉 src/ 前缀(路由源码目录写死为 src)\n const withoutPrefix = filePath.startsWith('src/') ? filePath.slice(4) : filePath;\n\n // 去掉文件名(最后一个路径段)\n const lastSlashIndex = withoutPrefix.lastIndexOf('/');\n const dirPath = lastSlashIndex === -1 ? '' : withoutPrefix.slice(0, lastSlashIndex);\n\n if (!dirPath) {\n return '';\n }\n\n // 将每个路径段中的 [name] 转换为 :name,忽略路由分组 (name)\n const segments = dirPath\n .split('/')\n .filter((s) => !isRouteGroup(s)) // 过滤掉路由分组\n .map(dynamicSegmentToParam);\n\n // 拼接并标准化路径\n return normalizePath(segments.join('/'));\n}\n","import { pathToFileURL } from 'node:url';\n\n/**\n * 当前模块加载时间戳(watch 模式下用于绕过 ESM 缓存)\n *\n * 由 createDevApp.reloadRoutes 调用 setLoadTimestamp 设置。\n * ES 模块单例保证所有 import 此模块的地方共享同一个值,无需 globalThis。\n */\nlet loadTs: number | undefined;\n\nexport function setLoadTimestamp(ts: number): void {\n loadTs = ts;\n}\n\n/**\n * vitest 的 importActual 函数类型\n *\n * vi.importActual(path) 走 Vite SSR pipeline:\n * - 识别 vitest.config.ts 的 resolve.alias 与 tsconfig paths 别名\n * - 让 vi.mock 在加载的模块内生效\n */\ntype ImportActualFn = (path: string) => Promise<unknown>;\n\n/**\n * 检测 vitest 的 vi.importActual(globals: true 时注入 globalThis.vi)\n *\n * 业务方 vitest.config.ts 需 test.globals: true,或在测试文件内\n * 显式 `import { vi } from 'vitest'` 后挂到 globalThis.vi。\n *\n * 不在 vitest 环境下返回 undefined,调用方回退到 Node 原生 import()。\n */\nfunction getVitestImportActual(): ImportActualFn | undefined {\n const vi = (globalThis as { vi?: { importActual?: ImportActualFn } }).vi;\n if (typeof vi?.importActual !== 'function') return undefined;\n return vi.importActual.bind(vi);\n}\n\n/**\n * 动态 import 文件\n *\n * 两种加载路径:\n *\n * 1. **vitest 环境**(`globalThis.vi.importActual` 可用):走 Vite SSR pipeline,\n * 识别 tsconfig paths 别名(如 `@/lib/db`)+ 让 `vi.mock` 生效。\n * 业务方在 vitest 下用 `createTestServer` 时自动启用。\n *\n * 2. **非 vitest 环境**:Node 原生 `import()`。watch 模式下拼接 `?t=<timestamp>`\n * query 绕过 ESM 缓存;非 watch 模式等价普通 `import()`。\n *\n * @param filePath 文件绝对路径\n * @param bustViteCache dev 按需模式下走 Node 原生 import(绕过 Vite SSR pipeline)。\n * handler.js / zod.js 是已编译产物(别名已在编译时重写为相对路径),不需要 Vite alias 解析,\n * 直接走 Node 原生 import + 时间戳 query 避免 Vite SSR 缓存干扰。\n * @returns 模块导出对象\n */\nexport async function importWithCacheBust(\n filePath: string,\n bustViteCache = false,\n): Promise<Record<string, unknown>> {\n // vitest 环境优先走 Vite pipeline(识别 tsconfig paths + vi.mock)\n const importActual = getVitestImportActual();\n if (importActual) {\n if (bustViteCache) {\n // dev 按需模式:handler.js / zod.js 是已编译产物(别名已在编译时重写为相对路径),\n // 不需要 Vite alias 解析,直接走 Node 原生 import + 时间戳 query 避免 Vite SSR 缓存干扰\n let url = pathToFileURL(filePath).href;\n url += `?t=${Date.now()}`;\n return (await import(url)) as Record<string, unknown>;\n }\n return (await importActual(filePath)) as Record<string, unknown>;\n }\n\n // 否则走 Node 原生 ESM import\n let url = pathToFileURL(filePath).href;\n if (loadTs !== undefined) {\n url += `?t=${loadTs}`;\n }\n return (await import(url)) as Record<string, unknown>;\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\nimport type { InjectorMap } from './injectorTypes';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\n\n/**\n * 中间件 + 注入器加载结果\n */\nexport interface LoadedMiddlewareBundle {\n middlewares: FaapiMiddleware[];\n injectors: InjectorMap;\n}\n\n/**\n * 中间件缓存,key 为 middlewares.ts 的绝对路径\n */\nconst middlewareCache = new Map<string, LoadedMiddlewareBundle>();\n\n/**\n * in-flight 加载去重:同一文件并发首载共享同一 Promise(冷启动并发首请求\n * 不重复 import + 合并)。invalidateMiddlewareCache 时一并清空\n */\nconst inFlight = new Map<string, Promise<LoadedMiddlewareBundle>>();\n\n/**\n * 失效所有中间件缓存(watch 模式下文件变化时调用)\n */\nexport function invalidateMiddlewareCache(): void {\n middlewareCache.clear();\n inFlight.clear();\n}\n\n/**\n * 从缓存中读取中间件 bundle(未命中返回 undefined)\n */\nexport function getCachedMiddlewares(absPath: string): LoadedMiddlewareBundle | undefined {\n return middlewareCache.get(absPath);\n}\n\n/**\n * 写入中间件缓存\n */\nexport function setCachedMiddlewares(absPath: string, bundle: LoadedMiddlewareBundle): void {\n middlewareCache.set(absPath, bundle);\n}\n\n/**\n * 从绝对路径加载 middlewares.ts 并校验\n *\n * 文件可导出:\n * - `default`:中间件数组(洋葱模型,每项为 async 函数)\n * - `injectors`:注入器映射表(按参数名匹配 handler 参数)\n *\n * 两者都是可选的,但至少要有一个。\n *\n * 加载失败时的语义:\n * - 文件 import 抛错(语法错误 / 路径不存在 / 运行时抛错)→ console.error 输出原始错误,\n * 返回空 bundle,确保服务仍可启动而非崩溃。鉴权等关键中间件失效后业务可由 onError 感知。\n * 开发期 watcher 会因 reloadRoutes 触发重新加载,实现自愈。\n */\nexport async function loadMiddlewaresFile(filePath: string): Promise<LoadedMiddlewareBundle> {\n let module: Record<string, unknown>;\n try {\n module = (await importWithCacheBust(filePath)) as Record<string, unknown>;\n } catch (err) {\n // 关键可见性:必须打印原始错误,否则业务方完全无感知(鉴权中间件失效等同于裸奔)\n console.error(\n `[faapi] Failed to load middlewares from ${filePath}:`,\n err instanceof Error ? (err.stack ?? err.message) : err,\n );\n return { middlewares: [], injectors: {} };\n }\n\n // 加载中间件数组\n const middlewares = (module.default ?? module.middlewares ?? []) as unknown[];\n if (!Array.isArray(middlewares)) {\n console.warn(`[faapi] middlewares.ts 应导出数组,已忽略: ${filePath}`);\n return { middlewares: [], injectors: {} };\n }\n\n const validMiddlewares = middlewares.filter((m: unknown) => {\n if (typeof m !== 'function') {\n console.warn(`[faapi] 无效的中间件项(应为函数),已忽略: ${typeof m}`);\n return false;\n }\n return true;\n }) as FaapiMiddleware[];\n\n // 加载注入器映射表(可选命名导出)\n const injectors = (module.injectors ?? {}) as InjectorMap;\n if (typeof injectors !== 'object' || injectors === null) {\n console.warn(`[faapi] injectors 应导出对象,已忽略: ${filePath}`);\n return { middlewares: validMiddlewares, injectors: {} };\n }\n\n // 校验注入器:每个值必须是函数\n const validInjectors: InjectorMap = {};\n for (const [name, injector] of Object.entries(injectors)) {\n if (typeof injector !== 'function') {\n console.warn(`[faapi] 注入器 ${name} 应为函数,已忽略`);\n continue;\n }\n validInjectors[name] = injector;\n }\n\n return { middlewares: validMiddlewares, injectors: validInjectors };\n}\n\n/**\n * 按路径列表加载并合并中间件(根在前,路由目录在后)\n *\n * 合并语义:\n * - 子级中间件追加在父级之后(洋葱模型:后注册的中间件在内层)\n * - 子级注入器覆盖父级同名注入器\n *\n * 单文件加载带缓存(getCachedMiddlewares/setCachedMiddlewares),重复调用仅首次真正加载。\n *\n * @param middlewarePaths 中间件文件绝对路径列表(根在前,路由目录在后)\n * @returns 合并后的中间件+注入器;无中间件时返回 undefined\n */\nexport async function loadMergedMiddlewares(\n middlewarePaths: string[],\n): Promise<LoadedMiddlewareBundle | undefined> {\n if (middlewarePaths.length === 0) return undefined;\n\n const mergedMiddlewares: FaapiMiddleware[] = [];\n const mergedInjectors: InjectorMap = {};\n\n for (const absMwPath of middlewarePaths) {\n let bundle: LoadedMiddlewareBundle | undefined = getCachedMiddlewares(absMwPath);\n if (bundle === undefined) {\n // in-flight 去重:并发首载共享同一 Promise(对照 compileOnDemand 的 mutex 模式)\n let loading = inFlight.get(absMwPath);\n if (!loading) {\n loading = loadMiddlewaresFile(absMwPath).then((result) => {\n setCachedMiddlewares(absMwPath, result);\n return result;\n });\n inFlight.set(absMwPath, loading);\n }\n bundle = await loading;\n }\n mergedMiddlewares.push(...bundle.middlewares);\n for (const [name, injector] of Object.entries(bundle.injectors)) {\n mergedInjectors[name] = injector;\n }\n }\n\n if (mergedMiddlewares.length === 0 && Object.keys(mergedInjectors).length === 0) {\n return undefined;\n }\n\n return { middlewares: mergedMiddlewares, injectors: mergedInjectors };\n}\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { RouteManifest, WsRouteManifest } from './routeTypes';\nimport { HTTP_METHODS } from './constants';\nimport { filePathToUrlPath, extractParamNames, isCatchAllSegment } from './parseRouteFile';\nimport { loadMergedMiddlewares } from '../middleware/loadMiddlewares';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { toProdFilePath } from '../utils/prodPaths';\n\n/**\n * 匹配源码中导出的 HTTP 方法或 WS 函数\n *\n * 支持:\n * - `export function GET() {}`\n * - `export async function POST() {}`\n * - `export const GET = () => {}` / `export const GET = async () => {}`\n * - `export function WS() {}`\n *\n * 不通过运行时 import 提取方法名,避免启动时全量加载 handler 模块(Vite 风格:\n * 路由发现与 handler 加载解耦,handler.js 按需编译/导入)。\n */\nconst HTTP_OR_WS_EXPORT_RE = new RegExp(\n String.raw`export\\s+(?:async\\s+)?(?:function\\s+|const\\s+)(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|WS)\\b`,\n 'g',\n);\n\n/**\n * 从源码内容中提取所有 HTTP 方法 + WS 导出名(去重)\n */\nfunction extractExportsFromSource(source: string): Set<string> {\n const names = new Set<string>();\n let match: RegExpExecArray | null;\n HTTP_OR_WS_EXPORT_RE.lastIndex = 0;\n while ((match = HTTP_OR_WS_EXPORT_RE.exec(source)) !== null) {\n names.add(match[1]!);\n }\n return names;\n}\n\n/**\n * 从路由文件所在目录向上逐级查找 middlewares.ts(源码),\n * 返回从根到路由目录的中间件文件**绝对路径列表**(不加载模块)。\n *\n * 设计意图:scanRoutes 不再启动时全量 import middlewares.js,仅收集路径;\n * 实际的中间件加载延后到 hydrateRoutes / 请求阶段(与 prod 的 hydrateRoutes 一致)。\n * 这样 dev 启动时 zero import,启动速度接近 Vite。\n *\n * 路径选择规则(与 generateRoutes.extractMiddlewarePaths 一致):\n * - dist 传入:返回**产物** middlewares.js 绝对路径(打平 src/ 前缀,存在性检查源码 .ts)\n * - dist 不传:返回**源码** middlewares.ts/.js 绝对路径(兼容无 dist 的旧调用方,如 testServer)\n *\n * @param routeFilePath 源码相对路径(如 src/api/hello/handler.ts)\n * @param rootDir 项目根目录\n * @param dist 产物目录(dist 或 .faapi),不传则返回源码路径\n * @returns 中间件文件绝对路径列表(根在前,路由目录在后);空数组表示无中间件\n */\nfunction collectMiddlewarePaths(routeFilePath: string, rootDir: string, dist?: string): string[] {\n const routeDir = path.dirname(routeFilePath);\n const resolvedRoot = path.resolve(rootDir);\n\n const paths: string[] = [];\n let currentDir = path.resolve(rootDir, routeDir);\n while (true) {\n if (dist) {\n // 产物模式:检查源码 middlewares.ts 是否存在,返回产物 middlewares.js 绝对路径\n const mwTsPath = path.join(currentDir, 'middlewares.ts');\n const mwJsPath = path.join(currentDir, 'middlewares.js');\n const absTsPath = path.resolve(rootDir, mwTsPath);\n const absJsPath = path.resolve(rootDir, mwJsPath);\n const absMwPath = fs.existsSync(absTsPath)\n ? absTsPath\n : fs.existsSync(absJsPath)\n ? absJsPath\n : null;\n if (absMwPath) {\n // 转为产物形式绝对路径(打平 src/ 前缀,加 dist 前缀)\n const relMwPath = path.relative(rootDir, absMwPath);\n const prodAbsPath = path.resolve(rootDir, toProdFilePath(relMwPath, dist));\n paths.push(prodAbsPath);\n }\n } else {\n // 源码模式(testServer / 单元测试用):直接返回源码 middlewares.ts/.js 绝对路径\n for (const ext of ['.ts', '.js']) {\n const mwPath = path.join(currentDir, `middlewares${ext}`);\n const absMwPath = path.resolve(rootDir, mwPath);\n if (fs.existsSync(absMwPath)) {\n paths.push(absMwPath);\n break;\n }\n }\n }\n if (currentDir === resolvedRoot) break;\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n\n // paths 是从路由目录向上收集的,需反转为根在前\n paths.reverse();\n return paths;\n}\n\n/**\n * 按路径列表加载并合并中间件(根在前,路由目录在后)\n *\n * 已移至 `middleware/loadMiddlewares.ts` 的 `loadMergedMiddlewares`,\n * scanRoutes 不再预加载中间件(dist 模式只收集路径,请求阶段按需加载)。\n */\n\n/**\n * 扫描 api 目录,同时生成 HTTP 路由清单和 WebSocket 路由清单\n *\n * **Vite 风格**:启动时只读源码 + 正则提取方法名,不 import handler.js / middlewares.js。\n * 中间件改为\"收集路径\",加载延后到 hydrateRoutes / 请求阶段。\n *\n * 路由文件格式:handler.ts,导出 HTTP 方法名作为 handler,也可导出 `WS` 函数声明 WebSocket 路由\n * ```ts\n * // api/user/handler.ts\n * export function GET() { return { list: [] } }\n * export function POST(body: any) { return { created: true } }\n *\n * // WebSocket 路由:导出 WS 函数\n * export function WS(ctx: WsContext) {\n * return { onMessage(ws, msg) { ws.send(`echo: ${msg}`) } };\n * }\n * ```\n * 一个 handler.ts 可同时导出 HTTP 方法(GET/POST...)和 WS 函数,\n * 分别生成 HTTP RouteRecord 和 WsRouteRecord。\n *\n * **中间件加载策略**:\n * - dev/prod(dist 传入):scanRoutes 仅收集 middlewarePaths,不加载模块;\n * 由 hydrateRoutes(prod)或 dev 启动流程加载\n * - 无 dist 模式(testServer / 单元测试):scanRoutes 直接加载中间件并塞入 route.middlewares\n * (保持向后兼容,避免破坏现有测试)\n *\n * @param rootDir 项目根目录\n * @param patterns glob patterns(源码 .ts 路径,如 src/api 下所有 .ts)\n * @param dist 产物目录(dist 或 .faapi)。传入时 scanRoutes 不 import 任何模块;\n * 不传时 import 源码 .ts(旧模式,需要 tsx,仅 testServer/单测使用)。\n */\nexport async function scanRoutes(\n rootDir: string,\n patterns: string[],\n dist?: string,\n): Promise<{ routes: RouteManifest; wsRoutes: WsRouteManifest }> {\n const files = await fg(patterns, {\n cwd: rootDir,\n onlyFiles: true,\n absolute: false,\n });\n\n const routes: RouteManifest = [];\n const wsRoutes: WsRouteManifest = [];\n\n for (const file of files) {\n const normalizedFile = file.replace(/\\\\/g, '/');\n const fileName = normalizedFile.split('/').pop()!;\n\n // 处理 handler.{ts,js} — API 路由 + WebSocket 路由\n // dev/build 模式扫源码 .ts(dist 传入),start 模式扫产物 .js(由 hydrateRoutes 处理)\n if (fileName === 'handler.ts' || fileName === 'handler.js') {\n const absPath = path.resolve(rootDir, normalizedFile);\n const urlPath = filePathToUrlPath(normalizedFile);\n const paramNames = extractParamNames(urlPath);\n const isDynamic = paramNames.length > 0;\n const isCatchAll = normalizedFile.split('/').some(isCatchAllSegment);\n\n // 中间件:dist 模式只收集路径(按需加载),无 dist 模式直接加载\n let middlewarePaths: string[] | undefined;\n let middlewareBundle: { middlewares: FaapiMiddleware[]; injectors: InjectorMap } | undefined;\n if (dist) {\n // dev/prod 模式:仅收集路径,请求阶段按需加载(Vite 风格)\n middlewarePaths = collectMiddlewarePaths(normalizedFile, rootDir, dist);\n } else {\n // 无 dist 模式(testServer/单测):直接加载源码中间件\n const mwPaths = collectMiddlewarePaths(normalizedFile, rootDir);\n middlewareBundle = await loadMergedMiddlewares(mwPaths);\n }\n\n // 提取导出名(HTTP 方法 + WS):读源码 + 正则匹配,不 import 模块\n const source = await fs.promises.readFile(absPath, 'utf8').catch(() => '');\n const exportNames = extractExportsFromSource(source);\n const methods = HTTP_METHODS.filter((m) => exportNames.has(m));\n\n for (const method of methods) {\n routes.push({\n method,\n urlPath,\n filePath: normalizedFile,\n paramNames,\n isDynamic,\n isCatchAll: isCatchAll || undefined,\n middlewarePaths,\n middlewares: middlewareBundle?.middlewares,\n injectors: middlewareBundle?.injectors,\n });\n }\n\n // WS 导出\n if (exportNames.has('WS')) {\n wsRoutes.push({\n urlPath,\n filePath: normalizedFile,\n paramNames,\n isDynamic,\n isCatchAll: isCatchAll || undefined,\n middlewarePaths,\n middlewares: middlewareBundle?.middlewares,\n injectors: middlewareBundle?.injectors,\n });\n }\n continue;\n }\n }\n\n return { routes, wsRoutes };\n}\n","import type { RouteManifest } from './routeTypes';\n\n/**\n * 路由排序规则:\n * 1. 静态路由优先于动态路由\n * 2. 动态路由优先于 catch-all 路由\n * 3. 路径段数少的优先\n * 4. 字母序排列\n */\nexport function sortRoutes(routes: RouteManifest): RouteManifest {\n return [...routes].sort((a, b) => {\n // 1. 静态路由优先于动态路由\n if (a.isDynamic !== b.isDynamic) {\n return a.isDynamic ? 1 : -1;\n }\n\n // 2. catch-all 路由优先级最低(排在所有动态路由之后)\n if (a.isCatchAll !== b.isCatchAll) {\n return a.isCatchAll ? 1 : -1;\n }\n\n // 3. 路径段数少的优先\n const aSegments = a.urlPath.split('/').filter(Boolean).length;\n const bSegments = b.urlPath.split('/').filter(Boolean).length;\n if (aSegments !== bSegments) {\n return aSegments - bSegments;\n }\n\n // 4. 字母序排列\n return a.urlPath.localeCompare(b.urlPath);\n });\n}\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { ToolManifestList } from './toolTypes';\n\n/**\n * tool 扫描 patterns(框架约定,非用户可配置项)\n *\n * 与路由的 src/api 扫描对称——tool 文件约定放在 `src/tools` 下任意层级子目录的 handler.ts。\n * 所有 tool 都是共享的,agent 通过 config 块的 `tools` 字段显式声明引用哪些。\n */\nexport const TOOL_PATTERNS = ['src/tools/**/*.ts'];\n\n/**\n * 匹配源码中导出的函数名(任意合法标识符,区别于路由的固定 HTTP 方法名)\n *\n * 支持:\n * - `export function getWeather(input) {}`\n * - `export async function getWeather(input) {}`\n * - `export const getWeather = (input) => {}`\n * - `export const getWeather = async (input) => {}`\n *\n * 不匹配:\n * - `export interface WeatherInput {}`(interface 关键字)\n * - `export type X = ...`(type 关键字)\n * - `export { getWeather }`(命名导出引用,非声明)\n * - `export default function ...`(default 后非 function/const)\n *\n * 不通过运行时 import 提取函数名,避免启动时全量加载 tool 模块(Vite 风格:\n * tool 发现与 tool.js 加载解耦,tool.js 按需编译/导入)。\n */\nconst TOOL_EXPORT_RE = new RegExp(\n String.raw`export\\s+(?:async\\s+)?(?:function\\s+|const\\s+)([A-Za-z_$][\\w$]*)\\s*(?:\\(|=)`,\n 'g',\n);\n\n/**\n * 保留导出名(即使匹配正则也不识别为 tool)\n *\n * - `default` — 默认导出保留\n * - `config` — agent 配置块(scanAgents 使用)\n * - `run` — agent 自定义运行函数(scanAgents 使用)\n */\nconst RESERVED_EXPORTS = new Set(['default', 'config', 'run']);\n\n/**\n * 从源码内容中提取所有 tool 函数导出名(去重 + 排除保留名)\n */\nfunction extractToolExportsFromSource(source: string): Set<string> {\n const names = new Set<string>();\n let match: RegExpExecArray | null;\n TOOL_EXPORT_RE.lastIndex = 0;\n while ((match = TOOL_EXPORT_RE.exec(source)) !== null) {\n const name = match[1]!;\n if (!RESERVED_EXPORTS.has(name)) {\n names.add(name);\n }\n }\n return names;\n}\n\n/**\n * 从相对路径(已剥离前缀)提取命名空间\n *\n * weather/handler.ts → 'weather'\n * handler.ts → ''\n * a/b/handler.ts → 'a.b'\n */\nfunction extractNamespaceFromRelPath(relPath: string): string {\n const lastSlash = relPath.lastIndexOf('/');\n const dirPath = lastSlash === -1 ? '' : relPath.slice(0, lastSlash);\n if (!dirPath) return '';\n return dirPath.split('/').join('.');\n}\n\n/**\n * 从文件路径生成 tool 命名空间(子目录,用 . 连接,不含函数名)\n *\n * 命名空间生成规则:\n * 1. 剥离 `tools/` 前缀(找第一个 `tools/`)\n * 2. 剥离文件名(handler.ts)\n * 3. 剩余路径段用 `.` 连接\n *\n * 例子:\n * - src/tools/weather/handler.ts → 'weather'\n * - src/tools/handler.ts → ''\n * - src/tools/a/b/handler.ts → 'a.b'\n */\nfunction filePathToToolNamespace(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n\n // 剥离 tools/ 前缀(找第一个 tools/)\n const toolsMatch = normalized.match(/(?:^|\\/)tools\\/(.+)$/);\n if (toolsMatch) {\n return extractNamespaceFromRelPath(toolsMatch[1]!);\n }\n\n // 不在 tools/ 下,返回空(不应发生)\n return '';\n}\n\n/**\n * 生成 tool 名:命名空间.函数名 或 纯函数名\n */\nfunction buildToolName(namespace: string, functionName: string): string {\n return namespace ? `${namespace}.${functionName}` : functionName;\n}\n\n/**\n * 扫描 tools 目录,生成 tool 清单\n *\n * Vite 风格:启动时只读源码 + 正则提取函数导出名,不 import tool.js。\n * tool.js 加载延后到 loadToolModule 请求阶段(详见 compileOnDemand)。\n *\n * tool 文件格式:handler.ts,导出任意函数即声明一个 tool(一个文件可多个)。\n * 排除保留导出名:default / config / run(agent 系统保留)。\n *\n * 所有 tool 都是共享的——放在 `src/tools` 下任意层级子目录的 handler.ts。\n * agent 通过 config 块的 `tools` 字段显式声明引用哪些 tool。\n *\n * tool 命名规则:子目录.函数名(如 weather.getWeather),无子目录时纯函数名。\n *\n * 重名检测:同名 tool 抛错。\n *\n * @param rootDir 项目根目录\n * @param patterns glob patterns(源码 .ts 路径,匹配 src/tools 下 tool 文件)\n */\nexport async function scanTools(rootDir: string, patterns: string[]): Promise<ToolManifestList> {\n const files = await fg(patterns, {\n cwd: rootDir,\n onlyFiles: true,\n absolute: false,\n });\n\n const tools: ToolManifestList = [];\n // 重名检测:toolName → filePath\n const seen = new Map<string, string>();\n\n for (const file of files) {\n const normalizedFile = file.replace(/\\\\/g, '/');\n const fileName = normalizedFile.split('/').pop()!;\n\n // 只处理 handler.ts(与路由 handler.ts 对称),其他 .ts 文件跳过\n if (fileName !== 'handler.ts' && fileName !== 'handler.js') {\n continue;\n }\n\n const absPath = path.resolve(rootDir, normalizedFile);\n const source = await fs.promises.readFile(absPath, 'utf8').catch(() => '');\n const exportNames = extractToolExportsFromSource(source);\n\n const namespace = filePathToToolNamespace(normalizedFile);\n\n for (const fnName of exportNames) {\n const toolName = buildToolName(namespace, fnName);\n\n // 重名检测:同名 tool 报错\n const prevFile = seen.get(toolName);\n if (prevFile) {\n throw new Error(\n `Tool conflict: \"${toolName}\" declared in both ${prevFile} and ${normalizedFile}`,\n );\n }\n\n seen.set(toolName, normalizedFile);\n\n tools.push({\n name: toolName,\n functionName: fnName,\n filePath: normalizedFile,\n });\n }\n }\n\n return tools;\n}\n","import ts from 'typescript';\n\n/**\n * 判断节点是否有 `export` 修饰符\n */\nexport function hasExportModifier(node: ts.Node): boolean {\n if (!ts.canHaveModifiers(node)) return false;\n const modifiers = ts.getModifiers(node);\n return !!modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);\n}\n\n/**\n * 从节点提取第一个 JSDoc 注释块\n *\n * 1. 标准 API(`ts.getJSDocCommentsAndTags`,jsDocCache 已缓存时命中)\n * 2. 回退:直接访问解析器存入的 `node.jsDoc` 数组(jsDocCache 未同步的情况)\n */\nexport function getJSDocFromNode(node: ts.Node): ts.JSDoc | undefined {\n const apiDocs = ts\n .getJSDocCommentsAndTags(node)\n .filter((entry): entry is ts.JSDoc => ts.isJSDoc(entry));\n if (apiDocs.length > 0) return apiDocs[0];\n\n const directDocs = (node as unknown as { jsDoc?: ts.JSDoc[] }).jsDoc;\n if (directDocs && directDocs.length > 0) return directDocs[0];\n\n return undefined;\n}\n\n/**\n * 提取 JSDoc 描述(注释块自由文本,`@tag` 之前的首段)\n *\n * 多行描述保留换行(TypeScript 已自动剥离每行前缀 ` * `)。\n * 无 JSDoc / JSDoc 无自由文本 / 描述仅空白 / comment 为富文本结构 → `undefined`。\n */\nexport function extractDescription(jsDoc: ts.JSDoc | undefined): string | undefined {\n if (!jsDoc) return undefined;\n if (typeof jsDoc.comment !== 'string') return undefined;\n const trimmed = jsDoc.comment.trim();\n return trimmed || undefined;\n}\n\n/**\n * 提取 `@<tagName>` 标签的覆盖名\n *\n * - `@tool weather.current` / `@agent researcher` → 标签值文本\n * - 花括号包裹(`@tool {name}`)去除花括号\n * - 无指定标签 / 标签无 comment 文本 → `undefined`(调用方回退到路径推导值,不报错)\n */\nexport function extractJSDocTagValue(\n jsDoc: ts.JSDoc | undefined,\n tagName: string,\n): string | undefined {\n if (!jsDoc || !jsDoc.tags) return undefined;\n for (const tag of jsDoc.tags) {\n if (tag.tagName.text !== tagName) continue;\n // tag.comment 类型为 string | false | undefined;false 表示该 tag 无 comment 文本\n if (typeof tag.comment !== 'string') return undefined;\n const text = tag.comment.trim();\n if (!text) return undefined;\n const cleaned = text.replace(/^\\{|\\}$/g, '').trim();\n return cleaned || undefined;\n }\n return undefined;\n}\n","import ts from 'typescript';\nimport {\n extractDescription,\n extractJSDocTagValue,\n getJSDocFromNode,\n hasExportModifier,\n} from './jsDocMetadata';\n\n/**\n * Tool 的 LLM 可见核心字段\n *\n * 描述\"tool 是什么\"——LLM 真正需要消费的字段(发往 LLM 的 tool 定义只含\n * `name` / `description` / input schema),**不含**代码本体加载细节\n * (`filePath` / `functionName` / `inputTypeName`)。\n *\n * 与 [AgentCore](./extractAgentMetadata.md) 对称——LLM-facing 字段与代码加载\n * 细节分离,便于未来扩展(如 DB-driven tool 只实现 `ToolCore` 即可)。\n *\n * `toolRegistry` 查询入口 / `@faapi/agent` 子包的 `buildToolDefinitions`\n * 都消费 `ToolCore` 字段组装 LLM tool 列表。\n */\nexport interface ToolCore {\n /** tool 名(`@tool` JSDoc 覆盖值 或 路径推导值) */\n name: string;\n /** JSDoc 描述(tool 描述,对 LLM 可见),无 JSDoc 或 JSDoc 无自由文本时为 `undefined` */\n description?: string;\n}\n\n/**\n * Tool 完整元数据(文件型 tool)\n *\n * 继承 [ToolCore](./extractToolMetadata.md) 的 LLM 字段,额外扩展**代码本体加载细节**:\n * - `filePath` — `loadToolModule` 加载 `handler.js` 产物定位函数用\n * - `functionName` — 源码导出函数名(不受 `@tool` 覆盖影响,AST 定位 + 运行时 resolveExport 用)\n * - `inputTypeName` — 第一个参数的 TypeReference 名(供 [extractTypeInfo](./extractHandlerTypes.md)\n * 生成 zod schema;运行时 `resolveToolSchema` 据此定位 `zod.js`)\n *\n * 由 [extractToolMetadata](./extractToolMetadata.md) 产出,合并路径推导字段\n * (来自 [scanTools](../tools/scanTools.md) 的 `ToolManifest`)与 AST 提取字段\n * (JSDoc 描述、`@tool` 覆盖名、第一个参数 interface 名)。\n *\n * 字段来源:\n * - `name` — `@tool` JSDoc 覆盖值,或 `pathMeta.name`(路径推导)\n * - `description` — JSDoc 注释块自由文本(对 LLM 可见)\n * - `filePath` / `functionName` — 由 `pathMeta` 透传\n * - `inputTypeName` — 第一个参数的 TypeReference 名(供 [extractTypeInfo](./extractHandlerTypes.md) 生成 zod schema)\n */\nexport interface ToolMetadata extends ToolCore {\n /** 第一个参数的 interface/type 名(用于生成 zod schema),\n * 无参数/参数无类型标注/参数为内联类型字面量时为 `undefined` */\n inputTypeName?: string;\n /** 源码相对路径(从 `pathMeta` 透传) */\n filePath: string;\n /** 源码中的导出函数名(从 `pathMeta` 透传,AST 定位用,不受 `@tool` 覆盖影响) */\n functionName: string;\n}\n\n/**\n * 路径推导的 tool 元数据(由 [scanTools](../tools/scanTools.ts) 计算)\n *\n * 透传到 [ToolMetadata](./extractToolMetadata.ts) 输出,与 AST 提取字段合并。\n */\nexport interface ToolPathMeta {\n /** 路径推导的 tool 名(如 `weather.getWeather`) */\n name: string;\n /** 源码相对路径(如 `src/tools/weather/handler.ts`) */\n filePath: string;\n}\n\n/** 函数节点联合类型(FunctionDeclaration / ArrowFunction / FunctionExpression) */\ntype FunctionLike = ts.FunctionDeclaration | ts.ArrowFunction | ts.FunctionExpression;\n\n/**\n * 从 tool handler.ts 提取单个 tool 的元数据\n *\n * 提取内容:\n * 1. **JSDoc 描述** — 注释块自由文本(`@tag` 之前的首段),对 LLM 可见\n * 2. **`@tool` 覆盖名** — JSDoc 中 `@tool` 标签后的文本(如 `customName`),覆盖路径推导的 `name`\n * 3. **第一个参数 interface 名** — 供 [extractTypeInfo](./extractHandlerTypes.ts) 生成 zod schema\n *\n * 不提取(由 `pathMeta` 透传): `filePath` / `functionName`\n *\n * 函数查找支持四种导出形式,与 [scanTools](../tools/scanTools.ts) 的 `TOOL_EXPORT_RE` 正则同构:\n * - `export function name() {}`\n * - `export async function name() {}`\n * - `export const name = () => {}`\n * - `export const name = async () => {}`(以及 `= function () {}`)\n *\n * JSDoc 查找对箭头函数/函数表达式自动回溯到外层 `VariableStatement`\n * (JSDoc 通常写在 `export const` 上方,而非箭头函数本身)。\n *\n * @param program TypeScript Program\n * @param filePath 源文件**绝对路径**(AST 用,需与 `program.getSourceFile` 一致)\n * @param functionName 源码导出函数名(在文件中查找的目标)\n * @param pathMeta 路径推导的元数据(scanTools 已计算)\n * @returns `ToolMetadata` 或 `null`(函数未找到或源文件不在 Program 中)\n */\nexport function extractToolMetadata(\n program: ts.Program,\n filePath: string,\n functionName: string,\n pathMeta: ToolPathMeta,\n): ToolMetadata | null {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const found = findExportedFunction(sourceFile, functionName);\n if (!found) return null;\n\n const { fn, jsDocOwner } = found;\n const jsDoc = getJSDocFromNode(jsDocOwner);\n const description = extractDescription(jsDoc);\n const toolNameOverride = extractJSDocTagValue(jsDoc, 'tool');\n const inputTypeName = getFirstParamTypeName(fn, sourceFile);\n\n return {\n name: toolNameOverride ?? pathMeta.name,\n description,\n inputTypeName,\n filePath: pathMeta.filePath,\n functionName,\n };\n}\n\n/** 函数查找结果:函数节点 + JSDoc 持有节点 */\ninterface FoundFunction {\n /** 函数节点(FunctionDeclaration / ArrowFunction / FunctionExpression) */\n fn: FunctionLike;\n /** JSDoc 持有节点(FunctionDeclaration 时为函数本身;箭头/函数表达式时为外层 VariableStatement) */\n jsDocOwner: ts.Node;\n}\n\n/**\n * 在源文件中查找指定名称的导出函数\n *\n * 遍历顶层节点,匹配四种导出形式。返回函数节点和 JSDoc 持有节点:\n * - `export function name` → 两者都是 FunctionDeclaration 本身\n * - `export const name = () => {}` → fn 是 ArrowFunction,jsDocOwner 是外层 VariableStatement\n * (JSDoc 通常写在 `export const` 上方,而非箭头函数本身)\n *\n * 不依赖 `node.parent` 链——`ts.createProgram` 配置 `noEmit: true` 时不会设置父指针,\n * 故改在遍历时直接绑定 JSDoc 持有节点。\n *\n * 非 export 的同名函数不被识别。\n */\nfunction findExportedFunction(\n sourceFile: ts.SourceFile,\n functionName: string,\n): FoundFunction | null {\n let result: FoundFunction | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n // export function name() {} / export async function name() {}\n if (\n ts.isFunctionDeclaration(node) &&\n hasExportModifier(node) &&\n node.name?.text === functionName\n ) {\n result = { fn: node, jsDocOwner: node };\n return;\n }\n\n // export const name = () => {} / export const name = function () {}\n if (ts.isVariableStatement(node) && hasExportModifier(node)) {\n for (const decl of node.declarationList.declarations) {\n if (result) break;\n const nameText = ts.isIdentifier(decl.name)\n ? decl.name.text\n : decl.name.getText(sourceFile);\n if (nameText !== functionName || !decl.initializer) continue;\n if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {\n // JSDoc 通常在 VariableStatement 上(写在 `export const` 上方)\n result = { fn: decl.initializer, jsDocOwner: node };\n }\n }\n }\n });\n\n return result;\n}\n\n/**\n * 提取第一个参数的 interface/type 名(TypeReference)\n *\n * - 参数有 TypeReference 类型标注(如 `input: WeatherInput`)→ `'WeatherInput'`\n * - 参数无类型标注 / 内联类型字面量(如 `input: { city: string }`)→ `undefined`\n * - 函数无参数 → `undefined`\n * - 多参数时取第一个参数的类型名\n *\n * 返回 `undefined` 而非抛错——tool 允许无参数输入或无 schema 校验,\n * 是否生成 `zod.js` 由下游 `generateToolArtifacts` 根据 `inputTypeName` 是否为 `undefined` 决定。\n */\nfunction getFirstParamTypeName(fn: FunctionLike, sourceFile: ts.SourceFile): string | undefined {\n const firstParam = fn.parameters[0];\n if (!firstParam) return undefined;\n if (!firstParam.type) return undefined;\n if (!ts.isTypeReferenceNode(firstParam.type)) return undefined;\n return firstParam.type.typeName.getText(sourceFile);\n}\n","import ts from 'typescript';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Program 缓存,key 为入口文件路径\n *\n * watch 模式下通过 invalidateProgramCache() 全量清理。\n * 同一文件多次创建 Program 时复用缓存,避免重复解析。\n */\nconst programCache = new Map<string, ts.Program>();\n\n/**\n * tsconfig 解析结果缓存,key 为 tsconfig.json 绝对路径\n *\n * 同一 tsconfig 下多个文件共享解析结果,避免每个文件都读盘 + parse。\n * invalidateProgramCache() 同时清空此缓存。\n */\ninterface TsConfigResult {\n /** parseJsonConfigFileContent 解析得到的 module 选项 */\n module?: ts.ModuleKind;\n /** parseJsonConfigFileContent 解析得到的 moduleResolution 选项 */\n moduleResolution?: ts.ModuleResolutionKind;\n /** tsconfig include 模式匹配的所有 .ts 文件绝对路径(作为 program rootNames) */\n fileNames: string[];\n}\nconst tsConfigCache = new Map<string, TsConfigResult>();\n\n/**\n * 清理所有 Program 缓存 + tsconfig 解析缓存(watch 模式下文件变化时调用)\n *\n * 全量清理而非增量清理,理由:\n * - 简单可靠,无状态一致性问题\n * - 跨文件类型引用需要所有文件的 Program 同步更新\n * - tsconfig 可能变化,需重新读取\n * - dev 模式文件量有限,全量重建在百毫秒级\n */\nexport function invalidateProgramCache(): void {\n programCache.clear();\n tsConfigCache.clear();\n}\n\n/**\n * 从指定文件路径向上查找最近的 tsconfig.json\n *\n * 从 filePath 所在目录开始逐级向上,返回第一个找到的 tsconfig.json 绝对路径。\n * 找不到返回 null(如 os.tmpdir() 测试场景)。\n */\nfunction findTsConfig(filePath: string): string | null {\n let dir = path.dirname(filePath);\n const root = path.parse(dir).root;\n while (true) {\n const candidate = path.join(dir, 'tsconfig.json');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n if (dir === root) return null;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * 解析 tsconfig.json,提取 module / moduleResolution / fileNames\n *\n * 使用 ts.readConfigFile 读取 + ts.parseJsonConfigFileContent 解析。\n * 保留 tsconfig 原始的 include/exclude/files 配置,让 ts 自动扫描所有相关 .ts 文件\n * 作为 program 的 rootNames——这样跨文件 import 的源文件会被加载到 program 中,\n * 使 resolveTypeReference 的兜底遍历(找同名声明)能成功。\n *\n * 只提取 module / moduleResolution 两个 compilerOptions 覆盖默认值,其余字段\n * (target / strict / jsx / paths 等)用框架默认值,避免业务项目配置干扰 AST 提取。\n *\n * 解析失败(语法错误 / 缺字段)返回空 fileNames + 空选项,回退到默认 NodeNext,\n * 保证 build 不被 tsconfig 损坏阻塞。\n */\nfunction parseTsConfig(tsconfigPath: string): TsConfigResult {\n const cached = tsConfigCache.get(tsconfigPath);\n if (cached) return cached;\n\n const result: TsConfigResult = { fileNames: [] };\n\n try {\n const configFile = ts.readConfigFile(tsconfigPath, (p) => fs.readFileSync(p, 'utf-8'));\n if (configFile.error) {\n tsConfigCache.set(tsconfigPath, result);\n return result;\n }\n\n const config = configFile.config ?? {};\n const basePath = path.dirname(tsconfigPath);\n\n // 保留 tsconfig 原始 include/exclude/files,让 ts 自动扫描 .ts 文件\n // 这些文件作为 program 的 rootNames,让跨文件 import 的源文件被加载进来\n const parsed = ts.parseJsonConfigFileContent(\n config,\n ts.sys,\n basePath,\n /* existingOptions */ undefined,\n tsconfigPath,\n );\n\n result.fileNames = parsed.fileNames;\n if (parsed.options.module !== undefined) {\n result.module = parsed.options.module;\n }\n if (parsed.options.moduleResolution !== undefined) {\n result.moduleResolution = parsed.options.moduleResolution;\n }\n } catch {\n // 任何异常都回退默认,保证 build 不被 tsconfig 阻塞\n }\n\n tsConfigCache.set(tsconfigPath, result);\n return result;\n}\n\n/**\n * 为指定文件创建 TypeScript Program(带缓存)\n *\n * 从 filePath 向上查找最近的 tsconfig.json:\n * - 找到:用 ts.parseJsonConfigFileContent 解析,取 module / moduleResolution\n * 覆盖默认值,同时取 parsed.fileNames 作为 program 的 rootNames(让跨文件\n * import 的源文件被加载进 program)\n * - 找不到(如 os.tmpdir() 测试场景):回退到默认 NodeNext,rootNames 仅包含 filePath\n *\n * 加载全部相关文件保证 `resolveTypeReference` 的兜底遍历(找同名声明)能成功,\n * 修复业务项目用 `moduleResolution: Bundler` + 无扩展名相对导入时跨文件\n * `import type` 解析失败的问题。\n *\n * 同一次生成中需要分析多个文件时用 {@link createPrograms}(按 tsconfig 分组共享\n * 同一个 Program),避免每个文件单独创建 Program 重复解析全项目源码。\n *\n * @param filePath 要分析的 .ts 文件绝对路径\n */\nexport function createProgram(filePath: string): ts.Program {\n const cached = programCache.get(filePath);\n if (cached) {\n return cached;\n }\n\n const program = buildProgram([filePath], findTsConfig(filePath));\n\n programCache.set(filePath, program);\n return program;\n}\n\n/**\n * 为多个文件创建 Program(按 tsconfig 分组共享,带缓存)\n *\n * 同一次批量提取(build 时生成 routes/tools/agents 产物)中,所有向上查找到\n * 同一个 tsconfig.json 的文件共用**同一个 Program**——rootNames 为\n * tsconfig fileNames ∪ 全部入口文件,跨文件类型解析语义与单文件 `createProgram`\n * 完全一致,但 N 个文件只创建 1 个 Program(原来每个文件都全量解析一遍项目源码,\n * 是 build 时间的最大单项开销)。\n *\n * 缓存 key 为 `shared::<tsconfigPath>`(同一 tsconfig 一份 Program):同一批次重复\n * 调用命中缓存;不同批次通过 `program.getSourceFile` 校验缓存已覆盖本次全部入口文件,\n * 未覆盖(入口在 tsconfig include 之外)时重建。若把文件列表纳入 key,dev 按需模式下\n * 每个路由文件会各自持有一份全项目 Program(内存 O(路由数 × 项目大小))。\n * `invalidateProgramCache()` 同时清理共享缓存与单文件缓存。\n *\n * 向上查找不到 tsconfig.json 的文件(如 os.tmpdir() 测试场景)逐个回退到\n * {@link createProgram} 单文件行为,不参与共享。\n *\n * @param filePaths 要分析的 .ts 文件绝对路径列表(内部去重)\n * @returns filePath → Program 映射(包含全部请求文件)\n */\nexport function createPrograms(filePaths: string[]): Map<string, ts.Program> {\n const unique = [...new Set(filePaths)];\n const result = new Map<string, ts.Program>();\n\n // 按向上查找到的 tsconfig.json 分组(同组共享一个 Program)\n const groups = new Map<string, { tsconfigPath: string; files: string[] }>();\n const noTsconfigFiles: string[] = [];\n for (const filePath of unique) {\n const tsconfigPath = findTsConfig(filePath);\n if (!tsconfigPath) {\n noTsconfigFiles.push(filePath);\n continue;\n }\n const group = groups.get(tsconfigPath);\n if (group) {\n group.files.push(filePath);\n } else {\n groups.set(tsconfigPath, { tsconfigPath, files: [filePath] });\n }\n }\n\n for (const { tsconfigPath, files } of groups.values()) {\n // 缓存 key 只含 tsconfigPath:buildProgram 的 rootNames = 入口 ∪ tsconfig 全部\n // fileNames,同一 tsconfig 下不同入口批次的 Program 内容几乎一致——若把文件列表\n // 纳入 key,dev 按需模式下每个路由文件会各自持有一份\"全项目 Program\"\n // (内存 O(路由数 × 项目大小),无淘汰)。改为按 tsconfig 共享一份,通过\n // getSourceFile 校验缓存里的 Program 覆盖了本次全部入口文件(入口在 tsconfig\n // include 之外的场景重建,保证不缺文件)\n const cacheKey = `shared::${tsconfigPath}`;\n let program = programCache.get(cacheKey);\n const coversAll =\n program !== undefined && files.every((f) => program!.getSourceFile(f) !== undefined);\n if (!program || !coversAll) {\n program = buildProgram(files, tsconfigPath);\n programCache.set(cacheKey, program);\n }\n for (const filePath of files) {\n result.set(filePath, program);\n }\n }\n\n // 无 tsconfig 的文件回退单文件行为(保持与 createProgram 一致)\n for (const filePath of noTsconfigFiles) {\n result.set(filePath, createProgram(filePath));\n }\n\n return result;\n}\n\n/**\n * 构建 Program:合并框架默认 options 与 tsconfig 的 module / moduleResolution,\n * rootNames = 入口文件 ∪ tsconfig fileNames\n *\n * 由 {@link createProgram}(单文件)和 {@link createPrograms}(批量共享)共用,\n * 保证两条路径的 Program 构建语义一致。\n */\nfunction buildProgram(entryFiles: string[], tsconfigPath: string | null): ts.Program {\n // 默认选项:与项目历史行为一致(NodeNext),保证向后兼容\n const options: ts.CompilerOptions = {\n strict: true,\n target: ts.ScriptTarget.ES2022,\n module: ts.ModuleKind.NodeNext,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n skipLibCheck: true,\n noEmit: true,\n };\n\n // rootNames 默认只包含入口文件;读 tsconfig 后扩展为全部相关文件\n const rootNames = [...entryFiles];\n\n if (tsconfigPath) {\n const tsOptions = parseTsConfig(tsconfigPath);\n if (tsOptions.module !== undefined) {\n options.module = tsOptions.module;\n }\n if (tsOptions.moduleResolution !== undefined) {\n options.moduleResolution = tsOptions.moduleResolution;\n }\n if (tsOptions.fileNames.length > 0) {\n // 确保入口文件在 rootNames 中(即使 tsconfig include 未覆盖到)\n for (const fileName of tsOptions.fileNames) {\n if (!rootNames.includes(fileName)) {\n rootNames.push(fileName);\n }\n }\n }\n }\n\n return ts.createProgram(rootNames, options);\n}\n","import ts from 'typescript';\n\n/**\n * 模块级 Program 上下文,供 `resolveImportAlias` 兜底遍历跨文件声明使用\n *\n * TypeScript 的 `TypeChecker` 类型声明了 `getProgram(): Program` 方法,但\n * 运行时实例未暴露该方法(实测 `typeof checker.getProgram === 'undefined'`)。\n * 因此采用模块级变量在调用入口设置当前 program,resolveImportAlias 兜底时读取。\n *\n * 调用入口(`extractTypeInfo` / `extractAllTypes`)在分析前调\n * `setProgramContext(program)`,分析结束后调 `setProgramContext(null)` 清空。\n *\n * 串行场景安全:faapi 的 AST 提取在单线程顺序执行,无并发。\n */\nlet currentProgram: ts.Program | null = null;\n\n/**\n * 设置当前 Program 上下文(供 resolveImportAlias 兜底遍历跨文件声明使用)\n *\n * @param program 当前 program;分析结束后传 null 清空\n */\nexport function setProgramContext(program: ts.Program | null): void {\n currentProgram = program;\n}\n\n/**\n * Schema 提取错误\n *\n * 遇到无法解析或不支持运行时校验的类型时抛出,\n * 避免静默降级为 any 导致用户不知情。\n */\n/** 错误在源文件中的位置(file:line:column,便于在几百行类型文件中定位) */\nexport interface SchemaErrorLocation {\n file: string;\n line: number;\n column: number;\n}\n\nexport class SchemaExtractionError extends Error {\n constructor(\n public readonly typeText: string,\n public readonly reason: string,\n options?: ErrorOptions,\n public readonly location?: SchemaErrorLocation,\n ) {\n super(\n `无法解析类型 \"${typeText}\": ${reason}` +\n (location ? ` (${location.file}:${location.line}:${location.column})` : ''),\n options,\n );\n this.name = 'SchemaExtractionError';\n }\n\n /**\n * 从 AST 节点构造错误(自动携带 file:line:column)\n *\n * 所有抛错点应优先使用此工厂——错误无行号时,几百行的类型文件只能靠\n * 类型名肉眼定位;解析 lib.d.ts 类型别名时还会出现错误文本与文件上下文错位\n */\n static at(node: ts.Node, typeText: string, reason: string): SchemaExtractionError {\n const sourceFile = node.getSourceFile();\n if (!sourceFile) {\n return new SchemaExtractionError(typeText, reason);\n }\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n return new SchemaExtractionError(typeText, reason, undefined, {\n file: sourceFile.fileName,\n line: line + 1,\n column: character + 1,\n });\n }\n}\n\n/**\n * 运行时类型描述\n *\n * 用于校验器在运行时判断值的结构是否符合声明类型。\n * 相比单纯的字符串,能描述数组元素类型、嵌套对象、联合类型等。\n */\nexport type RuntimeType =\n | { kind: 'string' }\n | { kind: 'number' }\n | { kind: 'boolean' }\n | { kind: 'bigint' }\n | { kind: 'null' }\n | { kind: 'undefined' }\n | { kind: 'any' } // 不校验\n | { kind: 'unknown' } // 不校验\n | { kind: 'literal'; value: string | number | boolean } // 字面量\n | { kind: 'array'; element: RuntimeType }\n | { kind: 'tuple'; elements: TupleElement[] }\n | { kind: 'object'; properties: PropertyType[]; catchall?: RuntimeType }\n | { kind: 'union'; members: RuntimeType[] }\n | { kind: 'date' }\n | { kind: 'record'; key: RuntimeType; value: RuntimeType }\n | { kind: 'map'; key: RuntimeType; value: RuntimeType } // Map<K,V>,JSON 序列化为 entries 数组\n | { kind: 'set'; element: RuntimeType } // Set<T>,JSON 序列化为数组\n | { kind: 'ref'; name: string }; // 命名类型引用(支持循环引用)\n\n/**\n * 元组元素类型\n *\n * - optional: 该位置元素可省略(如 `[string, number?]`)\n * - rest: 该位置是剩余元素(如 `[string, ...number[]]`),必须是数组类型,展开后逐个校验\n */\nexport interface TupleElement {\n type: RuntimeType;\n optional: boolean;\n rest: boolean;\n}\n\nexport interface PropertyType {\n name: string;\n type: RuntimeType;\n optional: boolean;\n /**\n * 字段级 JSDoc 约束标签(@max/@min/@maxLength 等)\n *\n * 来自字段 JSDoc 注释,由 generateZodSchema 转为 zod 链式调用。\n * 约束与字段类型不匹配时在提取阶段抛 SchemaExtractionError。\n */\n constraints?: TypeConstraint[];\n}\n\n/**\n * JSDoc 约束标签的运行时描述\n *\n * 由字段 JSDoc 注释提取,对应 zod schema 的链式约束方法。\n * 仅在 PropertyType.constraints 中出现,不挂在嵌套类型(array 元素、tuple 元素等)上。\n */\nexport type TypeConstraint =\n // 数值约束(仅 number 字段)\n | { kind: 'max'; value: number }\n | { kind: 'min'; value: number }\n | { kind: 'int' }\n | { kind: 'positive' }\n | { kind: 'negative' }\n | { kind: 'nonnegative' }\n | { kind: 'nonpositive' }\n // 长度约束(string 或 array 字段)\n | { kind: 'maxLength'; value: number }\n | { kind: 'minLength'; value: number }\n | { kind: 'length'; value: number }\n // 字符串格式约束(仅 string 字段)\n | { kind: 'regex'; pattern: string; flags?: string }\n | { kind: 'email' }\n | { kind: 'url' }\n | { kind: 'uuid' };\n\n/**\n * 将 TypeScript 类型节点解析为运行时类型描述\n *\n * 支持的类型:\n * - 基础类型:string / number / boolean / null / undefined / any / unknown / void\n * - bigint:不支持(HTTP/JSON 不能传输),AST 提取阶段抛 SchemaExtractionError\n * - 字面量类型:'foo' / 42 / true\n * - 数组类型:T[] / Array<T> / ReadonlyArray<T> / readonly T[]\n * - 元组类型:[string, number] / [string, number?] / [string, ...number[]] / readonly [T, U](按位置校验)\n * - 对象类型:{ name: string; age?: number }(含 readonly 字段修饰符,忽略 readonly)\n * - 联合类型:string | null\n * - 交叉类型:A & B(按对象合并处理)\n * - 引用类型:Date / 其他 interface(递归解析)\n * - 工具类型:Record<K, V> / Partial<T> / Readonly<T>(best effort)\n * - Pick<T, K> / Omit<T, K>:K 支持字面量联合、类型别名、keyof T\n *\n * readonly 是编译期约束,运行时不产生校验语义,所有 readonly 修饰符统一忽略。\n *\n * @param typeNode TypeScript 类型节点\n * @param checker 类型 checker(用于解析引用类型)\n * @param visited 防止递归循环\n * @param bindings 泛型形参绑定(形参名 → 已解析的实参类型),解析泛型声明的\n * 类型体时传入;形参名在 resolveTypeReference 入口优先命中,\n * 遮蔽同名的真实类型声明\n */\nexport function resolveTypeNode(\n typeNode: ts.TypeNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n bindings: Map<string, RuntimeType> = new Map(),\n): RuntimeType {\n const kind = typeNode.kind;\n\n // 基础类型\n switch (kind) {\n case ts.SyntaxKind.StringKeyword:\n return { kind: 'string' };\n case ts.SyntaxKind.NumberKeyword:\n return { kind: 'number' };\n case ts.SyntaxKind.BooleanKeyword:\n return { kind: 'boolean' };\n case ts.SyntaxKind.BigIntKeyword:\n // HTTP/JSON 不能传输 BigInt,在 AST 提取阶段就报错,避免运行时校验必然失败\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'bigint 无法通过 HTTP/JSON 传输,请改用 string 或 number',\n );\n case ts.SyntaxKind.SymbolKeyword:\n // HTTP/JSON 不能传输 Symbol\n throw new SchemaExtractionError(typeNode.getText(), 'symbol 无法通过 HTTP/JSON 传输');\n case ts.SyntaxKind.NullKeyword:\n return { kind: 'null' };\n case ts.SyntaxKind.UndefinedKeyword:\n return { kind: 'undefined' };\n case ts.SyntaxKind.UnknownKeyword:\n // unknown 是唯一允许的\"不校验\"显式声明\n return { kind: 'any' };\n case ts.SyntaxKind.AnyKeyword:\n throw new SchemaExtractionError(typeNode.getText(), 'any 不支持,请使用 unknown 表示不校验');\n case ts.SyntaxKind.VoidKeyword:\n throw new SchemaExtractionError(typeNode.getText(), 'void 不支持运行时校验');\n case ts.SyntaxKind.NeverKeyword:\n throw new SchemaExtractionError(typeNode.getText(), 'never 不支持运行时校验');\n case ts.SyntaxKind.ObjectKeyword:\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'object 不支持,请使用具体对象类型或 unknown',\n );\n }\n\n // 字面量类型\n if (ts.isLiteralTypeNode(typeNode)) {\n const literal = typeNode.literal;\n if (ts.isStringLiteral(literal)) {\n return { kind: 'literal', value: literal.text };\n }\n if (ts.isNumericLiteral(literal)) {\n return { kind: 'literal', value: Number(literal.text) };\n }\n if (literal.kind === ts.SyntaxKind.TrueKeyword) {\n return { kind: 'literal', value: true };\n }\n if (literal.kind === ts.SyntaxKind.FalseKeyword) {\n return { kind: 'literal', value: false };\n }\n if (literal.kind === ts.SyntaxKind.NullKeyword) {\n return { kind: 'null' };\n }\n throw new SchemaExtractionError(typeNode.getText(), '不支持的字面量类型');\n }\n\n // 数组类型:T[]\n if (ts.isArrayTypeNode(typeNode)) {\n return {\n kind: 'array',\n element: resolveTypeNode(typeNode.elementType, checker, visited, bindings),\n };\n }\n\n // 元组类型:[string, number] / [string, number?] / [string, ...number[]]\n if (ts.isTupleTypeNode(typeNode)) {\n const elements: TupleElement[] = typeNode.elements.map((e) => {\n // 剩余元素:...T\n if (ts.isRestTypeNode(e)) {\n const inner = resolveTypeNode(e.type, checker, visited, bindings);\n // ...T[] → 元素类型是 T 的数组元素\n if (inner.kind === 'array') {\n return { type: inner.element, optional: false, rest: true };\n }\n // ...T 但 T 不是数组(罕见,TS 通常要求 rest 是数组)\n return { type: inner, optional: false, rest: true };\n }\n // 命名元组成员:[name: string, age?: number]\n if (ts.isNamedTupleMember(e)) {\n return {\n type: resolveTypeNode(e.type, checker, visited, bindings),\n optional: !!e.questionToken,\n rest: false,\n };\n }\n // 可选元素:number?(无名称,有 ?)\n if (ts.isOptionalTypeNode(e)) {\n return {\n type: resolveTypeNode(e.type, checker, visited, bindings),\n optional: true,\n rest: false,\n };\n }\n // 普通元素\n return {\n type: resolveTypeNode(e, checker, visited, bindings),\n optional: false,\n rest: false,\n };\n });\n return { kind: 'tuple', elements };\n }\n\n // 联合类型:A | B\n if (ts.isUnionTypeNode(typeNode)) {\n const members = typeNode.types.map((t) => resolveTypeNode(t, checker, visited, bindings));\n return { kind: 'union', members };\n }\n\n // 交叉类型:A & B → 合并对象属性(全部成员均为 object 时)\n if (ts.isIntersectionTypeNode(typeNode)) {\n const propMap = new Map<string, PropertyType>();\n for (const t of typeNode.types) {\n const resolved = resolveTypeNode(t, checker, visited, bindings);\n if (resolved.kind !== 'object') {\n // branded 类型(string & {...})等混合交叉在运行时无对应校验物——\n // 静默丢弃非 object 成员会放宽校验,按约定显式抛错(见 AGENTS.md §6.3)\n throw new SchemaExtractionError(\n typeNode.getText(),\n `交叉类型包含非 object 成员(${resolved.kind}),运行时无法校验——branded 类型建议改用具体类型或 unknown`,\n );\n }\n for (const prop of resolved.properties) {\n const existing = propMap.get(prop.name);\n if (!existing) {\n propMap.set(prop.name, prop);\n continue;\n }\n // 同名字段:类型或可选性不一致即 TS 语义下的 never——运行时无法校验,\n // 显式抛错;一致则去重(优先保留带约束的声明,更严格)\n if (\n JSON.stringify(existing.type) !== JSON.stringify(prop.type) ||\n existing.optional !== prop.optional\n ) {\n throw SchemaExtractionError.at(\n typeNode,\n typeNode.getText(),\n `交叉类型成员的同名字段 \"${prop.name}\" 类型冲突(TS 中为 never),运行时无法校验`,\n );\n }\n if (!existing.constraints?.length && prop.constraints?.length) {\n propMap.set(prop.name, prop);\n }\n }\n }\n return { kind: 'object', properties: [...propMap.values()] };\n }\n\n // 内联对象类型:{ name: string; age?: number }\n if (ts.isTypeLiteralNode(typeNode)) {\n return resolveTypeLiteral(typeNode, checker, visited, bindings);\n }\n\n // keyof T — 用 checker 解析为字面量联合\n if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.KeyOfKeyword) {\n return resolveKeyOf(typeNode, checker);\n }\n\n // readonly T(如 readonly string[] / readonly [T, U])— 编译期约束,运行时不校验\n // 递归解析内部类型即可,等同于去掉 readonly 修饰符\n if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.ReadonlyKeyword) {\n return resolveTypeNode(typeNode.type, checker, visited, bindings);\n }\n\n // 引用类型:Date / 自定义 interface / Array<T> / Record<K,V> / Partial<T> 等\n if (ts.isTypeReferenceNode(typeNode)) {\n return resolveTypeReference(typeNode, checker, visited, bindings);\n }\n\n // interface extends 的 heritage 子句(ExpressionWithTypeArguments)\n // 构造 TypeRefLike 视图(expression → typeName),复用 resolveTypeReference\n // 解析(覆盖 Date / Array<T> / 自定义 interface 等全部分支)\n if (ts.isExpressionWithTypeArguments(typeNode)) {\n return resolveTypeReference(\n {\n getText: () => typeNode.getText(),\n typeName: typeNode.expression as ts.EntityName,\n typeArguments: typeNode.typeArguments,\n },\n checker,\n visited,\n bindings,\n );\n }\n\n // 其他无法识别的语法节点\n throw SchemaExtractionError.at(typeNode, typeNode.getText(), '不支持的类型语法');\n}\n\n/**\n * 解析内联对象类型字面量\n */\nfunction resolveTypeLiteral(\n typeNode: ts.TypeLiteralNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n bindings: Map<string, RuntimeType> = new Map(),\n): RuntimeType {\n const properties: PropertyType[] = [];\n // 索引签名([k: string]: T)与属性共存合法:属性保留,索引签名转为 catchall\n let catchall: RuntimeType | undefined;\n\n for (const member of typeNode.members) {\n // 方法签名/存取器:运行时 JSON 数据无方法语义,静默丢弃会弱化校验——显式抛错\n if (\n ts.isMethodSignature(member) ||\n ts.isGetAccessorDeclaration(member) ||\n ts.isSetAccessorDeclaration(member)\n ) {\n throw SchemaExtractionError.at(\n member,\n member.getText(),\n '对象类型含方法签名或存取器,运行时 JSON 数据无法校验方法——请改用具体属性类型',\n );\n }\n // 属性签名:name: string\n if (ts.isPropertySignature(member) && member.name) {\n const name = member.name.getText();\n const optional = !!member.questionToken;\n const type = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n const constraints = extractConstraintsFromJsDoc(member, name);\n validateConstraints(constraints, type, name);\n properties.push(\n constraints.length > 0 ? { name, type, optional, constraints } : { name, type, optional },\n );\n }\n // 索引签名:[key: string]: T → catchall(与属性共存时不丢弃属性)\n if (ts.isIndexSignatureDeclaration(member)) {\n catchall = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n }\n }\n\n return catchall !== undefined\n ? { kind: 'object', properties, catchall }\n : { kind: 'object', properties };\n}\n\n/**\n * 从 RuntimeType 提取字面量字符串集合\n * 用于 Pick<T, K> / Omit<T, K> 的 K 参数解析\n *\n * @returns 字面量值数组;无法解析时返回 null\n */\nfunction extractLiteralKeys(type: RuntimeType): string[] | null {\n if (type.kind === 'literal' && typeof type.value === 'string') {\n return [type.value];\n }\n if (type.kind === 'union') {\n const keys: string[] = [];\n for (const member of type.members) {\n if (member.kind === 'literal' && typeof member.value === 'string') {\n keys.push(member.value);\n } else {\n return null;\n }\n }\n return keys;\n }\n return null;\n}\n\n/**\n * 用 checker 解析 K 的最终类型,提取字面量字符串集合\n *\n * 用于 Pick<T, K> / Omit<T, K> 的 K 参数解析,覆盖 AST 无法直接拿到的场景:\n * - K 为类型别名(type Keys = 'id' | 'name')\n * - K 为 keyof T\n *\n * @returns 字面量值数组;无法解析时返回 null\n */\nfunction extractKeysFromChecker(typeNode: ts.TypeNode, checker?: ts.TypeChecker): string[] | null {\n if (!checker) return null;\n const type = checker.getTypeFromTypeNode(typeNode);\n\n if (type.isUnion()) {\n const keys: string[] = [];\n for (const member of type.types) {\n if (member.isStringLiteral()) {\n keys.push(member.value);\n } else if (member.isNumberLiteral()) {\n keys.push(String(member.value));\n } else {\n return null;\n }\n }\n return keys;\n }\n\n if (type.isStringLiteral()) {\n return [type.value];\n }\n if (type.isNumberLiteral()) {\n return [String(type.value)];\n }\n\n return null;\n}\n\n/**\n * 解析 keyof T — 用 checker 计算最终类型,返回字面量联合\n *\n * 例如 keyof { id: number; name: string } → 'id' | 'name'\n */\nfunction resolveKeyOf(typeNode: ts.TypeOperatorNode, checker?: ts.TypeChecker): RuntimeType {\n if (!checker) {\n throw new SchemaExtractionError(typeNode.getText(), 'keyof T 需要 checker 才能解析');\n }\n const type = checker.getTypeFromTypeNode(typeNode);\n\n if (type.isUnion()) {\n const members: RuntimeType[] = [];\n for (const member of type.types) {\n if (member.isStringLiteral()) {\n members.push({ kind: 'literal', value: member.value });\n } else if (member.isNumberLiteral()) {\n members.push({ kind: 'literal', value: member.value });\n } else {\n throw new SchemaExtractionError(typeNode.getText(), 'keyof T 的结果包含非字面量类型');\n }\n }\n return { kind: 'union', members };\n }\n\n if (type.isStringLiteral()) {\n return { kind: 'literal', value: type.value };\n }\n if (type.isNumberLiteral()) {\n return { kind: 'literal', value: type.value };\n }\n\n throw new SchemaExtractionError(typeNode.getText(), 'keyof T 的结果无法解析为字面量联合');\n}\n\n/**\n * resolveTypeReference 所需的最小节点结构\n *\n * TypeReferenceNode 与 interface extends 的 heritage 节点(ExpressionWithTypeArguments)\n * 均满足该结构——后者用 expression 充当 typeName,typeArguments 同名。\n */\ninterface TypeRefLike {\n getText(): string;\n typeName: ts.EntityName | ts.LeftHandSideExpression;\n typeArguments?: readonly ts.TypeNode[];\n}\n\n/**\n * 解析类型引用(Date / 自定义 interface / Array<T> / Record<K,V> 等)\n *\n * 泛型形参优先:typeName 命中 bindings 时直接返回绑定的实参类型\n * (泛型声明体内部引用形参的场景,形参遮蔽同名真实类型)。\n */\nfunction resolveTypeReference(\n typeNode: TypeRefLike,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n bindings: Map<string, RuntimeType> = new Map(),\n): RuntimeType {\n const typeName = typeNode.typeName.getText();\n\n // 泛型形参命中:直接返回已绑定的实参类型\n const bound = bindings.get(typeName);\n if (bound) {\n return bound;\n }\n\n // Date 类型\n if (typeName === 'Date') {\n return { kind: 'date' };\n }\n\n // Array<T> / ReadonlyArray<T> — 等同处理,readonly 是编译期约束\n if (\n (typeName === 'Array' || typeName === 'ReadonlyArray') &&\n typeNode.typeArguments?.length === 1\n ) {\n const [arg] = typeNode.typeArguments;\n return {\n kind: 'array',\n element: resolveTypeNode(arg!, checker, visited, bindings),\n };\n }\n\n // Record<K, V>\n if (typeName === 'Record' && typeNode.typeArguments?.length === 2) {\n const [keyArg, valueArg] = typeNode.typeArguments;\n return {\n kind: 'record',\n key: resolveTypeNode(keyArg!, checker, visited, bindings),\n value: resolveTypeNode(valueArg!, checker, visited, bindings),\n };\n }\n\n // Partial<T> / Required<T> / Readonly<T> — 解析内部类型\n if (\n (typeName === 'Partial' || typeName === 'Required' || typeName === 'Readonly') &&\n typeNode.typeArguments?.length === 1\n ) {\n const inner = resolveTypeNode(typeNode.typeArguments[0]!, checker, visited, bindings);\n if (inner.kind === 'object' && typeName === 'Partial') {\n // Partial 所有字段变可选\n return {\n kind: 'object',\n properties: inner.properties.map((p) => ({ ...p, optional: true })),\n };\n }\n if (inner.kind === 'object' && typeName === 'Required') {\n // Required 恢复所有字段为必填——静默保留 optional 会让校验弱于 TS 类型\n return {\n kind: 'object',\n properties: inner.properties.map((p) => ({ ...p, optional: false })),\n };\n }\n // Readonly:编译期约束,运行时无校验语义,原样返回(等同去掉 readonly 修饰符)\n return inner;\n }\n\n // Pick<T, K> / Omit<T, K> — 解析 T 的字段,按 K 筛选/排除\n if ((typeName === 'Pick' || typeName === 'Omit') && typeNode.typeArguments?.length === 2) {\n const innerType = resolveTypeNode(typeNode.typeArguments[0]!, checker, visited, bindings);\n if (innerType.kind !== 'object') {\n throw new SchemaExtractionError(\n typeNode.getText(),\n `${typeName} 的 T 必须是对象类型,实际为 ${innerType.kind}`,\n );\n }\n\n // K 解析顺序:AST 字面量联合 → checker 解析(覆盖类型别名 / keyof T)\n const keyTypeNode = typeNode.typeArguments[1]!;\n let keys = extractLiteralKeys(resolveTypeNode(keyTypeNode, checker, visited, bindings));\n if (keys === null) {\n keys = extractKeysFromChecker(keyTypeNode, checker);\n }\n if (keys === null) {\n throw new SchemaExtractionError(typeNode.getText(), `${typeName} 的 K 无法解析为字面量集合`);\n }\n\n const keySet = new Set(keys);\n const properties =\n typeName === 'Pick'\n ? innerType.properties.filter((p) => keySet.has(p.name))\n : innerType.properties.filter((p) => !keySet.has(p.name));\n\n return { kind: 'object', properties };\n }\n\n // Map<K, V> — JSON 序列化为 entries 数组,运行时用 z.preprocess(coerceMap, z.map(...)) 还原\n if (typeName === 'Map') {\n if (!typeNode.typeArguments || typeNode.typeArguments.length !== 2) {\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'Map 必须带 2 个类型参数,如 Map<K, V>,裸 Map 不支持',\n );\n }\n const [mapKey, mapValue] = typeNode.typeArguments;\n return {\n kind: 'map',\n key: resolveTypeNode(mapKey!, checker, visited, bindings),\n value: resolveTypeNode(mapValue!, checker, visited, bindings),\n };\n }\n\n // Set<T> — JSON 序列化为数组,运行时用 z.preprocess(coerceSet, z.set(...)) 还原\n if (typeName === 'Set') {\n if (!typeNode.typeArguments || typeNode.typeArguments.length !== 1) {\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'Set 必须带 1 个类型参数,如 Set<T>,裸 Set 不支持',\n );\n }\n const [setArg] = typeNode.typeArguments;\n return {\n kind: 'set',\n element: resolveTypeNode(setArg!, checker, visited, bindings),\n };\n }\n\n // WeakMap / WeakSet — 运行时无法枚举校验,仍然抛错(Map/Set 不复用其语义)\n if (typeName === 'WeakMap' || typeName === 'WeakSet') {\n throw new SchemaExtractionError(\n typeNode.getText(),\n `${typeName} 运行时无法枚举校验,请改用 Map / Set 或对象`,\n );\n }\n\n // Promise<T> — 运行时无法校验异步值\n if (typeName === 'Promise') {\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'Promise 运行时无法校验,请勿在 query/body 类型中使用',\n );\n }\n\n // Function — HTTP/JSON 不能传输函数\n if (typeName === 'Function') {\n throw new SchemaExtractionError(typeNode.getText(), 'Function 无法通过 HTTP/JSON 传输');\n }\n\n // 防止递归循环:遇到已访问的类型返回 ref,支持循环引用\n if (visited.has(typeName)) {\n return { kind: 'ref', name: typeName };\n }\n visited.add(typeName);\n\n // 使用 checker 解析引用类型(interface / type 别名)\n if (checker) {\n const symbol =\n ts.isIdentifier(typeNode.typeName) || ts.isQualifiedName(typeNode.typeName)\n ? checker.getSymbolAtLocation(typeNode.typeName)\n : undefined;\n\n if (symbol) {\n const declaration = symbol.declarations?.[0];\n if (declaration) {\n // interface 声明\n if (ts.isInterfaceDeclaration(declaration)) {\n return resolveInterfaceDeclaration(\n declaration,\n checker,\n visited,\n bindings,\n typeNode.typeArguments,\n );\n }\n // type 别名声明\n if (ts.isTypeAliasDeclaration(declaration)) {\n const declBindings = bindTypeParameters(\n declaration.typeParameters,\n typeNode.typeArguments,\n bindings,\n checker,\n visited,\n typeNode,\n );\n return resolveTypeNode(declaration.type, checker, visited, declBindings);\n }\n // enum 声明 → 字面量联合\n if (ts.isEnumDeclaration(declaration)) {\n return resolveEnumDeclaration(declaration);\n }\n // import 别名(跨文件 import type):跟到真实声明\n // 业务项目用 moduleResolution: Bundler + 无扩展名导入时,\n // checker 拿到的 symbol 是 ImportSpecifier(Alias),不是真实声明\n if (ts.isImportSpecifier(declaration) || ts.isImportClause(declaration)) {\n const resolved = resolveImportAlias(\n typeNode,\n symbol,\n checker,\n visited,\n bindings,\n typeNode.typeArguments,\n );\n if (resolved) return resolved;\n }\n }\n }\n }\n\n throw new SchemaExtractionError(typeNode.getText(), `无法解析的引用类型 \"${typeName}\"`);\n}\n\n/**\n * 绑定泛型形参与实参,返回声明体解析用的 bindings\n *\n * - 实参表达式在调用方作用域解析(用 outerBindings,可引用外层形参)\n * - 默认类型在声明方作用域解析(用正在构造的 bindings,可引用前面形参,如 `<T, U = T[]>`)\n * - 实参与默认类型均缺失时抛 SchemaExtractionError(TS 本身也编译不过,防御性报错)\n * - 声明无 typeParameters 时原样返回 outerBindings\n */\nfunction bindTypeParameters(\n typeParameters: readonly ts.TypeParameterDeclaration[] | undefined,\n typeArguments: readonly ts.TypeNode[] | undefined,\n outerBindings: Map<string, RuntimeType>,\n checker: ts.TypeChecker | undefined,\n visited: Set<string>,\n errorNode: { getText(): string },\n): Map<string, RuntimeType> {\n if (!typeParameters || typeParameters.length === 0) return outerBindings;\n\n const bindings = new Map(outerBindings);\n for (let i = 0; i < typeParameters.length; i++) {\n const param = typeParameters[i];\n if (!param) continue;\n const arg = typeArguments?.[i];\n if (arg) {\n bindings.set(param.name.text, resolveTypeNode(arg, checker, visited, outerBindings));\n } else if (param.default) {\n bindings.set(param.name.text, resolveTypeNode(param.default, checker, visited, bindings));\n } else {\n throw new SchemaExtractionError(\n errorNode.getText(),\n `泛型参数 \"${param.name.text}\" 缺少类型实参(且无默认类型)`,\n );\n }\n }\n return bindings;\n}\n\n/**\n * 解析 import 别名符号,跟到真实的 interface / type alias / enum 声明\n *\n * 业务项目用 `moduleResolution: Bundler` + 无扩展名相对导入(如\n * `import type { StyleGuide } from '../../db/schema'`)时,checker 拿到的 symbol\n * 是 `ImportSpecifier`(Alias symbol),不是真实声明节点。\n *\n * 解析顺序:\n * 1. `checker.getAliasedSymbol(symbol)` — 优先路径,直接跟到真实 symbol\n * 2. 兜底:遍历 program 的所有 sourceFiles 找名为 `typeName` 的\n * `InterfaceDeclaration` / `TypeAliasDeclaration` / `EnumDeclaration`\n *\n * 兜底路径用于 `getAliasedSymbol` 在 `noEmit` 模式下未完全绑定 alias 的场景\n * (实测:即使业务项目 tsconfig 用 Bundler + program 加载了全部相关文件,\n * `getAliasedSymbol` 也可能返回 unknown / 无 declarations)。\n *\n * @returns RuntimeType 解析结果;无法解析返回 null(由调用方抛 SchemaExtractionError)\n */\nfunction resolveImportAlias(\n typeNode: TypeRefLike,\n symbol: ts.Symbol,\n checker: ts.TypeChecker,\n visited: Set<string>,\n bindings: Map<string, RuntimeType> = new Map(),\n typeArguments?: readonly ts.TypeNode[],\n): RuntimeType | null {\n const typeName = typeNode.typeName.getText();\n\n // 路径1:getAliasedSymbol 直接跟到真实 symbol\n try {\n const aliased = checker.getAliasedSymbol(symbol);\n if (aliased && aliased.declarations && aliased.declarations.length > 0) {\n const decl = aliased.declarations[0]!;\n if (ts.isInterfaceDeclaration(decl)) {\n return resolveInterfaceDeclaration(decl, checker, visited, bindings, typeArguments);\n }\n if (ts.isTypeAliasDeclaration(decl)) {\n const declBindings = bindTypeParameters(\n decl.typeParameters,\n typeArguments,\n bindings,\n checker,\n visited,\n typeNode,\n );\n return resolveTypeNode(decl.type, checker, visited, declBindings);\n }\n if (ts.isEnumDeclaration(decl)) {\n return resolveEnumDeclaration(decl);\n }\n }\n } catch {\n // getAliasedSymbol 抛错时走兜底\n }\n\n // 路径2:兜底遍历 program 的 sourceFiles 找同名声明\n // 用模块级变量 currentProgram(createProgram 时通过 setProgramContext 设置)\n // TypeChecker 类型上声明了 getProgram() 但运行时未暴露,只能通过外部传入\n const program = currentProgram;\n if (!program) return null;\n\n const allSFs = program.getSourceFiles();\n for (const sourceFile of allSFs) {\n // 跳过 lib.d.ts / node_modules / TypeScript 内置文件\n if (\n sourceFile.fileName.includes('/node_modules/') ||\n sourceFile.fileName.includes('typescript/lib/')\n ) {\n continue;\n }\n // 顶层声明查找(用独立函数返回,避免 TS 控制流分析把闭包赋值的 let 变量收窄为 never)\n const found = findTopLevelDecl(sourceFile, typeName);\n if (found) {\n if (found.kind === 'interface') {\n return resolveInterfaceDeclaration(found.node, checker, visited, bindings, typeArguments);\n }\n if (found.kind === 'typeAlias') {\n const declBindings = bindTypeParameters(\n found.node.typeParameters,\n typeArguments,\n bindings,\n checker,\n visited,\n typeNode,\n );\n return resolveTypeNode(found.node.type, checker, visited, declBindings);\n }\n if (found.kind === 'enum') {\n return resolveEnumDeclaration(found.node);\n }\n }\n }\n\n return null;\n}\n\n/**\n * 在 sourceFile 顶层查找名为 typeName 的 interface / type alias / enum 声明\n *\n * 独立为函数是因为 TS 控制流分析对\"闭包中赋值的 let 变量\"保守处理:\n * 即使 forEachChild 同步调用回调,分析器仍认为赋值可能未发生,\n * 导致外层读取时变量被收窄为初始值类型(null)。\n * 用函数返回值(带显式返回类型)绕过此限制。\n */\nexport type TopLevelDecl =\n | { kind: 'interface'; node: ts.InterfaceDeclaration }\n | { kind: 'typeAlias'; node: ts.TypeAliasDeclaration }\n | { kind: 'enum'; node: ts.EnumDeclaration };\n\nexport function findTopLevelDecl(sourceFile: ts.SourceFile, typeName: string): TopLevelDecl | null {\n let found: TopLevelDecl | null = null;\n ts.forEachChild(sourceFile, (node) => {\n if (found) return;\n if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {\n found = { kind: 'interface', node };\n } else if (ts.isTypeAliasDeclaration(node) && node.name.text === typeName) {\n found = { kind: 'typeAlias', node };\n } else if (ts.isEnumDeclaration(node) && node.name.text === typeName) {\n found = { kind: 'enum', node };\n }\n });\n return found;\n}\n\n/**\n * 解析 enum 声明为字面量联合类型\n *\n * - 字符串枚举:`enum Role { Admin = 'admin' }` → `'admin' | ...`\n * - 数值枚举:`enum Code { OK = 200 }` → `200 | ...`\n * - 隐式数值枚举:`enum Dir { Up, Down }` → `0 | 1 | ...`\n *\n * HTTP 视角:枚举值在 JSON 中是普通 string/number,按字面量联合校验即可。\n */\nexport function resolveEnumDeclaration(node: ts.EnumDeclaration): RuntimeType {\n const members: RuntimeType[] = [];\n let nextNumericValue = 0;\n\n for (const member of node.members) {\n if (member.initializer) {\n if (ts.isStringLiteral(member.initializer)) {\n members.push({ kind: 'literal', value: member.initializer.text });\n } else if (ts.isNumericLiteral(member.initializer)) {\n const num = Number(member.initializer.text);\n members.push({ kind: 'literal', value: num });\n nextNumericValue = num + 1;\n } else {\n throw new SchemaExtractionError(\n node.name.text,\n `enum 成员 \"${member.name.getText()}\" 的初始化值类型不支持,仅支持 string/number 字面量`,\n );\n }\n } else {\n // 隐式数值枚举:无初始化值,使用递增整数\n members.push({ kind: 'literal', value: nextNumericValue });\n nextNumericValue++;\n }\n }\n\n return { kind: 'union', members };\n}\n\n/**\n * 解析 interface 声明(含继承与泛型形参绑定)\n *\n * @param node interface 声明节点\n * @param checker 类型 checker\n * @param visited 防止递归循环\n * @param outerBindings 外层泛型形参绑定(调用方作用域)\n * @param typeArguments 本次引用携带的类型实参(如 `Base<string>` 的 [string]),\n * 与 node.typeParameters 按位置配对构造声明体 bindings\n */\nexport function resolveInterfaceDeclaration(\n node: ts.InterfaceDeclaration,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n outerBindings: Map<string, RuntimeType> = new Map(),\n typeArguments?: readonly ts.TypeNode[],\n): RuntimeType {\n const properties: PropertyType[] = [];\n const propMap = new Map<string, PropertyType>();\n // 索引签名([k: string]: T)与属性共存合法:属性保留,索引签名转为 catchall\n let catchall: RuntimeType | undefined;\n\n // 绑定本声明的泛型形参(声明体与 heritage 均在该作用域下解析)\n const bindings = bindTypeParameters(\n node.typeParameters,\n typeArguments,\n outerBindings,\n checker,\n visited,\n node,\n );\n\n // 处理继承的父接口\n for (const heritageClause of node.heritageClauses ?? []) {\n if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword) {\n for (const expr of heritageClause.types) {\n const parentType = resolveTypeNode(expr, checker, visited, bindings);\n if (parentType.kind === 'object') {\n for (const prop of parentType.properties) {\n propMap.set(prop.name, prop);\n }\n }\n }\n }\n }\n\n // 处理自身成员(覆盖继承的同名字段)\n for (const member of node.members) {\n // 方法签名/存取器:运行时 JSON 数据无方法语义,静默丢弃会弱化校验——显式抛错\n if (\n ts.isMethodSignature(member) ||\n ts.isGetAccessorDeclaration(member) ||\n ts.isSetAccessorDeclaration(member)\n ) {\n throw SchemaExtractionError.at(\n member,\n member.getText(),\n '接口含方法签名或存取器,运行时 JSON 数据无法校验方法——请改用具体属性类型',\n );\n }\n if (ts.isPropertySignature(member) && member.name) {\n const name = member.name.getText();\n const optional = !!member.questionToken;\n const type = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n const constraints = extractConstraintsFromJsDoc(member, name);\n validateConstraints(constraints, type, name);\n propMap.set(\n name,\n constraints.length > 0 ? { name, type, optional, constraints } : { name, type, optional },\n );\n }\n // 索引签名 → catchall(与属性/继承属性共存时不丢弃)\n if (ts.isIndexSignatureDeclaration(member)) {\n catchall = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n }\n }\n\n for (const prop of propMap.values()) {\n properties.push(prop);\n }\n\n return catchall !== undefined\n ? { kind: 'object', properties, catchall }\n : { kind: 'object', properties };\n}\n\n/**\n * 数值约束标签集合(仅 number 字段)\n */\nconst NUMBER_CONSTRAINT_KINDS = new Set<TypeConstraint['kind']>([\n 'max',\n 'min',\n 'int',\n 'positive',\n 'negative',\n 'nonnegative',\n 'nonpositive',\n]);\n\n/**\n * 长度约束标签集合(string 或 array 字段)\n */\nconst LENGTH_CONSTRAINT_KINDS = new Set<TypeConstraint['kind']>([\n 'maxLength',\n 'minLength',\n 'length',\n]);\n\n/**\n * 字符串格式约束标签集合(仅 string 字段)\n */\nconst STRING_FORMAT_CONSTRAINT_KINDS = new Set<TypeConstraint['kind']>([\n 'regex',\n 'email',\n 'url',\n 'uuid',\n]);\n\n/**\n * 从字段的 JSDoc 注释中提取约束标签\n *\n * 解析 `/** ... *\\/` 块注释中的 `@max`/`@min`/`@maxLength` 等标签。\n * 行内注释 `// @max 100` 不识别。\n *\n * @param node 字段节点(PropertySignature)\n * @param fieldName 字段名,用于错误信息\n * @returns 约束数组(无约束时返回空数组)\n */\nfunction extractConstraintsFromJsDoc(\n node: ts.PropertySignature,\n fieldName: string,\n): TypeConstraint[] {\n // ts.getJSDocCommentsAndTags 返回 JSDoc/JsDocTag 数组\n const jsDocs = ts\n .getJSDocCommentsAndTags(node)\n .filter((entry): entry is ts.JSDoc => ts.isJSDoc(entry));\n\n if (jsDocs.length === 0) return [];\n\n const constraints: TypeConstraint[] = [];\n\n for (const jsDoc of jsDocs) {\n if (!jsDoc.tags) continue;\n for (const tag of jsDoc.tags) {\n const constraint = parseJsDocTag(tag, fieldName);\n if (constraint) constraints.push(constraint);\n }\n }\n\n return constraints;\n}\n\n/**\n * 提取 JSDoc 标签后的文本注释\n *\n * `tag.comment` 类型为 `string | NodeArray<JSDocComment> | undefined`,\n * 此处仅取纯文本形式(约束标签后跟的均为字面量值,不会包含 JSDocLink 等节点)。\n *\n * @returns 标签后的文本;无文本或非纯字符串时返回 undefined\n */\nfunction getTagCommentText(tag: ts.JSDocTag): string | undefined {\n const comment = tag.comment;\n if (typeof comment === 'string') return comment;\n return undefined;\n}\n\n/**\n * 解析单个 JSDoc 标签为约束\n *\n * @param tag JSDocTag 节点\n * @param fieldName 字段名,用于错误信息\n * @returns 约束对象;非约束标签返回 null\n */\nfunction parseJsDocTag(tag: ts.JSDocTag, fieldName: string): TypeConstraint | null {\n const tagName = tag.tagName.text;\n\n switch (tagName) {\n // 数值约束(带值)\n case 'max':\n case 'min': {\n const value = parseNumberValue(tag, fieldName, tagName);\n return { kind: tagName, value };\n }\n\n // 长度约束(带值)\n case 'maxLength':\n case 'minLength':\n case 'length': {\n const value = parseNumberValue(tag, fieldName, tagName);\n return { kind: tagName, value };\n }\n\n // 正则约束(带 /pattern/flags 值)\n case 'regex':\n case 'pattern': {\n const text = getTagCommentText(tag);\n if (!text) {\n throw new SchemaExtractionError(fieldName, `@${tagName} 标签需要 /pattern/flags 形式的值`);\n }\n const regex = parseRegexLiteral(text.trim(), fieldName);\n return { kind: 'regex', pattern: regex.pattern, flags: regex.flags };\n }\n\n // 数值约束(无值)\n case 'int':\n case 'positive':\n case 'negative':\n case 'nonnegative':\n case 'nonpositive':\n return { kind: tagName };\n\n // 字符串格式约束(无值)\n case 'email':\n case 'url':\n case 'uuid':\n return { kind: tagName };\n\n default:\n return null;\n }\n}\n\n/**\n * 解析标签后的数字值\n *\n * @param tag JSDocTag 节点\n * @param fieldName 字段名,用于错误信息\n * @param tagName 标签名,用于错误信息\n */\nfunction parseNumberValue(tag: ts.JSDocTag, fieldName: string, tagName: string): number {\n const text = getTagCommentText(tag);\n if (!text) {\n throw new SchemaExtractionError(fieldName, `@${tagName} 标签需要一个数字值`);\n }\n const trimmed = text.trim();\n const num = Number(trimmed);\n if (!Number.isFinite(num)) {\n throw new SchemaExtractionError(fieldName, `@${tagName} 标签的值 \"${trimmed}\" 不是有效数字`);\n }\n return num;\n}\n\n/**\n * 解析 /pattern/flags 形式的正则字面量\n *\n * @param text 标签后的文本,如 \"/^[a-z]+$/i\"\n * @param fieldName 字段名,用于错误信息\n */\nfunction parseRegexLiteral(text: string, fieldName: string): { pattern: string; flags?: string } {\n // 必须以 / 开头,匹配结尾的 / 和可选 flags\n const match = /^\\/(.+)\\/([gimsuy]*)$/.exec(text);\n if (!match) {\n throw new SchemaExtractionError(fieldName, `正则值 \"${text}\" 不是 /pattern/flags 形式`);\n }\n const [, pattern, flags] = match;\n if (!pattern) {\n throw new SchemaExtractionError(fieldName, `正则值 \"${text}\" 的 pattern 部分为空`);\n }\n return flags ? { pattern, flags } : { pattern };\n}\n\n/**\n * 校验约束与字段类型是否匹配\n *\n * 不匹配时抛 SchemaExtractionError(复用现有错误类型)。\n *\n * @param constraints 约束数组\n * @param type 字段类型\n * @param fieldName 字段名,用于错误信息\n */\nfunction validateConstraints(\n constraints: TypeConstraint[],\n type: RuntimeType,\n fieldName: string,\n): void {\n if (constraints.length === 0) return;\n\n for (const constraint of constraints) {\n const kind = constraint.kind;\n\n if (NUMBER_CONSTRAINT_KINDS.has(kind)) {\n if (type.kind !== 'number') {\n throw new SchemaExtractionError(\n fieldName,\n `@${kind} 约束仅适用于 number 字段,实际为 ${type.kind}`,\n );\n }\n continue;\n }\n\n if (LENGTH_CONSTRAINT_KINDS.has(kind)) {\n if (type.kind !== 'string' && type.kind !== 'array') {\n throw new SchemaExtractionError(\n fieldName,\n `@${kind} 约束仅适用于 string 或 array 字段,实际为 ${type.kind}`,\n );\n }\n continue;\n }\n\n if (STRING_FORMAT_CONSTRAINT_KINDS.has(kind)) {\n if (type.kind !== 'string') {\n throw new SchemaExtractionError(\n fieldName,\n `@${kind} 约束仅适用于 string 字段,实际为 ${type.kind}`,\n );\n }\n continue;\n }\n }\n}\n","import ts from 'typescript';\nimport {\n resolveTypeNode,\n resolveInterfaceDeclaration,\n resolveEnumDeclaration,\n findTopLevelDecl,\n setProgramContext,\n type PropertyType,\n type RuntimeType,\n SchemaExtractionError,\n} from './resolveTypeNode';\n\nexport interface HandlerTypeInfo {\n name: string; // 类型名,如 'GETQuery'\n properties: PropertyType[];\n /** 完整的运行时类型描述(用于嵌套校验) */\n runtimeType: RuntimeType;\n}\n\n/**\n * 从源文件中提取指定名称的类型信息\n *\n * 支持的类型声明:\n * - interface 声明(含继承)\n * - type 别名(type Query = { ... })\n * - enum 声明(跨文件回退路径支持)\n *\n * 自身文件找不到目标声明时,回退到 program 的其他源文件查找同名顶层声明\n * (跳过 node_modules / TypeScript lib,首个匹配生效,与 resolveImportAlias\n * 兜底路径语义一致)。该回退服务于入口类型中第二次出现的跨文件引用:首次引用\n * 已由 checker 内联,二次引用被标记为 ref,代码生成阶段经\n * createLazyTypeResolver → extractTypeInfo 解析,若无回退会静默生成\n * z.unknown(),违背「不降级放行」约定。\n *\n * 遇到不支持的类型时抛 `SchemaExtractionError`,错误信息包含文件路径和类型名。\n *\n * @param program TypeScript Program\n * @param filePath 源文件路径\n * @param typeName 类型名,如 'GETQuery'\n */\nexport function extractTypeInfo(\n program: ts.Program,\n filePath: string,\n typeName: string,\n): HandlerTypeInfo | null {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const checker = program.getTypeChecker();\n\n // 设置 program 上下文,供 resolveImportAlias 兜底遍历跨文件声明使用\n // (TypeChecker 运行时未暴露 getProgram(),通过模块级变量传入)\n setProgramContext(program);\n try {\n // 1. 查找 interface 声明\n let result: HandlerTypeInfo | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {\n const visited = new Set<string>();\n visited.add(typeName); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, typeName, () =>\n resolveInterfaceDeclaration(node, checker, visited),\n );\n result = {\n name: typeName,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n };\n return;\n }\n\n // 2. 查找 type 别名\n if (ts.isTypeAliasDeclaration(node) && node.name.text === typeName) {\n const visited = new Set<string>();\n visited.add(typeName); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, typeName, () =>\n resolveTypeNode(node.type, checker, visited),\n );\n result = {\n name: typeName,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n };\n return;\n }\n });\n\n if (result) return result;\n\n // 3. 跨文件回退:自身文件无该声明时,在 program 的其他源文件中查找同名顶层声明\n for (const sf of program.getSourceFiles()) {\n if (sf === sourceFile) continue;\n // 与 resolveImportAlias 兜底路径使用相同的过滤规则\n if (sf.fileName.includes('/node_modules/') || sf.fileName.includes('typescript/lib/')) {\n continue;\n }\n const found = findTopLevelDecl(sf, typeName);\n if (!found) continue;\n\n const visited = new Set<string>();\n visited.add(typeName);\n const runtimeType = withFileContext(filePath, typeName, () => {\n if (found.kind === 'interface') {\n return resolveInterfaceDeclaration(found.node, checker, visited);\n }\n if (found.kind === 'typeAlias') {\n return resolveTypeNode(found.node.type, checker, visited);\n }\n return resolveEnumDeclaration(found.node);\n });\n return {\n name: typeName,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n };\n }\n\n return null;\n } finally {\n setProgramContext(null);\n }\n}\n\n/**\n * 从源文件中提取所有 interface 和 type 别名的类型信息\n *\n * 用于:\n * - 生成 schema 模块时遍历所有命名类型\n * - 作为 typeResolver 提供给 generateZodSchema,解析循环引用中的 ref\n *\n * @returns Map<类型名, HandlerTypeInfo>\n */\nexport function extractAllTypes(\n program: ts.Program,\n filePath: string,\n): Map<string, HandlerTypeInfo> {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return new Map();\n\n const checker = program.getTypeChecker();\n\n // 设置 program 上下文,供 resolveImportAlias 兜底遍历跨文件声明使用\n setProgramContext(program);\n try {\n const result = new Map<string, HandlerTypeInfo>();\n\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isInterfaceDeclaration(node)) {\n const visited = new Set<string>();\n visited.add(node.name.text); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, node.name.text, () =>\n resolveInterfaceDeclaration(node, checker, visited),\n );\n result.set(node.name.text, {\n name: node.name.text,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n });\n return;\n }\n\n if (ts.isTypeAliasDeclaration(node)) {\n const visited = new Set<string>();\n visited.add(node.name.text); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, node.name.text, () =>\n resolveTypeNode(node.type, checker, visited),\n );\n result.set(node.name.text, {\n name: node.name.text,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n });\n return;\n }\n });\n\n return result;\n } finally {\n setProgramContext(null);\n }\n}\n\n/**\n * 惰性类型解析器\n *\n * 按名称解析类型并缓存。与 extractAllTypes 的差异:extractAllTypes 提前解析\n * 文件中全部顶层类型——与路由无关的类型也会被解析,其中任何类型含不支持语法\n * 都会拖垮整个 build/reload;惰性解析器只在类型真正被需要时(入口类型或\n * ref 引用)才解析,无关类型零开销。\n */\nexport interface LazyTypeResolver {\n /**\n * 按名称解析类型\n *\n * @returns 类型信息;文件中无该名称的声明时返回 null\n * @throws SchemaExtractionError 类型声明含不支持语法时(与 extractTypeInfo 一致,不降级)\n */\n resolve(name: string): HandlerTypeInfo | null;\n}\n\n/**\n * 创建文件的惰性类型解析器\n *\n * 缓存保证同一类型只解析一次(幂等返回同一实例)。\n *\n * @param program TypeScript Program(解析期间会临时设置 program 上下文,供\n * resolveImportAlias 兜底遍历跨文件声明使用)\n * @param filePath 源文件绝对路径\n */\nexport function createLazyTypeResolver(program: ts.Program, filePath: string): LazyTypeResolver {\n const cache = new Map<string, HandlerTypeInfo | null>();\n return {\n resolve(name: string): HandlerTypeInfo | null {\n if (cache.has(name)) return cache.get(name)!;\n const info = extractTypeInfo(program, filePath, name);\n cache.set(name, info);\n return info;\n },\n };\n}\n\n/**\n * 包装类型解析,捕获 SchemaExtractionError 并补充文件路径和类型名上下文\n *\n * 只包装一次(最外层),避免嵌套调用时重复包装。\n */\nfunction withFileContext<T>(filePath: string, typeName: string, fn: () => T): T {\n try {\n return fn();\n } catch (err) {\n if (err instanceof SchemaExtractionError) {\n // 补充文件路径和类型名,只包装一次;保留行号位置(错误在类型文件中的精确定位)\n const fileName = filePath.split('/').pop() ?? filePath;\n const enriched = new SchemaExtractionError(\n err.typeText,\n `${err.reason}(文件: ${fileName}, 类型: ${typeName})`,\n { cause: err },\n err.location,\n );\n throw enriched;\n }\n throw err;\n }\n}\n","/**\n * 根据 HTTP 方法判断主输入类型\n *\n * - GET / DELETE / HEAD:query(URL 查询参数)\n * - POST / PUT / PATCH:body(请求体)\n *\n * 注意:所有方法都可能同时有 query 和 body,\n * 这里返回的是\"主输入\"(用于校验和注入)。\n * DELETE 请求也支持 body(见 resolveInput.ts)。\n */\nexport function getInputTypeForMethod(method: string): 'query' | 'body' {\n const upper = method.toUpperCase();\n if (upper === 'GET' || upper === 'DELETE' || upper === 'HEAD') {\n return 'query';\n }\n return 'body';\n}\n\n/**\n * 判断方法是否有请求体\n *\n * POST / PUT / PATCH / DELETE 可能有 body\n * GET / HEAD 不应该有 body\n */\nexport function hasBody(method: string): boolean {\n const upper = method.toUpperCase();\n return upper === 'POST' || upper === 'PUT' || upper === 'PATCH' || upper === 'DELETE';\n}\n","/**\n * 根据 HTTP 方法和输入类型生成 schema 类型名\n * GET + query -> GETQuery\n * POST + body -> POSTBody\n * GET + params -> GETParams\n */\nexport function getSchemaName(method: string, inputType: 'query' | 'body' | 'params'): string {\n return `${method.toUpperCase()}${inputType.charAt(0).toUpperCase() + inputType.slice(1)}`;\n}\n","import ts from 'typescript';\n\n/**\n * 参数注入类型\n *\n * Phase 2.3 扩展 `agent` / `agents`:\n * - `agent` —— 默认 agent 元数据(Phase 2.4 实现 `config.defaultAgent` 后注入值)\n * - `agents` —— 所有已注册 agent 的元数据列表(`agentRegistry.listAgents()`)\n */\nexport type InjectionType =\n | 'query'\n | 'body'\n | 'form'\n | 'headers'\n | 'params'\n | 'context'\n | 'cookies'\n | 'ip'\n | 'ua'\n | 'files'\n | 'fields'\n | 'agent'\n | 'agents'\n | 'unknown';\n\n/**\n * 注入项信息\n */\nexport interface InjectionItem {\n name: string; // 参数名\n type: InjectionType; // 注入类型\n hasType: boolean; // 是否有类型标注(运行时类型已擦除,始终为 false)\n}\n\n/**\n * 参数名到注入类型的映射(单一来源,构建期和运行时共用)\n *\n * `form` 与 `body` 互斥:handler 声明其一即可。`form` 适用于\n * `application/x-www-form-urlencoded` 请求体(值均为 string,schema coerce=true);\n * `body` 适用于 JSON 请求体(coerce=false)。两者共享 `resolveInput` 的解析结果。\n *\n * Phase 2.3 添加 `agent` / `agents`:\n * - `agent` → 注入默认 agent 元数据(Phase 2.4 实现,暂返回 `undefined`)\n * - `agents` → 注入所有已注册 agent 元数据列表(`agentRegistry.listAgents()`)\n *\n * Phase 3.x 的 `@faapi/agent` 插件通过注入器机制增强为 `AgentHandle`\n * (含可调用 `run` 函数),覆盖内置的元数据注入。\n */\nexport const PARAM_TYPE_MAP: Record<string, InjectionType> = {\n query: 'query',\n body: 'body',\n form: 'form',\n headers: 'headers',\n params: 'params',\n context: 'context',\n ctx: 'context', // 别名\n cookies: 'cookies',\n ip: 'ip',\n ua: 'ua',\n files: 'files',\n fields: 'fields',\n agent: 'agent', // Phase 2.3\n agents: 'agents', // Phase 2.3\n};\n\n/**\n * 分析结果按函数引用缓存。\n *\n * handler 函数在路由模块加载后引用稳定,参数列表不会变化;用 WeakMap 弱键,\n * dev 热替换后旧模块的函数不再被引用即可随 GC 回收,不需要手动失效。\n */\ntype AnyFn = (...args: unknown[]) => unknown;\nconst injectionCache = new WeakMap<AnyFn, InjectionItem[]>();\n\n/**\n * 分析函数参数,决定需要注入什么内容\n *\n * 使用 TypeScript AST 解析 fn.toString() 的结果,\n * 正确处理解构参数、默认值、rest 参数等情况。\n *\n * 注意:运行时类型信息已被擦除,hasType 始终为 false。\n *\n * 返回的数组是缓存的共享实例,调用方不得就地修改。\n */\nexport function resolveInjection(fn: (...args: unknown[]) => unknown): InjectionItem[] {\n const cached = injectionCache.get(fn);\n if (cached) {\n return cached;\n }\n\n const fnStr = fn.toString();\n const params = extractParamsWithAst(fnStr);\n\n const items = params.map((param) => {\n const type = PARAM_TYPE_MAP[param.name] || 'unknown';\n return {\n name: param.name,\n type,\n hasType: false, // 运行时类型已擦除\n };\n });\n\n injectionCache.set(fn, items);\n return items;\n}\n\n/**\n * 使用 TypeScript AST 从函数字符串中提取参数名\n *\n * 相比正则解析,AST 能正确处理:\n * - 解构参数 `function GET({ page, size })` → page, size\n * - 默认值 `function GET(query = {})` → query\n * - rest 参数 `function GET(...args)` → args\n * - 含逗号的泛型(类型擦除后不存在该问题)\n */\nfunction extractParamsWithAst(fnStr: string): Array<{ name: string }> {\n // 包装成模块,让 TS 能解析\n const sourceFile = ts.createSourceFile(\n '__faapi_injection__.ts',\n fnStr,\n ts.ScriptTarget.Latest,\n true,\n );\n\n const paramNames: string[] = [];\n\n function visit(node: ts.Node): void {\n // 函数声明:function GET(query) {}\n if (ts.isFunctionDeclaration(node) && node.parameters.length > 0) {\n for (const param of node.parameters) {\n extractParamName(param, paramNames);\n }\n return;\n }\n\n // 箭头函数 / 函数表达式:(query) => {} 或 function(query) {}\n if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && node.parameters.length > 0) {\n for (const param of node.parameters) {\n extractParamName(param, paramNames);\n }\n return;\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n\n return paramNames.map((name) => ({ name }));\n}\n\n/**\n * 从单个参数节点提取参数名\n *\n * 支持的参数形式:\n * - 标识符:query → query\n * - 解构对象:{ page, size } → page, size(每个属性都作为一个注入项)\n * - 解构数组:[a, b] → a, b\n * - rest 参数:...args → args\n * - 默认值:query = {} → query\n */\nfunction extractParamName(param: ts.ParameterDeclaration, names: string[]): void {\n const name = param.name;\n\n // 标识符:query\n if (ts.isIdentifier(name)) {\n names.push(name.text);\n return;\n }\n\n // 解构对象:{ page, size }\n if (ts.isObjectBindingPattern(name)) {\n for (const element of name.elements) {\n if (ts.isBindingElement(element)) {\n const elemName = element.name;\n if (ts.isIdentifier(elemName)) {\n names.push(elemName.text);\n }\n }\n }\n return;\n }\n\n // 解构数组:[a, b]\n if (ts.isArrayBindingPattern(name)) {\n for (const element of name.elements) {\n if (element && ts.isBindingElement(element)) {\n const elemName = element.name;\n if (ts.isIdentifier(elemName)) {\n names.push(elemName.text);\n }\n }\n }\n return;\n }\n}\n","import ts from 'typescript';\nimport type { InjectionType } from './resolveInjection';\nimport { PARAM_TYPE_MAP } from './resolveInjection';\n\n/**\n * 参数元数据\n */\nexport interface ParamMeta {\n name: string; // 参数名\n type: InjectionType; // 注入类型\n typeName?: string; // 引用类型名(如 'Query')\n schema?: PropertyInfo[]; // 类型结构(内联类型时)\n}\n\n/**\n * 属性信息\n */\nexport interface PropertyInfo {\n name: string;\n type: string;\n optional: boolean;\n}\n\n/**\n * 注入元数据\n */\nexport interface InjectionMeta {\n params: ParamMeta[];\n}\n\n/**\n * AST 分析 handler 函数,提取参数注入元数据\n *\n * @param code handler 源码字符串\n * @param functionName 函数导出名(如 'GET')\n */\nexport function analyzeInjection(code: string, functionName: string): InjectionMeta {\n const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);\n return analyzeInjectionInSourceFile(sourceFile, functionName);\n}\n\n/**\n * 从已有 SourceFile 分析 handler 函数的参数注入元数据\n *\n * 与 analyzeInjection 语义一致,但接受 AST 节点而非源码字符串——\n * 调用方(如 collectRouteSchemaSources)的 program 中已有解析好的 SourceFile,\n * 逐方法重新 parse 同一文件字符串是纯浪费(同文件 N 个方法 = N 次全量重复解析)。\n *\n * @param sourceFile 已解析的 AST\n * @param functionName 函数导出名(如 'GET')\n */\nexport function analyzeInjectionInSourceFile(\n sourceFile: ts.SourceFile,\n functionName: string,\n): InjectionMeta {\n const params: ParamMeta[] = [];\n\n // 遍历 AST 查找目标函数\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {\n // 分析参数\n for (const param of node.parameters) {\n const paramMeta = analyzeParam(param, sourceFile);\n params.push(paramMeta);\n }\n }\n });\n\n return { params };\n}\n\n/**\n * 分析单个参数\n */\nfunction analyzeParam(param: ts.ParameterDeclaration, sourceFile: ts.SourceFile): ParamMeta {\n const name = param.name.getText(sourceFile);\n const type = PARAM_TYPE_MAP[name] || 'unknown';\n\n const result: ParamMeta = { name, type };\n\n // 分析类型\n if (param.type) {\n if (ts.isTypeReferenceNode(param.type)) {\n // 引用类型:Query, Body 等\n result.typeName = param.type.typeName.getText(sourceFile);\n } else if (ts.isTypeLiteralNode(param.type)) {\n // 内联类型:{ page: number }\n result.schema = extractSchema(param.type, sourceFile);\n }\n }\n\n return result;\n}\n\n/**\n * 从类型字面量提取 schema\n */\nfunction extractSchema(typeNode: ts.TypeLiteralNode, sourceFile: ts.SourceFile): PropertyInfo[] {\n const schema: PropertyInfo[] = [];\n\n for (const member of typeNode.members) {\n if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {\n const propName = member.name.text;\n const optional = !!member.questionToken;\n const propType = member.type?.getText(sourceFile) || 'unknown';\n\n schema.push({\n name: propName,\n type: propType,\n optional,\n });\n }\n }\n\n return schema;\n}\n","import { createPrograms } from '../ast/createProgram';\nimport {\n createLazyTypeResolver,\n type HandlerTypeInfo,\n type LazyTypeResolver,\n} from '../ast/extractHandlerTypes';\nimport { getInputTypeForMethod } from '../runtime/inputType';\nimport { getSchemaName } from '../validator/schemaName';\nimport { analyzeInjectionInSourceFile } from '../injection/analyzeInjection';\nimport type { RouteManifest } from '../router/routeTypes';\nimport path from 'node:path';\n\n/**\n * 单个路由的 schema 提取结果\n *\n * key 使用 urlPath(如 '/api/hello')而非 filePath,因为 urlPath 在 dev/prod 完全一致,\n * 无需 remapManifestKeys 桥接 .ts/.js 路径差异。\n */\nexport interface RouteSchemaSource {\n /** 路由 URL 路径(如 '/api/hello'),作为 schema key */\n urlPath: string;\n /** 源文件绝对路径(用于 generateSchemaFiles 按文件分组生成 zod.js) */\n filePath: string;\n schemaName: string;\n typeInfo: HandlerTypeInfo | null;\n /**\n * 是否对 number/boolean 字段生成 z.preprocess 字符串转换(coerce)。\n *\n * - query/params:始终 coerce=true(URL 来源均为 string)\n * - body:始终 coerce=false(JSON 解析已是天然 JS 类型)\n * - form:coerce=true(form-urlencoded 来源均为 string),由本函数在提取时\n * 检测到 handler 声明 `form` 参数时显式设置。schema 名仍为 `POSTBody`\n * (与 body 共享运行时 schema key),运行时 validateInput 无需感知 form/body 差异。\n *\n * 未设置时由 generateSchemaFileSource 回退到 schemaName 后缀正则推断(Query/Params → true)。\n */\n coerce?: boolean;\n}\n\n/**\n * 从路由清单收集 schema 提取所需的原始数据\n *\n * dev 和 prd 共享的核心提取流程:\n * 1. 按文件分组遍历路由\n * 2. 批量共享 Program(同一次提取只按文件组创建少量 Program)\n * 3. 对每个路由用 analyzeInjectionInSourceFile 定位入口参数类型,\n * extractTypeInfo 解析入口类型(入口类型必须严格解析,失败抛 SchemaExtractionError)\n * 4. 返回按文件分组的惰性类型解析器——generateSchemaFiles 生成 zod.js 时\n * 遇到 ref(同文件循环引用)按需解析并缓存\n *\n * 惰性语义:与路由无关的类型(未被任何入口类型引用)不会被解析——文件里\n * 存在一个含不支持语法的无关类型不再拖垮整个 build/reload。\n */\nexport function collectRouteSchemaSources(\n routes: RouteManifest,\n rootDir?: string,\n): {\n sources: RouteSchemaSource[];\n /** 按文件分组的惰性类型解析器(generateSchemaFiles 解析 ref 用) */\n resolversByFile: Map<string, LazyTypeResolver>;\n} {\n // 按文件分组收集方法(去重)\n // key 是文件绝对路径(createProgram 需要),但 schema key 用 urlPath\n const methodsByFile = new Map<string, { urlPath: string; methods: Set<string> }>();\n for (const route of routes) {\n const filePath = rootDir ? path.resolve(rootDir, route.filePath) : route.filePath;\n let entry = methodsByFile.get(filePath);\n if (!entry) {\n entry = { urlPath: route.urlPath, methods: new Set() };\n methodsByFile.set(filePath, entry);\n }\n entry.methods.add(route.method);\n }\n\n // 批量共享 Program:同一次提取只创建一个 Program,避免逐文件全量解析\n const programByFile = createPrograms([...methodsByFile.keys()]);\n\n // 每个文件一个惰性解析器:入口类型立即解析,ref 按需解析(缓存幂等)\n const resolversByFile = new Map<string, LazyTypeResolver>();\n for (const filePath of methodsByFile.keys()) {\n resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath)!, filePath));\n }\n\n // 每个文件提取 schema(key 用 urlPath)\n const sources: RouteSchemaSource[] = [];\n for (const [filePath, entry] of methodsByFile) {\n const program = programByFile.get(filePath)!;\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) continue;\n const resolver = resolversByFile.get(filePath)!;\n for (const method of entry.methods) {\n const inputType = getInputTypeForMethod(method);\n const schemaName = getSchemaName(method, inputType);\n // 复用 program 已解析的 SourceFile,逐方法零重复 parse\n const meta = analyzeInjectionInSourceFile(sourceFile, method);\n // POST/PUT/PATCH(inputType='body'):优先找 body 参数,找不到再找 form 参数。\n // form 与 body 共享 schema 名(POSTBody),运行时 validateInput 仍按 POSTBodySchema 查找;\n // 差异仅在校验:form 声明时通过 source.coerce=true 显式覆盖(form 值均为 string,\n // 需 z.preprocess 转换 number/boolean 字段)。\n const param =\n meta.params.find((p) => p.type === inputType) ??\n (inputType === 'body' ? meta.params.find((p) => p.type === 'form') : undefined);\n const isForm = param?.type === 'form';\n // 入口类型:必须严格解析(失败抛 SchemaExtractionError,带文件/类型上下文)\n const typeInfo = param?.typeName ? resolver.resolve(param.typeName) : null;\n sources.push({\n urlPath: entry.urlPath,\n filePath,\n schemaName,\n typeInfo,\n coerce: isForm || undefined,\n });\n }\n }\n\n return { sources, resolversByFile };\n}\n","import {\n SchemaExtractionError,\n type RuntimeType,\n type TupleElement,\n type TypeConstraint,\n} from './resolveTypeNode';\nimport type { HandlerTypeInfo } from './extractHandlerTypes';\n\n/**\n * 类型解析器:根据类型名获取 RuntimeType\n *\n * 用于解析循环引用中的 ref。调用方负责提供(通常从 AST 提取)。\n */\nexport type TypeResolver = (name: string) => RuntimeType | undefined;\n\n/**\n * 代码生成上下文\n *\n * 跟踪已收集的命名类型,用于生成 z.lazy 声明。\n */\nclass CodeGenContext {\n /** 命名类型集合:name → RuntimeType */\n readonly namedTypes = new Map<string, RuntimeType>();\n /** 类型解析器(用于解析 ref 的实际类型) */\n readonly resolveType: TypeResolver;\n /** 入口类型原始名(typeInfo.name,用于识别入口类型的自引用) */\n entryTypeName = '';\n /** 入口类型导出名(exportName,自引用时用此名生成变量名) */\n entryExportName = '';\n /**\n * 是否生成 coerce 逻辑(query/params 场景,URL 来源均为 string)\n *\n * true 时为 number/boolean 字段包 z.preprocess,把合法的字符串转成对应类型。\n * 嵌套类型(array/object/tuple/union 等)的元素递归处理。\n */\n coerce = false;\n\n constructor(resolveType: TypeResolver) {\n this.resolveType = resolveType;\n }\n}\n\n/**\n * 从 RuntimeType 收集所有命名类型(ref)\n *\n * 递归遍历 RuntimeType,遇到 ref 时解析并加入 namedTypes。\n * 已收集的类型不重复处理(防止循环引用无限递归)。\n */\nexport function collectNamedTypes(type: RuntimeType, ctx: CodeGenContext): void {\n switch (type.kind) {\n case 'string':\n case 'number':\n case 'boolean':\n case 'bigint':\n case 'null':\n case 'undefined':\n case 'any':\n case 'unknown':\n case 'literal':\n case 'date':\n return;\n\n case 'array':\n collectNamedTypes(type.element, ctx);\n return;\n\n case 'tuple':\n for (const el of type.elements) {\n collectNamedTypes(el.type, ctx);\n }\n return;\n\n case 'object':\n for (const prop of type.properties) {\n collectNamedTypes(prop.type, ctx);\n }\n return;\n\n case 'union':\n for (const member of type.members) {\n collectNamedTypes(member, ctx);\n }\n return;\n\n case 'record':\n collectNamedTypes(type.key, ctx);\n collectNamedTypes(type.value, ctx);\n return;\n\n case 'map':\n collectNamedTypes(type.key, ctx);\n collectNamedTypes(type.value, ctx);\n return;\n\n case 'set':\n collectNamedTypes(type.element, ctx);\n return;\n\n case 'ref': {\n if (ctx.namedTypes.has(type.name)) return;\n // 先占位,防止循环引用无限递归\n ctx.namedTypes.set(type.name, { kind: 'any' });\n const resolved = ctx.resolveType(type.name);\n if (resolved) {\n ctx.namedTypes.set(type.name, resolved);\n collectNamedTypes(resolved, ctx);\n return;\n }\n // ref 只在 AST 提取阶段遇到真实 TS 类型引用时产生(首现内联、二次标 ref),\n // 声明必然存在于某个源文件中;解析不到说明查找能力不足(如命名空间内声明),\n // 按框架约定显式抛错而非静默降级为 z.unknown()(AGENTS.md §6.3)\n throw new SchemaExtractionError(\n type.name,\n `无法解析命名类型引用 \"${type.name}\"(handler 文件与 program 源文件中均未找到同名顶层声明)`,\n );\n }\n }\n}\n\n/**\n * RuntimeType → zod 表达式字符串(不含声明)\n *\n * @param type 类型描述\n * @param ctx 代码生成上下文\n * @param constraints 字段级约束(仅 object 字段传入,影响最外层 zod 链)\n */\nexport function runtimeTypeToZodExpression(\n type: RuntimeType,\n ctx: CodeGenContext,\n constraints?: TypeConstraint[],\n): string {\n const expr = baseExpression(type, ctx);\n // 字段级约束链(@max/@min/@regex 等)追加到基础表达式后\n // coerce 模式下约束需作用在 preprocess 内部的 z.X() 上,故先应用约束再包裹 preprocess\n const withConstraints =\n constraints && constraints.length > 0 ? applyConstraints(expr, constraints, type.kind) : expr;\n // coerce 模式下,number/boolean 在外层包 z.preprocess\n // 嵌套类型(array/object/tuple/union 等)内部已递归处理,外层不再包裹\n if (ctx.coerce && (type.kind === 'number' || type.kind === 'boolean')) {\n return wrapCoercePreprocess(type.kind, withConstraints);\n }\n // coerce 模式下,数字/布尔字面量同样需要字符串转换:query 声明 `status: 1 | 2`\n // 时 URL 传来 \"1\"(string),裸 z.literal(1) 必然校验失败。union 成员经本函数\n // 递归生成,字面量成员逐个包裹(string 字面量天然命中,无需转换)\n if (\n ctx.coerce &&\n type.kind === 'literal' &&\n (typeof type.value === 'number' || typeof type.value === 'boolean')\n ) {\n return wrapCoercePreprocess(\n typeof type.value === 'number' ? 'number' : 'boolean',\n withConstraints,\n );\n }\n return withConstraints;\n}\n\n/**\n * 在 zod 表达式上追加约束链\n *\n * 约束与字段类型的匹配在 AST 提取阶段已校验,这里直接生成链式调用。\n *\n * coerce 模式下调用方应先 applyConstraints 再 wrapCoercePreprocess,\n * 使约束作用在 preprocess 内部的 z.X() 上,而非 preprocess 外壳。\n *\n * @param baseExpr 基础 zod 表达式(未包裹 preprocess)\n * @param constraints 约束数组\n * @param typeKind 字段类型 kind\n */\nfunction applyConstraints(\n baseExpr: string,\n constraints: TypeConstraint[],\n typeKind: RuntimeType['kind'],\n): string {\n const suffix = constraints.map((c) => constraintToZodChain(c, typeKind)).join('');\n return `${baseExpr}${suffix}`;\n}\n\n/**\n * 单个约束 → zod 链式方法字符串\n *\n * @param constraint 约束\n * @param typeKind 字段类型 kind(用于区分 string/array 的 max/min 含义)\n */\nfunction constraintToZodChain(constraint: TypeConstraint, _typeKind: RuntimeType['kind']): string {\n switch (constraint.kind) {\n case 'max':\n return `.max(${constraint.value})`;\n case 'min':\n return `.min(${constraint.value})`;\n case 'int':\n return '.int()';\n case 'positive':\n return '.positive()';\n case 'negative':\n return '.negative()';\n case 'nonnegative':\n return '.nonnegative()';\n case 'nonpositive':\n return '.nonpositive()';\n case 'maxLength':\n return `.max(${constraint.value})`;\n case 'minLength':\n return `.min(${constraint.value})`;\n case 'length':\n return `.length(${constraint.value})`;\n case 'regex': {\n const flags = constraint.flags ?? '';\n // 用 new RegExp 构造,避免 pattern 中含 / 字符导致的字面量语法错误\n return `.regex(new RegExp(${JSON.stringify(constraint.pattern)}${flags ? `, ${JSON.stringify(flags)}` : ''}))`;\n }\n case 'email':\n return '.email()';\n case 'url':\n return '.url()';\n case 'uuid':\n return '.uuid()';\n }\n}\n\n/**\n * 生成基础 zod 表达式(不含 coerce 包裹)\n */\nfunction baseExpression(type: RuntimeType, ctx: CodeGenContext): string {\n switch (type.kind) {\n case 'string':\n return 'z.string()';\n case 'number':\n return 'z.number()';\n case 'boolean':\n return 'z.boolean()';\n case 'bigint':\n // AST 提取阶段已抛错,这里不会到达\n return 'z.never()';\n case 'null':\n return 'z.null()';\n case 'undefined':\n return 'z.undefined()';\n case 'any':\n case 'unknown':\n return 'z.unknown()';\n case 'literal':\n return `z.literal(${JSON.stringify(type.value)})`;\n case 'array':\n return `z.array(${runtimeTypeToZodExpression(type.element, ctx)})`;\n case 'tuple':\n return generateTupleExpression(type.elements, ctx);\n case 'object':\n return generateObjectExpression(type, ctx);\n case 'union':\n return generateUnionExpression(type.members, ctx);\n case 'date':\n // z.preprocess 限制只接受 string/Date:string → new Date(string),其他原样传入\n // z.coerce.date() 会把 number 也转为 Date(new Date(123)),不符合需求\n // date 类型本身已用 preprocess,coerce 模式下无需再包一层\n return 'z.preprocess((v) => (typeof v === \"string\" ? new Date(v) : v), z.date())';\n case 'record':\n return `z.record(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)})`;\n case 'map':\n // Map<K,V> — JSON 序列化为 entries 数组,coerceMap 还原为 Map 实例后再交给 z.map 校验\n // coerce=false(body)场景也需要 preprocess:JSON.parse 出来是数组,不是 Map 实例\n return `z.preprocess(coerceMap, z.map(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)}))`;\n case 'set':\n // Set<T> — JSON 序列化为数组,coerceSet 还原为 Set 实例后再交给 z.set 校验\n // coerce=false(body)场景也需要 preprocess:JSON.parse 出来是数组,不是 Set 实例\n return `z.preprocess(coerceSet, z.set(${runtimeTypeToZodExpression(type.element, ctx)}))`;\n case 'ref':\n // ref 引用命名类型,直接用 NameSchema 变量\n // 入口类型的自引用需用 exportName(exportName 可能与 typeInfo.name 不同)\n if (type.name === ctx.entryTypeName) {\n return `${ctx.entryExportName}Schema`;\n }\n return `${type.name}Schema`;\n }\n}\n\n/**\n * coerceNumber 公用函数源码(ESM export 格式)\n *\n * 用于 query/params 的 string → number 转换:\n * - 合法数字字符串(\"1\"、\"3.14\")转为 number\n * - 空串/NaN 保留原值,让 z.number() 报错(避免 Number(\"\") = 0 的陷阱)\n *\n * 写入 dist 根部的 faapi-helpers.js,各 zod.js 通过相对路径 import 复用。\n */\nexport const COERCE_NUMBER_HELPER =\n 'export const coerceNumber = (v) => typeof v === \"string\" && v.trim() !== \"\" && !isNaN(Number(v)) ? Number(v) : v;';\n\n/**\n * coerceBoolean 公用函数源码(ESM export 格式)\n *\n * 用于 query/params 的 string → boolean 转换:\n * - 'true'/'1' → true\n * - 'false'/'0' → false\n * - 其他值保留原值,让 z.boolean() 报错\n *\n * 写入 dist 根部的 faapi-helpers.js,各 zod.js 通过相对路径 import 复用。\n */\nexport const COERCE_BOOLEAN_HELPER =\n 'export const coerceBoolean = (v) => {\\n' +\n ' const lower = typeof v === \"string\" ? v.toLowerCase() : v;\\n' +\n ' return lower === \"true\" || lower === \"1\" ? true : lower === \"false\" || lower === \"0\" ? false : v;\\n' +\n '};';\n\n/**\n * coerceMap 公用函数源码(ESM export 格式)\n *\n * 用于 Map<K,V> 的 JSON 还原:JSON 序列化为 entries 数组,JSON.parse 后还原为 Map 实例\n * - entries 数组 [[\"k\",v],...] → new Map(entries)\n * - Map 实例 → 原样返回\n * - 普通对象 { k: v } → new Map(Object.entries(v))\n * - 其他值保留原值,让 z.map() 报错\n *\n * 与 coerceNumber/coerceBoolean 不同,coerceMap 在 coerce=false(body 场景)下也使用:\n * JSON.parse 出来的是数组/对象,不是 Map 实例,必须还原才能交给 z.map() 校验。\n */\nexport const COERCE_MAP_HELPER =\n 'export const coerceMap = (v) => Array.isArray(v) ? new Map(v) : v instanceof Map ? v : (v && typeof v === \"object\" ? new Map(Object.entries(v)) : v);';\n\n/**\n * coerceSet 公用函数源码(ESM export 格式)\n *\n * 用于 Set<T> 的 JSON 还原:JSON 序列化为数组,JSON.parse 后还原为 Set 实例\n * - Set 实例 → 原样返回\n * - 数组 [item,...] → new Set(array)\n * - 其他值保留原值,让 z.set() 报错\n *\n * 与 coerceNumber/coerceBoolean 不同,coerceSet 在 coerce=false(body 场景)下也使用:\n * JSON.parse 出来的是数组,不是 Set 实例,必须还原才能交给 z.set() 校验。\n */\nexport const COERCE_SET_HELPER =\n 'export const coerceSet = (v) => v instanceof Set ? v : (Array.isArray(v) ? new Set(v) : v);';\n\n/**\n * faapi-helpers.js 文件名(生成在 dist 根部)\n */\nexport const HELPERS_FILENAME = 'faapi-helpers.js';\n\n/**\n * 生成 faapi-helpers.js 文件源码\n *\n * 包含 coerceNumber / coerceBoolean / coerceMap / coerceSet 四个公用函数(ESM export)。\n * 由 generateSchemaFiles 按需生成:query/params 场景的 number/boolean 字段触发 coerceNumber/coerceBoolean;\n * Map/Set 字段(query/params 与 body 均包含)触发 coerceMap/coerceSet。\n */\nexport function generateHelpersFileSource(): string {\n return [\n '// faapi-helpers.js — faapi 自动生成的公用函数(请勿手动编辑)',\n COERCE_NUMBER_HELPER,\n COERCE_BOOLEAN_HELPER,\n COERCE_MAP_HELPER,\n COERCE_SET_HELPER,\n '',\n ].join('\\n');\n}\n\n/**\n * 检测代码是否引用了 coerceNumber / coerceBoolean / coerceMap / coerceSet 变量\n *\n * 用于 generateSchemaFiles 判断是否需要生成 faapi-helpers.js。\n * 注意:Map/Set 在 coerce=false(body 场景)下也会引用 coerceMap/coerceSet,\n * 故仅凭此检测即可覆盖所有需要生成 helpers 的场景。\n */\nexport function usesCoerceHelpers(code: string): boolean {\n return (\n code.includes('coerceNumber') ||\n code.includes('coerceBoolean') ||\n code.includes('coerceMap') ||\n code.includes('coerceSet')\n );\n}\n\n/**\n * 为 number/boolean 表达式包裹 z.preprocess(用于 query coerce)\n *\n * 引用从 faapi-helpers.js import 的 coerceNumber / coerceBoolean 变量,\n * 避免每个字段都内联一长串函数。\n */\nfunction wrapCoercePreprocess(kind: 'number' | 'boolean', inner: string): string {\n if (kind === 'number') {\n return `z.preprocess(coerceNumber, ${inner})`;\n }\n return `z.preprocess(coerceBoolean, ${inner})`;\n}\n\n/**\n * 生成元组 zod 表达式\n *\n * zod 的 tuple 不直接支持可选元素,需要用 union 模拟:\n * - 全部必填:`z.tuple([e0, e1, ...])`\n * - 含可选元素:`z.union([z.tuple([e0]), z.tuple([e0, e1]), ...])`(从短到长)\n * - 含 rest:`z.tuple([e0, ...]).rest(eRest)`\n */\nfunction generateTupleExpression(elements: TupleElement[], ctx: CodeGenContext): string {\n const fixedExprs: string[] = [];\n const fixedOptional: boolean[] = [];\n let restExpression = '';\n let restStarted = false;\n\n for (const el of elements) {\n if (el.rest) {\n restExpression = runtimeTypeToZodExpression(el.type, ctx);\n restStarted = true;\n } else if (!restStarted) {\n fixedExprs.push(runtimeTypeToZodExpression(el.type, ctx));\n fixedOptional.push(el.optional);\n }\n }\n\n // 含 rest 元素:z.tuple([fixed...]).rest(restType)。\n // 可选前缀保留(zod v4 原生支持 z.tuple([X.optional()]).rest(...))——\n // 此前丢弃 fixedOptional 导致 [string?, ...number[]] 的空数组被误拒\n if (restExpression) {\n const fixedWithOptional = fixedExprs.map((expr, i) =>\n fixedOptional[i] ? `${expr}.optional()` : expr,\n );\n return `z.tuple([${fixedWithOptional.join(', ')}]).rest(${restExpression})`;\n }\n\n // 无可选元素:z.tuple([e0, e1, ...])\n const hasOptional = fixedOptional.some((o) => o);\n if (!hasOptional) {\n return `z.tuple([${fixedExprs.join(', ')}])`;\n }\n\n // 含可选元素:从后往前截断可选元素,生成 union\n // TS 语法要求可选元素在必填元素之后,所以只需从后往前去掉可选元素\n const variants: string[] = [];\n for (let len = fixedExprs.length; len >= 0; len--) {\n // len === 0 时只有全部可选才有效(空元组)\n // len > 0 时,第 len-1 个元素必须存在(必填或可选都行)\n // 截断点:去掉 fixedExprs[len..end],这些必须都是可选的\n const removed = fixedOptional.slice(len);\n if (removed.length > 0 && removed.some((o) => !o)) {\n // 被截断的元素中有必填元素,无效截断\n break;\n }\n const subset = fixedExprs.slice(0, len);\n variants.push(`z.tuple([${subset.join(', ')}])`);\n }\n\n // 从短到长排序(更自然的校验顺序)\n variants.reverse();\n if (variants.length === 1) {\n return variants[0]!;\n }\n return `z.union([${variants.join(', ')}])`;\n}\n\n/**\n * 生成对象 zod 表达式\n *\n * 可选字段用 .optional(),约束链在 .optional() 之前生成。\n * 带索引签名的对象(catchall)生成 z.object({...}).catchall(valueSchema)——\n * 索引签名与属性共存时属性保留(resolveTypeNode 不再丢弃)。\n */\nfunction generateObjectExpression(\n type: Extract<RuntimeType, { kind: 'object' }>,\n ctx: CodeGenContext,\n): string {\n const fields = type.properties.map((prop) => {\n const expr = runtimeTypeToZodExpression(prop.type, ctx, prop.constraints);\n const finalExpr = prop.optional ? `${expr}.optional()` : expr;\n return `${JSON.stringify(prop.name)}: ${finalExpr}`;\n });\n const objectExpr = `z.object({ ${fields.join(', ')} })`;\n return type.catchall !== undefined\n ? `${objectExpr}.catchall(${runtimeTypeToZodExpression(type.catchall, ctx)})`\n : objectExpr;\n}\n\n/**\n * 生成联合类型 zod 表达式\n *\n * 优化:如果联合中包含 null,用 .nullable() 简化\n * 否则用 z.union([m0, m1, ...])\n */\nfunction generateUnionExpression(members: RuntimeType[], ctx: CodeGenContext): string {\n const hasNull = members.some((m) => m.kind === 'null');\n const nonNull = members.filter((m) => m.kind !== 'null');\n\n // 只有一个非 null 成员 + null → .nullable()\n if (hasNull && nonNull.length === 1) {\n return `${runtimeTypeToZodExpression(nonNull[0]!, ctx)}.nullable()`;\n }\n\n // 多个成员 + null → z.union([...]).nullable()\n if (hasNull) {\n const unionInner = nonNull.map((m) => runtimeTypeToZodExpression(m, ctx)).join(', ');\n return `z.union([${unionInner}]).nullable()`;\n }\n\n // 无 null → z.union([...])\n const unionInner = members.map((m) => runtimeTypeToZodExpression(m, ctx)).join(', ');\n return `z.union([${unionInner}])`;\n}\n\n/**\n * 生成命名类型的 zod schema 声明\n *\n * - 无循环引用:`const NameSchema = z.object({ ... })`\n * - 有循环引用:`const NameSchema = z.lazy(() => z.object({ ... }))`\n */\nfunction generateNamedTypeDeclaration(\n name: string,\n type: RuntimeType,\n ctx: CodeGenContext,\n): string {\n const expr = runtimeTypeToZodExpression(type, ctx);\n const hasRef = containsRef(type, new Set([name]));\n if (hasRef) {\n // 循环引用:用 z.lazy 延迟求值\n return `const ${name}Schema = z.lazy(() => ${expr});`;\n }\n return `const ${name}Schema = ${expr};`;\n}\n\n/**\n * 检查 RuntimeType 是否包含对自身的 ref(循环引用)\n */\nfunction containsRef(type: RuntimeType, visited: Set<string>): boolean {\n switch (type.kind) {\n case 'ref':\n return visited.has(type.name);\n case 'array':\n return containsRef(type.element, visited);\n case 'tuple':\n return type.elements.some((el) => containsRef(el.type, visited));\n case 'object':\n return type.properties.some((prop) => containsRef(prop.type, visited));\n case 'union':\n return type.members.some((m) => containsRef(m, visited));\n case 'record':\n return containsRef(type.key, visited) || containsRef(type.value, visited);\n case 'map':\n return containsRef(type.key, visited) || containsRef(type.value, visited);\n case 'set':\n return containsRef(type.element, visited);\n default:\n return false;\n }\n}\n\n/**\n * 生成单个类型的 zod schema 代码(含命名类型声明)\n *\n * 返回的代码包含:\n * - `import { z } from 'zod'`\n * - 内部命名类型的 const 声明(带 z.lazy 循环引用保护)\n * - `export const NameSchema = ...` 入口导出\n *\n * @param typeInfo 类型信息\n * @param resolveType 类型解析器(用于解析 ref 的实际类型)\n * @param exportName 导出的 schema 变量名(不含 Schema 后缀)。默认用 typeInfo.name。\n * validateInput 按 `${schemaName}Schema` 查找,需与导出名一致。\n * @param coerce 是否生成 coerce 逻辑(query/params 场景)。默认 false。\n * true 时为 number/boolean 字段(含嵌套元素)包 z.preprocess。\n */\nexport function generateZodSchemaSource(\n typeInfo: HandlerTypeInfo,\n resolveType: TypeResolver,\n exportName?: string,\n coerce = false,\n): string {\n const { namedTypeDeclarations, entryDeclaration } = generateZodSchemaSourceParts(\n typeInfo,\n resolveType,\n exportName,\n coerce,\n );\n\n const lines: string[] = [];\n lines.push(\"import { z } from 'zod';\");\n lines.push('');\n\n for (const { declaration } of namedTypeDeclarations) {\n lines.push(declaration);\n }\n if (namedTypeDeclarations.length > 0) lines.push('');\n\n lines.push(entryDeclaration);\n\n return lines.join('\\n');\n}\n\n/**\n * 生成单个类型的 zod schema 代码片段(结构化形式)\n *\n * 与 generateZodSchemaSource 的区别:不拼 import 头,命名类型声明与入口导出分开返回,\n * 供文件级组装按名去重——同一 handler 文件的多个方法引用同一命名类型时,命名类型\n * 声明只允许出现一次,否则拼接产物含重复 const 声明,import 即 SyntaxError。\n *\n * 语义与 generateZodSchemaSource 完全一致(含 coerce / z.lazy 循环引用处理)。\n */\nexport interface ZodSchemaSourceParts {\n /** 命名类型声明(按首次引用顺序排列;name 为不含 Schema 后缀的类型名) */\n namedTypeDeclarations: Array<{ name: string; declaration: string }>;\n /** 入口 schema 导出(`export const NameSchema = ...;`) */\n entryDeclaration: string;\n}\n\nexport function generateZodSchemaSourceParts(\n typeInfo: HandlerTypeInfo,\n resolveType: TypeResolver,\n exportName?: string,\n coerce = false,\n): ZodSchemaSourceParts {\n const ctx = new CodeGenContext(resolveType);\n const name = exportName ?? typeInfo.name;\n ctx.entryTypeName = typeInfo.name;\n ctx.entryExportName = name;\n ctx.coerce = coerce;\n\n // 收集入口类型中的所有命名类型(不含入口类型本身,避免重复声明)\n collectNamedTypes(typeInfo.runtimeType, ctx);\n ctx.namedTypes.delete(typeInfo.name);\n\n const namedTypeDeclarations = [...ctx.namedTypes].map(([n, type]) => ({\n name: n,\n declaration: generateNamedTypeDeclaration(n, type, ctx),\n }));\n\n // 入口 schema 导出(含循环引用时用 z.lazy)\n const entryExpr = runtimeTypeToZodExpression(typeInfo.runtimeType, ctx);\n const hasSelfRef = containsRef(typeInfo.runtimeType, new Set([typeInfo.name]));\n const entryDeclaration = hasSelfRef\n ? `export const ${name}Schema = z.lazy(() => ${entryExpr});`\n : `export const ${name}Schema = ${entryExpr};`;\n\n return { namedTypeDeclarations, entryDeclaration };\n}\n","import path from 'node:path';\nimport { atomicWriteFile } from '../utils/atomicWrite';\nimport type { RouteManifest } from '../router/routeTypes';\nimport type { RouteSchemaSource } from './collectRouteSchemaSources';\nimport type { RuntimeType } from '../ast/resolveTypeNode';\nimport { collectRouteSchemaSources } from './collectRouteSchemaSources';\nimport {\n generateZodSchemaSourceParts,\n generateHelpersFileSource,\n usesCoerceHelpers,\n HELPERS_FILENAME,\n} from '../ast/generateZodSchema';\n\n/**\n * 源文件路径 → 产物 zod.js 路径\n *\n * 每个 handler 目录下生成一个 `zod.js`(与 handler.js 同级,文件名固定为 zod.js):\n * - `src/api/hello/handler.ts` → `<dist>/api/hello/zod.js`\n *\n * 路由源码目录写死为 src,剥离 `src/` 前缀。\n *\n * @param sourceFile 源文件相对路径(相对 rootDir,如 'src/api/hello/handler.ts')\n * @param dist 输出目录(如 'dist' 或 '.faapi')\n * @param rootDir 项目根目录\n */\nexport function getSchemaOutputPath(sourceFile: string, dist: string, rootDir: string): string {\n let rel = sourceFile.replace(/\\\\/g, '/');\n // 去掉 src/ 前缀(打平产物结构)\n if (rel.startsWith('src/')) {\n rel = rel.slice(4);\n }\n // 取目录部分,basename 固定为 zod.js(每个 handler 目录一个 zod.js)\n const idx = rel.lastIndexOf('/');\n const relDir = idx >= 0 ? rel.slice(0, idx) : '';\n return path.resolve(rootDir, dist, relDir, 'zod.js');\n}\n\n/**\n * 运行时从 route.filePath 计算对应 zod.js 绝对路径\n *\n * 每个 handler 目录下的 zod.js(文件名固定为 zod.js,与 handler.js 同级):\n * - dev 模式:route.filePath 是源码路径(如 'src/api/hello/handler.ts'),\n * zod.js 在 `<rootDir>/<dist>/api/hello/zod.js`\n * - prod 模式:route.filePath 是产物路径(如 'dist/api/hello/handler.js'),\n * zod.js 在 `<rootDir>/dist/api/hello/zod.js`(与 handler.js 同级)\n *\n * 通过同时检查 src 和 dist 前缀,统一处理两种模式:\n * - dev:strip 'src/' 前缀,join dist\n * - prod:strip 'dist/' 前缀,join dist(dist='dist',结果与 rootDir 一致)\n *\n * @param filePath route.filePath(dev 为源码路径,prod 为产物路径)\n * @param dist 输出目录(如 'dist' 或 '.faapi')\n * @param rootDir 项目根目录\n */\nexport function getRuntimeSchemaPath(filePath: string, dist: string, rootDir: string): string {\n let rel = filePath.replace(/\\\\/g, '/');\n // strip src/ 前缀(dev 模式:filePath = 'src/...')\n if (rel.startsWith('src/')) {\n rel = rel.slice(4);\n }\n // strip dist 前缀(prod 模式:filePath = 'dist/...')\n else if (rel.startsWith(`${dist}/`)) {\n rel = rel.slice(dist.length + 1);\n }\n // 取目录部分,basename 固定为 zod.js\n const idx = rel.lastIndexOf('/');\n const relDir = idx >= 0 ? rel.slice(0, idx) : '';\n return path.resolve(rootDir, dist, relDir, 'zod.js');\n}\n\n/**\n * 计算 zod.js 到 faapi-helpers.js 的相对 import 路径\n *\n * faapi-helpers.js 固定在 dist 根部,zod.js 可能在子目录(如 'api/hello/zod.js')。\n * 根据子目录深度计算 `../` 数量:\n * - zod.js 在 `api/hello/`(深度 2)→ `../../faapi-helpers.js`\n * - zod.js 在 `api/`(深度 1)→ `../faapi-helpers.js`\n * - zod.js 在 dist 根(深度 0,理论不会发生)→ `./faapi-helpers.js`\n *\n * @param relDir zod.js 所在目录相对 dist 的路径(如 'api/hello' 或 '')\n * @returns ESM import 路径(如 '../../faapi-helpers.js')\n */\nexport function getHelpersImportPath(relDir: string): string {\n if (!relDir) return `./${HELPERS_FILENAME}`;\n const depth = relDir.split('/').filter(Boolean).length;\n return `${'../'.repeat(depth)}${HELPERS_FILENAME}`;\n}\n\n/**\n * 生成单个 handler 文件的 zod.js 源码\n *\n * 自包含:extractAllTypes 在 AST 阶段已通过 TypeScript checker 内联跨文件类型,\n * 每个 zod.js 无需 import 其他 zod.js。ref 仅用于同文件内的循环引用(通过 z.lazy 处理)。\n *\n * 导出格式:\n * - `<SchemaName>Schema`:zod schema 对象(用于 safeParse 校验)\n *\n * 无类型声明的方法不导出对应 Schema(validateInput 检测到 undefined 跳过校验)。\n *\n * coerce 逻辑:query/params 来源均为 string,schemaName 以 \"Query\" 或 \"Params\" 结尾时\n * 生成 coerce=true 的 schema(number/boolean 字段用 z.preprocess 包裹字符串转换)。\n * body 是 JSON 解析的天然 JS 类型,不需要 coerce。form 与 body 共享 schema 名(POSTBody),\n * 但 `RouteSchemaSource.coerce=true` 显式覆盖(form 值均为 string,需 coerce)。\n * Map/Set 字段在两种场景下都生成 z.preprocess 包裹(JSON.parse 出来的是数组/对象,需还原为 Map/Set 实例)。\n *\n * 命名类型声明文件级去重:同一文件的多个方法引用同一命名类型时,`const XSchema` 只在\n * 文件头声明一次(按首次引用顺序),避免重复 const 声明导致 zod.js import 即 SyntaxError。\n *\n * 公用函数复用:schema 引用 coerceNumber / coerceBoolean / coerceMap / coerceSet 变量时,\n * 这些变量从 dist 根部的 faapi-helpers.js import(跨文件复用,仅一份声明)。\n *\n * @param sources 同一文件的 schema 提取结果(含多个方法,如 GETQuery + POSTBody)\n * @param resolveType ref 解析函数(解析循环引用中的 ref):名称 → 运行时类型,\n * 未声明返回 undefined。惰性实现由 collectRouteSchemaSources 的 resolversByFile 提供。\n * @param helpersImportPath 到 faapi-helpers.js 的相对 import 路径(如 '../../faapi-helpers.js')。\n * 传空字符串表示不注入 coerce helpers 的 import(用于无 coerce schema 的文件或不支持外部 import 的测试场景)。\n * @returns zod.js 源码字符串\n */\nexport function generateSchemaFileSource(\n sources: RouteSchemaSource[],\n resolveType: (name: string) => RuntimeType | undefined,\n helpersImportPath: string,\n): string {\n const lines: string[] = [\"import { z } from 'zod';\"];\n\n // 文件级命名类型声明去重:同一文件的多个方法引用同一命名类型(如 GETQuery 与\n // POSTBody 都引用 Item)时,const 声明只提升到文件头生成一次——否则拼接产物含\n // 重复 const 声明,zod.js import 即 SyntaxError,该文件所有路由 500。\n const namedTypeDeclarations: string[] = [];\n const seenNamedTypes = new Set<string>();\n\n // 先生成所有 schema 入口导出,暂存到 schemaBlocks\n const schemaBlocks: string[] = [];\n for (const source of sources) {\n const { schemaName, typeInfo } = source;\n if (!typeInfo) {\n // 无类型声明,不导出对应 Schema\n continue;\n }\n\n // 推断 coerce:\n // - source.coerce 显式设置时优先使用(form 声明时由 collectRouteSchemaSources 设置为 true)\n // - 否则回退到 schemaName 后缀正则:query/params 需要 coerce(URL 来源均为 string),body 不需要\n const coerce = source.coerce ?? /(?:Query|Params)$/.test(schemaName);\n\n // 生成结构化片段:命名类型声明按名去重后提升到文件头,入口导出留在方法 block 内\n const { namedTypeDeclarations: decls, entryDeclaration } = generateZodSchemaSourceParts(\n typeInfo,\n resolveType,\n schemaName,\n coerce,\n );\n for (const { name, declaration } of decls) {\n if (seenNamedTypes.has(name)) continue;\n seenNamedTypes.add(name);\n namedTypeDeclarations.push(declaration);\n }\n\n schemaBlocks.push([`// ${schemaName}`, entryDeclaration, ''].join('\\n'));\n }\n\n if (namedTypeDeclarations.length > 0) {\n lines.push(...namedTypeDeclarations);\n lines.push('');\n }\n\n // 检测是否有 schema 引用了 coerce 公用函数,若有则注入 import 语句\n // 公用函数包含 coerceNumber / coerceBoolean(query/params 的 string 转换)和\n // coerceMap / coerceSet(Map/Set 的 JSON 还原,body 场景也会引用)\n const allSchemaCode = [...namedTypeDeclarations, ...schemaBlocks].join('\\n');\n if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {\n lines.push(\n `import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`,\n );\n }\n lines.push('');\n\n lines.push(...schemaBlocks);\n\n return lines.join('\\n').replace(/\\n+$/, '\\n');\n}\n\n/**\n * 为路由清单中每个 handler 生成 zod.js\n *\n * 输出路径与 handler.js 同级(文件名固定为 zod.js):\n * - dev:`.faapi/api/hello/zod.js`\n * - prod:`dist/api/hello/zod.js`\n *\n * 公用函数复用:若项目中存在 coerce schema(query/params)或 Map/Set 字段,\n * 在 dist 根部生成 `faapi-helpers.js`(导出 coerceNumber / coerceBoolean / coerceMap / coerceSet),\n * 各 zod.js 通过相对路径 import。\n *\n * 内部流程:\n * 1. collectRouteSchemaSources 从路由清单提取 schema 源数据(AST 从源码 .ts)\n * 2. 按 filePath 分组 sources\n * 3. 为每个文件生成 zod.js 源码(暂存,用于检测是否需要 helpers)\n * 4. 检测是否需要 helpers,需要则生成 faapi-helpers.js\n * 5. 并行写入所有 zod.js\n *\n * @param routes 排序后的路由清单\n * @param rootDir 项目根目录\n * @param dist 输出目录(如 '.faapi' 或 'dist')\n */\nexport async function generateSchemaFiles(\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n): Promise<void> {\n if (routes.length === 0) return;\n\n const { sources, resolversByFile } = collectRouteSchemaSources(routes, rootDir);\n\n // 按 filePath 分组 sources(同一 handler 的多个方法合并到一个 zod.js)\n const sourcesByFile = new Map<string, RouteSchemaSource[]>();\n for (const source of sources) {\n let list = sourcesByFile.get(source.filePath);\n if (!list) {\n list = [];\n sourcesByFile.set(source.filePath, list);\n }\n list.push(source);\n }\n\n // 为每个文件生成 zod.js 源码(先暂存,用于检测是否需要 helpers)\n const fileEntries: { outputPath: string; source: string }[] = [];\n for (const [filePath, fileSources] of sourcesByFile) {\n // filePath 是绝对路径,转为相对 rootDir 的路径用于计算输出路径\n const relFile = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const outputPath = getSchemaOutputPath(relFile, dist, rootDir);\n // 计算 zod.js 所在目录相对 dist 的路径(用于 import helpers)\n // 与 getSchemaOutputPath 的目录计算逻辑一致:strip src/ 前缀后取目录部分\n let relForDir = relFile;\n if (relForDir.startsWith('src/')) {\n relForDir = relForDir.slice(4);\n }\n const dirIdx = relForDir.lastIndexOf('/');\n const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : '';\n const helpersImportPath = getHelpersImportPath(zodRelDir);\n\n const resolver = resolversByFile.get(filePath);\n const source = generateSchemaFileSource(\n fileSources,\n (name) => resolver?.resolve(name)?.runtimeType,\n helpersImportPath,\n );\n fileEntries.push({ outputPath, source });\n }\n\n // 检测是否需要生成 faapi-helpers.js\n const allSourceCode = fileEntries.map((e) => e.source).join('\\n');\n if (usesCoerceHelpers(allSourceCode)) {\n const helpersPath = path.resolve(rootDir, dist, HELPERS_FILENAME);\n await writeSchemaFile(helpersPath, generateHelpersFileSource());\n }\n\n // 并行写入所有 zod.js\n await Promise.all(\n fileEntries.map(({ outputPath, source }) => writeSchemaFile(outputPath, source)),\n );\n}\n\n/**\n * 写入 zod.js 文件(确保目录存在)\n */\nasync function writeSchemaFile(outputPath: string, source: string): Promise<void> {\n // 原子写:dev watch 重建与在途请求并发时,请求 import zod.js 不能读到半成品\n await atomicWriteFile(outputPath, source);\n}\n","import path from 'node:path';\nimport { existsSync } from 'node:fs';\nimport type { ToolManifestList } from '../tools/toolTypes';\nimport type { ToolMetadata } from '../ast/extractToolMetadata';\nimport { extractToolMetadata } from '../ast/extractToolMetadata';\nimport { createPrograms } from '../ast/createProgram';\nimport { toProdFilePath } from '../utils/prodPaths';\nimport { atomicWriteFile } from '../utils/atomicWrite';\nimport { getSchemaOutputPath } from './generateSchemaFiles';\nimport {\n extractTypeInfo,\n createLazyTypeResolver,\n type HandlerTypeInfo,\n type LazyTypeResolver,\n} from '../ast/extractHandlerTypes';\nimport type { RuntimeType } from '../ast/resolveTypeNode';\nimport {\n generateZodSchemaSource,\n generateHelpersFileSource,\n usesCoerceHelpers,\n HELPERS_FILENAME,\n} from '../ast/generateZodSchema';\nimport { getHelpersImportPath } from './generateSchemaFiles';\n\n/**\n * 序列化的 tool manifest 记录(可写入 JS 模块,无函数引用)\n *\n * 与 [ToolMetadata](../ast/extractToolMetadata.md) 字段一一对应,仅 `filePath`\n * 由源码形式(`src/...`)转为产物形式(`<dist>/...`,打平 `src/` 前缀 + dist 前缀 + `.js`)。\n *\n * `undefined` 字段(description / inputTypeName)在 JSON.stringify 时自动省略,\n * 水合时通过 `??` 兜底为 undefined。\n */\nexport interface SerializedToolRecord {\n /** tool 名(`@tool` 覆盖值 或 路径推导值) */\n name: string;\n /** 源码中的导出函数名(供 loadToolModule 在 handler.js 中按名 import) */\n functionName: string;\n /** JSDoc 描述(对 LLM 可见) */\n description?: string;\n /** 第一个参数 interface 名(用于从 zod.js 加载 schema) */\n inputTypeName?: string;\n /** 产物形式路径(如 `dist/tools/weather/handler.js`),供运行时 import tool.js */\n filePath: string;\n}\n\n/**\n * 单个 tool 的 schema 提取结果\n *\n * 与 [RouteSchemaSource](./collectRouteSchemaSources.md) 对称,但 schema key 用 tool 名而非 urlPath。\n */\nexport interface ToolSchemaSource {\n /** tool 名(含 `@tool` 覆盖),用作注释标识 */\n name: string;\n /** 源文件绝对路径(用于 generateToolArtifacts 按文件分组生成 zod.js) */\n filePath: string;\n /** schema 名 = inputTypeName(导出 `${inputTypeName}Schema`) */\n schemaName: string;\n /** AST 提取的类型信息,null 时不导出 Schema */\n typeInfo: HandlerTypeInfo | null;\n}\n\n/**\n * faapi-tools.js 文件名(与 faapi-routes.js 同构)\n */\nconst TOOLS_FILE = 'faapi-tools.js';\n\n/**\n * tool zod.js 输出路径 / 运行时路径计算\n *\n * 与路由 schema 的路径规则完全一致(strip src/ 前缀 + dist + 固定 zod.js 文件名,\n * 见 generateSchemaFiles.ts 的实现),此处别名 re-export 保持 tool 侧语义命名。\n * 此前两份逐字同构实现已合并。\n */\nexport {\n getSchemaOutputPath as getToolSchemaOutputPath,\n getRuntimeSchemaPath as getRuntimeToolSchemaPath,\n} from './generateSchemaFiles';\n/**\n * 序列化 tool 清单为可写入 JS 模块的结构\n *\n * - `filePath` 转为产物形式(打平 `src/` 前缀 + dist 前缀 + `.js`)\n * - 其他字段(name/functionName/description/inputTypeName)直接透传\n * - `undefined` 字段在 JSON.stringify 时自动省略\n *\n * @param tools AST 增强后的 ToolMetadata[](由 generateToolArtifacts 内部从 ToolManifest 增强)\n * @param dist 产物目录(默认 `dist`),用于转换 filePath\n */\nexport function serializeTools(\n tools: ToolMetadata[],\n dist: string = 'dist',\n): SerializedToolRecord[] {\n return tools.map((t) => ({\n name: t.name,\n functionName: t.functionName,\n description: t.description,\n inputTypeName: t.inputTypeName,\n filePath: toProdFilePath(t.filePath, dist),\n }));\n}\n\n/**\n * 把序列化的 tool 清单写入 faapi-tools.js\n *\n * 生成 ESM 模块,运行时 `createAppCore` 通过 `importWithCacheBust` 加载。\n * 用 JSON.stringify 嵌入,保证字符串转义安全(与 `writeRoutesModule` 一致)。\n */\nexport async function writeToolsModule(\n manifest: SerializedToolRecord[],\n outputPath: string,\n): Promise<void> {\n const content = `// 自动生成,请勿手动编辑(faapi build/dev 产物)\nexport const tools = ${JSON.stringify(manifest, null, 2)};\n`;\n await atomicWriteFile(outputPath, content);\n}\n\n/**\n * 从序列化清单水合还原 ToolMetadata[]\n *\n * 字段一一对应(无函数引用需还原)。`undefined` 字段在 JSON.parse 时缺失,\n * 通过 `??` 兜底为 undefined(保证 `ToolMetadata` 类型完整)。\n */\nexport function hydrateTools(manifest: SerializedToolRecord[]): ToolMetadata[] {\n return manifest.map((t) => ({\n name: t.name,\n functionName: t.functionName,\n description: t.description ?? undefined,\n inputTypeName: t.inputTypeName ?? undefined,\n filePath: t.filePath,\n }));\n}\n\n/**\n * 从 ToolMetadata[] 收集 schema 提取所需的原始数据\n *\n * 与 [collectRouteSchemaSources](./collectRouteSchemaSources.ts) 对称,但:\n * - 按 tool handler.ts 文件分组(同一 handler.ts 多个 tool 合并到一个 zod.js)\n * - schema 名 = inputTypeName(导出 `${inputTypeName}Schema`,与路由的 `<METHOD><InputType>Schema` 不同)\n * - coerce=false(tool input 来自 LLM JSON 调用,与 body 一致,无需字符串转换)\n *\n * 跳过 inputTypeName 为 undefined 的 tool(无参数/无类型标注/内联类型字面量),\n * 不生成对应 schema(与路由的\"无类型声明的方法不导出 Schema\"对齐)。\n *\n * 与路由的 collectRouteSchemaSources 一致采用惰性解析:入口类型(inputTypeName)\n * 立即解析且必须严格校验,ref 按需解析并缓存——与 tool 无关的类型零开销。\n *\n * @returns sources(用于生成 zod.js) + resolversByFile(用于解析 ref)\n */\nfunction collectToolSchemaSources(\n tools: ToolMetadata[],\n rootDir: string,\n): {\n sources: ToolSchemaSource[];\n resolversByFile: Map<string, LazyTypeResolver>;\n} {\n // 按文件分组 tool(同一 handler.ts 多个 tool 合并到一个 zod.js)\n const toolsByFile = new Map<string, ToolMetadata[]>();\n for (const tool of tools) {\n if (!tool.inputTypeName) continue; // 跳过无 inputTypeName 的 tool\n const absPath = path.resolve(rootDir, tool.filePath);\n let list = toolsByFile.get(absPath);\n if (!list) {\n list = [];\n toolsByFile.set(absPath, list);\n }\n list.push(tool);\n }\n\n // 批量共享 Program:同一次提取只创建一个 Program,避免逐文件全量解析\n const programByFile = createPrograms([...toolsByFile.keys()]);\n const resolversByFile = new Map<string, LazyTypeResolver>();\n for (const filePath of toolsByFile.keys()) {\n resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath)!, filePath));\n }\n\n // 为每个 tool 提取 schema\n const sources: ToolSchemaSource[] = [];\n for (const [filePath, fileTools] of toolsByFile) {\n const program = programByFile.get(filePath)!;\n for (const tool of fileTools) {\n // inputTypeName 已在外层过滤,这里一定非空(防御性兜底)\n const inputTypeName = tool.inputTypeName!;\n const typeInfo = extractTypeInfo(program, filePath, inputTypeName); // 入口类型,严格解析\n sources.push({\n name: tool.name,\n filePath,\n schemaName: inputTypeName, // schema 名 = inputTypeName\n typeInfo,\n });\n }\n }\n\n return { sources, resolversByFile };\n}\n\n/**\n * 生成单个 tool handler.ts 的 zod.js 源码\n *\n * 自包含:[extractAllTypes](../ast/extractHandlerTypes.md) 在 AST 阶段已通过 TypeScript checker\n * 内联跨文件类型,每个 zod.js 无需 import 其他 zod.js。`ref` 仅用于同文件内的循环引用\n * (通过 `z.lazy` 处理)。\n *\n * 导出格式:\n * - `${inputTypeName}Schema`:zod schema 对象(用于 safeParse 校验)\n *\n * 与 [generateSchemaFileSource](./generateSchemaFiles.ts) 的差异:\n * - schema 名直接取自 `inputTypeName`(无 HTTP 方法前缀)\n * - coerce=false(tool input 来自 LLM JSON 调用,与 body 一致)\n *\n * 无 inputTypeName 的 tool 不导出对应 Schema(collectToolSchemaSources 已过滤)。\n *\n * @param sources 同一文件的 schema 提取结果(含多个 tool)\n * @param resolveType ref 解析函数(解析循环引用中的 ref):名称 → 运行时类型,未声明返回 undefined\n * @param helpersImportPath 到 faapi-helpers.js 的相对 import 路径(如 `../../faapi-helpers.js`)。\n * 传空字符串表示不注入 coerce helpers 的 import(用于无 helpers 引用的文件或测试场景)。\n */\nexport function generateToolSchemaFileSource(\n sources: ToolSchemaSource[],\n resolveType: (name: string) => RuntimeType | undefined,\n helpersImportPath: string,\n): string {\n const lines: string[] = [\"import { z } from 'zod';\"];\n\n // 先生成所有 schema 代码,暂存到 schemaBlocks\n const schemaBlocks: string[] = [];\n for (const source of sources) {\n const { schemaName, typeInfo } = source;\n if (!typeInfo) {\n // 无类型声明,不导出对应 Schema\n continue;\n }\n\n // tool schema coerce=false(input 来自 LLM JSON 调用,与 body 一致)\n const coerce = false;\n\n const block = [`// ${source.name} → ${schemaName}`];\n // generateZodSchemaSource 自带 import 语句,剥离后由本函数统一管理 import\n // 传入 schemaName 作为 exportName,确保导出名与运行时查找一致(`${inputTypeName}Schema`)\n const schemaCode = generateZodSchemaSource(typeInfo, resolveType, schemaName, coerce).replace(\n /^import \\{ z \\} from 'zod';\\s*\\n\\s*\\n/,\n '',\n );\n block.push(schemaCode);\n block.push('');\n schemaBlocks.push(block.join('\\n'));\n }\n\n // 检测是否有 schema 引用了 coerce 公用函数(Map/Set 还原,即使 coerce=false 也可能引用)\n const allSchemaCode = schemaBlocks.join('\\n');\n if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {\n lines.push(\n `import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`,\n );\n }\n lines.push('');\n\n lines.push(...schemaBlocks);\n\n return lines.join('\\n').replace(/\\n+$/, '\\n');\n}\n\n/**\n * 检测并生成 faapi-helpers.js(若不存在且 schema 引用了公用函数)\n *\n * tool schema 在两种场景下会引用 faapi-helpers.js 的公用变量:\n * 1. Map/Set 字段:即使 coerce=false 也引用 `coerceMap` / `coerceSet`(JSON.parse 出来的是数组/对象,需还原)\n * 2. number/boolean 字段:coerce=true 时引用 `coerceNumber` / `coerceBoolean`\n * (tool schema coerce=false,不会触发此场景)\n *\n * 与 [generateSchemaFiles](./generateSchemaFiles.ts) 共享同一份 `faapi-helpers.js`——\n * 若文件已存在则不重复生成(路由 schema 生成阶段可能已生成)。\n */\nasync function maybeGenerateHelpers(allSourceCode: string, distDir: string): Promise<void> {\n if (!usesCoerceHelpers(allSourceCode)) return;\n const helpersPath = path.resolve(distDir, HELPERS_FILENAME);\n if (existsSync(helpersPath)) return;\n await atomicWriteFile(helpersPath, generateHelpersFileSource());\n}\n\n/**\n * 主入口:从 ToolManifest[] 生成 faapi-tools.js + 每个 tool 的 zod.js\n *\n * 内部流程:\n * 1. 对每个 ToolManifest 调 `createProgram` + `extractToolMetadata` → ToolMetadata[]\n * (AST 增强:补全 description / inputTypeName / `@tool` 覆盖后的 name)\n * 2. `serializeTools(metadata, dist)` → SerializedToolRecord[](filePath 转产物形式)\n * 3. `writeToolsModule(serialized, faapiToolsPath)` → 写入 `<dist>/faapi-tools.js`\n * 4. 若 `skipSchema=true`(dev 按需模式)→ 返回,不生成 zod.js\n * 5. `collectToolSchemaSources` 从 ToolMetadata[] 收集 schema 源数据(按文件分组)\n * 6. 为每个文件计算 `helpersImportPath` + 生成 zod.js 源码(暂存)\n * 7. `maybeGenerateHelpers` 检测并生成/复用 `faapi-helpers.js`\n * 8. 并行写入所有 zod.js\n *\n * @param tools scanTools 产出的 ToolManifest[](仅路径推导字段)\n * @param rootDir 项目根目录\n * @param dist 产物目录(`.faapi` 或 `dist`)\n * @param options.skipSchema 跳过 zod.js 生成(dev 按需模式启动时仅生成清单)\n * @returns AST 增强后的 ToolMetadata[](供调用方日志/调试)\n */\nexport async function generateToolArtifacts(\n tools: ToolManifestList,\n rootDir: string,\n dist: string,\n options?: { skipSchema?: boolean },\n): Promise<ToolMetadata[]> {\n // 1. AST 增强:对每个 manifest 调 extractToolMetadata(批量共享 Program)\n const metadata: ToolMetadata[] = [];\n const programByFile = createPrograms(tools.map((m) => path.resolve(rootDir, m.filePath)));\n for (const manifest of tools) {\n const absPath = path.resolve(rootDir, manifest.filePath);\n const program = programByFile.get(absPath)!;\n const result = extractToolMetadata(program, absPath, manifest.functionName, {\n name: manifest.name,\n filePath: manifest.filePath,\n });\n if (result) {\n metadata.push(result);\n }\n }\n\n // 2. 序列化 + 写入 faapi-tools.js\n const serialized = serializeTools(metadata, dist);\n const toolsPath = path.resolve(rootDir, dist, TOOLS_FILE);\n await writeToolsModule(serialized, toolsPath);\n\n // 4. dev 按需模式:跳过 zod.js 生成(首次请求时由 ensureToolSchemaGenerated 按需生成)\n if (options?.skipSchema) {\n return metadata;\n }\n\n // 5-8. 生成 zod.js\n if (metadata.length === 0) {\n return metadata;\n }\n\n const { sources, resolversByFile } = collectToolSchemaSources(metadata, rootDir);\n if (sources.length === 0) {\n return metadata; // 无 inputTypeName 的 tool,不生成 zod.js\n }\n\n // 按文件分组 sources(同一 handler.ts 多个 tool 合并到一个 zod.js)\n const sourcesByFile = new Map<string, ToolSchemaSource[]>();\n for (const source of sources) {\n let list = sourcesByFile.get(source.filePath);\n if (!list) {\n list = [];\n sourcesByFile.set(source.filePath, list);\n }\n list.push(source);\n }\n\n // 为每个文件生成 zod.js 源码(先暂存,用于检测是否需要 helpers)\n const fileEntries: { outputPath: string; source: string }[] = [];\n for (const [filePath, fileSources] of sourcesByFile) {\n const relFile = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const outputPath = getSchemaOutputPath(relFile, dist, rootDir);\n const resolver = resolversByFile.get(filePath);\n\n // 计算 zod.js 所在目录相对 dist 的路径(用于 import helpers)\n let relForDir = relFile;\n if (relForDir.startsWith('src/')) {\n relForDir = relForDir.slice(4);\n }\n const dirIdx = relForDir.lastIndexOf('/');\n const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : '';\n const helpersImportPath = getHelpersImportPath(zodRelDir);\n\n const source = generateToolSchemaFileSource(\n fileSources,\n (name) => resolver?.resolve(name)?.runtimeType,\n helpersImportPath,\n );\n fileEntries.push({ outputPath, source });\n }\n\n // 检测是否需要生成 faapi-helpers.js\n const allSourceCode = fileEntries.map((e) => e.source).join('\\n');\n const distDir = path.resolve(rootDir, dist);\n await maybeGenerateHelpers(allSourceCode, distDir);\n\n // 并行写入所有 zod.js\n await Promise.all(\n fileEntries.map(({ outputPath, source }) => writeToolSchemaFile(outputPath, source)),\n );\n\n return metadata;\n}\n\n/**\n * 写入 tool zod.js 文件(确保目录存在)\n */\nasync function writeToolSchemaFile(outputPath: string, source: string): Promise<void> {\n await atomicWriteFile(outputPath, source);\n}\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { AgentManifestList } from './agentTypes';\n\n/**\n * 默认 agent 扫描 patterns\n *\n * 与 tool 扫描([TOOL_PATTERNS](../tools/scanTools.md))对称——\n * agent 定义文件约定放在 `src/agents/<agentName>/handler.ts`(一级目录)。\n *\n * 由 devCommand / buildCommand / createDevApp.reloadAgents(Phase 1.9)共享,\n * 避免多处重复定义。\n */\nexport const DEFAULT_AGENT_PATTERNS = ['src/agents/*/handler.ts'];\n\n/**\n * 检测 `run` 函数导出\n *\n * 匹配:\n * - `export function run() { ... }`\n * - `export async function run() { ... }`\n * - `export const run = () => { ... }`\n * - `export const run = async () => { ... }`\n *\n * 不匹配(`\\b` 词边界保证精确匹配 `run`):\n * - `export const runtime = ...`(run 后跟 `time`)\n *\n * > `config` 导出检测已移除(`hasConfig` 字段已废弃,见 [agentTypes](./agentTypes.md))。\n * > AST 提取阶段 [extractAgentMetadata](../ast/extractAgentMetadata.md) 仍会查找 config\n * > 导出(用于提取 JSDoc 描述 + config 块字段),但运行时不再需要 `hasConfig` 标志。\n */\nconst RUN_EXPORT_RE = /export\\s+(?:async\\s+)?(?:function\\s+|const\\s+)run\\b/;\n\n/**\n * 从文件路径提取 agent 名\n *\n * 匹配 `src/agents/<agentName>/handler.ts` 模式,提取 `<agentName>`。\n *\n * - `src/agents/researcher/handler.ts` → `researcher`\n * - `backup/agents/researcher/handler.ts` → `researcher`(任意前缀,只要匹配 agents/<name>/handler.ts)\n *\n * @throws 路径不匹配 agent 模式时抛错(不应发生——glob pattern 已限制)\n */\nfunction extractAgentNameFromPath(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n const match = normalized.match(/(?:^|\\/)agents\\/([^/]+)\\/handler\\.ts$/);\n if (!match) {\n throw new Error(\n `Not an agent path: \"${filePath}\". Expected pattern: src/agents/<name>/handler.ts`,\n );\n }\n return match[1]!;\n}\n\n/**\n * 从源码检测 `run` 导出是否存在\n *\n * 不 import 模块——正则匹配源码文本(Vite 风格,与 [scanTools](../tools/scanTools.md) 的\n * `extractToolExportsFromSource` 同构)。\n */\nfunction detectAgentExports(source: string): { hasRun: boolean } {\n return {\n hasRun: RUN_EXPORT_RE.test(source),\n };\n}\n\n/**\n * 扫描 agents 目录,生成 agent 清单\n *\n * Vite 风格:启动时只读源码 + 正则检测 `run` 导出,不 import agent.js。\n * agent.js 加载延后到 [loadAgentModule](../loader/loadAgentModule.md) 请求阶段(Phase 1.9)。\n *\n * agent 文件格式:`src/agents/<agentName>/handler.ts`,导出 `config` 块(可选)和\n * `run` 函数(可选)。一个目录一份 handler.ts = 一个 agent。\n *\n * 重名检测:同 agent 名出现在多个文件 → 抛错(agent 名全局唯一,无作用域维度)。\n *\n * @param rootDir 项目根目录\n * @param patterns glob patterns(源码 .ts 路径,匹配 agent handler 文件)\n * @returns `AgentManifestList`\n */\nexport async function scanAgents(rootDir: string, patterns: string[]): Promise<AgentManifestList> {\n const files = await fg(patterns, {\n cwd: rootDir,\n onlyFiles: true,\n absolute: false,\n });\n\n const agents: AgentManifestList = [];\n // 重名检测:agentName → filePath\n const seen = new Map<string, string>();\n\n for (const file of files) {\n const normalizedFile = file.replace(/\\\\/g, '/');\n const fileName = normalizedFile.split('/').pop()!;\n\n // 只处理 handler.ts(与路由/tool handler.ts 对称),其他 .ts 文件跳过\n if (fileName !== 'handler.ts' && fileName !== 'handler.js') {\n continue;\n }\n\n const absPath = path.resolve(rootDir, normalizedFile);\n const source = await fs.promises.readFile(absPath, 'utf8').catch(() => '');\n const { hasRun } = detectAgentExports(source);\n const name = extractAgentNameFromPath(normalizedFile);\n\n // 重名检测:同 agent 名报错\n const prevFile = seen.get(name);\n if (prevFile) {\n throw new Error(\n `Agent conflict: \"${name}\" declared in both ${prevFile} and ${normalizedFile}`,\n );\n }\n seen.set(name, normalizedFile);\n\n agents.push({\n name,\n filePath: normalizedFile,\n hasRun,\n });\n }\n\n return agents;\n}\n","import ts from 'typescript';\nimport {\n extractDescription,\n extractJSDocTagValue,\n getJSDocFromNode,\n hasExportModifier,\n} from './jsDocMetadata';\n\n/**\n * Agent 的 LLM 可见核心字段\n *\n * 描述\"agent 是什么\"——LLM 真正需要消费的字段,**不含**代码本体加载细节\n * (filePath / hasRun)。文件型 agent 与 DB-driven skill 都实现此接口。\n *\n * - 文件型 agent:由 [AgentMetadata](./extractAgentMetadata.md) 继承扩展,\n * 额外含 `filePath` / `hasRun`(代码本体加载用)\n * - DB-driven skill:业务方 plugin 从 DB 字段映射到本接口即可,无需填占位值\n * (skill 无源文件,不走 `loadAgentModule`,自然不读 filePath / hasRun)\n *\n * `@faapi/agent` 子包的 `Agent` 类、`agentRegistry` 查询入口、`asTool` 包装\n * 都消费 `AgentCore`,实现\"agent 与 skill 走同一运行时链路\"。\n */\nexport interface AgentCore {\n /** agent 名(`@agent` JSDoc 覆盖值 或 目录推导值) */\n name: string;\n /** JSDoc 描述(agent 描述,对 LLM 可见),无 JSDoc 或 JSDoc 无自由文本时为 `undefined` */\n description?: string;\n /** 系统提示词(config 块字面量提取),无/非字面量时为 `undefined` */\n systemPrompt?: string;\n /** agent 显式声明可用的 tool 引用列表(config 块字面量提取),无/含非字面量元素时为 `undefined` */\n tools?: string[];\n /** 可调用的其他 agent 名列表(config 块字面量提取),无/含非字面量元素时为 `undefined` */\n agents?: string[];\n /** LLM 模型名(config 块字面量提取),无/非字面量时为 `undefined` */\n model?: string;\n /** 最大对话轮数(config 块字面量提取),无/非字面量时为 `undefined` */\n maxTurns?: number;\n}\n\n/**\n * Agent 完整元数据(文件型 agent)\n *\n * 继承 [AgentCore](./extractAgentMetadata.md) 的 LLM 字段,额外扩展**代码本体加载细节**:\n * - `filePath` — `loadAgentModule` 加载 `handler.js` 产物提取 `run` 函数用\n * - `hasRun` — 是否导出 `run` 函数(`Agent.executeSubAgent` 据此决定走自定义 run\n * 还是默认 reactLoop)\n *\n * DB-driven skill 不实现此接口(无源文件,无需加载),只实现 `AgentCore`。\n *\n * 由 [extractAgentMetadata](./extractAgentMetadata.md) 产出,合并路径推导字段\n * (来自 [scanAgents](../agents/scanAgents.md) 的 `AgentManifest`)与 AST 提取字段\n * (JSDoc 描述、`@agent` 覆盖名、config 块字段)。\n *\n * 字段来源:\n * - `name` — `@agent` JSDoc 覆盖值,或 `pathMeta.name`(目录推导)\n * - `filePath` / `hasRun` — 由 `pathMeta` 透传\n * - `description` — JSDoc 注释块自由文本(对 LLM 可见)\n * - `systemPrompt` / `tools` / `agents` / `model` / `maxTurns` — config 块字面量提取\n */\nexport interface AgentMetadata extends AgentCore {\n /** 源码相对路径(从 `pathMeta` 透传),`loadAgentModule` 据此加载 `handler.js` 提取 `run` */\n filePath: string;\n /** 是否导出 `run` 函数(从 `pathMeta` 透传),`Agent.executeSubAgent` 据此选择自定义 run / 默认 reactLoop */\n hasRun: boolean;\n}\n\n/**\n * 路径推导的 agent 元数据(由 [scanAgents](../agents/scanAgents.ts) 计算)\n *\n * 透传到 [AgentMetadata](./extractAgentMetadata.ts) 输出,与 AST 提取字段合并。\n * 与 [ToolPathMeta](./extractToolMetadata.md) 对称。\n */\nexport interface AgentPathMeta {\n /** 目录推导的 agent 名(如 `researcher`) */\n name: string;\n /** 源码相对路径(如 `src/agents/researcher/handler.ts`) */\n filePath: string;\n /** 是否导出 `run` 函数(scanAgents 正则检测) */\n hasRun: boolean;\n}\n\n/** config 导出查找结果:JSDoc 持有节点 + 对象字面量(可能为 null) */\ninterface FoundConfig {\n /** JSDoc 持有节点(VariableStatement 或 FunctionDeclaration) */\n jsDocOwner: ts.Node;\n /** config 对象字面量(export const config = {...} 的 initializer 或 export function config() 的 return 值) */\n objectLiteral: ts.ObjectLiteralExpression | null;\n}\n\n/**\n * 从 agent handler.ts 提取 agent 的完整元数据\n *\n * 提取内容:\n * 1. **JSDoc 描述** — config 导出的 JSDoc 自由文本(无 config 时从 run 导出提取)\n * 2. **`@agent` 覆盖名** — JSDoc 中 `@agent` 标签后的文本,覆盖目录推导的 `name`\n * 3. **config 块字段** — systemPrompt / tools / agents / model / maxTurns\n *\n * 不提取(由 `pathMeta` 透传):`filePath` / `hasRun`\n *\n * config 查找支持两种导出形式,与 [scanAgents](../agents/scanAgents.md) 的 `CONFIG_EXPORT_RE` 正则同构:\n * - `export const config = { ... }` — 对象字面量(最常见)\n * - `export function config() { return { ... } }` — 函数返回对象\n *\n * JSDoc 查找对箭头函数/函数表达式自动回溯到外层 `VariableStatement`\n * (JSDoc 通常写在 `export const` 上方,而非箭头函数本身)。\n * 与 [extractToolMetadata](./extractToolMetadata.md) 的 JSDoc 查找同构。\n *\n * config 块字段提取仅处理字面量值——变量引用/Spread/模板字符串等非静态值返回 `undefined`。\n *\n * @param program TypeScript Program\n * @param filePath 源文件**绝对路径**(AST 用,需与 `program.getSourceFile` 一致)\n * @param pathMeta 路径推导的元数据(scanAgents 已计算)\n * @returns `AgentMetadata` 或 `null`(源文件不在 Program 中)\n */\nexport function extractAgentMetadata(\n program: ts.Program,\n filePath: string,\n pathMeta: AgentPathMeta,\n): AgentMetadata | null {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n // 查找 config 导出(优先提取 JSDoc + config 块字段)\n const configFound = findConfigExport(sourceFile);\n let jsDocOwner: ts.Node | null = null;\n let objectLiteral: ts.ObjectLiteralExpression | null = null;\n\n if (configFound) {\n jsDocOwner = configFound.jsDocOwner;\n objectLiteral = configFound.objectLiteral;\n } else if (pathMeta.hasRun) {\n // 无 config 时从 run 导出提取 JSDoc\n const runNode = findRunExport(sourceFile);\n if (runNode) {\n jsDocOwner = runNode;\n }\n }\n\n const jsDoc = jsDocOwner ? getJSDocFromNode(jsDocOwner) : undefined;\n const description = extractDescription(jsDoc);\n const agentNameOverride = extractJSDocTagValue(jsDoc, 'agent');\n\n // config 块字段提取\n let systemPrompt: string | undefined;\n let tools: string[] | undefined;\n let agents: string[] | undefined;\n let model: string | undefined;\n let maxTurns: number | undefined;\n\n if (objectLiteral) {\n const fields = extractConfigFields(objectLiteral);\n systemPrompt = fields.systemPrompt;\n tools = fields.tools;\n agents = fields.agents;\n model = fields.model;\n maxTurns = fields.maxTurns;\n }\n\n return {\n name: agentNameOverride ?? pathMeta.name,\n description,\n filePath: pathMeta.filePath,\n hasRun: pathMeta.hasRun,\n systemPrompt,\n tools,\n agents,\n model,\n maxTurns,\n };\n}\n\n/**\n * 在源文件中查找 `config` 导出\n *\n * 支持两种形式:\n * - `export const config = { ... }` → VariableStatement,对象字面量是 initializer\n * - `export function config() { return { ... } }` → FunctionDeclaration,对象字面量是 return 表达式\n *\n * 返回 JSDoc 持有节点和对象字面量(可能为 null——函数无 return / return 非对象字面量)。\n *\n * 不依赖 `node.parent` 链——`ts.createProgram` 配置 `noEmit: true` 时不会设置父指针。\n */\nfunction findConfigExport(sourceFile: ts.SourceFile): FoundConfig | null {\n let result: FoundConfig | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n // export const config = { ... }\n if (ts.isVariableStatement(node) && hasExportModifier(node)) {\n for (const decl of node.declarationList.declarations) {\n if (result) break;\n const nameText = ts.isIdentifier(decl.name) ? decl.name.text : '';\n if (nameText !== 'config' || !decl.initializer) continue;\n\n // 对象字面量:export const config = { ... }\n if (ts.isObjectLiteralExpression(decl.initializer)) {\n result = { jsDocOwner: node, objectLiteral: decl.initializer };\n }\n // 箭头函数返回对象:export const config = () => ({ ... })\n else if (ts.isArrowFunction(decl.initializer)) {\n const returnObj = getReturnObjectLiteral(decl.initializer);\n result = { jsDocOwner: node, objectLiteral: returnObj };\n }\n }\n }\n\n // export function config() { return { ... } }\n if (ts.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === 'config') {\n const returnObj = getReturnObjectLiteral(node);\n result = { jsDocOwner: node, objectLiteral: returnObj };\n }\n });\n\n return result;\n}\n\n/**\n * 在源文件中查找 `run` 导出(用于 JSDoc 回退)\n *\n * 支持:`export function run` / `export async function run` / `export const run = () =>`\n * 返回 JSDoc 持有节点(FunctionDeclaration 本身 或 VariableStatement)。\n */\nfunction findRunExport(sourceFile: ts.SourceFile): ts.Node | null {\n let result: ts.Node | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n // export function run() / export async function run()\n if (ts.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === 'run') {\n result = node;\n return;\n }\n\n // export const run = () => {}\n if (ts.isVariableStatement(node) && hasExportModifier(node)) {\n for (const decl of node.declarationList.declarations) {\n if (result) break;\n const nameText = ts.isIdentifier(decl.name) ? decl.name.text : '';\n if (nameText !== 'run' || !decl.initializer) continue;\n if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {\n result = node; // JSDoc 持有者是 VariableStatement\n }\n }\n }\n });\n\n return result;\n}\n\n/**\n * 从函数体中提取 return 的对象字面量\n *\n * - `() => ({ ... })` — 箭头函数直接返回对象字面量(body 是 ObjectLiteralExpression)\n * - `() => { return { ... }; }` — block body 中的 return 语句\n * - `function f() { return { ... }; }` — block body 中的 return 语句\n *\n * 无 return / return 非对象字面量 → null\n */\nfunction getReturnObjectLiteral(\n fn: ts.FunctionDeclaration | ts.ArrowFunction,\n): ts.ObjectLiteralExpression | null {\n const body = fn.body;\n if (!body) return null;\n\n // 箭头函数直接返回对象字面量:() => ({ ... })\n if (ts.isObjectLiteralExpression(body)) {\n return body;\n }\n\n // block body:查找 return 语句\n if (ts.isBlock(body)) {\n for (const stmt of body.statements) {\n if (\n ts.isReturnStatement(stmt) &&\n stmt.expression &&\n ts.isObjectLiteralExpression(stmt.expression)\n ) {\n return stmt.expression;\n }\n }\n }\n\n return null;\n}\n\n/**\n * 从 config 对象字面量提取 config 块字段\n *\n * 遍历对象属性,按属性名匹配提取对应字段。仅处理字面量值——\n * 变量引用/Spread/模板字符串等非静态值返回 `undefined`。\n *\n * 属性名匹配支持 Identifier 和 StringLiteral 两种形式:\n * - `{ systemPrompt: 'x' }` — Identifier 属性名\n * - `{ 'systemPrompt': 'x' }` — StringLiteral 属性名\n *\n * SpreadAssignment(`...other`)跳过——无法静态求值。\n */\nfunction extractConfigFields(objLit: ts.ObjectLiteralExpression): {\n systemPrompt?: string;\n tools?: string[];\n agents?: string[];\n model?: string;\n maxTurns?: number;\n} {\n const result: {\n systemPrompt?: string;\n tools?: string[];\n agents?: string[];\n model?: string;\n maxTurns?: number;\n } = {};\n\n for (const prop of objLit.properties) {\n // 跳过 SpreadAssignment(...other)\n if (!ts.isPropertyAssignment(prop)) continue;\n\n const propName = getPropertyName(prop.name);\n if (!propName) continue;\n\n switch (propName) {\n case 'systemPrompt':\n result.systemPrompt = extractStringValue(prop.initializer);\n break;\n case 'tools':\n result.tools = extractStringArrayValue(prop.initializer);\n break;\n case 'agents':\n result.agents = extractStringArrayValue(prop.initializer);\n break;\n case 'model':\n result.model = extractStringValue(prop.initializer);\n break;\n case 'maxTurns':\n result.maxTurns = extractNumberValue(prop.initializer);\n break;\n }\n }\n\n return result;\n}\n\n/**\n * 从属性名节点提取字符串名\n *\n * 支持:\n * - Identifier(`systemPrompt` → `'systemPrompt'`)\n * - StringLiteral(`'systemPrompt'` → `'systemPrompt'`)\n *\n * ComputedPropertyName / NumericLiteral / PrivateIdentifier → null\n */\nfunction getPropertyName(name: ts.PropertyName): string | null {\n if (ts.isIdentifier(name)) return name.text;\n if (ts.isStringLiteral(name)) return name.text;\n return null;\n}\n\n/**\n * 从表达式提取字符串值(仅 StringLiteral)\n *\n * - `'hello'` → `'hello'`\n * - `\"hello\"` → `'hello'`\n * - 模板字符串 / 变量引用 / 数字 → `undefined`\n */\nfunction extractStringValue(expr: ts.Expression): string | undefined {\n if (ts.isStringLiteral(expr)) return expr.text;\n return undefined;\n}\n\n/**\n * 从表达式提取数字值(仅 NumericLiteral)\n *\n * - `10` → `10`\n * - `'10'` / 变量引用 → `undefined`\n */\nfunction extractNumberValue(expr: ts.Expression): number | undefined {\n if (ts.isNumericLiteral(expr)) {\n const num = Number(expr.text);\n return Number.isNaN(num) ? undefined : num;\n }\n return undefined;\n}\n\n/**\n * 从表达式提取字符串数组(ArrayLiteralExpression,全 StringLiteral 元素)\n *\n * - `['a', 'b']` → `['a', 'b']`\n * - `['a', someVar]` → `undefined`(含非 StringLiteral 元素)\n * - `[]` → `[]`(空数组)\n * - 非数组 → `undefined`\n */\nfunction extractStringArrayValue(expr: ts.Expression): string[] | undefined {\n if (!ts.isArrayLiteralExpression(expr)) return undefined;\n const values: string[] = [];\n for (const element of expr.elements) {\n if (!ts.isStringLiteral(element)) return undefined;\n values.push(element.text);\n }\n return values;\n}\n","import path from 'node:path';\nimport type { AgentManifestList } from '../agents/agentTypes';\nimport type { AgentMetadata } from '../ast/extractAgentMetadata';\nimport { extractAgentMetadata } from '../ast/extractAgentMetadata';\nimport { createPrograms } from '../ast/createProgram';\nimport { toProdFilePath } from '../utils/prodPaths';\nimport { atomicWriteFile } from '../utils/atomicWrite';\n\n/**\n * 序列化的 agent manifest 记录(可写入 JS 模块,无函数引用)\n *\n * 与 [AgentMetadata](../ast/extractAgentMetadata.md) 字段一一对应,仅 `filePath`\n * 由源码形式(`src/...`)转为产物形式(`<dist>/...`,打平 `src/` 前缀 + dist 前缀 + `.js`)。\n *\n * `undefined` 字段(description / systemPrompt / tools / agents / model / maxTurns)\n * 在 JSON.stringify 时自动省略,水合时通过 `??` 兜底为 undefined。\n *\n * > `hasConfig` 字段已移除——它原本用于控制 `loadAgentModule` 是否提取 `config` 对象,\n * > 但 `AgentModule.config` 已废弃(`executeSubAgent` 拿到后从不读取),属于死链路。\n */\nexport interface SerializedAgentRecord {\n /** agent 名(`@agent` 覆盖值 或 目录推导值) */\n name: string;\n /** JSDoc 描述(对 LLM 可见),无则省略 */\n description?: string;\n /** 是否导出 run 函数(从 manifest 透传) */\n hasRun: boolean;\n /** 系统提示词(config 块字面量提取),无/非字面量时省略 */\n systemPrompt?: string;\n /** agent 显式声明可用的 tool 引用列表(config 块字面量提取),无/含非字面量元素时省略 */\n tools?: string[];\n /** 可调用的其他 agent 名列表(config 块字面量提取),无/含非字面量元素时省略 */\n agents?: string[];\n /** LLM 模型名(config 块字面量提取),无/非字面量时省略 */\n model?: string;\n /** 最大对话轮数(config 块字面量提取),无/非字面量时省略 */\n maxTurns?: number;\n /** 产物形式路径(如 `dist/agents/researcher/handler.js`),供运行时 import agent.js */\n filePath: string;\n}\n\n/**\n * faapi-agents.js 文件名(与 faapi-routes.js / faapi-tools.js 同构)\n */\nconst AGENTS_FILE = 'faapi-agents.js';\n\n/**\n * 序列化 agent 清单为可写入 JS 模块的结构\n *\n * - `filePath` 转为产物形式(打平 `src/` 前缀 + dist 前缀 + `.js`)\n * - 其他字段(name/description/hasRun/systemPrompt/tools/agents/model/maxTurns)直接透传\n * - `undefined` 字段在 JSON.stringify 时自动省略\n *\n * @param agents AST 增强后的 AgentMetadata[](由 generateAgentArtifacts 内部从 AgentManifest 增强)\n * @param dist 产物目录(默认 `dist`),用于转换 filePath\n */\nexport function serializeAgents(\n agents: AgentMetadata[],\n dist: string = 'dist',\n): SerializedAgentRecord[] {\n return agents.map((a) => ({\n name: a.name,\n description: a.description,\n hasRun: a.hasRun,\n systemPrompt: a.systemPrompt,\n tools: a.tools,\n agents: a.agents,\n model: a.model,\n maxTurns: a.maxTurns,\n filePath: toProdFilePath(a.filePath, dist),\n }));\n}\n\n/**\n * 把序列化的 agent 清单写入 faapi-agents.js\n *\n * 生成 ESM 模块,运行时 `createAppCore` 通过 `importWithCacheBust` 加载。\n * 用 JSON.stringify 嵌入,保证字符串转义安全(与 `writeRoutesModule` / `writeToolsModule` 一致)。\n */\nexport async function writeAgentsModule(\n manifest: SerializedAgentRecord[],\n outputPath: string,\n): Promise<void> {\n const content = `// 自动生成,请勿手动编辑(faapi build/dev 产物)\nexport const agents = ${JSON.stringify(manifest, null, 2)};\n`;\n await atomicWriteFile(outputPath, content);\n}\n\n/**\n * 从序列化清单水合还原 AgentMetadata[]\n *\n * 字段一一对应(无函数引用需还原)。`undefined` 字段在 JSON.parse 时缺失,\n * 通过 `??` 兜底为 undefined(保证 `AgentMetadata` 类型完整)。\n */\nexport function hydrateAgents(manifest: SerializedAgentRecord[]): AgentMetadata[] {\n return manifest.map((a) => ({\n name: a.name,\n description: a.description ?? undefined,\n filePath: a.filePath,\n hasRun: a.hasRun,\n systemPrompt: a.systemPrompt ?? undefined,\n tools: a.tools ?? undefined,\n agents: a.agents ?? undefined,\n model: a.model ?? undefined,\n maxTurns: a.maxTurns ?? undefined,\n }));\n}\n\n/**\n * 主入口:从 AgentManifest[] 生成 faapi-agents.js\n *\n * agent **不生成 zod.js**——与 tool 不同,agent 没有用户输入参数(config 块字段\n * 已在 [extractAgentMetadata](../ast/extractAgentMetadata.md) AST 阶段提取为字面量),\n * 运行时无需 schema 校验。`run` 函数参数由 [agentRegistry](../injection/agentRegistry.md)\n * 的 `asTool()` 在 Phase 2.2 处理(若需输入校验,由 agent 子包在 Phase 3.x 自行生成)。\n *\n * 内部流程:\n * 1. 对每个 AgentManifest 调 `createProgram` + `extractAgentMetadata` → AgentMetadata[]\n * (AST 增强:补全 description / `@agent` 覆盖名 / config 块字段)\n * 2. `serializeAgents(metadata, dist)` → SerializedAgentRecord[](filePath 转产物形式)\n * 3. `writeAgentsModule(serialized, faapiAgentsPath)` → 写入 `<dist>/faapi-agents.js`\n *\n * 与 [generateToolArtifacts](./generateToolArtifacts.md) 的差异:\n * - 不生成 zod.js(agent 无输入参数)\n * - 无 `skipSchema` 选项(没有 schema 可跳过)\n * - 文件名常量为 `faapi-agents.js`,导出 `agents` 而非 `tools`\n *\n * @param agents scanAgents 产出的 AgentManifest[](仅路径推导字段)\n * @param rootDir 项目根目录\n * @param dist 产物目录(`.faapi` 或 `dist`)\n * @returns AST 增强后的 AgentMetadata[](供调用方日志/调试)\n */\nexport async function generateAgentArtifacts(\n agents: AgentManifestList,\n rootDir: string,\n dist: string,\n): Promise<AgentMetadata[]> {\n // 1. AST 增强:对每个 manifest 调 extractAgentMetadata(批量共享 Program)\n const metadata: AgentMetadata[] = [];\n const programByFile = createPrograms(agents.map((m) => path.resolve(rootDir, m.filePath)));\n for (const manifest of agents) {\n const absPath = path.resolve(rootDir, manifest.filePath);\n const program = programByFile.get(absPath)!;\n const result = extractAgentMetadata(program, absPath, {\n name: manifest.name,\n filePath: manifest.filePath,\n hasRun: manifest.hasRun,\n });\n if (result) {\n metadata.push(result);\n }\n }\n\n // 2. 序列化 + 写入 faapi-agents.js\n const serialized = serializeAgents(metadata, dist);\n const agentsPath = path.resolve(rootDir, dist, AGENTS_FILE);\n await writeAgentsModule(serialized, agentsPath);\n\n return metadata;\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport type { FaapiConfig } from './configTypes';\n\n/**\n * 配置产物文件名(compileConfig 生成)\n */\nconst CONFIG_PRODUCT_FILE = 'faapi-config.js';\n\n/**\n * 加载 faapi 配置文件\n *\n * 统一读取 `<dist>/faapi-config.js` 产物:\n * - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/faapi-config.js`\n * - prod 模式:`faapi build` 时由 `compileConfig` 生成 `dist/faapi-config.js`\n *\n * 产物由 `compileConfig` 在构建阶段编译,运行时不读源码、不现场编译。\n * 环境变量由 `loadEnv` 从 `.env` 系列文件加载到 `process.env`,配置文件中通过 `process.env.XXX` 读取。\n *\n * - 产物存在 → import 并返回 default\n * - 产物不存在但源码有配置文件 → 抛错(强制 rebuild)\n * - 源码也无配置文件 → 返回 `null`(配置可选)\n *\n * @param rootDir 项目根目录\n * @param dist 产物目录(如 'dist' 或 '.faapi')\n * @returns 配置对象,无配置文件时返回 null\n */\nexport async function loadConfig(\n rootDir: string,\n dist: string,\n): Promise<Partial<FaapiConfig> | null> {\n const configProductPath = path.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);\n\n if (fs.existsSync(configProductPath)) {\n const module = (await importWithCacheBust(configProductPath)) as {\n default?: Partial<FaapiConfig>;\n };\n return module.default ?? {};\n }\n\n // 产物不存在:检查源码是否有配置文件\n const hasSourceConfig =\n fs.existsSync(path.join(rootDir, 'faapi.config.ts')) ||\n fs.existsSync(path.join(rootDir, 'faapi.config.js'));\n\n if (hasSourceConfig) {\n throw new Error(\n `[faapi] ${dist}/${CONFIG_PRODUCT_FILE} 不存在,请先执行 \\`faapi build\\`(或 \\`faapi dev\\`)生成产物。`,\n );\n }\n\n return null;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * env 决定规则:NODE_ENV || 'development'\n *\n * 调用方应在调 loadEnv 之前自行兜底 NODE_ENV(dev 设 'development',prod 设 'production')。\n */\nfunction resolveEnv(): string {\n return process.env.NODE_ENV || 'development';\n}\n\n/**\n * .env 文件加载顺序(从低到高优先级,后加载的覆盖先加载的)\n *\n * 1. .env — 所有环境共享\n * 2. .env.local — 本地覆盖(不提交 git)\n * 3. .env.{env} — 按环境覆盖\n * 4. .env.{env}.local — 按环境本地覆盖\n */\nfunction getEnvFiles(env: string): string[] {\n return ['.env', '.env.local', `.env.${env}`, `.env.${env}.local`];\n}\n\n/**\n * 解析 .env 文件内容为键值对象\n *\n * 格式:\n * - `KEY=VALUE` 基本格式\n * - `# 注释` 行首注释\n * - `export KEY=VALUE` 支持 export 前缀\n * - 单引号:字面量,不展开变量,不处理转义\n * - 双引号:支持变量展开($VAR / ${VAR})和转义(\\n \\t \\r \\\\ \\\")\n * - 无引号:字面量,` #` 后为行内注释\n *\n * @param content .env 文件内容\n * @param fileVars 本文件之前已解析的变量(用于变量展开)\n * @returns 解析出的键值对象\n */\nfunction parseEnvFile(content: string, fileVars: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n // 兼容 CRLF / LF\n const lines = content.replace(/\\r\\n/g, '\\n').split('\\n');\n\n for (const line of lines) {\n const trimmed = line.trim();\n // 空行 / 注释行跳过\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n // 匹配 KEY=VALUE 或 export KEY=VALUE\n // KEY 必须符合 [A-Za-z_][A-Za-z0-9_]*\n const match = /^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/.exec(trimmed);\n if (!match) continue;\n\n // 正则有两个捕获组,匹配成功则必非空\n const key = match[1]!;\n const rawValue = match[2]!;\n const value = parseValue(rawValue, { ...fileVars, ...result });\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * 解析单个值的引号/展开/转义\n *\n * - 单引号:字面量(去除首尾单引号)\n * - 双引号:处理转义 + 展开变量\n * - 无引号:字面量,去除行内注释(` #` 后为注释)\n */\nfunction parseValue(raw: string, env: Record<string, string>): string {\n if (raw === '') return '';\n\n // 单引号:字面量(raw 非空已由调用方保证)\n if (raw[0] === \"'\") {\n const end = raw.indexOf(\"'\", 1);\n return end === -1 ? raw.slice(1) : raw.slice(1, end);\n }\n\n // 双引号:转义 + 展开变量(支持 \\\" 转义引号,不作为结束符)\n if (raw[0] === '\"') {\n const match = /^\"((?:\\\\.|[^\"\\\\])*)\"/.exec(raw);\n // 匹配失败时 slice(1) 兜底(无闭合引号)\n const inner = match ? match[1]! : raw.slice(1);\n return expandEscapesAndVars(inner, env);\n }\n\n // 无引号:字面量,处理行内注释\n // ` #` 前必须有空白才算注释;# 紧贴值(如 url#anchor)不算注释\n const commentMatch = /^(.*?)(\\s+#.*)$/.exec(raw);\n const value = commentMatch ? commentMatch[1]! : raw;\n return value.trim();\n}\n\n/**\n * 双引号值:处理转义字符 + 展开变量\n *\n * 转义:\\n \\r \\t \\\\ \\\" (未知转义保留字符本身)\n * 展开:$VAR / ${VAR},优先 env 参数,其次 process.env,未定义返回空字符串\n */\nfunction expandEscapesAndVars(str: string, env: Record<string, string>): string {\n // 先处理转义(避免变量值中的 $ 被二次展开)\n const escaped = str.replace(/\\\\(.)/g, (_, ch: string) => {\n switch (ch) {\n case 'n':\n return '\\n';\n case 'r':\n return '\\r';\n case 't':\n return '\\t';\n case '\\\\':\n return '\\\\';\n case '\"':\n return '\"';\n default:\n return ch; // 未知转义保留字符\n }\n });\n\n // 展开变量:$VAR 或 ${VAR}\n return escaped.replace(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n (_, braced, plain) => {\n const varName = braced || plain;\n return env[varName] ?? process.env[varName] ?? '';\n },\n );\n}\n\n/**\n * 加载 `.env` 系列文件到 `process.env`\n *\n * 按 Next.js 约定加载四级文件(从低到高):\n * 1. `.env` — 所有环境共享\n * 2. `.env.local` — 本地覆盖\n * 3. `.env.{env}` — 按环境覆盖\n * 4. `.env.{env}.local` — 按环境本地覆盖\n *\n * env 由 `NODE_ENV || 'development'` 决定。调用方应在调 loadEnv 之前自行兜底 NODE_ENV\n * (dev 设 'development',prod 设 'production')。\n *\n * 合并规则:\n * - 后加载的文件覆盖先加载的同名变量\n * - **shell 已设置的变量不被覆盖**(`process.env` 已有的值优先)\n *\n * @param rootDir 项目根目录(`.env` 文件所在目录)\n */\nexport function loadEnv(rootDir: string): void {\n const env = resolveEnv();\n const files = getEnvFiles(env);\n\n // 收集所有文件解析结果,按优先级合并(后面的覆盖前面的)\n const merged: Record<string, string> = {};\n for (const file of files) {\n const filePath = path.join(rootDir, file);\n if (!fs.existsSync(filePath)) continue;\n const content = fs.readFileSync(filePath, 'utf-8');\n const parsed = parseEnvFile(content, merged);\n Object.assign(merged, parsed);\n }\n\n // 写入 process.env(不覆盖已有的 shell 变量)\n for (const [key, value] of Object.entries(merged)) {\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n","import { stat, lstat, readdir, realpath } from 'node:fs/promises';\nimport { Readable } from 'node:stream';\nimport { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from 'node:path';\nexport const EntryTypes = {\n FILE_TYPE: 'files',\n DIR_TYPE: 'directories',\n FILE_DIR_TYPE: 'files_directories',\n EVERYTHING_TYPE: 'all',\n};\nconst defaultOptions = {\n root: '.',\n fileFilter: (_entryInfo) => true,\n directoryFilter: (_entryInfo) => true,\n type: EntryTypes.FILE_TYPE,\n lstat: false,\n depth: 2147483648,\n alwaysStat: false,\n highWaterMark: 4096,\n};\nObject.freeze(defaultOptions);\nconst RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';\nconst NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);\nconst ALL_TYPES = [\n EntryTypes.DIR_TYPE,\n EntryTypes.EVERYTHING_TYPE,\n EntryTypes.FILE_DIR_TYPE,\n EntryTypes.FILE_TYPE,\n];\nconst DIR_TYPES = new Set([\n EntryTypes.DIR_TYPE,\n EntryTypes.EVERYTHING_TYPE,\n EntryTypes.FILE_DIR_TYPE,\n]);\nconst FILE_TYPES = new Set([\n EntryTypes.EVERYTHING_TYPE,\n EntryTypes.FILE_DIR_TYPE,\n EntryTypes.FILE_TYPE,\n]);\nconst isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);\nconst wantBigintFsStats = process.platform === 'win32';\nconst emptyFn = (_entryInfo) => true;\nconst normalizeFilter = (filter) => {\n if (filter === undefined)\n return emptyFn;\n if (typeof filter === 'function')\n return filter;\n if (typeof filter === 'string') {\n const fl = filter.trim();\n return (entry) => entry.basename === fl;\n }\n if (Array.isArray(filter)) {\n const trItems = filter.map((item) => item.trim());\n return (entry) => trItems.some((f) => entry.basename === f);\n }\n return emptyFn;\n};\n/** Readable readdir stream, emitting new files as they're being listed. */\nexport class ReaddirpStream extends Readable {\n constructor(options = {}) {\n super({\n objectMode: true,\n autoDestroy: true,\n highWaterMark: options.highWaterMark,\n });\n const opts = { ...defaultOptions, ...options };\n const { root, type } = opts;\n this._fileFilter = normalizeFilter(opts.fileFilter);\n this._directoryFilter = normalizeFilter(opts.directoryFilter);\n const statMethod = opts.lstat ? lstat : stat;\n // Use bigint stats if it's windows and stat() supports options (node 10+).\n if (wantBigintFsStats) {\n this._stat = (path) => statMethod(path, { bigint: true });\n }\n else {\n this._stat = statMethod;\n }\n this._maxDepth = opts.depth ?? defaultOptions.depth;\n this._wantsDir = type ? DIR_TYPES.has(type) : false;\n this._wantsFile = type ? FILE_TYPES.has(type) : false;\n this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;\n this._root = presolve(root);\n this._isDirent = !opts.alwaysStat;\n this._statsProp = this._isDirent ? 'dirent' : 'stats';\n this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };\n // Launch stream with one parent, the root dir.\n this.parents = [this._exploreDir(root, 1)];\n this.reading = false;\n this.parent = undefined;\n }\n async _read(batch) {\n if (this.reading)\n return;\n this.reading = true;\n try {\n while (!this.destroyed && batch > 0) {\n const par = this.parent;\n const fil = par && par.files;\n if (fil && fil.length > 0) {\n const { path, depth } = par;\n const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));\n const awaited = await Promise.all(slice);\n for (const entry of awaited) {\n if (!entry)\n continue;\n if (this.destroyed)\n return;\n const entryType = await this._getEntryType(entry);\n if (entryType === 'directory' && this._directoryFilter(entry)) {\n if (depth <= this._maxDepth) {\n this.parents.push(this._exploreDir(entry.fullPath, depth + 1));\n }\n if (this._wantsDir) {\n this.push(entry);\n batch--;\n }\n }\n else if ((entryType === 'file' || this._includeAsFile(entry)) &&\n this._fileFilter(entry)) {\n if (this._wantsFile) {\n this.push(entry);\n batch--;\n }\n }\n }\n }\n else {\n const parent = this.parents.pop();\n if (!parent) {\n this.push(null);\n break;\n }\n this.parent = await parent;\n if (this.destroyed)\n return;\n }\n }\n }\n catch (error) {\n this.destroy(error);\n }\n finally {\n this.reading = false;\n }\n }\n async _exploreDir(path, depth) {\n let files;\n try {\n files = await readdir(path, this._rdOptions);\n }\n catch (error) {\n this._onError(error);\n }\n return { files, depth, path };\n }\n async _formatEntry(dirent, path) {\n let entry;\n const basename = this._isDirent ? dirent.name : dirent;\n try {\n const fullPath = presolve(pjoin(path, basename));\n entry = { path: prelative(this._root, fullPath), fullPath, basename };\n entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);\n }\n catch (err) {\n this._onError(err);\n return;\n }\n return entry;\n }\n _onError(err) {\n if (isNormalFlowError(err) && !this.destroyed) {\n this.emit('warn', err);\n }\n else {\n this.destroy(err);\n }\n }\n async _getEntryType(entry) {\n // entry may be undefined, because a warning or an error were emitted\n // and the statsProp is undefined\n if (!entry && this._statsProp in entry) {\n return '';\n }\n const stats = entry[this._statsProp];\n if (stats.isFile())\n return 'file';\n if (stats.isDirectory())\n return 'directory';\n if (stats && stats.isSymbolicLink()) {\n const full = entry.fullPath;\n try {\n const entryRealPath = await realpath(full);\n const entryRealPathStats = await lstat(entryRealPath);\n if (entryRealPathStats.isFile()) {\n return 'file';\n }\n if (entryRealPathStats.isDirectory()) {\n const len = entryRealPath.length;\n if (full.startsWith(entryRealPath) && full.substr(len, 1) === psep) {\n const recursiveError = new Error(`Circular symlink detected: \"${full}\" points to \"${entryRealPath}\"`);\n // @ts-ignore\n recursiveError.code = RECURSIVE_ERROR_CODE;\n return this._onError(recursiveError);\n }\n return 'directory';\n }\n }\n catch (error) {\n this._onError(error);\n return '';\n }\n }\n }\n _includeAsFile(entry) {\n const stats = entry && entry[this._statsProp];\n return stats && this._wantsEverything && !stats.isDirectory();\n }\n}\n/**\n * Streaming version: Reads all files and directories in given root recursively.\n * Consumes ~constant small amount of RAM.\n * @param root Root directory\n * @param options Options to specify root (start directory), filters and recursion depth\n */\nexport function readdirp(root, options = {}) {\n // @ts-ignore\n let type = options.entryType || options.type;\n if (type === 'both')\n type = EntryTypes.FILE_DIR_TYPE; // backwards-compatibility\n if (type)\n options.type = type;\n if (!root) {\n throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');\n }\n else if (typeof root !== 'string') {\n throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');\n }\n else if (type && !ALL_TYPES.includes(type)) {\n throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);\n }\n options.root = root;\n return new ReaddirpStream(options);\n}\n/**\n * Promise version: Reads all files and directories in given root recursively.\n * Compared to streaming version, will consume a lot of RAM e.g. when 1 million files are listed.\n * @returns array of paths and their entry infos\n */\nexport function readdirpPromise(root, options = {}) {\n return new Promise((resolve, reject) => {\n const files = [];\n readdirp(root, options)\n .on('data', (entry) => files.push(entry))\n .on('end', () => resolve(files))\n .on('error', (error) => reject(error));\n });\n}\nexport default readdirp;\n","import { watchFile, unwatchFile, watch as fs_watch } from 'fs';\nimport { open, stat, lstat, realpath as fsrealpath } from 'fs/promises';\nimport * as sysPath from 'path';\nimport { type as osType } from 'os';\nexport const STR_DATA = 'data';\nexport const STR_END = 'end';\nexport const STR_CLOSE = 'close';\nexport const EMPTY_FN = () => { };\nexport const IDENTITY_FN = (val) => val;\nconst pl = process.platform;\nexport const isWindows = pl === 'win32';\nexport const isMacos = pl === 'darwin';\nexport const isLinux = pl === 'linux';\nexport const isFreeBSD = pl === 'freebsd';\nexport const isIBMi = osType() === 'OS400';\nexport const EVENTS = {\n ALL: 'all',\n READY: 'ready',\n ADD: 'add',\n CHANGE: 'change',\n ADD_DIR: 'addDir',\n UNLINK: 'unlink',\n UNLINK_DIR: 'unlinkDir',\n RAW: 'raw',\n ERROR: 'error',\n};\nconst EV = EVENTS;\nconst THROTTLE_MODE_WATCH = 'watch';\nconst statMethods = { lstat, stat };\nconst KEY_LISTENERS = 'listeners';\nconst KEY_ERR = 'errHandlers';\nconst KEY_RAW = 'rawEmitters';\nconst HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];\n// prettier-ignore\nconst binaryExtensions = new Set([\n '3dm', '3ds', '3g2', '3gp', '7z', 'a', 'aac', 'adp', 'afdesign', 'afphoto', 'afpub', 'ai',\n 'aif', 'aiff', 'alz', 'ape', 'apk', 'appimage', 'ar', 'arj', 'asf', 'au', 'avi',\n 'bak', 'baml', 'bh', 'bin', 'bk', 'bmp', 'btif', 'bz2', 'bzip2',\n 'cab', 'caf', 'cgm', 'class', 'cmx', 'cpio', 'cr2', 'cur', 'dat', 'dcm', 'deb', 'dex', 'djvu',\n 'dll', 'dmg', 'dng', 'doc', 'docm', 'docx', 'dot', 'dotm', 'dra', 'DS_Store', 'dsk', 'dts',\n 'dtshd', 'dvb', 'dwg', 'dxf',\n 'ecelp4800', 'ecelp7470', 'ecelp9600', 'egg', 'eol', 'eot', 'epub', 'exe',\n 'f4v', 'fbs', 'fh', 'fla', 'flac', 'flatpak', 'fli', 'flv', 'fpx', 'fst', 'fvt',\n 'g3', 'gh', 'gif', 'graffle', 'gz', 'gzip',\n 'h261', 'h263', 'h264', 'icns', 'ico', 'ief', 'img', 'ipa', 'iso',\n 'jar', 'jpeg', 'jpg', 'jpgv', 'jpm', 'jxr', 'key', 'ktx',\n 'lha', 'lib', 'lvp', 'lz', 'lzh', 'lzma', 'lzo',\n 'm3u', 'm4a', 'm4v', 'mar', 'mdi', 'mht', 'mid', 'midi', 'mj2', 'mka', 'mkv', 'mmr', 'mng',\n 'mobi', 'mov', 'movie', 'mp3',\n 'mp4', 'mp4a', 'mpeg', 'mpg', 'mpga', 'mxu',\n 'nef', 'npx', 'numbers', 'nupkg',\n 'o', 'odp', 'ods', 'odt', 'oga', 'ogg', 'ogv', 'otf', 'ott',\n 'pages', 'pbm', 'pcx', 'pdb', 'pdf', 'pea', 'pgm', 'pic', 'png', 'pnm', 'pot', 'potm',\n 'potx', 'ppa', 'ppam',\n 'ppm', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'psd', 'pya', 'pyc', 'pyo', 'pyv',\n 'qt',\n 'rar', 'ras', 'raw', 'resources', 'rgb', 'rip', 'rlc', 'rmf', 'rmvb', 'rpm', 'rtf', 'rz',\n 's3m', 's7z', 'scpt', 'sgi', 'shar', 'snap', 'sil', 'sketch', 'slk', 'smv', 'snk', 'so',\n 'stl', 'suo', 'sub', 'swf',\n 'tar', 'tbz', 'tbz2', 'tga', 'tgz', 'thmx', 'tif', 'tiff', 'tlz', 'ttc', 'ttf', 'txz',\n 'udf', 'uvh', 'uvi', 'uvm', 'uvp', 'uvs', 'uvu',\n 'viv', 'vob',\n 'war', 'wav', 'wax', 'wbmp', 'wdp', 'weba', 'webm', 'webp', 'whl', 'wim', 'wm', 'wma',\n 'wmv', 'wmx', 'woff', 'woff2', 'wrm', 'wvx',\n 'xbm', 'xif', 'xla', 'xlam', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', 'xm',\n 'xmind', 'xpi', 'xpm', 'xwd', 'xz',\n 'z', 'zip', 'zipx',\n]);\nconst isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());\n// TODO: emit errors properly. Example: EMFILE on Macos.\nconst foreach = (val, fn) => {\n if (val instanceof Set) {\n val.forEach(fn);\n }\n else {\n fn(val);\n }\n};\nconst addAndConvert = (main, prop, item) => {\n let container = main[prop];\n if (!(container instanceof Set)) {\n main[prop] = container = new Set([container]);\n }\n container.add(item);\n};\nconst clearItem = (cont) => (key) => {\n const set = cont[key];\n if (set instanceof Set) {\n set.clear();\n }\n else {\n delete cont[key];\n }\n};\nconst delFromSet = (main, prop, item) => {\n const container = main[prop];\n if (container instanceof Set) {\n container.delete(item);\n }\n else if (container === item) {\n delete main[prop];\n }\n};\nconst isEmptySet = (val) => (val instanceof Set ? val.size === 0 : !val);\nconst FsWatchInstances = new Map();\n/**\n * Instantiates the fs_watch interface\n * @param path to be watched\n * @param options to be passed to fs_watch\n * @param listener main event handler\n * @param errHandler emits info about errors\n * @param emitRaw emits raw event data\n * @returns {NativeFsWatcher}\n */\nfunction createFsWatchInstance(path, options, listener, errHandler, emitRaw) {\n const handleEvent = (rawEvent, evPath) => {\n listener(path);\n emitRaw(rawEvent, evPath, { watchedPath: path });\n // emit based on events occurring for files from a directory's watcher in\n // case the file's watcher misses it (and rely on throttling to de-dupe)\n if (evPath && path !== evPath) {\n fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));\n }\n };\n try {\n return fs_watch(path, {\n persistent: options.persistent,\n }, handleEvent);\n }\n catch (error) {\n errHandler(error);\n return undefined;\n }\n}\n/**\n * Helper for passing fs_watch event data to a collection of listeners\n * @param fullPath absolute path bound to fs_watch instance\n */\nconst fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {\n const cont = FsWatchInstances.get(fullPath);\n if (!cont)\n return;\n foreach(cont[listenerType], (listener) => {\n listener(val1, val2, val3);\n });\n};\n/**\n * Instantiates the fs_watch interface or binds listeners\n * to an existing one covering the same file system entry\n * @param path\n * @param fullPath absolute path\n * @param options to be passed to fs_watch\n * @param handlers container for event listener functions\n */\nconst setFsWatchListener = (path, fullPath, options, handlers) => {\n const { listener, errHandler, rawEmitter } = handlers;\n let cont = FsWatchInstances.get(fullPath);\n let watcher;\n if (!options.persistent) {\n watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter);\n if (!watcher)\n return;\n return watcher.close.bind(watcher);\n }\n if (cont) {\n addAndConvert(cont, KEY_LISTENERS, listener);\n addAndConvert(cont, KEY_ERR, errHandler);\n addAndConvert(cont, KEY_RAW, rawEmitter);\n }\n else {\n watcher = createFsWatchInstance(path, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, // no need to use broadcast here\n fsWatchBroadcast.bind(null, fullPath, KEY_RAW));\n if (!watcher)\n return;\n watcher.on(EV.ERROR, async (error) => {\n const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);\n if (cont)\n cont.watcherUnusable = true; // documented since Node 10.4.1\n // Workaround for https://github.com/joyent/node/issues/4337\n if (isWindows && error.code === 'EPERM') {\n try {\n const fd = await open(path, 'r');\n await fd.close();\n broadcastErr(error);\n }\n catch (err) {\n // do nothing\n }\n }\n else {\n broadcastErr(error);\n }\n });\n cont = {\n listeners: listener,\n errHandlers: errHandler,\n rawEmitters: rawEmitter,\n watcher,\n };\n FsWatchInstances.set(fullPath, cont);\n }\n // const index = cont.listeners.indexOf(listener);\n // removes this instance's listeners and closes the underlying fs_watch\n // instance if there are no more listeners left\n return () => {\n delFromSet(cont, KEY_LISTENERS, listener);\n delFromSet(cont, KEY_ERR, errHandler);\n delFromSet(cont, KEY_RAW, rawEmitter);\n if (isEmptySet(cont.listeners)) {\n // Check to protect against issue gh-730.\n // if (cont.watcherUnusable) {\n cont.watcher.close();\n // }\n FsWatchInstances.delete(fullPath);\n HANDLER_KEYS.forEach(clearItem(cont));\n // @ts-ignore\n cont.watcher = undefined;\n Object.freeze(cont);\n }\n };\n};\n// fs_watchFile helpers\n// object to hold per-process fs_watchFile instances\n// (may be shared across chokidar FSWatcher instances)\nconst FsWatchFileInstances = new Map();\n/**\n * Instantiates the fs_watchFile interface or binds listeners\n * to an existing one covering the same file system entry\n * @param path to be watched\n * @param fullPath absolute path\n * @param options options to be passed to fs_watchFile\n * @param handlers container for event listener functions\n * @returns closer\n */\nconst setFsWatchFileListener = (path, fullPath, options, handlers) => {\n const { listener, rawEmitter } = handlers;\n let cont = FsWatchFileInstances.get(fullPath);\n // let listeners = new Set();\n // let rawEmitters = new Set();\n const copts = cont && cont.options;\n if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {\n // \"Upgrade\" the watcher to persistence or a quicker interval.\n // This creates some unlikely edge case issues if the user mixes\n // settings in a very weird way, but solving for those cases\n // doesn't seem worthwhile for the added complexity.\n // listeners = cont.listeners;\n // rawEmitters = cont.rawEmitters;\n unwatchFile(fullPath);\n cont = undefined;\n }\n if (cont) {\n addAndConvert(cont, KEY_LISTENERS, listener);\n addAndConvert(cont, KEY_RAW, rawEmitter);\n }\n else {\n // TODO\n // listeners.add(listener);\n // rawEmitters.add(rawEmitter);\n cont = {\n listeners: listener,\n rawEmitters: rawEmitter,\n options,\n watcher: watchFile(fullPath, options, (curr, prev) => {\n foreach(cont.rawEmitters, (rawEmitter) => {\n rawEmitter(EV.CHANGE, fullPath, { curr, prev });\n });\n const currmtime = curr.mtimeMs;\n if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {\n foreach(cont.listeners, (listener) => listener(path, curr));\n }\n }),\n };\n FsWatchFileInstances.set(fullPath, cont);\n }\n // const index = cont.listeners.indexOf(listener);\n // Removes this instance's listeners and closes the underlying fs_watchFile\n // instance if there are no more listeners left.\n return () => {\n delFromSet(cont, KEY_LISTENERS, listener);\n delFromSet(cont, KEY_RAW, rawEmitter);\n if (isEmptySet(cont.listeners)) {\n FsWatchFileInstances.delete(fullPath);\n unwatchFile(fullPath);\n cont.options = cont.watcher = undefined;\n Object.freeze(cont);\n }\n };\n};\n/**\n * @mixin\n */\nexport class NodeFsHandler {\n constructor(fsW) {\n this.fsw = fsW;\n this._boundHandleError = (error) => fsW._handleError(error);\n }\n /**\n * Watch file for changes with fs_watchFile or fs_watch.\n * @param path to file or dir\n * @param listener on fs change\n * @returns closer for the watcher instance\n */\n _watchWithNodeFs(path, listener) {\n const opts = this.fsw.options;\n const directory = sysPath.dirname(path);\n const basename = sysPath.basename(path);\n const parent = this.fsw._getWatchedDir(directory);\n parent.add(basename);\n const absolutePath = sysPath.resolve(path);\n const options = {\n persistent: opts.persistent,\n };\n if (!listener)\n listener = EMPTY_FN;\n let closer;\n if (opts.usePolling) {\n const enableBin = opts.interval !== opts.binaryInterval;\n options.interval = enableBin && isBinaryPath(basename) ? opts.binaryInterval : opts.interval;\n closer = setFsWatchFileListener(path, absolutePath, options, {\n listener,\n rawEmitter: this.fsw._emitRaw,\n });\n }\n else {\n closer = setFsWatchListener(path, absolutePath, options, {\n listener,\n errHandler: this._boundHandleError,\n rawEmitter: this.fsw._emitRaw,\n });\n }\n return closer;\n }\n /**\n * Watch a file and emit add event if warranted.\n * @returns closer for the watcher instance\n */\n _handleFile(file, stats, initialAdd) {\n if (this.fsw.closed) {\n return;\n }\n const dirname = sysPath.dirname(file);\n const basename = sysPath.basename(file);\n const parent = this.fsw._getWatchedDir(dirname);\n // stats is always present\n let prevStats = stats;\n // if the file is already being watched, do nothing\n if (parent.has(basename))\n return;\n const listener = async (path, newStats) => {\n if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))\n return;\n if (!newStats || newStats.mtimeMs === 0) {\n try {\n const newStats = await stat(file);\n if (this.fsw.closed)\n return;\n // Check that change event was not fired because of changed only accessTime.\n const at = newStats.atimeMs;\n const mt = newStats.mtimeMs;\n if (!at || at <= mt || mt !== prevStats.mtimeMs) {\n this.fsw._emit(EV.CHANGE, file, newStats);\n }\n if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats.ino) {\n this.fsw._closeFile(path);\n prevStats = newStats;\n const closer = this._watchWithNodeFs(file, listener);\n if (closer)\n this.fsw._addPathCloser(path, closer);\n }\n else {\n prevStats = newStats;\n }\n }\n catch (error) {\n // Fix issues where mtime is null but file is still present\n this.fsw._remove(dirname, basename);\n }\n // add is about to be emitted if file not already tracked in parent\n }\n else if (parent.has(basename)) {\n // Check that change event was not fired because of changed only accessTime.\n const at = newStats.atimeMs;\n const mt = newStats.mtimeMs;\n if (!at || at <= mt || mt !== prevStats.mtimeMs) {\n this.fsw._emit(EV.CHANGE, file, newStats);\n }\n prevStats = newStats;\n }\n };\n // kick off the watcher\n const closer = this._watchWithNodeFs(file, listener);\n // emit an add event if we're supposed to\n if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {\n if (!this.fsw._throttle(EV.ADD, file, 0))\n return;\n this.fsw._emit(EV.ADD, file, stats);\n }\n return closer;\n }\n /**\n * Handle symlinks encountered while reading a dir.\n * @param entry returned by readdirp\n * @param directory path of dir being read\n * @param path of this item\n * @param item basename of this item\n * @returns true if no more processing is needed for this entry.\n */\n async _handleSymlink(entry, directory, path, item) {\n if (this.fsw.closed) {\n return;\n }\n const full = entry.fullPath;\n const dir = this.fsw._getWatchedDir(directory);\n if (!this.fsw.options.followSymlinks) {\n // watch symlink directly (don't follow) and detect changes\n this.fsw._incrReadyCount();\n let linkPath;\n try {\n linkPath = await fsrealpath(path);\n }\n catch (e) {\n this.fsw._emitReady();\n return true;\n }\n if (this.fsw.closed)\n return;\n if (dir.has(item)) {\n if (this.fsw._symlinkPaths.get(full) !== linkPath) {\n this.fsw._symlinkPaths.set(full, linkPath);\n this.fsw._emit(EV.CHANGE, path, entry.stats);\n }\n }\n else {\n dir.add(item);\n this.fsw._symlinkPaths.set(full, linkPath);\n this.fsw._emit(EV.ADD, path, entry.stats);\n }\n this.fsw._emitReady();\n return true;\n }\n // don't follow the same symlink more than once\n if (this.fsw._symlinkPaths.has(full)) {\n return true;\n }\n this.fsw._symlinkPaths.set(full, true);\n }\n _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {\n // Normalize the directory name on Windows\n directory = sysPath.join(directory, '');\n throttler = this.fsw._throttle('readdir', directory, 1000);\n if (!throttler)\n return;\n const previous = this.fsw._getWatchedDir(wh.path);\n const current = new Set();\n let stream = this.fsw._readdirp(directory, {\n fileFilter: (entry) => wh.filterPath(entry),\n directoryFilter: (entry) => wh.filterDir(entry),\n });\n if (!stream)\n return;\n stream\n .on(STR_DATA, async (entry) => {\n if (this.fsw.closed) {\n stream = undefined;\n return;\n }\n const item = entry.path;\n let path = sysPath.join(directory, item);\n current.add(item);\n if (entry.stats.isSymbolicLink() &&\n (await this._handleSymlink(entry, directory, path, item))) {\n return;\n }\n if (this.fsw.closed) {\n stream = undefined;\n return;\n }\n // Files that present in current directory snapshot\n // but absent in previous are added to watch list and\n // emit `add` event.\n if (item === target || (!target && !previous.has(item))) {\n this.fsw._incrReadyCount();\n // ensure relativeness of path is preserved in case of watcher reuse\n path = sysPath.join(dir, sysPath.relative(dir, path));\n this._addToNodeFs(path, initialAdd, wh, depth + 1);\n }\n })\n .on(EV.ERROR, this._boundHandleError);\n return new Promise((resolve, reject) => {\n if (!stream)\n return reject();\n stream.once(STR_END, () => {\n if (this.fsw.closed) {\n stream = undefined;\n return;\n }\n const wasThrottled = throttler ? throttler.clear() : false;\n resolve(undefined);\n // Files that absent in current directory snapshot\n // but present in previous emit `remove` event\n // and are removed from @watched[directory].\n previous\n .getChildren()\n .filter((item) => {\n return item !== directory && !current.has(item);\n })\n .forEach((item) => {\n this.fsw._remove(directory, item);\n });\n stream = undefined;\n // one more time for any missed in case changes came in extremely quickly\n if (wasThrottled)\n this._handleRead(directory, false, wh, target, dir, depth, throttler);\n });\n });\n }\n /**\n * Read directory to add / remove files from `@watched` list and re-read it on change.\n * @param dir fs path\n * @param stats\n * @param initialAdd\n * @param depth relative to user-supplied path\n * @param target child path targeted for watch\n * @param wh Common watch helpers for this path\n * @param realpath\n * @returns closer for the watcher instance.\n */\n async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {\n const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));\n const tracked = parentDir.has(sysPath.basename(dir));\n if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {\n this.fsw._emit(EV.ADD_DIR, dir, stats);\n }\n // ensure dir is tracked (harmless if redundant)\n parentDir.add(sysPath.basename(dir));\n this.fsw._getWatchedDir(dir);\n let throttler;\n let closer;\n const oDepth = this.fsw.options.depth;\n if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {\n if (!target) {\n await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);\n if (this.fsw.closed)\n return;\n }\n closer = this._watchWithNodeFs(dir, (dirPath, stats) => {\n // if current directory is removed, do nothing\n if (stats && stats.mtimeMs === 0)\n return;\n this._handleRead(dirPath, false, wh, target, dir, depth, throttler);\n });\n }\n return closer;\n }\n /**\n * Handle added file, directory, or glob pattern.\n * Delegates call to _handleFile / _handleDir after checks.\n * @param path to file or ir\n * @param initialAdd was the file added at watch instantiation?\n * @param priorWh depth relative to user-supplied path\n * @param depth Child path actually targeted for watch\n * @param target Child path actually targeted for watch\n */\n async _addToNodeFs(path, initialAdd, priorWh, depth, target) {\n const ready = this.fsw._emitReady;\n if (this.fsw._isIgnored(path) || this.fsw.closed) {\n ready();\n return false;\n }\n const wh = this.fsw._getWatchHelpers(path);\n if (priorWh) {\n wh.filterPath = (entry) => priorWh.filterPath(entry);\n wh.filterDir = (entry) => priorWh.filterDir(entry);\n }\n // evaluate what is at the path we're being asked to watch\n try {\n const stats = await statMethods[wh.statMethod](wh.watchPath);\n if (this.fsw.closed)\n return;\n if (this.fsw._isIgnored(wh.watchPath, stats)) {\n ready();\n return false;\n }\n const follow = this.fsw.options.followSymlinks;\n let closer;\n if (stats.isDirectory()) {\n const absPath = sysPath.resolve(path);\n const targetPath = follow ? await fsrealpath(path) : path;\n if (this.fsw.closed)\n return;\n closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);\n if (this.fsw.closed)\n return;\n // preserve this symlink's target path\n if (absPath !== targetPath && targetPath !== undefined) {\n this.fsw._symlinkPaths.set(absPath, targetPath);\n }\n }\n else if (stats.isSymbolicLink()) {\n const targetPath = follow ? await fsrealpath(path) : path;\n if (this.fsw.closed)\n return;\n const parent = sysPath.dirname(wh.watchPath);\n this.fsw._getWatchedDir(parent).add(wh.watchPath);\n this.fsw._emit(EV.ADD, wh.watchPath, stats);\n closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);\n if (this.fsw.closed)\n return;\n // preserve this symlink's target path\n if (targetPath !== undefined) {\n this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);\n }\n }\n else {\n closer = this._handleFile(wh.watchPath, stats, initialAdd);\n }\n ready();\n if (closer)\n this.fsw._addPathCloser(path, closer);\n return false;\n }\n catch (error) {\n if (this.fsw._handleError(error)) {\n ready();\n return path;\n }\n }\n }\n}\n","/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */\nimport { stat as statcb } from 'fs';\nimport { stat, readdir } from 'fs/promises';\nimport { EventEmitter } from 'events';\nimport * as sysPath from 'path';\nimport { readdirp } from 'readdirp';\nimport { NodeFsHandler, EVENTS as EV, isWindows, isIBMi, EMPTY_FN, STR_CLOSE, STR_END, } from './handler.js';\nconst SLASH = '/';\nconst SLASH_SLASH = '//';\nconst ONE_DOT = '.';\nconst TWO_DOTS = '..';\nconst STRING_TYPE = 'string';\nconst BACK_SLASH_RE = /\\\\/g;\nconst DOUBLE_SLASH_RE = /\\/\\//;\nconst DOT_RE = /\\..*\\.(sw[px])$|~$|\\.subl.*\\.tmp/;\nconst REPLACER_RE = /^\\.[/\\\\]/;\nfunction arrify(item) {\n return Array.isArray(item) ? item : [item];\n}\nconst isMatcherObject = (matcher) => typeof matcher === 'object' && matcher !== null && !(matcher instanceof RegExp);\nfunction createPattern(matcher) {\n if (typeof matcher === 'function')\n return matcher;\n if (typeof matcher === 'string')\n return (string) => matcher === string;\n if (matcher instanceof RegExp)\n return (string) => matcher.test(string);\n if (typeof matcher === 'object' && matcher !== null) {\n return (string) => {\n if (matcher.path === string)\n return true;\n if (matcher.recursive) {\n const relative = sysPath.relative(matcher.path, string);\n if (!relative) {\n return false;\n }\n return !relative.startsWith('..') && !sysPath.isAbsolute(relative);\n }\n return false;\n };\n }\n return () => false;\n}\nfunction normalizePath(path) {\n if (typeof path !== 'string')\n throw new Error('string expected');\n path = sysPath.normalize(path);\n path = path.replace(/\\\\/g, '/');\n let prepend = false;\n if (path.startsWith('//'))\n prepend = true;\n const DOUBLE_SLASH_RE = /\\/\\//;\n while (path.match(DOUBLE_SLASH_RE))\n path = path.replace(DOUBLE_SLASH_RE, '/');\n if (prepend)\n path = '/' + path;\n return path;\n}\nfunction matchPatterns(patterns, testString, stats) {\n const path = normalizePath(testString);\n for (let index = 0; index < patterns.length; index++) {\n const pattern = patterns[index];\n if (pattern(path, stats)) {\n return true;\n }\n }\n return false;\n}\nfunction anymatch(matchers, testString) {\n if (matchers == null) {\n throw new TypeError('anymatch: specify first argument');\n }\n // Early cache for matchers.\n const matchersArray = arrify(matchers);\n const patterns = matchersArray.map((matcher) => createPattern(matcher));\n if (testString == null) {\n return (testString, stats) => {\n return matchPatterns(patterns, testString, stats);\n };\n }\n return matchPatterns(patterns, testString);\n}\nconst unifyPaths = (paths_) => {\n const paths = arrify(paths_).flat();\n if (!paths.every((p) => typeof p === STRING_TYPE)) {\n throw new TypeError(`Non-string provided as watch path: ${paths}`);\n }\n return paths.map(normalizePathToUnix);\n};\n// If SLASH_SLASH occurs at the beginning of path, it is not replaced\n// because \"//StoragePC/DrivePool/Movies\" is a valid network path\nconst toUnix = (string) => {\n let str = string.replace(BACK_SLASH_RE, SLASH);\n let prepend = false;\n if (str.startsWith(SLASH_SLASH)) {\n prepend = true;\n }\n while (str.match(DOUBLE_SLASH_RE)) {\n str = str.replace(DOUBLE_SLASH_RE, SLASH);\n }\n if (prepend) {\n str = SLASH + str;\n }\n return str;\n};\n// Our version of upath.normalize\n// TODO: this is not equal to path-normalize module - investigate why\nconst normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));\n// TODO: refactor\nconst normalizeIgnored = (cwd = '') => (path) => {\n if (typeof path === 'string') {\n return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));\n }\n else {\n return path;\n }\n};\nconst getAbsolutePath = (path, cwd) => {\n if (sysPath.isAbsolute(path)) {\n return path;\n }\n return sysPath.join(cwd, path);\n};\nconst EMPTY_SET = Object.freeze(new Set());\n/**\n * Directory entry.\n */\nclass DirEntry {\n constructor(dir, removeWatcher) {\n this.path = dir;\n this._removeWatcher = removeWatcher;\n this.items = new Set();\n }\n add(item) {\n const { items } = this;\n if (!items)\n return;\n if (item !== ONE_DOT && item !== TWO_DOTS)\n items.add(item);\n }\n async remove(item) {\n const { items } = this;\n if (!items)\n return;\n items.delete(item);\n if (items.size > 0)\n return;\n const dir = this.path;\n try {\n await readdir(dir);\n }\n catch (err) {\n if (this._removeWatcher) {\n this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));\n }\n }\n }\n has(item) {\n const { items } = this;\n if (!items)\n return;\n return items.has(item);\n }\n getChildren() {\n const { items } = this;\n if (!items)\n return [];\n return [...items.values()];\n }\n dispose() {\n this.items.clear();\n this.path = '';\n this._removeWatcher = EMPTY_FN;\n this.items = EMPTY_SET;\n Object.freeze(this);\n }\n}\nconst STAT_METHOD_F = 'stat';\nconst STAT_METHOD_L = 'lstat';\nexport class WatchHelper {\n constructor(path, follow, fsw) {\n this.fsw = fsw;\n const watchPath = path;\n this.path = path = path.replace(REPLACER_RE, '');\n this.watchPath = watchPath;\n this.fullWatchPath = sysPath.resolve(watchPath);\n this.dirParts = [];\n this.dirParts.forEach((parts) => {\n if (parts.length > 1)\n parts.pop();\n });\n this.followSymlinks = follow;\n this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;\n }\n entryPath(entry) {\n return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));\n }\n filterPath(entry) {\n const { stats } = entry;\n if (stats && stats.isSymbolicLink())\n return this.filterDir(entry);\n const resolvedPath = this.entryPath(entry);\n // TODO: what if stats is undefined? remove !\n return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);\n }\n filterDir(entry) {\n return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);\n }\n}\n/**\n * Watches files & directories for changes. Emitted events:\n * `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`\n *\n * new FSWatcher()\n * .add(directories)\n * .on('add', path => log('File', path, 'was added'))\n */\nexport class FSWatcher extends EventEmitter {\n // Not indenting methods for history sake; for now.\n constructor(_opts = {}) {\n super();\n this.closed = false;\n this._closers = new Map();\n this._ignoredPaths = new Set();\n this._throttled = new Map();\n this._streams = new Set();\n this._symlinkPaths = new Map();\n this._watched = new Map();\n this._pendingWrites = new Map();\n this._pendingUnlinks = new Map();\n this._readyCount = 0;\n this._readyEmitted = false;\n const awf = _opts.awaitWriteFinish;\n const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };\n const opts = {\n // Defaults\n persistent: true,\n ignoreInitial: false,\n ignorePermissionErrors: false,\n interval: 100,\n binaryInterval: 300,\n followSymlinks: true,\n usePolling: false,\n // useAsync: false,\n atomic: true, // NOTE: overwritten later (depends on usePolling)\n ..._opts,\n // Change format\n ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),\n awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === 'object' ? { ...DEF_AWF, ...awf } : false,\n };\n // Always default to polling on IBM i because fs.watch() is not available on IBM i.\n if (isIBMi)\n opts.usePolling = true;\n // Editor atomic write normalization enabled by default with fs.watch\n if (opts.atomic === undefined)\n opts.atomic = !opts.usePolling;\n // opts.atomic = typeof _opts.atomic === 'number' ? _opts.atomic : 100;\n // Global override. Useful for developers, who need to force polling for all\n // instances of chokidar, regardless of usage / dependency depth\n const envPoll = process.env.CHOKIDAR_USEPOLLING;\n if (envPoll !== undefined) {\n const envLower = envPoll.toLowerCase();\n if (envLower === 'false' || envLower === '0')\n opts.usePolling = false;\n else if (envLower === 'true' || envLower === '1')\n opts.usePolling = true;\n else\n opts.usePolling = !!envLower;\n }\n const envInterval = process.env.CHOKIDAR_INTERVAL;\n if (envInterval)\n opts.interval = Number.parseInt(envInterval, 10);\n // This is done to emit ready only once, but each 'add' will increase that?\n let readyCalls = 0;\n this._emitReady = () => {\n readyCalls++;\n if (readyCalls >= this._readyCount) {\n this._emitReady = EMPTY_FN;\n this._readyEmitted = true;\n // use process.nextTick to allow time for listener to be bound\n process.nextTick(() => this.emit(EV.READY));\n }\n };\n this._emitRaw = (...args) => this.emit(EV.RAW, ...args);\n this._boundRemove = this._remove.bind(this);\n this.options = opts;\n this._nodeFsHandler = new NodeFsHandler(this);\n // You’re frozen when your heart’s not open.\n Object.freeze(opts);\n }\n _addIgnoredPath(matcher) {\n if (isMatcherObject(matcher)) {\n // return early if we already have a deeply equal matcher object\n for (const ignored of this._ignoredPaths) {\n if (isMatcherObject(ignored) &&\n ignored.path === matcher.path &&\n ignored.recursive === matcher.recursive) {\n return;\n }\n }\n }\n this._ignoredPaths.add(matcher);\n }\n _removeIgnoredPath(matcher) {\n this._ignoredPaths.delete(matcher);\n // now find any matcher objects with the matcher as path\n if (typeof matcher === 'string') {\n for (const ignored of this._ignoredPaths) {\n // TODO (43081j): make this more efficient.\n // probably just make a `this._ignoredDirectories` or some\n // such thing.\n if (isMatcherObject(ignored) && ignored.path === matcher) {\n this._ignoredPaths.delete(ignored);\n }\n }\n }\n }\n // Public methods\n /**\n * Adds paths to be watched on an existing FSWatcher instance.\n * @param paths_ file or file list. Other arguments are unused\n */\n add(paths_, _origAdd, _internal) {\n const { cwd } = this.options;\n this.closed = false;\n this._closePromise = undefined;\n let paths = unifyPaths(paths_);\n if (cwd) {\n paths = paths.map((path) => {\n const absPath = getAbsolutePath(path, cwd);\n // Check `path` instead of `absPath` because the cwd portion can't be a glob\n return absPath;\n });\n }\n paths.forEach((path) => {\n this._removeIgnoredPath(path);\n });\n this._userIgnored = undefined;\n if (!this._readyCount)\n this._readyCount = 0;\n this._readyCount += paths.length;\n Promise.all(paths.map(async (path) => {\n const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, undefined, 0, _origAdd);\n if (res)\n this._emitReady();\n return res;\n })).then((results) => {\n if (this.closed)\n return;\n results.forEach((item) => {\n if (item)\n this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));\n });\n });\n return this;\n }\n /**\n * Close watchers or start ignoring events from specified paths.\n */\n unwatch(paths_) {\n if (this.closed)\n return this;\n const paths = unifyPaths(paths_);\n const { cwd } = this.options;\n paths.forEach((path) => {\n // convert to absolute path unless relative path already matches\n if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {\n if (cwd)\n path = sysPath.join(cwd, path);\n path = sysPath.resolve(path);\n }\n this._closePath(path);\n this._addIgnoredPath(path);\n if (this._watched.has(path)) {\n this._addIgnoredPath({\n path,\n recursive: true,\n });\n }\n // reset the cached userIgnored anymatch fn\n // to make ignoredPaths changes effective\n this._userIgnored = undefined;\n });\n return this;\n }\n /**\n * Close watchers and remove all listeners from watched paths.\n */\n close() {\n if (this._closePromise) {\n return this._closePromise;\n }\n this.closed = true;\n // Memory management.\n this.removeAllListeners();\n const closers = [];\n this._closers.forEach((closerList) => closerList.forEach((closer) => {\n const promise = closer();\n if (promise instanceof Promise)\n closers.push(promise);\n }));\n this._streams.forEach((stream) => stream.destroy());\n this._userIgnored = undefined;\n this._readyCount = 0;\n this._readyEmitted = false;\n this._watched.forEach((dirent) => dirent.dispose());\n this._closers.clear();\n this._watched.clear();\n this._streams.clear();\n this._symlinkPaths.clear();\n this._throttled.clear();\n this._closePromise = closers.length\n ? Promise.all(closers).then(() => undefined)\n : Promise.resolve();\n return this._closePromise;\n }\n /**\n * Expose list of watched paths\n * @returns for chaining\n */\n getWatched() {\n const watchList = {};\n this._watched.forEach((entry, dir) => {\n const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;\n const index = key || ONE_DOT;\n watchList[index] = entry.getChildren().sort();\n });\n return watchList;\n }\n emitWithAll(event, args) {\n this.emit(event, ...args);\n if (event !== EV.ERROR)\n this.emit(EV.ALL, event, ...args);\n }\n // Common helpers\n // --------------\n /**\n * Normalize and emit events.\n * Calling _emit DOES NOT MEAN emit() would be called!\n * @param event Type of event\n * @param path File or directory path\n * @param stats arguments to be passed with event\n * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag\n */\n async _emit(event, path, stats) {\n if (this.closed)\n return;\n const opts = this.options;\n if (isWindows)\n path = sysPath.normalize(path);\n if (opts.cwd)\n path = sysPath.relative(opts.cwd, path);\n const args = [path];\n if (stats != null)\n args.push(stats);\n const awf = opts.awaitWriteFinish;\n let pw;\n if (awf && (pw = this._pendingWrites.get(path))) {\n pw.lastChange = new Date();\n return this;\n }\n if (opts.atomic) {\n if (event === EV.UNLINK) {\n this._pendingUnlinks.set(path, [event, ...args]);\n setTimeout(() => {\n this._pendingUnlinks.forEach((entry, path) => {\n this.emit(...entry);\n this.emit(EV.ALL, ...entry);\n this._pendingUnlinks.delete(path);\n });\n }, typeof opts.atomic === 'number' ? opts.atomic : 100);\n return this;\n }\n if (event === EV.ADD && this._pendingUnlinks.has(path)) {\n event = EV.CHANGE;\n this._pendingUnlinks.delete(path);\n }\n }\n if (awf && (event === EV.ADD || event === EV.CHANGE) && this._readyEmitted) {\n const awfEmit = (err, stats) => {\n if (err) {\n event = EV.ERROR;\n args[0] = err;\n this.emitWithAll(event, args);\n }\n else if (stats) {\n // if stats doesn't exist the file must have been deleted\n if (args.length > 1) {\n args[1] = stats;\n }\n else {\n args.push(stats);\n }\n this.emitWithAll(event, args);\n }\n };\n this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);\n return this;\n }\n if (event === EV.CHANGE) {\n const isThrottled = !this._throttle(EV.CHANGE, path, 50);\n if (isThrottled)\n return this;\n }\n if (opts.alwaysStat &&\n stats === undefined &&\n (event === EV.ADD || event === EV.ADD_DIR || event === EV.CHANGE)) {\n const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;\n let stats;\n try {\n stats = await stat(fullPath);\n }\n catch (err) {\n // do nothing\n }\n // Suppress event when fs_stat fails, to avoid sending undefined 'stat'\n if (!stats || this.closed)\n return;\n args.push(stats);\n }\n this.emitWithAll(event, args);\n return this;\n }\n /**\n * Common handler for errors\n * @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag\n */\n _handleError(error) {\n const code = error && error.code;\n if (error &&\n code !== 'ENOENT' &&\n code !== 'ENOTDIR' &&\n (!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))) {\n this.emit(EV.ERROR, error);\n }\n return error || this.closed;\n }\n /**\n * Helper utility for throttling\n * @param actionType type being throttled\n * @param path being acted upon\n * @param timeout duration of time to suppress duplicate actions\n * @returns tracking object or false if action should be suppressed\n */\n _throttle(actionType, path, timeout) {\n if (!this._throttled.has(actionType)) {\n this._throttled.set(actionType, new Map());\n }\n const action = this._throttled.get(actionType);\n if (!action)\n throw new Error('invalid throttle');\n const actionPath = action.get(path);\n if (actionPath) {\n actionPath.count++;\n return false;\n }\n // eslint-disable-next-line prefer-const\n let timeoutObject;\n const clear = () => {\n const item = action.get(path);\n const count = item ? item.count : 0;\n action.delete(path);\n clearTimeout(timeoutObject);\n if (item)\n clearTimeout(item.timeoutObject);\n return count;\n };\n timeoutObject = setTimeout(clear, timeout);\n const thr = { timeoutObject, clear, count: 0 };\n action.set(path, thr);\n return thr;\n }\n _incrReadyCount() {\n return this._readyCount++;\n }\n /**\n * Awaits write operation to finish.\n * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.\n * @param path being acted upon\n * @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished\n * @param event\n * @param awfEmit Callback to be called when ready for event to be emitted.\n */\n _awaitWriteFinish(path, threshold, event, awfEmit) {\n const awf = this.options.awaitWriteFinish;\n if (typeof awf !== 'object')\n return;\n const pollInterval = awf.pollInterval;\n let timeoutHandler;\n let fullPath = path;\n if (this.options.cwd && !sysPath.isAbsolute(path)) {\n fullPath = sysPath.join(this.options.cwd, path);\n }\n const now = new Date();\n const writes = this._pendingWrites;\n function awaitWriteFinishFn(prevStat) {\n statcb(fullPath, (err, curStat) => {\n if (err || !writes.has(path)) {\n if (err && err.code !== 'ENOENT')\n awfEmit(err);\n return;\n }\n const now = Number(new Date());\n if (prevStat && curStat.size !== prevStat.size) {\n writes.get(path).lastChange = now;\n }\n const pw = writes.get(path);\n const df = now - pw.lastChange;\n if (df >= threshold) {\n writes.delete(path);\n awfEmit(undefined, curStat);\n }\n else {\n timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);\n }\n });\n }\n if (!writes.has(path)) {\n writes.set(path, {\n lastChange: now,\n cancelWait: () => {\n writes.delete(path);\n clearTimeout(timeoutHandler);\n return event;\n },\n });\n timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);\n }\n }\n /**\n * Determines whether user has asked to ignore this path.\n */\n _isIgnored(path, stats) {\n if (this.options.atomic && DOT_RE.test(path))\n return true;\n if (!this._userIgnored) {\n const { cwd } = this.options;\n const ign = this.options.ignored;\n const ignored = (ign || []).map(normalizeIgnored(cwd));\n const ignoredPaths = [...this._ignoredPaths];\n const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];\n this._userIgnored = anymatch(list, undefined);\n }\n return this._userIgnored(path, stats);\n }\n _isntIgnored(path, stat) {\n return !this._isIgnored(path, stat);\n }\n /**\n * Provides a set of common helpers and properties relating to symlink handling.\n * @param path file or directory pattern being watched\n */\n _getWatchHelpers(path) {\n return new WatchHelper(path, this.options.followSymlinks, this);\n }\n // Directory helpers\n // -----------------\n /**\n * Provides directory tracking objects\n * @param directory path of the directory\n */\n _getWatchedDir(directory) {\n const dir = sysPath.resolve(directory);\n if (!this._watched.has(dir))\n this._watched.set(dir, new DirEntry(dir, this._boundRemove));\n return this._watched.get(dir);\n }\n // File helpers\n // ------------\n /**\n * Check for read permissions: https://stackoverflow.com/a/11781404/1358405\n */\n _hasReadPermissions(stats) {\n if (this.options.ignorePermissionErrors)\n return true;\n return Boolean(Number(stats.mode) & 0o400);\n }\n /**\n * Handles emitting unlink events for\n * files and directories, and via recursion, for\n * files and directories within directories that are unlinked\n * @param directory within which the following item is located\n * @param item base path of item/directory\n */\n _remove(directory, item, isDirectory) {\n // if what is being deleted is a directory, get that directory's paths\n // for recursive deleting and cleaning of watched object\n // if it is not a directory, nestedDirectoryChildren will be empty array\n const path = sysPath.join(directory, item);\n const fullPath = sysPath.resolve(path);\n isDirectory =\n isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);\n // prevent duplicate handling in case of arriving here nearly simultaneously\n // via multiple paths (such as _handleFile and _handleDir)\n if (!this._throttle('remove', path, 100))\n return;\n // if the only watched file is removed, watch for its return\n if (!isDirectory && this._watched.size === 1) {\n this.add(directory, item, true);\n }\n // This will create a new entry in the watched object in either case\n // so we got to do the directory check beforehand\n const wp = this._getWatchedDir(path);\n const nestedDirectoryChildren = wp.getChildren();\n // Recursively remove children directories / files.\n nestedDirectoryChildren.forEach((nested) => this._remove(path, nested));\n // Check if item was on the watched list and remove it\n const parent = this._getWatchedDir(directory);\n const wasTracked = parent.has(item);\n parent.remove(item);\n // Fixes issue #1042 -> Relative paths were detected and added as symlinks\n // (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),\n // but never removed from the map in case the path was deleted.\n // This leads to an incorrect state if the path was recreated:\n // https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553\n if (this._symlinkPaths.has(fullPath)) {\n this._symlinkPaths.delete(fullPath);\n }\n // If we wait for this file to be fully written, cancel the wait.\n let relPath = path;\n if (this.options.cwd)\n relPath = sysPath.relative(this.options.cwd, path);\n if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {\n const event = this._pendingWrites.get(relPath).cancelWait();\n if (event === EV.ADD)\n return;\n }\n // The Entry will either be a directory that just got removed\n // or a bogus entry to a file, in either case we have to remove it\n this._watched.delete(path);\n this._watched.delete(fullPath);\n const eventName = isDirectory ? EV.UNLINK_DIR : EV.UNLINK;\n if (wasTracked && !this._isIgnored(path))\n this._emit(eventName, path);\n // Avoid conflicts if we later create another file with the same name\n this._closePath(path);\n }\n /**\n * Closes all watchers for a path\n */\n _closePath(path) {\n this._closeFile(path);\n const dir = sysPath.dirname(path);\n this._getWatchedDir(dir).remove(sysPath.basename(path));\n }\n /**\n * Closes only file-specific watchers\n */\n _closeFile(path) {\n const closers = this._closers.get(path);\n if (!closers)\n return;\n closers.forEach((closer) => closer());\n this._closers.delete(path);\n }\n _addPathCloser(path, closer) {\n if (!closer)\n return;\n let list = this._closers.get(path);\n if (!list) {\n list = [];\n this._closers.set(path, list);\n }\n list.push(closer);\n }\n _readdirp(root, opts) {\n if (this.closed)\n return;\n const options = { type: EV.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };\n let stream = readdirp(root, options);\n this._streams.add(stream);\n stream.once(STR_CLOSE, () => {\n stream = undefined;\n });\n stream.once(STR_END, () => {\n if (stream) {\n this._streams.delete(stream);\n stream = undefined;\n }\n });\n return stream;\n }\n}\n/**\n * Instantiates watcher with paths to be tracked.\n * @param paths file / directory paths\n * @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others\n * @returns an instance of FSWatcher for chaining.\n * @example\n * const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });\n * watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })\n */\nexport function watch(paths, options = {}) {\n const watcher = new FSWatcher(options);\n watcher.add(paths);\n return watcher;\n}\nexport default { watch, FSWatcher };\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport fg from 'fast-glob';\nimport { buildAliasPlugins } from './aliasPlugin';\nimport { APP_DIR } from '../utils/prodPaths';\n\n/**\n * 编译结果\n */\nexport interface CompileResult {\n /** 已编译的文件列表(绝对路径) */\n compiledFiles: string[];\n}\n\nexport interface CompileSourceFilesOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(dev 为 `.faapi`,build 为 `dist`) */\n dist: string;\n /**\n * 增量编译:传入要编译的文件列表(绝对路径)。\n * 不传则全量编译 src 下所有 .ts(排除测试文件和声明文件)。\n */\n files?: string[];\n /** 是否输出 esbuild 日志(默认静默) */\n logLevel?: 'silent' | 'info';\n /**\n * prod 语义:`define` 把 `process.env.NODE_ENV` 编译期替换为 `\"production\"` +\n * `minifySyntax` 删除死分支(两者在 `bundle: false` 下均生效,单文件级别优化)。\n */\n production?: boolean;\n /**\n * dev 语义:原子写(write: false + 临时文件 + rename),避免 watch 模式下\n * esbuild 非原子写期间运行时 import 读到半成品产物。\n */\n atomicWrite?: boolean;\n}\n\n/**\n * dev/prod 共用的逐文件 TypeScript 编译实现\n *\n * 每个 `.ts` 独立编译为 `.js`,不分析 import 关系(`bundle: false`——\n * 保证 config 与 routes 共享同一运行时对象,`instanceof` 跨边界生效)。\n * 产物**打平 src/ 前缀**:`src/api/hello/handler.ts` → `<dist>/api/hello/handler.js`。\n *\n * 别名在编译时重写为相对路径,运行时无需 loader。\n *\n * 通过 `compileDevRoutes` / `compileBuildRoutes` 调用,见 ./compileSourceFiles.md\n * 的选项矩阵。\n */\nexport async function compileSourceFiles(\n options: CompileSourceFilesOptions,\n): Promise<CompileResult> {\n const { rootDir, dist, files, logLevel = 'silent', production, atomicWrite } = options;\n\n // 收集要编译的文件:files 优先,否则全量扫描 src 下所有 .ts\n const entryPoints =\n files ??\n (await fg([`${APP_DIR}/**/*.ts`], {\n cwd: rootDir,\n onlyFiles: true,\n absolute: true,\n ignore: ['**/*.test.ts', '**/*.e2e.test.ts', '**/*.d.ts'],\n }));\n\n if (entryPoints.length === 0) {\n return { compiledFiles: [] };\n }\n\n // 确保输出目录存在\n const absDist = path.resolve(rootDir, dist);\n await fs.promises.mkdir(absDist, { recursive: true });\n\n // 构造别名重写插件(无 tsconfig/paths 时为空,相对路径重写仍生效)\n const plugins = buildAliasPlugins(rootDir);\n\n // 逐文件编译,outbase 设为 src 以打平产物结构:\n // `src/api/hello/handler.ts` → `<dist>/api/hello/handler.js`(去掉 src/ 前缀)\n const esbuild = await import('esbuild');\n const outbase = path.resolve(rootDir, APP_DIR);\n const result = await esbuild.build({\n entryPoints,\n outdir: absDist,\n outbase,\n bundle: false,\n platform: 'node',\n format: 'esm',\n sourcemap: true,\n packages: 'external',\n plugins,\n // build 语义:编译期 NODE_ENV 替换 + 死分支删除(见 AGENTS.md §5.3)\n ...(production\n ? { define: { 'process.env.NODE_ENV': '\"production\"' }, minifySyntax: true }\n : {}),\n logLevel,\n // dev 语义:esbuild 返回内存内容,由下方原子写落盘\n ...(atomicWrite ? { write: false as const } : {}),\n });\n\n // 原子写:逐个写临时文件 + rename。rename 在同一文件系统上是原子的(POSIX),\n // HTTP 请求要么看到旧文件要么看到新文件\n if (atomicWrite && result.outputFiles) {\n await Promise.all(\n result.outputFiles.map(async (file) => {\n await fs.promises.mkdir(path.dirname(file.path), { recursive: true });\n const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;\n await fs.promises.writeFile(tmp, file.contents);\n await fs.promises.rename(tmp, file.path);\n }),\n );\n }\n\n return { compiledFiles: entryPoints };\n}\n\n/**\n * 全量扫描 src 下的 .ts 文件(排除测试文件和声明文件)\n *\n * 与 compileSourceFiles 的全量分支使用同一 ignore 规则,供 watch 等场景独立扫描。\n */\nexport async function collectSourceFiles(rootDir: string): Promise<string[]> {\n return fg([`${APP_DIR}/**/*.ts`], {\n cwd: rootDir,\n onlyFiles: true,\n absolute: true,\n ignore: ['**/*.test.ts', '**/*.e2e.test.ts', '**/*.d.ts'],\n });\n}\n","import { compileSourceFiles, collectSourceFiles, type CompileResult } from './compileSourceFiles';\n\nexport { collectSourceFiles };\nexport type { CompileResult };\n\n/**\n * dev 编译选项\n */\nexport interface CompileDevOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(dev 模式为 `.faapi`) */\n dist: string;\n /**\n * 增量编译:传入要编译的文件列表(绝对路径)。\n * 不传则全量编译 src 下所有 .ts(排除测试文件和声明文件)。\n */\n files?: string[];\n /** 是否输出 esbuild 日志(dev 模式默认静默) */\n logLevel?: 'silent' | 'info';\n}\n\n/**\n * dev 模式编译 TypeScript:逐文件编译,启动快、增量编译友好\n *\n * 共享实现见 [compileSourceFiles](./compileSourceFiles.ts);dev 差异仅两点:\n * 不传 `define`(`process.env.NODE_ENV` 运行时读取环境变量,便于热替换)+\n * **原子写**(写临时文件 + rename,避免 watch 模式下运行时 import 读到半成品产物,\n * 详见 compileDevRoutes.md 的\"dev 原子写\"章节)。\n *\n * @example\n * // 全量编译\n * await compileDevRoutes({ rootDir, dist: '.faapi' });\n * // watch 增量:只编译变化的文件\n * await compileDevRoutes({ rootDir, dist: '.faapi', files: changedFiles });\n */\nexport async function compileDevRoutes(options: CompileDevOptions): Promise<CompileResult> {\n return compileSourceFiles({ ...options, atomicWrite: true });\n}\n","/**\n * watcher 重建调度器\n *\n * 收敛 watcher 的调度逻辑:debounce 合并、重入保护、失败回灌。\n * 行为定义见 ./rebuildScheduler.md。\n */\n\nexport interface RebuildScheduler {\n /** 文件变化/新增:加入待编译集合并调度重建 */\n addFiles(files: string[]): void;\n /** 仅调度重建(如 unlink,无文件可编译) */\n schedule(): void;\n}\n\nexport interface RebuildSchedulerOptions {\n /**\n * 重建回调,接收本轮待编译文件列表(可能为空数组——纯结构变化场景)\n *\n * 抛错时该轮文件回灌待编译集合(不主动重试),错误经 onError 上报。\n */\n rebuild: (files: string[]) => Promise<void>;\n /** debounce 窗口(毫秒),默认 100 */\n debounceMs?: number;\n /** 重建失败回调(如 console.error) */\n onError?: (err: unknown) => void;\n}\n\n/**\n * 创建重建调度器\n *\n * - debounce 窗口内的事件合并为一轮重建\n * - 重建进行中不重入:新事件累积文件并标记补跑,当前轮结束后串行补跑\n * - 回调抛错:文件回灌 + onError 上报,等待下一次事件自然触发(不主动重试,\n * 避免语法错误场景每 100ms 刷一次错误)\n */\nexport function createRebuildScheduler(options: RebuildSchedulerOptions): RebuildScheduler {\n const { rebuild, debounceMs = 100, onError } = options;\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let pendingFiles: string[] = [];\n let isRebuilding = false;\n let needsAnotherRun = false;\n\n async function run(): Promise<void> {\n // 重入保护:重建进行中只标记补跑,当前轮结束时串行补跑\n if (isRebuilding) {\n needsAnotherRun = true;\n return;\n }\n isRebuilding = true;\n try {\n // 串行补跑循环:每轮取走当前累积的文件,期间新事件再标记补跑\n do {\n needsAnotherRun = false;\n const files = pendingFiles;\n pendingFiles = [];\n try {\n await rebuild(files);\n } catch (err) {\n // 失败回灌:合并回待编译集合,等下次文件事件一起编译(不主动重试)\n pendingFiles = [...files, ...pendingFiles];\n onError?.(err);\n }\n } while (needsAnotherRun);\n } finally {\n isRebuilding = false;\n }\n }\n\n function scheduleDebounce(): void {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n void run();\n }, debounceMs);\n }\n\n function requestRun(): void {\n // 重入保护:重建进行中只标记补跑(不等 debounce——重建耗时已天然\n // 合并连续事件),当前轮 while 循环结束时立即串行补跑\n if (isRebuilding) {\n needsAnotherRun = true;\n return;\n }\n scheduleDebounce();\n }\n\n return {\n addFiles(files) {\n // 去重:编辑器保存常连续触发多次 change,同一文件在同一轮里只编译一次\n for (const file of files) {\n if (!pendingFiles.includes(file)) pendingFiles.push(file);\n }\n requestRun();\n },\n schedule() {\n requestRun();\n },\n };\n}\n","import chokidar from 'chokidar';\nimport path from 'node:path';\nimport { compileDevRoutes } from './compileDevRoutes';\nimport { compileConfig } from './compileConfig';\nimport type { DevApp } from './createDevApp';\nimport { createRebuildScheduler } from './rebuildScheduler';\n\nexport interface WatchOptions {\n /** 项目根目录 */\n rootDir: string;\n /** dev 应用实例(调用 app.reloadRoutes 热替换) */\n app: DevApp;\n /** dev 产物目录(如 .faapi),用于增量编译与配置重生成 */\n devDist: string;\n}\n\n/**\n * 启动文件 watcher(仅 dev 模式)\n *\n * 监听源码 `.ts` 变化,增量编译 + 重生成 config/schema 产物 + 调 `app.reloadRoutes()` 热替换。\n *\n * 与 `app.reloadRoutes()` 的分工:\n * - watcher:增量编译变化文件 + 重生成 `faapi-config.js`(compileConfig 内部有 mtime 短路,无变化时跳过编译)\n * - `reloadRoutes()`:重新扫描路由 + 重新生成 schema + 清缓存 + 更新 server 路由引用\n *\n * 注意:`faapi-routes.js` 不在 watcher 中重生成——reloadRoutes 直接调 scanRoutes 重新扫描,\n * 不依赖重新 import faapi-routes.js(ESM 模块缓存难以可靠绕过)。\n *\n * chokidar v4 移除了 glob 模式支持,改为监听整个 `src` 目录 + `ignored` 函数过滤。\n * 监听整个 src 比 glob 更合理:handler.ts 引用的 util.ts 变化也能触发重建。\n *\n * 重建调度(createRebuildScheduler,debounce 100ms):\n * 1. 增量编译变化的文件(add/change 事件累积的文件)\n * 2. 重生成 `faapi-config.js`(compileConfig 内部 mtime 短路:源文件无变化时跳过编译)\n * 3. 调 `app.reloadRoutes()`(scanRoutes + generateSchemaFiles + 更新引用)\n *\n * 重建进行中不重入:新事件只累积文件,当前轮结束后自动串行补跑(见 rebuildScheduler.md)。\n * 重建失败时待编译文件回灌,等待下次文件事件一起重编译(不主动重试)。\n *\n * unlink(文件删除)不增量编译(无文件可编译),但触发 reloadRoutes(路由结构变化)。\n */\nexport function startWatcher(options: WatchOptions): void {\n const { rootDir, app, devDist } = options;\n\n async function rebuildRoutes(files: string[]): Promise<void> {\n // 1. 增量编译变化的文件(add/change 事件累积的文件)\n if (files.length > 0) {\n await compileDevRoutes({\n rootDir,\n dist: devDist,\n files,\n });\n }\n\n // 2. 重生成 faapi-config.js(compileConfig 内部 mtime 短路,配置源无变化时跳过)\n await compileConfig({ rootDir, dist: devDist });\n\n // 3. 调 app.reloadRoutes()(scanRoutes + generateSchemaFiles + 清缓存 + 更新引用)\n await app.reloadRoutes();\n // 4. 调 app.reloadTools()(scanTools + 重生成 faapi-tools.js + 清缓存)\n // 与 reloadRoutes 分离——tool 清单独立重建,无 tool 文件时 scanTools 返回空(快速跳过)\n await app.reloadTools();\n // 5. 调 app.reloadAgents()(scanAgents + 重生成 faapi-agents.js + 清缓存)\n // 与 reloadTools 分离——agent 清单独立重建,无 agent 文件时 scanAgents 返回空(快速跳过)\n await app.reloadAgents();\n\n console.log(\n `- Routes rebuilt${files.length > 0 ? `, ${files.length} file(s) recompiled` : ''}`,\n );\n }\n\n // 重建调度器:debounce 合并 + 重入保护 + 失败回灌(见 rebuildScheduler.md)\n const scheduler = createRebuildScheduler({\n rebuild: rebuildRoutes,\n onError: (err) => {\n // esbuild 抛错带结构化 errors 数组(file/line/text/frame)——逐条输出,\n // 压扁成 err.message 会让用户只剩一句摘要找不到位置\n console.error('- Error rebuilding routes:');\n const esbuildErr = err as {\n errors?: Array<{ location?: { file?: string; line?: number }; text?: string }>;\n };\n if (Array.isArray(esbuildErr?.errors) && esbuildErr.errors.length > 0) {\n for (const e of esbuildErr.errors) {\n const loc = e.location?.file\n ? `${e.location.file}${e.location.line != null ? `:${e.location.line}` : ''}: `\n : '';\n console.error(` ${loc}${e.text ?? 'compile error'}`);\n }\n } else {\n console.error(' ', err instanceof Error ? err.message : String(err));\n }\n },\n });\n\n // 监听文件变化\n // chokidar v4 移除了 glob 模式支持,改为监听 src 整个目录 + ignored 函数过滤\n // 监听整个 src 比 glob 更合理:handler.ts 引用的 util.ts 变化也能触发重建\n // 同时监听根目录的 faapi.config.{ts,js}(配置变化时重生成 faapi-config.js)\n const CONFIG_FILES = ['faapi.config.ts', 'faapi.config.js'];\n const configAbsPaths = new Set(CONFIG_FILES.map((f) => path.resolve(rootDir, f)));\n // tsconfig.json 变化影响 alias 重写与 mtime 缓存输入(compileConfig 明确把\n // tsconfig 计入缓存输入),不监听的话改 paths 后要等下一个无关文件变化才生效\n const watchPaths = ['src', 'tsconfig.json', ...CONFIG_FILES];\n const scheduleOnly = new Set([...configAbsPaths, path.resolve(rootDir, 'tsconfig.json')]);\n const watcher = chokidar.watch(watchPaths, {\n cwd: rootDir,\n ignoreInitial: true,\n ignored: (filePath, stats) => {\n // 按路径段判断,避免子串误伤(如 src/lib/node_modules-helper.ts、src/api/x.faapi.ts)\n const parts = filePath.split(/[\\\\/]/);\n if (\n parts.includes('node_modules') ||\n parts.includes('.git') ||\n parts.includes('.faapi') ||\n (devDist && parts.includes(devDist))\n ) {\n return true;\n }\n // 无 stats 时不忽略(chokidar 会再次调用并传入 stats)\n if (!stats) return false;\n // 目录不忽略(chokidar 需要递归进入子目录)\n if (stats.isDirectory()) return false;\n // 只监听 .ts/.js 文件(.js 用于 faapi.config.js)\n return !filePath.endsWith('.ts') && !filePath.endsWith('.js');\n },\n });\n\n // config 文件事件只触发重生成(compileConfig mtime 短路:无变化时跳过),\n // 不进增量编译——config 在 src/outbase 之外,喂给 compileDevRoutes 会让 esbuild\n // 把 `..` 段转义成 `_.._` 目录,在 .faapi 下堆积垃圾产物\n watcher.on('add', (file) => handleFileEvent(file));\n watcher.on('change', (file) => handleFileEvent(file));\n\n /**\n * 文件事件分流:\n * - config / tsconfig:只 schedule(compileConfig mtime 短路,无变化跳过)\n * - test/d.ts:完全跳过(build/dev 全量编译都不含它们,喂给增量编译会让\n * 测试文件的语法错误打断整轮重建)\n * - 其余源码:进增量编译\n */\n function handleFileEvent(file: string): void {\n const abs = path.resolve(rootDir, file);\n if (scheduleOnly.has(abs)) {\n scheduler.schedule();\n return;\n }\n if (/\\.(test|e2e\\.test)\\.tsx?$/.test(abs.replace(/\\\\/g, '/')) || abs.endsWith('.d.ts')) {\n return;\n }\n scheduler.addFiles([abs]);\n }\n watcher.on('unlink', () => {\n // 文件删除:不增量编译(无文件可编译),但触发重生成产物 + reloadRoutes(路由结构变化)\n scheduler.schedule();\n });\n watcher.on('error', (err) => {\n console.error('- Watcher error:', err instanceof Error ? err.message : String(err));\n });\n watcher.on('ready', () => {\n const watched = watcher.getWatched();\n const dirCount = Object.keys(watched).length;\n const fileCount = Object.values(watched).reduce((sum, files) => sum + files.length, 0);\n console.log(`- Watcher ready: ${dirCount} dir(s), ${fileCount} file(s) watched`);\n });\n\n console.log('- Watch mode enabled');\n}\n","import type { RouteManifest } from './routeTypes';\n\nexport interface RouteConflict {\n method: string;\n urlPath: string;\n files: string[];\n}\n\n/**\n * 检测路由冲突(相同 method + urlPath 的多个文件)\n */\nexport function detectRouteConflicts(routes: RouteManifest): RouteConflict[] {\n const map = new Map<string, RouteConflict>();\n\n for (const route of routes) {\n const key = `${route.method} ${route.urlPath}`;\n const existing = map.get(key);\n if (existing) {\n existing.files.push(route.filePath);\n } else {\n map.set(key, {\n method: route.method,\n urlPath: route.urlPath,\n files: [route.filePath],\n });\n }\n }\n\n // 只返回有冲突的(文件数 > 1)\n const conflicts: RouteConflict[] = [];\n for (const conflict of map.values()) {\n if (conflict.files.length > 1) {\n conflicts.push(conflict);\n }\n }\n\n return conflicts;\n}\n","import type { RouteManifest, RouteMatch, WsRouteManifest, WsRouteMatch } from './routeTypes';\n\n/**\n * 路由匹配索引——把 O(n) 线性扫描降为静态路由 O(1) 命中\n *\n * 每个请求都要做路由匹配,原实现逐条遍历清单(静态路由字符串比较、动态路由\n * split('/') 比对),大清单下是热路径开销。索引把清单分为两部分:\n * - **静态路由**:`method|urlPath` → route 的 Map,一次查找命中\n * - **动态路由**:保持清单顺序的数组(`sortRoutes` 保证静态恒在动态之前,\n * 且同一 `(method, path)` 至多一条静态路由,静态优先查与原 first-match\n * 语义完全等价)\n *\n * 索引按**清单数组身份**缓存(WeakMap):`reloadRoutes` / `hydrateRoutes`\n * 整体替换清单数组(`routesRef.current = newRoutes`),旧数组被替换后索引\n * 自动失效可被 GC,无需手动清理——无生命周期侵入。\n *\n * 详见 [matchRoute.md](./matchRoute.md)。\n */\n\n/**\n * HTTP 路由匹配索引\n */\ninterface HttpRoutesIndex {\n /** 静态路由:`method|urlPath` → route */\n static: Map<string, RouteManifest[number]>;\n /** 静态路径 → 方法集合(405 findAllowedMethods 反查用,免再扫静态段) */\n methodsByStaticPath: Map<string, Set<string>>;\n /** 动态路由(含 catch-all),保持清单顺序,pattern 已预编译 */\n dynamics: DynamicEntry[];\n}\n\n/** 动态路由条目(pattern segments 预编译,免去每请求对固定 pattern 重复 split) */\ninterface DynamicEntry {\n route: RouteManifest[number];\n /** 预编译的模式段(pattern.split('/').filter(Boolean),对固定 route 恒定) */\n segments: string[];\n}\n\n/** WS 路由匹配索引(无 HTTP 方法维度) */\ninterface WsRoutesIndex {\n /** 静态路由:urlPath → route */\n static: Map<string, WsRouteManifest[number]>;\n /** 动态路由(含 catch-all),保持清单顺序 */\n dynamics: WsRouteManifest;\n}\n\nconst httpIndexCache = new WeakMap<RouteManifest, HttpRoutesIndex>();\nconst wsIndexCache = new WeakMap<WsRouteManifest, WsRoutesIndex>();\n\n/** 构建 HTTP 路由索引(惰性,每个清单数组只构建一次) */\nfunction getHttpIndex(routes: RouteManifest): HttpRoutesIndex {\n let index = httpIndexCache.get(routes);\n if (index) return index;\n\n index = { static: new Map(), methodsByStaticPath: new Map(), dynamics: [] };\n for (const route of routes) {\n if (route.isDynamic) {\n index.dynamics.push({\n route,\n segments: route.urlPath.split('/').filter(Boolean),\n });\n } else {\n index.static.set(`${route.method}|${route.urlPath}`, route);\n let methods = index.methodsByStaticPath.get(route.urlPath);\n if (!methods) {\n methods = new Set();\n index.methodsByStaticPath.set(route.urlPath, methods);\n }\n methods.add(route.method);\n }\n }\n\n httpIndexCache.set(routes, index);\n return index;\n}\n\n/** 构建 WS 路由索引(惰性,每个清单数组只构建一次) */\nfunction getWsIndex(routes: WsRouteManifest): WsRoutesIndex {\n let index = wsIndexCache.get(routes);\n if (index) return index;\n\n index = { static: new Map(), dynamics: [] };\n for (const route of routes) {\n if (route.isDynamic) {\n index.dynamics.push(route);\n } else {\n index.static.set(route.urlPath, route);\n }\n }\n\n wsIndexCache.set(routes, index);\n return index;\n}\n\n/**\n * 根据请求路径和方法匹配路由\n *\n * HEAD 回退:无显式 HEAD 路由时复用 GET handler(Node ServerResponse 对 HEAD\n * 自动丢弃 body)。探活、CDN 健康检查、HTTP 客户端预检常用 HEAD——没有回退时\n * 只定义 GET 的路由对 HEAD 返回 405,监控大面积误报。\n *\n * @param routes 已排序的路由清单\n * @param method HTTP 方法\n * @param path 请求路径\n * @returns 匹配结果,包含路由记录和参数;无匹配返回 null\n */\nexport function matchRoute(routes: RouteManifest, method: string, path: string): RouteMatch | null {\n const index = getHttpIndex(routes);\n const upper = method.toUpperCase();\n\n const hit = matchByMethod(index, upper, path);\n if (hit) return hit;\n\n if (upper === 'HEAD') {\n return matchByMethod(index, 'GET', path);\n }\n\n return null;\n}\n\n/** 按方法匹配(静态 O(1) → 动态线性),matchRoute 内部复用(HEAD 回退 GET) */\nfunction matchByMethod(index: HttpRoutesIndex, method: string, path: string): RouteMatch | null {\n // 静态路由 O(1) 命中(sortRoutes 保证静态优先,等价于原遍历的首个命中)\n const staticHit = index.static.get(`${method}|${path}`);\n if (staticHit) {\n return { route: staticHit, params: {} };\n }\n\n // 动态路由按清单顺序匹配(pattern segments 已预编译)\n for (const entry of index.dynamics) {\n const route = entry.route;\n if (route.method !== method) {\n continue;\n }\n const params = matchSegments(entry.segments, path, route.paramNames, route.isCatchAll);\n if (params !== null) {\n return { route, params };\n }\n }\n\n return null;\n}\n\n/**\n * 匹配 WebSocket 路由(无 HTTP 方法维度)\n *\n * WS 路由只按路径匹配,协议升级时调用。\n * @param wsRoutes WebSocket 路由清单\n * @param path 请求路径\n */\nexport function matchWsRoute(wsRoutes: WsRouteManifest, path: string): WsRouteMatch | null {\n const index = getWsIndex(wsRoutes);\n\n const staticHit = index.static.get(path);\n if (staticHit) {\n return { route: staticHit, params: {} };\n }\n\n for (const route of index.dynamics) {\n const params = matchDynamicPath(route.urlPath, path, route.paramNames, route.isCatchAll);\n if (params !== null) {\n return { route, params };\n }\n }\n\n return null;\n}\n\n/**\n * 查找路径的所有允许方法(用于 405 响应)\n *\n * 静态段直接查索引(O(1)),仅动态段线性扫描——原实现对全部路由做\n * O(n) 遍历 + 静态路径比较,404/405 高频场景(扫描器、探活探测)开销显著。\n */\nexport function findAllowedMethods(routes: RouteManifest, path: string): string[] {\n const index = getHttpIndex(routes);\n const methods = new Set<string>();\n\n // 静态路由:索引直查\n const staticMethods = index.methodsByStaticPath.get(path);\n if (staticMethods) {\n for (const method of staticMethods) {\n methods.add(method);\n }\n }\n\n // 动态路由:线性扫描(segments 已预编译)\n for (const entry of index.dynamics) {\n const params = matchSegments(\n entry.segments,\n path,\n entry.route.paramNames,\n entry.route.isCatchAll,\n );\n if (params !== null) {\n methods.add(entry.route.method);\n }\n }\n\n // RFC 9110:GET 允许时 HEAD 必然允许(matchRoute 有 HEAD→GET 回退),\n // 405 的 Allow 头如实反映,避免客户端误判 HEAD 不可用\n if (methods.has('GET')) {\n methods.add('HEAD');\n }\n\n return Array.from(methods);\n}\n\n/**\n * 动态路径匹配\n * 将路由模式(如 /user/:id)与请求路径(如 /user/123)匹配\n * 支持 catch-all 路由(如 /shop/:...slug 匹配 /shop/clothes/tops)\n * 返回提取的参数对象,不匹配返回 null\n */\nexport function matchDynamicPath(\n pattern: string,\n path: string,\n paramNames: string[],\n isCatchAll?: boolean,\n): Record<string, string> | null {\n return matchSegments(pattern.split('/').filter(Boolean), path, paramNames, isCatchAll);\n}\n\n/**\n * 基于**预编译 segments** 的动态匹配(热路径内部用)\n *\n * 与 matchDynamicPath 语义一致,但模式段由索引构建期一次性 split,\n * 免去每请求对固定 pattern 重复字符串分割\n */\nfunction matchSegments(\n patternSegments: string[],\n path: string,\n paramNames: string[],\n isCatchAll?: boolean,\n): Record<string, string> | null {\n const pathSegments = path.split('/').filter(Boolean);\n\n // catch-all 路由:最后一个模式段为 :...slug\n if (isCatchAll) {\n // catch-all 前面的静态/动态段必须匹配\n // catch-all 段至少匹配一个路径段\n const nonCatchAllCount = patternSegments.length - 1;\n if (pathSegments.length <= nonCatchAllCount) {\n return null;\n }\n\n const params: Record<string, string> = {};\n\n for (let i = 0; i < nonCatchAllCount; i++) {\n // i < nonCatchAllCount ≤ 两数组长度,索引必然存在\n const patternSeg = patternSegments[i]!;\n const pathSeg = pathSegments[i]!;\n\n if (patternSeg.startsWith(':')) {\n const paramName = patternSeg.slice(1);\n params[paramName] = pathSeg;\n } else if (patternSeg !== pathSeg) {\n return null;\n }\n }\n\n // catch-all 段:剩余所有路径段用 / 连接\n const catchAllValue = pathSegments.slice(nonCatchAllCount).join('/');\n // 前置检查 pathSegments.length > nonCatchAllCount 保证 catch-all 段存在\n const catchAllParamName = patternSegments[nonCatchAllCount]!.slice(4); // 去掉 ':...'\n params[catchAllParamName] = catchAllValue;\n\n if (Object.keys(params).length !== paramNames.length) {\n return null;\n }\n\n return params;\n }\n\n // 普通动态路由:段数必须一致\n if (patternSegments.length !== pathSegments.length) {\n return null;\n }\n\n const params: Record<string, string> = {};\n\n for (let i = 0; i < patternSegments.length; i++) {\n // 段数一致性检查后索引必然存在\n const patternSeg = patternSegments[i]!;\n const pathSeg = pathSegments[i]!;\n\n if (patternSeg.startsWith(':')) {\n // 动态段,提取参数值\n const paramName = patternSeg.slice(1);\n params[paramName] = pathSeg;\n } else if (patternSeg !== pathSeg) {\n // 静态段不匹配\n return null;\n }\n }\n\n // 确保所有参数名都被提取到\n if (Object.keys(params).length !== paramNames.length) {\n return null;\n }\n\n return params;\n}\n","/**\n * 从模块中解析出指定名称的导出\n *\n * 解析顺序:\n * 1. 具名导出:export function GET() {}\n * 2. 默认导出的对象属性:export default { GET() {} }\n *\n * @param module 动态 import 得到的模块对象\n * @param exportName 导出名(如 'GET')\n */\nexport function resolveExport(module: Record<string, unknown>, exportName: string): unknown {\n // 1. 具名导出\n if (exportName in module && typeof module[exportName] !== 'undefined') {\n return module[exportName];\n }\n\n // 2. 默认导出的对象属性\n const defaultExport = module.default;\n if (defaultExport !== null && typeof defaultExport === 'object') {\n const value = (defaultExport as Record<string, unknown>)[exportName];\n if (value !== undefined) {\n return value;\n }\n }\n\n return undefined;\n}\n","/**\n * 校验导出值是否为合法的 handler 函数\n */\nexport function validateRouteModule(\n value: unknown,\n method: string,\n filePath: string,\n): asserts value is (...args: unknown[]) => unknown {\n if (typeof value !== 'function') {\n throw new Error(\n `Route module \"${filePath}\" does not export a valid handler for method \"${method}\". ` +\n `Expected a function, got ${typeof value}.`,\n );\n }\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { compileDevRoutes } from './compileDevRoutes';\nimport { generateSchemaFiles } from './generateSchemaFiles';\nimport { getRuntimeSchemaPath } from './generateSchemaFiles';\nimport { collectRelativeImports } from './collectImports';\nimport type { RouteManifest } from '../router/routeTypes';\n\n/**\n * 按需编译(Vite 风格):仅在请求时编译被访问的 handler.ts 及其依赖闭包 + 生成 zod.js\n *\n * 设计目标:dev 启动时零编译、零 schema 生成,handler.js / zod.js 首次被需要时才触发。\n * 与阶段 1(scanRoutes 去 import)配合,dev 冷启动近乎瞬开。\n *\n * 依赖闭包:`bundle: false` 逐文件编译不分析 import 关系,只编译 handler 单文件时,\n * handler 引用的共享模块(如 `../../lib/db`)没有产物,首次请求 import 即\n * ERR_MODULE_NOT_FOUND。因此 ensureCompiled 先收集依赖闭包\n * (collectRelativeImports,相对 + tsconfig paths 别名),跳过产物已新鲜的文件后\n * 批量编译。src 外依赖不收集(outbase=src 语义限制,与 watcher 增量编译一致)。\n *\n * 四种触发路径:\n * 1. `loadRouteModule` 先调 `ensureCompiled` 编译源码 → 再 import handler.js\n * 2. `loadWsHandler` 同上(WS handler.js)\n * 3. `createServer` 调 `ensureSchemaGenerated` 检查 zod.js 是否存在/最新 → 不存在则生成\n * 4. watcher 文件变化 → 增量编译该文件(保留现有逻辑)+ 删除 stale zod.js\n * 5. `createServer` / `handleWsUpgrade` 首次加载目录中间件前调 `ensureMiddlewaresCompiled`\n *\n * mtime 缓存(阶段 4):\n * - `ensureCompiled`:handler.js 存在且 mtime ≥ 源码 mtime → 跳过编译\n * - `ensureSchemaGenerated`:zod.js 存在且 mtime ≥ 源码 mtime → 跳过生成\n * - 首次调用后写入内存 Set,后续直接跳过(避免 fs.statSync 调用)\n * - watcher 触发时清除内存 Set,mtime 重新评估\n *\n * 并发去重(mutex):\n * - 同一文件的并发编译请求会共享同一个 in-flight Promise,避免重复触发 esbuild\n * - 同一 schemaPath 的并发生成请求同理\n * - watcher 触发 clear 时同步清空 in-flight Map\n */\n\n/**\n * 比较源文件和产物文件的 mtime\n *\n * @returns true 表示产物是最新的(可复用),false 表示需要重新生成\n */\nfunction isProductFresh(sourceAbsPath: string, productAbsPath: string): boolean {\n try {\n const srcStat = fs.statSync(sourceAbsPath);\n const prodStat = fs.statSync(productAbsPath);\n return prodStat.mtimeMs >= srcStat.mtimeMs;\n } catch {\n return false;\n }\n}\n\n// ─── DevOnDemand 状态封装 ────────────────────────────────────────────\n\n/**\n * 按需编译运行时状态\n *\n * 封装原本散落的 4 个模块级可变状态 + 2 个 mutex Map,避免全局污染 +\n * 便于测试隔离(_resetDevOnDemandState 重置回初始状态)。\n *\n * faapi 单进程单 server 设计下,单例 state 已足够。多实例测试场景通过\n * _resetDevOnDemandState 在 beforeEach / afterEach 中清空状态。\n */\ninterface DevOnDemandState {\n /** 是否启用 dev 按需编译模式(prod 始终 false) */\n enabled: boolean;\n /** dev 模式产物目录(仅 enabled 时使用) */\n distDir: string | undefined;\n /** 已编译文件缓存(源码绝对路径) */\n compiledFiles: Set<string>;\n /** 已生成 schema 缓存(schemaPath 绝对路径) */\n generatedSchemas: Set<string>;\n /** handler.js 编译 mutex:同一 sourceAbsPath 的并发请求共享同一 Promise */\n inFlightCompilations: Map<string, Promise<void>>;\n /** zod.js 生成 mutex:同一 schemaPath 的并发请求共享同一 Promise */\n inFlightSchemaGenerations: Map<string, Promise<void>>;\n}\n\nfunction createDevOnDemandState(): DevOnDemandState {\n return {\n enabled: false,\n distDir: undefined,\n compiledFiles: new Set(),\n generatedSchemas: new Set(),\n inFlightCompilations: new Map(),\n inFlightSchemaGenerations: new Map(),\n };\n}\n\nconst state: DevOnDemandState = createDevOnDemandState();\n\n/**\n * 重置 dev on demand 状态(仅测试用)\n *\n * 清空所有缓存 + mutex + 标记位,让下一次测试从干净状态开始。\n * 生产代码不应调用此函数——会丢失已编译产物的缓存。\n */\nexport function _resetDevOnDemandState(): void {\n state.enabled = false;\n state.distDir = undefined;\n state.compiledFiles.clear();\n state.generatedSchemas.clear();\n state.inFlightCompilations.clear();\n state.inFlightSchemaGenerations.clear();\n}\n\n// ─── handler.js 按需编译 ────────────────────────────────────────────\n\n/**\n * 清空所有按需编译缓存(reloadRoutes 时调用)\n *\n * 同时清空 in-flight mutex Map + 产物→源码映射缓存(源文件新建/删除后映射需重建),\n * 避免 watcher 重置后旧 Promise 永久阻塞。\n */\nexport function clearCompiledFiles(): void {\n state.compiledFiles.clear();\n state.inFlightCompilations.clear();\n sourcePathCache.clear();\n}\n\n/**\n * 确保源文件及其依赖闭包已编译为产物,未编译则触发批量编译\n *\n * 调用方(`loadRouteModule` / `loadWsHandler`)在 import 产物之前调用此函数,确保产物已生成。\n *\n * 依赖闭包:handler 引用的项目内共享模块(相对 import + tsconfig paths 别名)一并编译,\n * 跳过产物已新鲜的文件(watcher 已编译或此前请求已编译),只补缺口。\n *\n * mtime 缓存(阶段 4):\n * 1. 内存 Set 命中 → 跳过(最快路径)\n * 2. 产物存在且 mtime ≥ 源码 mtime → 跳过(复用已有产物,如 watcher 已编译)\n * 3. 产物不存在或 stale → 编译(含 stale 的依赖闭包文件) → 加入内存 Set\n *\n * 并发去重(mutex):\n * - 同一 sourceAbsPath 的并发请求共享同一 in-flight Promise\n * - 第二个请求 await 后返回 false(表示「别的请求已触发编译,本次不需要再触发」)\n *\n * @param sourceAbsPath 源码 .ts 绝对路径\n * @param rootDir 项目根目录\n * @param dist 产物目录(dev 模式为 '.faapi')\n * @returns 是否实际触发了编译(false 表示已编译过或产物已最新或源文件不存在)\n * @throws 编译失败时抛错(带原始 cause),调用方可据此给出精确错误信息\n */\nexport async function ensureCompiled(\n sourceAbsPath: string,\n rootDir: string,\n dist: string,\n): Promise<boolean> {\n // mutex: 同一文件正在被别的请求编译 → 等待并返回 false\n const inFlight = state.inFlightCompilations.get(sourceAbsPath);\n if (inFlight) {\n await inFlight.catch(() => {\n // 别的请求编译失败时不在这里抛——让本请求按正常流程自己重试\n });\n return false;\n }\n\n // 内存缓存命中:跳过\n if (state.compiledFiles.has(sourceAbsPath)) {\n return false;\n }\n\n // 源文件不存在:无法编译\n if (!fs.existsSync(sourceAbsPath)) {\n return false;\n }\n\n // mtime 缓存:产物已存在且最新 → 复用(watcher 已编译过的情况)\n const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);\n if (productPath && isProductFresh(sourceAbsPath, productPath)) {\n state.compiledFiles.add(sourceAbsPath);\n return false;\n }\n\n // 触发编译:注册 in-flight Promise 防止并发重复编译\n const compilePromise = (async () => {\n // 依赖闭包:入口本身 + src 内全部传递依赖;产物已新鲜的文件跳过(只补缺口)\n const { insideFiles } = await collectRelativeImports([sourceAbsPath], rootDir);\n const files = [sourceAbsPath];\n for (const dep of insideFiles) {\n if (state.compiledFiles.has(dep)) continue;\n const depProduct = prodSourcePathToProductPath(dep, rootDir, dist);\n if (depProduct && isProductFresh(dep, depProduct)) continue;\n files.push(dep);\n }\n\n await compileDevRoutes({\n rootDir,\n dist,\n files,\n logLevel: 'silent',\n });\n for (const file of files) {\n state.compiledFiles.add(file);\n }\n state.compiledFiles.add(sourceAbsPath);\n })();\n\n state.inFlightCompilations.set(sourceAbsPath, compilePromise);\n try {\n await compilePromise;\n return true;\n } finally {\n state.inFlightCompilations.delete(sourceAbsPath);\n }\n}\n\n/**\n * 确保目录中间件文件已编译(dev 按需模式)\n *\n * middlewarePaths 是产物绝对路径(`.faapi/api/users/middlewares.js`),dev 按需模式下\n * 产物在首次请求前不存在——若不先编译,`loadMergedMiddlewares` import 即\n * ERR_MODULE_NOT_FOUND。通过 `prodPathToSourcePath` 反推源码路径后走 `ensureCompiled`\n * (含依赖闭包)。prod 模式(按需开关关闭)直接返回——产物由 build 固化。\n *\n * @param middlewarePaths 中间件产物绝对路径列表(scanRoutes 收集)\n * @param rootDir 项目根目录\n */\nexport async function ensureMiddlewaresCompiled(\n middlewarePaths: string[],\n rootDir: string,\n): Promise<void> {\n if (!isDevOnDemandEnabled() || middlewarePaths.length === 0) return;\n const dist = getDevDist();\n if (!dist) return;\n\n for (const mwPath of middlewarePaths) {\n const sourcePath = prodPathToSourcePath(mwPath, rootDir, dist);\n try {\n await ensureCompiled(sourcePath, rootDir, dist);\n } catch (err) {\n // 中间件编译失败不阻断请求——loadMiddlewaresFile 对 import 失败已有\n // console.error + 空 bundle 降级语义(鉴权失效由 onError 感知),此处保持一致\n console.error(`[faapi] Failed to compile middleware source ${sourcePath}:`, err);\n }\n }\n}\n\n/**\n * 从源码绝对路径推算产物绝对路径\n *\n * 源码 `<rootDir>/src/api/hello/handler.ts` → 产物 `<rootDir>/<dist>/api/hello/handler.js`\n */\nfunction prodSourcePathToProductPath(\n sourceAbsPath: string,\n rootDir: string,\n dist: string,\n): string | null {\n const rel = path.relative(rootDir, sourceAbsPath).replace(/\\\\/g, '/');\n if (!rel.startsWith('src/')) return null;\n const relWithoutSrc = rel.slice(4); // 去掉 src/\n const jsRel = relWithoutSrc.replace(/\\.ts$/, '.js');\n return path.resolve(rootDir, dist, jsRel);\n}\n\n// ─── zod.js 按需生成 ────────────────────────────────────────────────\n\n/**\n * 清空所有 schema 生成缓存(reloadRoutes 时调用)\n *\n * 同时清空 in-flight mutex Map。\n */\nexport function clearGeneratedSchemas(): void {\n state.generatedSchemas.clear();\n state.inFlightSchemaGenerations.clear();\n}\n\n/**\n * 确保 zod.js 已生成,未生成则触发单文件 schema 生成\n *\n * 调用方(createServer)在 validateInput 之前调用此函数。\n *\n * route.filePath 在 dev/prod 模式下均为产物路径(如 '.faapi/api/hello/handler.js'),\n * 需通过 prodPathToSourcePath 反推源码 .ts 路径用于 AST 分析和 mtime 比较。\n *\n * mtime 缓存(阶段 4):\n * 1. 内存 Set 命中 → 跳过\n * 2. zod.js 存在且 mtime ≥ 源码 mtime → 跳过(复用已有产物)\n * 3. zod.js 不存在或 stale → 生成 → 加入内存 Set\n *\n * 并发去重(mutex):\n * - 同一 schemaPath 的并发请求共享同一 in-flight Promise\n * - 第二个请求 await 后返回 false\n *\n * @param schemaPath zod.js 绝对路径\n * @param routeFilePath route.filePath(产物路径,如 '.faapi/api/hello/handler.js')\n * @param routes 完整路由清单(用于过滤同文件的所有方法)\n * @param rootDir 项目根目录\n * @param dist 产物目录\n * @returns 是否实际触发了生成(false 表示已生成过或产物已最新或源文件不存在)\n * @throws schema 生成失败时抛错(带原始 cause),由 createServer 错误处理链接管\n */\nexport async function ensureSchemaGenerated(\n schemaPath: string,\n routeFilePath: string,\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n): Promise<boolean> {\n // mutex: 同一 schemaPath 正在被别的请求生成 → 等待并返回 false\n const inFlight = state.inFlightSchemaGenerations.get(schemaPath);\n if (inFlight) {\n await inFlight.catch(() => {\n // 别的请求生成失败时不在这里抛——让本请求按正常流程自己重试\n });\n return false;\n }\n\n // 内存缓存命中:跳过\n if (state.generatedSchemas.has(schemaPath)) {\n return false;\n }\n\n // route.filePath 是产物路径,反推源码绝对路径(用于 mtime 比较和 AST 分析)\n const prodAbsPath = path.resolve(rootDir, routeFilePath);\n const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);\n if (!fs.existsSync(sourceAbsPath)) {\n return false;\n }\n\n // mtime 缓存:zod.js 已存在且最新 → 复用\n if (isProductFresh(sourceAbsPath, schemaPath)) {\n state.generatedSchemas.add(schemaPath);\n return false;\n }\n\n // 过滤同文件的所有路由(一个 handler.ts 可能有 GET/POST/WS 多个方法)\n const fileRoutes = routes.filter((r) => r.filePath === routeFilePath);\n if (fileRoutes.length === 0) {\n return false;\n }\n\n // 把产物 filePath 转为源码 filePath(generateSchemaFiles → collectRouteSchemaSources\n // 需要源码 .ts 路径做 AST 分析)\n const sourceRelPath = path.relative(rootDir, sourceAbsPath).replace(/\\\\/g, '/');\n const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));\n\n // 触发生成:注册 in-flight Promise 防止并发重复生成\n const generatePromise = (async () => {\n // 生成失败时抛错(不吞错误),由 createServer 错误处理链接管\n await generateSchemaFiles(sourceRoutes, rootDir, dist);\n state.generatedSchemas.add(schemaPath);\n })();\n\n state.inFlightSchemaGenerations.set(schemaPath, generatePromise);\n try {\n await generatePromise;\n return true;\n } finally {\n state.inFlightSchemaGenerations.delete(schemaPath);\n }\n}\n\n/**\n * 删除指定路由清单对应的所有 zod.js 文件(reloadRoutes 时调用)\n *\n * watcher 文件变化后,旧 zod.js 可能 stale(类型引用变化等)。\n * 删除后下次请求触发 ensureSchemaGenerated 重新生成。\n *\n * 仅删除文件,不删除目录(faapi-helpers.js 保留,内容确定性不变)。\n */\nexport async function deleteSchemaFiles(\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n): Promise<void> {\n const deleted = new Set<string>();\n for (const route of routes) {\n const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);\n if (deleted.has(schemaPath)) continue;\n deleted.add(schemaPath);\n try {\n await fs.promises.unlink(schemaPath);\n } catch {\n // 文件不存在:忽略\n }\n }\n}\n\n// ─── 路径转换 ────────────────────────────────────────────────────────\n\n/**\n * 从产物绝对路径反推源码绝对路径(带映射缓存)\n *\n * 源码 `<rootDir>/src/api/hello/handler.ts` → 产物 `<rootDir>/<dist>/api/hello/handler.js`\n *\n * dev 按需模式下**每请求**都会调此函数(loadRouteModule / ensureSchemaGenerated),\n * 缓存映射后稳态请求零 fs 访问。映射仅取决于源文件的存在性——源文件新建/删除\n * 通过 `clearCompiledFiles()`(reloadRoutes 时调用)清缓存。\n */\nconst sourcePathCache = new Map<string, string>();\n\nexport function prodPathToSourcePath(prodAbsPath: string, rootDir: string, dist: string): string {\n const cached = sourcePathCache.get(prodAbsPath);\n if (cached) return cached;\n\n const rel = path.relative(rootDir, prodAbsPath).replace(/\\\\/g, '/');\n // 去掉 <dist>/ 前缀\n let relWithoutDist = rel;\n if (relWithoutDist.startsWith(`${dist}/`)) {\n relWithoutDist = relWithoutDist.slice(dist.length + 1);\n }\n // 加 src/ 前缀\n const srcRel = `src/${relWithoutDist}`;\n // .js → .ts(.ts 不存在时回退 .js)\n const tsRel = srcRel.replace(/\\.js$/, '.ts');\n const tsAbs = path.resolve(rootDir, tsRel);\n let result: string;\n if (fs.existsSync(tsAbs)) {\n result = tsAbs;\n } else {\n // .ts 不存在,保持 .js(极少见,可能是用户直接放 .js 源码)\n result = path.resolve(rootDir, srcRel);\n }\n sourcePathCache.set(prodAbsPath, result);\n return result;\n}\n\n// ─── Dev 按需模式标记 ────────────────────────────────────────────────\n\n/**\n * Dev 按需编译模式标记\n *\n * 由 devCommand 在按需模式启动时设置为 true,loadRouteModule / loadWsHandler /\n * createServer 通过此标记判断是否启用按需编译/schema 生成回退。\n *\n * prod 模式(node dist/main)始终为 false——产物在 build 阶段已固化,import 失败即报错。\n */\nexport function setDevOnDemandEnabled(enabled: boolean): void {\n state.enabled = enabled;\n}\n\nexport function isDevOnDemandEnabled(): boolean {\n return state.enabled;\n}\n\n/**\n * Dev 模式产物目录(仅 devOnDemandEnabled 时使用)\n *\n * 由 devCommand 设置,loadRouteModule / loadWsHandler / createServer 通过 getDevDist() 读取。\n */\nexport function setDevDist(dist: string): void {\n state.distDir = dist;\n}\n\nexport function getDevDist(): string | undefined {\n return state.distDir;\n}\n","import type { HttpMethod } from '../router/constants';\nimport { resolveExport } from './resolveExports';\nimport { validateRouteModule } from './validateRouteModule';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport {\n ensureCompiled,\n isDevOnDemandEnabled,\n getDevDist,\n prodPathToSourcePath,\n} from '../cli/compileOnDemand';\n\nexport interface RouteModule {\n handler: (...args: unknown[]) => unknown;\n method: HttpMethod;\n}\n\n/**\n * 动态 import 路由文件并提取 handler\n *\n * Dev 按需编译模式(Vite 风格):先确保编译再 import,避免 import 不存在的文件。\n * Prod 模式:产物在 build 阶段已固化,直接 import,失败即报错。\n *\n * 错误传递:\n * - 编译失败 → 抛出编译错误(\"Failed to compile route module\")\n * - import 失败 → 抛出加载错误(\"Failed to load route module\")\n *\n * @param filePath 路由文件的绝对路径\n * @param method HTTP 方法名(也是导出名)\n * @param rootDir 项目根目录(按需编译模式用,可选)\n */\nexport async function loadRouteModule(\n filePath: string,\n method: HttpMethod,\n rootDir?: string,\n): Promise<RouteModule> {\n // Dev 按需模式:先确保编译再 import\n // 避免\"import 失败 → 编译 → 重试 import\"模式——首次 import 不存在的文件会污染\n // Vite SSR 内部状态,导致编译创建文件后重试 import 仍失败(CI Linux 上复现)\n //\n // 不在此处 existsSync(sourcePath):ensureCompiled 内部对缺失源文件直接返回 false,\n // 外层检查是冗余的每请求同步 IO(编译完成一次后内存 Set 命中即短路)\n if (isDevOnDemandEnabled() && rootDir) {\n const dist = getDevDist();\n if (dist) {\n const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);\n if (sourcePath) {\n try {\n await ensureCompiled(sourcePath, rootDir, dist);\n } catch (compileErr) {\n const reason = compileErr instanceof Error ? compileErr.message : String(compileErr);\n throw new Error(`Failed to compile route module \"${sourcePath}\": ${reason}`, {\n cause: compileErr,\n });\n }\n }\n }\n }\n\n // import 模块(dev 按需模式下走 Node 原生 import 绕过 Vite SSR 缓存)\n let module: Record<string, unknown>;\n try {\n module = await importWithCacheBust(filePath, isDevOnDemandEnabled());\n } catch (err: unknown) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load route module \"${filePath}\": ${reason}`, { cause: err });\n }\n\n const handler = resolveExport(module, method);\n validateRouteModule(handler, method, filePath);\n\n return { handler, method };\n}\n","/**\n * SSE(Server-Sent Events)支持\n *\n * 让 handler 能向客户端推送流式事件,用于 LLM token 流、进度通知等场景。\n *\n * 核心导出:\n * - `encodeSseEvent(event)`:把 SSE 事件对象编码为符合 HTML5 SSE 规范的字符串\n * - `createSseWriter()`:创建一个 SseWriter,封装 ReadableStream + Response,提供 send/close/sendError API\n * - `SseWriter`:writer 类型,ctx.sse() 返回此类型\n *\n * 设计要点:\n * - writer 内部用 ReadableStream + TextEncoder,send 时 enqueue,close 时 close controller\n * - response 预设 text/event-stream、no-cache、keep-alive 头,状态码默认 200\n * - close 后再 send 静默忽略,避免 handler 异步流程中误写已关闭的流\n * - sendError 向流写入 event: error 后关闭,用于流式输出中报错的优雅终止\n *\n * 与 ctx 的集成:\n * - ctx.sse() 调用 createSseWriter(),并把 response 缓存到 ctx 内部字段\n * - invokeHandler 在 handler 返回后检查 ctx 是否持有 SSE response,有则优先使用\n */\n\n/**\n * SSE 事件字段\n *\n * 遵循 HTML5 SSE 规范:\n * - `data`:消息数据,多行时每行加 `data: ` 前缀;对象自动 JSON.stringify\n * - `event`:事件类型,客户端可用 addEventListener(event) 监听\n * - `id`:事件 ID,客户端断线重连时通过 Last-Event-ID 头发送\n * - `retry`:重连等待时间(毫秒)\n * - `comment`:注释行(以 `:` 开头),用于 keep-alive 心跳,不传递给客户端消息\n */\nexport interface SseEvent {\n /** 消息数据。字符串原样输出;对象自动 JSON.stringify;多行时每行加 data: 前缀 */\n data?: unknown;\n /** 事件类型,客户端可用 addEventListener 监听 */\n event?: string;\n /** 事件 ID,客户端断线重连时通过 Last-Event-ID 头发送 */\n id?: string | number;\n /** 重连等待时间(毫秒) */\n retry?: number;\n /** 注释行(以 : 开头),用于 keep-alive 心跳 */\n comment?: string;\n}\n\n/**\n * 把 SSE 事件对象编码为符合 HTML5 SSE 规范的字符串\n *\n * 字段顺序固定:comment > event > id > retry > data,末尾空行分隔事件。\n *\n * @param event SSE 事件对象\n * @returns 编码后的字符串(含末尾空行)\n */\nexport function encodeSseEvent(event: SseEvent): string {\n let out = '';\n\n // comment(注释行,以 : 开头)\n if (event.comment !== undefined) {\n out += `: ${event.comment}\\n`;\n }\n\n // event\n if (event.event !== undefined) {\n out += `event: ${event.event}\\n`;\n }\n\n // id\n if (event.id !== undefined) {\n out += `id: ${event.id}\\n`;\n }\n\n // retry\n if (event.retry !== undefined) {\n out += `retry: ${event.retry}\\n`;\n }\n\n // data:对象 JSON.stringify,多行每行加前缀\n if (event.data !== undefined) {\n let dataStr: string;\n if (typeof event.data === 'string') {\n dataStr = event.data;\n } else if (event.data === null) {\n dataStr = 'null';\n } else {\n dataStr = JSON.stringify(event.data);\n }\n // 多行 data:每行加 data: 前缀\n const lines = dataStr.split('\\n');\n for (const line of lines) {\n out += `data: ${line}\\n`;\n }\n }\n\n // 空行结束事件\n out += '\\n';\n return out;\n}\n\n/**\n * SSE writer:封装流式推送 API\n *\n * 通过 `ctx.sse()` 创建,handler 调用 `send` 推送事件,`close` 关闭流。\n * 框架在 handler 返回后,自动使用 writer.response 作为 HTTP 响应。\n */\nexport interface SseWriter {\n /** 推送一个 SSE 事件 */\n send(event: SseEvent): void;\n /**\n * 直接写入原始字节/字符串,不做任何 SSE 序列化\n *\n * 用于透传上游已有的 SSE 原文(如 LLM 中转平台逐 chunk 转发 OpenAI 响应)。\n * 调用方负责保证内容符合 HTML5 SSE 规范;`send` 会再次加 `data: ` 前缀,\n * 不适用于原文透传场景。\n *\n * 接受 string 或 Uint8Array(Buffer 是 Uint8Array 子类,自然兼容)。\n * 与 `send` 一致:close/aborted 后静默忽略,不抛错。\n */\n sendRaw(chunk: string | Uint8Array): void;\n /** 推送一个 error 事件并关闭流(用于流式输出中报错的优雅终止) */\n sendError(error: unknown): void;\n /** 关闭流(多次调用安全) */\n close(): void;\n /** 流是否已关闭(handler 主动 close 或框架自动 close) */\n readonly closed: boolean;\n /** 客户端是否已断开(ReadableStream 被 cancel) */\n readonly aborted: boolean;\n /** 对应的 HTTP Response(由框架使用,用户一般不需要直接访问) */\n readonly response: Response;\n}\n\n/**\n * 创建一个 SSE writer\n *\n * 内部用 ReadableStream + TextEncoder 实现,send 时把编码后的事件 enqueue 到流,\n * close 时关闭 controller。response 预设标准 SSE 头。\n *\n * aborted 检测:监听 ReadableStream 的 cancel 钩子,客户端断开(cancel)时置为 true。\n * 此时 send 静默忽略,handler 可通过 writer.aborted 退出循环。\n */\nexport function createSseWriter(): SseWriter {\n const encoder = new TextEncoder();\n let controller: ReadableStreamDefaultController<Uint8Array> | null = null;\n let closed = false;\n let aborted = false;\n\n const stream = new ReadableStream<Uint8Array>({\n start(c) {\n controller = c;\n },\n cancel() {\n // 客户端断开连接(cancel ReadableStream)\n aborted = true;\n closed = true;\n controller = null;\n },\n });\n\n const response = new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n\n const writer: SseWriter = {\n send(event: SseEvent): void {\n if (closed || !controller) return;\n const text = encodeSseEvent(event);\n controller.enqueue(encoder.encode(text));\n },\n\n sendRaw(chunk: string | Uint8Array): void {\n if (closed || !controller) return;\n const bytes = typeof chunk === 'string' ? encoder.encode(chunk) : chunk;\n controller.enqueue(bytes);\n },\n\n sendError(error: unknown): void {\n if (closed || !controller) return;\n const message = error instanceof Error ? error.message : String(error);\n const text = encodeSseEvent({ event: 'error', data: message });\n try {\n controller.enqueue(encoder.encode(text));\n } finally {\n writer.close();\n }\n },\n\n close(): void {\n if (closed) return;\n closed = true;\n if (controller) {\n try {\n controller.close();\n } catch {\n // controller 可能已关闭,忽略\n }\n controller = null;\n }\n },\n\n get closed(): boolean {\n return closed;\n },\n\n get aborted(): boolean {\n return aborted;\n },\n\n get response(): Response {\n return response;\n },\n };\n\n return writer;\n}\n","import type { ErrorCode } from './errorCodes';\n\nexport class FaapiError extends Error {\n constructor(\n public readonly code: ErrorCode,\n message: string,\n public readonly statusCode: number,\n ) {\n super(message);\n this.name = 'FaapiError';\n }\n}\n","import { FaapiError } from './FaapiError';\n\n/**\n * 根据 issues 中的 code 推导 HTTP 状态码\n *\n * 语义参考 RFC 7807 / Rails / Laravel / Spring:\n * - 400 Bad Request:请求语法错误或结构不完整\n * - INVALID_FORMAT:JSON 格式错误、Date 非 ISO 8601\n * - MISSING_FIELD:缺少必填字段\n * - 422 Unprocessable Entity:请求语法正确但语义错误\n * - TYPE_MISMATCH:类型不匹配(string 期望 number)\n * - INVALID_VALUE:值不在允许范围(字面量/enum 不匹配)\n * - COERCE_FAILED:query 字符串转换失败\n *\n * 多个 issue 时取最高严重度(400 优先于 422)。\n */\nfunction deriveStatusCode(issues: ValidationIssue[]): number {\n // 有 400 类 issue → 400;否则 422\n const has400 = issues.some((i) => i.code === 'INVALID_FORMAT' || i.code === 'MISSING_FIELD');\n return has400 ? 400 : 422;\n}\n\nexport class ValidationError extends FaapiError {\n constructor(\n message: string,\n public readonly issues: ValidationIssue[],\n ) {\n super('VALIDATION_ERROR', message, deriveStatusCode(issues));\n this.name = 'ValidationError';\n }\n}\n\nexport class RouteNotFoundError extends FaapiError {\n constructor(path: string) {\n super('ROUTE_NOT_FOUND', `Route not found: ${path}`, 404);\n this.name = 'RouteNotFoundError';\n }\n}\n\nexport class MethodNotAllowedError extends FaapiError {\n constructor(\n method: string,\n path: string,\n public readonly allowedMethods: string[],\n ) {\n super('METHOD_NOT_ALLOWED', `Method ${method} not allowed for ${path}`, 405);\n this.name = 'MethodNotAllowedError';\n }\n}\n\nexport class InternalError extends FaapiError {\n constructor(message: string) {\n super('INTERNAL_ERROR', message, 500);\n this.name = 'InternalError';\n }\n}\n\nexport class ModuleLoadError extends FaapiError {\n constructor(filePath: string, reason: string) {\n super('MODULE_LOAD_ERROR', `Failed to load module ${filePath}: ${reason}`, 500);\n this.name = 'ModuleLoadError';\n }\n}\n\n/**\n * 请求体超过大小限制(413 Payload Too Large)\n *\n * 由 createServer 的 limitStreamSize 在流式读取阶段抛出,\n * 经 formatErrorResponse 转换为 413 响应,而非兜底 500。\n */\nexport class PayloadTooLargeError extends FaapiError {\n constructor(maxSize: number) {\n super('PAYLOAD_TOO_LARGE', `Request body exceeds size limit of ${maxSize} bytes`, 413);\n this.name = 'PayloadTooLargeError';\n }\n}\n\n/**\n * 校验问题类型\n *\n * 结构化错误信息,便于上层(全局错误中间件/前端)按 code 做不同处理,\n * 不依赖字符串解析。message 仅为人类可读的兜底描述。\n *\n * code 与 HTTP 状态码的映射(由 ValidationError 推导):\n * - INVALID_FORMAT / MISSING_FIELD → 400 Bad Request\n * - TYPE_MISMATCH / INVALID_VALUE / COERCE_FAILED → 422 Unprocessable Entity\n */\nexport interface ValidationIssue {\n /** 字段路径,如 'user.address.city' */\n path: string;\n /** 错误码,机器可读的契约 */\n code: ValidationErrorCode;\n /** 期望类型/值,如 'number' / '\"admin\" | \"user\"' */\n expected: string;\n /** 实际类型/值,如 'string' / 'undefined' */\n received: string;\n /** 人类可读的本地化消息(兜底,不保证稳定) */\n message: string;\n}\n\nexport type ValidationErrorCode =\n | 'TYPE_MISMATCH' // 类型不匹配(string 期望 number) → 422\n | 'MISSING_FIELD' // 缺少必填字段 → 400\n | 'INVALID_FORMAT' // 格式错误(JSON 解析失败、Date 非 ISO 等) → 400\n | 'INVALID_VALUE' // 值不在允许范围(字面量/enum 不匹配) → 422\n | 'COERCE_FAILED'; // 类型转换失败(query 字符串转 number/boolean 失败) → 422\n","import type { ResponseConfig } from '../config/configTypes';\nimport { FaapiError } from '../errors/FaapiError';\nimport { ValidationError, MethodNotAllowedError, PayloadTooLargeError } from '../errors/httpErrors';\n\n/**\n * 统一响应格式化中心\n *\n * 集中所有响应包装规则,确保 handler return 值的自动包裹、ctx.fail() 主动错误响应、\n * formatErrorResponse 抛错兜底 三条路径共享同一套 ok/fail 函数,避免响应格式漂移。\n *\n * 路径分工:\n * - 成功路径: handler `return data` → wrapOkResult → toResponse 多类型序列化\n * - 显式成功: handler `return ctx.ok(data)` → wrapOkResult → jsonOk 构造 Response\n * - 主动错误: handler `return ctx.fail({...})` → formatFailResponse → jsonOk\n * - 抛错兜底: handler throw → formatErrorResponse(err, config) → jsonOk\n *\n * 业务方通过 faapi.config.ts 的 `response.ok` / `response.fail` 自定义包装函数,\n * 三条路径自动一致生效。\n */\n\n/** 默认 ok 包装:(data) => ({ data }) */\nexport function defaultOk(data: unknown): Record<string, unknown> {\n return { data };\n}\n\n/**\n * 默认 fail 包装:省略的字段不放入 error 对象\n *\n * `code` 为 undefined 时不放入响应,保持 body 紧凑。\n */\nexport function defaultFail(e: {\n status?: number;\n code?: string;\n message: string;\n}): Record<string, unknown> {\n const error: Record<string, unknown> = { message: e.message };\n if (e.code !== undefined) error.code = e.code;\n return { error };\n}\n\n/** 从 ctx.config 读取 ResponseConfig(业务方在 faapi.config.ts 中配置的 response 字段) */\nexport function getResponseConfig(\n config: Record<string, unknown> | undefined,\n): ResponseConfig | undefined {\n return (config as { response?: ResponseConfig } | undefined)?.response;\n}\n\n/** 解析 ok 函数:业务方自定义 ?? 框架默认 */\nexport function resolveOkFn(config: Record<string, unknown> | undefined) {\n return getResponseConfig(config)?.ok ?? defaultOk;\n}\n\n/** 解析 fail 函数:业务方自定义 ?? 框架默认 */\nexport function resolveFailFn(config: Record<string, unknown> | undefined) {\n return getResponseConfig(config)?.fail ?? defaultFail;\n}\n\n/**\n * 构造 JSON 成功响应(不包裹,原样序列化)\n *\n * 用于 ctx.json / ctx.ok 等显式响应场景。\n */\nexport function jsonOk(body: unknown, status = 200, extraHeaders?: HeadersInit): Response {\n return jsonRaw(body, status, extraHeaders);\n}\n\n/** 内部:构造 JSON Response,不包外层 */\nfunction jsonRaw(body: unknown, status: number, extraHeaders?: HeadersInit): Response {\n const headers = new Headers({ 'Content-Type': 'application/json' });\n if (extraHeaders) {\n const extra = new Headers(extraHeaders);\n extra.forEach((value, key) => headers.set(key, value));\n }\n return new Response(JSON.stringify(body), { status, headers });\n}\n\n/**\n * 成功响应自动包装:handler return 值 → okFn(data) 包裹\n *\n * 规则:\n * - Response 对象:不包裹(ctx.ok/ctx.fail/ctx.json 等返回的 Response 原样透传)\n * - 其他值(含 null/undefined):用 config.response.ok(或默认 (data) => ({ data })) 包裹\n *\n * 注:null/undefined 也会被包裹为 { data: null } / { data: undefined }。\n *\n * 不构造 Response,只返回包装后的值——最终序列化交给 toResponse 处理(支持多类型分发 +\n * 合并 ctx.meta 的 setStatus/setHeader/setCookie)。\n */\nexport function wrapOkResult(\n result: unknown,\n config: Record<string, unknown> | undefined,\n): unknown {\n if (result instanceof Response) return result;\n return resolveOkFn(config)(result);\n}\n\n/**\n * 业务方主动错误响应:FailOptions → Response\n *\n * handler `return ctx.fail({ status, code, message })` 的实现。\n * 用 config.response.fail(或默认实现)包装为 `{ error: { ... } }` 结构。\n *\n * `status` 省略时默认 500;`code` 省略时由 fail 函数决定是否放入 body。\n */\nexport function formatFailResponse(\n options: { status?: number; code?: string; message: string },\n config: Record<string, unknown> | undefined,\n): Response {\n const failFn = resolveFailFn(config);\n const body = failFn({\n status: options.status,\n code: options.code,\n message: options.message,\n });\n return jsonOk(body, options.status ?? 500);\n}\n\n/**\n * 抛错兜底响应:把 handler 抛出的错误转换为统一格式的 Response\n *\n * 覆盖的 FaapiError 子类:\n * - ValidationError → 400/422 + issues\n * - MethodNotAllowedError → 405 + Allow header\n * - PayloadTooLargeError → 413\n * - 其他 FaapiError 子类 → 用 error.statusCode\n * - 未知 Error / 非 Error → 500 INTERNAL_ERROR\n *\n * 与 [formatFailResponse] 共享同一套 fail 函数:业务方在 faapi.config.ts 自定义的\n * `response.fail` 会同时应用到「主动错误响应」和「抛错兜底响应」,确保错误格式一致。\n *\n * @param error handler 抛出的错误\n * @param config 业务方配置(用于读取 response.fail 自定义包装函数,可选)\n */\nexport function formatErrorResponse(\n error: unknown,\n config?: Record<string, unknown> | undefined,\n): Response {\n const failFn = resolveFailFn(config);\n\n if (error instanceof ValidationError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n // ValidationError 需要附加 issues 字段。业务方 failFn 可能返回共享/复用对象\n // (性能优化写法)或冻结对象——就地改写会让 issues 跨请求残留污染或静默失败,\n // 因此浅拷贝后再扩展,不改写业务方返回的原对象\n const bodyObj =\n typeof body === 'object' && body !== null\n ? ({ ...(body as Record<string, unknown>) } as Record<string, unknown>)\n : { error: body };\n const existingError = bodyObj.error as Record<string, unknown> | undefined;\n if (existingError && typeof existingError === 'object') {\n bodyObj.error = { ...existingError, issues: error.issues };\n } else if (typeof body === 'object' && body !== null) {\n // 无 error 包装层:body 本身就是错误对象\n bodyObj.issues = error.issues;\n } else {\n bodyObj.error = { issues: error.issues };\n }\n return jsonOk(bodyObj, error.statusCode);\n }\n\n if (error instanceof MethodNotAllowedError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n return jsonOk(body, error.statusCode, { Allow: error.allowedMethods.join(', ') });\n }\n\n if (error instanceof PayloadTooLargeError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n return jsonOk(body, error.statusCode);\n }\n\n if (error instanceof FaapiError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n return jsonOk(body, error.statusCode);\n }\n\n // 未知错误:500 INTERNAL_ERROR\n const message = error instanceof Error ? error.message : 'An unknown error occurred';\n const body = failFn({ status: 500, code: 'INTERNAL_ERROR', message });\n return jsonOk(body, 500);\n}\n","import type { FaapiContext, ResponseMeta, CookieOptions, FailOptions } from './contextTypes';\nimport { createSseWriter, type SseWriter } from './sse';\nimport { wrapOkResult, formatFailResponse, jsonOk } from '../response/responseFormatter';\n\n/**\n * 解析 Cookie 请求头为 Map\n */\nfunction parseCookies(cookieHeader: string): Map<string, string> {\n const cookies = new Map<string, string>();\n if (!cookieHeader) return cookies;\n for (const pair of cookieHeader.split(';')) {\n const [name, ...rest] = pair.split('=');\n const trimmed = name?.trim();\n if (trimmed) {\n cookies.set(trimmed, rest.join('=').trim());\n }\n }\n return cookies;\n}\n\n/**\n * 格式化 Set-Cookie 值\n */\nfunction formatSetCookie(name: string, value: string, options?: CookieOptions): string {\n let cookie = `${name}=${value}`;\n if (options?.domain) cookie += `; Domain=${options.domain}`;\n if (options?.path) cookie += `; Path=${options.path}`;\n if (options?.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`;\n if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;\n if (options?.httpOnly) cookie += `; HttpOnly`;\n if (options?.secure) cookie += `; Secure`;\n if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;\n return cookie;\n}\n\n/**\n * 从 Request 对象创建 FaapiContext\n * @param request Web Request 对象\n * @param params 动态路由参数\n * @param config 自定义业务配置(来自 faapi.config.ts)\n * @param ip 客户端 IP(由调用方从 IncomingMessage 提取,HTTP/WS 握手均通过 utils/getClientIp)\n *\n * ua 不作为参数传入:User-Agent 是标准 HTTP 请求头,createContext 内部直接从\n * request.headers 读取(与 ip 不同,ip 需要从 IncomingMessage 提取故由调用方传入)。\n */\nexport function createContext(\n request: Request,\n params: Record<string, string>,\n config: Record<string, unknown> = {},\n ip: string = '',\n registries?: FaapiContext['registries'],\n): FaapiContext {\n return createContextFromUrl(request, new URL(request.url), params, config, ip, registries);\n}\n\n/**\n * createContext 的热路径变体:URL 由调用方解析一次后传入\n *\n * createServer 每请求需要 pathname/searchParams,若各自 `new URL(request.url)`\n * 一次请求要重复解析 3~4 次。此变体接收已解析的 URL 对象,searchParams 与\n * 调用方共享(只读)。普通场景用 {@link createContext}。\n */\nexport function createContextFromUrl(\n request: Request,\n url: URL,\n params: Record<string, string>,\n config: Record<string, unknown> = {},\n ip: string = '',\n registries?: FaapiContext['registries'],\n): FaapiContext {\n const meta: ResponseMeta = { headers: {}, setCookies: [] };\n const parsedCookies = parseCookies(request.headers.get('cookie') ?? '');\n const cookiesObj: Record<string, string> = {};\n for (const [key, val] of parsedCookies) {\n cookiesObj[key] = val;\n }\n\n const ctx = {\n request,\n params,\n query: url.searchParams,\n headers: request.headers,\n method: request.method,\n path: url.pathname,\n ip,\n ua: request.headers.get('user-agent') ?? '',\n cookies: cookiesObj,\n config,\n meta,\n\n setStatus(status: number) {\n meta.status = status;\n },\n\n setHeader(key: string, value: string) {\n meta.headers[key] = value;\n },\n\n setETag(value: string) {\n meta.headers['etag'] = value;\n },\n\n redirect(url: string, status = 302): Response {\n return new Response(null, {\n status,\n headers: { Location: url },\n });\n },\n\n json(data: unknown, status?: number): Response {\n return jsonOk(data, status ?? 200);\n },\n\n html(html: string, status?: number): Response {\n const headers: Record<string, string> = { 'Content-Type': 'text/html; charset=utf-8' };\n return new Response(html, {\n status: status ?? 200,\n headers,\n });\n },\n\n getCookie(name: string): string | undefined {\n return parsedCookies.get(name);\n },\n\n setCookie(name: string, value: string, options?: CookieOptions): void {\n meta.setCookies.push(formatSetCookie(name, value, options));\n },\n\n deleteCookie(name: string): void {\n meta.setCookies.push(formatSetCookie(name, '', { maxAge: 0 }));\n },\n\n /**\n * 创建 SSE writer,用于流式推送事件\n *\n * handler 调用此方法后,通过返回的 writer 推送事件,框架自动把 writer.response\n * 作为 HTTP 响应(Content-Type: text/event-stream)。\n *\n * 与 ctx.json / ctx.html 互斥:一个 handler 只能用一种响应方式。\n */\n sse(): SseWriter {\n const writer = createSseWriter();\n const ctxWithSse = ctx as FaapiContext & {\n __sseResponse?: Response;\n __sseWriter?: SseWriter;\n };\n ctxWithSse.__sseResponse = writer.response;\n ctxWithSse.__sseWriter = writer;\n return writer;\n },\n\n /**\n * 显式包装成功响应(返回 Response,不会被自动包裹再次包装)\n *\n * 实现委托给 [responseFormatter.wrapOkResult](../response/responseFormatter.ts),\n * 与 handler `return data` 走的自动包裹路径共享同一套 ok 函数。\n *\n * 用 config.response.ok(或默认 (data) => ({ data })) 包裹 data 并返回 JSON Response。\n * handler 也可直接 return data,框架会自动用 ok 包裹,两者等价。\n */\n ok(data: unknown): Response {\n const body = wrapOkResult(data, config);\n // wrapOkResult 对 Response 对象直接透传(不会包裹),ctx.ok 接收到的是 okFn(data) 的对象\n // 业务方不应在 ctx.ok 里传 Response,这里 fallback 调 jsonOk 序列化\n return jsonOk(body, 200);\n },\n\n /**\n * 返回错误响应(对象形式参数,status 和 code 均可省略)\n *\n * 实现委托给 [responseFormatter.formatFailResponse](../response/responseFormatter.ts),\n * 与 formatErrorResponse(handler 抛错兜底)共享同一套 fail 函数,确保错误格式一致。\n *\n * - status 省略时 HTTP 状态码默认 500\n * - code 省略时响应 body 里不含 code 字段(默认 fail 函数只放非 undefined 的字段)\n * - status 和 code 独立无关联\n *\n * body 用 config.response.fail(或默认实现)包装。\n */\n fail(options: FailOptions): Response {\n return formatFailResponse(options, config);\n },\n } as FaapiContext & { meta: ResponseMeta; __sseResponse?: Response; __sseWriter?: SseWriter };\n\n // app 级注册表(方案 A 实例化)——框架请求链路的 tool/agent/agentHandle 读取来源\n if (registries) {\n ctx.registries = registries;\n }\n\n // 执行用户自定义的 ctx 扩展钩子(config.extendContext)\n const extend = config?.extendContext;\n if (typeof extend === 'function') {\n extend(ctx);\n }\n\n return ctx;\n}\n\n/**\n * 测试专用:从选项对象创建 FaapiContext,免去手写 `new Request(url)` 的样板代码\n *\n * 与 createContext 的关系:createTestContext 内部构造 Request 后调 createContext,\n * 语义完全一致,仅是测试场景的语法糖——不写无意义的 host、query 用对象形式、headers 直接传对象。\n *\n * 为什么不合并进 createContext:createContext 运行时也从真实 HTTP 请求构造 Request,\n * 保持 `(request: Request)` 签名使运行时与测试同构;createTestContext 是纯测试便捷封装,\n * 不引入运行时分支。\n *\n * body 不在此处理:createContext 本身不读 `request.body`,body 注入由 `invokeHandler`\n * 的第 3 个参数负责。POST/PUT/PATCH 测试时 body 单独传给 invokeHandler,避免在两处传 body 产生混淆。\n *\n * @param options 请求选项(path 必填,其余可选)\n * @returns FaapiContext\n */\nexport function createTestContext(options: CreateTestContextOptions): FaapiContext {\n const {\n method = 'GET',\n path,\n query,\n headers,\n params = {},\n config = {},\n ip = '',\n registries,\n } = options;\n\n // 用 URL 解析 + 拼 query,避免手动拼接字符串的转义问题\n const url = new URL(`http://localhost${path}`);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n url.searchParams.append(key, String(item));\n }\n } else {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const request = new Request(url.toString(), {\n method,\n headers: headers as HeadersInit | undefined,\n });\n\n return createContext(request, params, config, ip, registries);\n}\n\n/**\n * createTestContext 的选项\n */\nexport interface CreateTestContextOptions {\n /** app 级注册表(测试 handler 声明 agent/agents 参数时注入用,可选) */\n registries?: FaapiContext['registries'];\n /** 请求方法,默认 'GET' */\n method?: string;\n /** 请求路径,必填,如 '/api/user'(无需写 host) */\n path: string;\n /** 查询参数,对象形式,自动拼接到 URL(值会被 String() 转换;数组生成同名多值参数) */\n query?: Record<string, string | number | boolean | Array<string | number | boolean>>;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 动态路由参数,默认 {} */\n params?: Record<string, string>;\n /** 业务配置(来自 faapi.config.ts),默认 {} */\n config?: Record<string, unknown>;\n /** 客户端 IP,默认 '' */\n ip?: string;\n}\n","/**\n * URLSearchParams → query 对象\n *\n * 重复 key 聚合为数组(对齐 Express qs / Hono getAll 语义):\n * - `?page=1` → `{ page: '1' }`\n * - `?tag=a&tag=b` → `{ tag: ['a', 'b'] }`\n *\n * 此前 last-wins 静默丢弃前面的值(`?ids=1&ids=2` 得 `'2'`),且 query schema\n * 的 coerce 管线对 array 元素同样生成 preprocess——声明 `ids: number[]` 的字段\n * 此前永远校验失败(解析端从未产出数组),属于校验管线输入端漏洞。\n */\nexport function queryToObject(params: URLSearchParams): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of params) {\n const existing = result[key];\n if (existing === undefined) {\n result[key] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n result[key] = [existing, value];\n }\n }\n return result;\n}\n","export type JsonParseResult = { success: true; data: unknown } | { success: false; error: string };\n\nexport function parseJsonBody(text: string): JsonParseResult {\n try {\n const data = JSON.parse(text);\n return { success: true, data };\n } catch {\n return { success: false, error: 'Invalid JSON body' };\n }\n}\n","/**\n * 解析 multipart/form-data 请求\n * 使用 Web 标准 Request.formData() API\n */\n\nexport interface UploadedFile {\n name: string; // 字段名\n filename: string; // 原始文件名\n type: string; // MIME 类型\n size: number; // 文件大小(字节)\n arrayBuffer: () => Promise<ArrayBuffer>; // 获取文件内容\n}\n\nexport interface MultipartResult {\n /** 表单字段(同名字段用数组收集,单值保持 string) */\n fields: Record<string, string | string[]>;\n files: UploadedFile[];\n}\n\n/**\n * 解析 multipart/form-data\n *\n * 同名字段的处理:\n * - 第一次出现:string\n * - 第二次出现:string[](包含所有同名字段的值)\n * - 文件始终收集到 files 数组(通过 name 字段标识归属)\n */\nexport async function parseMultipart(request: Request): Promise<MultipartResult> {\n const formData = await request.formData();\n const fields: Record<string, string | string[]> = {};\n const files: UploadedFile[] = [];\n\n for (const [key, value] of formData.entries()) {\n if (value instanceof File) {\n files.push({\n name: key,\n filename: value.name,\n type: value.type,\n size: value.size,\n arrayBuffer: () => value.arrayBuffer(),\n });\n } else {\n // 同名字段收集为数组\n if (key in fields) {\n const existing = fields[key];\n if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n // in 检查已证明存在\n fields[key] = [existing!, value];\n }\n } else {\n fields[key] = value;\n }\n }\n }\n\n return { fields, files };\n}\n","import { queryToObject } from '../utils/queryToObject';\nimport { parseJsonBody } from '../utils/parseJsonBody';\nimport { parseMultipart } from '../utils/parseMultipart';\nimport { getInputTypeForMethod } from './inputType';\nimport { ValidationError } from '../errors/httpErrors';\n\n/**\n * 根据 HTTP 方法解析主输入\n *\n * - GET / DELETE / HEAD:主输入是 query(URL 查询参数)\n * - POST / PUT / PATCH:主输入是 body(请求体)\n *\n * 注意:DELETE 也可能有 body,但主输入(用于校验)是 query。\n * body 由 createServer 调 resolveBodyForQueryMethod 单独解析后注入\n * (同时消费请求体流,保证 keep-alive 连接可复用)。\n *\n * HTTP 传输语义:\n * - 空请求体(或纯空白)视为无 body,返回 null(handler 可不声明 body 参数)\n * - 请求体非空但 JSON 格式非法,抛 ValidationError(code=INVALID_FORMAT),\n * 不再静默返回 null 导致后续报\"字段缺失\"\n *\n * @param method HTTP 方法\n * @param request Request 对象\n * @returns 解析后的输入值(query 对象或 body 对象)\n * @throws {ValidationError} 当请求体非空但 JSON 格式非法时\n */\nexport async function resolveInput(method: string, request: Request): Promise<unknown> {\n return resolveInputFromUrl(method, request, new URL(request.url));\n}\n\n/**\n * resolveInput 的热路径变体:URL 由调用方解析一次后传入\n *\n * createServer 每请求已持有解析好的 URL(pathname/searchParams 复用),\n * 通过此变体避免 query 分支的重复 `new URL(request.url)`。\n */\nexport async function resolveInputFromUrl(\n method: string,\n request: Request,\n url: URL,\n): Promise<unknown> {\n const inputType = getInputTypeForMethod(method);\n\n // 主输入是 body 的方法:解析请求体\n if (inputType === 'body') {\n const contentType = request.headers.get('content-type') ?? '';\n\n // multipart/form-data:字段 + 文件\n if (contentType.includes('multipart/form-data')) {\n return parseMultipart(request);\n }\n\n // application/x-www-form-urlencoded:表单字段\n if (contentType.includes('application/x-www-form-urlencoded')) {\n const text = await request.text();\n if (isBlankText(text)) return null;\n const params = new URLSearchParams(text);\n const obj: Record<string, string> = {};\n for (const [key, value] of params) {\n obj[key] = value;\n }\n return obj;\n }\n\n // 默认按 JSON 解析(application/json 及其它未明确类型)\n const text = await request.text();\n\n // 空请求体:视为无 body(handler 可不声明 body 参数)\n if (isBlankText(text)) {\n return null;\n }\n\n // 非空请求体:必须能解析为 JSON,否则是格式错误\n const result = parseJsonBody(text);\n if (!result.success) {\n throw new ValidationError('请求体不是合法的 JSON', [\n {\n path: 'body',\n code: 'INVALID_FORMAT',\n expected: 'JSON',\n received: 'text',\n message: '请求体不是合法的 JSON',\n },\n ]);\n }\n return result.data;\n }\n\n // 主输入是 query 的方法(GET / DELETE / HEAD):从 URL 提取 query\n return queryToObject(url.searchParams);\n}\n\n/** 空白文本判断(length 短路 + 正则扫描,避免 trim 的全量字符串拷贝) */\nfunction isBlankText(text: string): boolean {\n return text.length === 0 || !/\\S/.test(text);\n}\n\n/**\n * 解析\"主输入是 query 但允许携带 body\"的方法(DELETE)的请求体\n *\n * DELETE 的主输入(用于校验)是 query,但请求体流必须被消费(keep-alive 连接\n * 上有未读 body 时 Node 只能断开连接,无法复用),且 handler 声明 `body` 参数\n * 时应注入真正的请求体而非 query。\n *\n * 与主输入 body 的方法不同:DELETE body 无对应 schema(schema 提取只产出\n * DELETEQuery),不做校验,原样注入。空请求体返回 undefined(handler 未声明\n * body 参数时注入 undefined,行为一致)。\n *\n * @throws {ValidationError} 当请求体非空但 JSON 格式非法时\n */\nexport async function resolveBodyForQueryMethod(request: Request): Promise<unknown> {\n const text = await request.text();\n if (isBlankText(text)) return undefined;\n const result = parseJsonBody(text);\n if (!result.success) {\n throw new ValidationError('请求体不是合法的 JSON', [\n {\n path: 'body',\n code: 'INVALID_FORMAT',\n expected: 'JSON',\n received: 'text',\n message: '请求体不是合法的 JSON',\n },\n ]);\n }\n return result.data;\n}\n","export function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n // 数组不是普通对象\n if (Array.isArray(value)) {\n return false;\n }\n\n // 检查原型链:普通对象的 prototype 是 Object.prototype 或 null(Object.create(null))\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n}\n","import type { ResponseMeta } from '../runtime/contextTypes';\n\n/**\n * 仅 headers 型 meta 的延迟落头通道\n *\n * Web Response 一旦构造不可变——此前 `ctx.ok()` / `ctx.fail()` 等返回 Response\n * 的场景,即使 meta 只有 headers(helmet 开启后每请求 ~13 个静态头),也要\n * `new Headers()` 拷贝 + `new Response()` 重建一次。改为把 headers 记入\n * WeakMap(按响应对象弱键,响应被 GC 自动清理),由 Node 发送层\n * (sendNodeResponse)最后一次性 `setHeader`——热路径零重建。\n *\n * 中间件若重建 Response(如 compression / etag 的大 body 分支),需用\n * `consumePendingMetaHeaders` 把延迟头并入新 Response 的 headers,避免丢失。\n */\n\nconst pending = new WeakMap<Response, Record<string, string>>();\n\n/**\n * 记录延迟落头(headers-only meta 场景)\n *\n * 同一响应多次记录按 key 合并(后写覆盖,与 mergeMeta 的 headers.set 语义一致)。\n */\nexport function deferMetaHeaders(response: Response, headers: Record<string, string>): void {\n const existing = pending.get(response);\n if (existing) {\n Object.assign(existing, headers);\n return;\n }\n pending.set(response, { ...headers });\n}\n\n/**\n * 取出并清除响应的延迟头(发送层 / 重建方消费)\n */\nexport function consumePendingMetaHeaders(response: Response): Record<string, string> | undefined {\n const headers = pending.get(response);\n if (headers) pending.delete(response);\n return headers;\n}\n\n/** meta 是否仅含 headers(无 status / setCookies,可走延迟通道) */\nexport function isHeadersOnlyMeta(meta: ResponseMeta): boolean {\n return meta.status === undefined && meta.setCookies.length === 0;\n}\n","import { isPlainObject } from '../utils/isPlainObject';\nimport type { ResponseMeta } from '../runtime/contextTypes';\nimport { deferMetaHeaders, isHeadersOnlyMeta } from './pendingMeta';\n\n/**\n * 将 handler 返回值统一转换为 Response\n *\n * 规则:\n * - Response 对象:原样返回\n * - ReadableStream:直接作为 body\n * - Buffer / Uint8Array:作为二进制 body\n * - 普通对象/数组:JSON.stringify,Content-Type: application/json\n * - string:text/plain\n * - number/boolean:text/plain,String(value)\n * - null/undefined:204 No Content\n * - Promise:await 后再处理\n *\n * 如果传入了 meta,会将 ctx.setStatus / ctx.setHeader 的设置合并到 Response 中\n */\nexport async function toResponse(value: unknown, meta?: ResponseMeta): Promise<Response> {\n // Promise:await 后再处理\n if (value instanceof Promise) {\n return toResponse(await value, meta);\n }\n\n // 辅助函数:将 meta headers 和 setCookies 合并到 Headers 对象\n const applyMeta = (headers: Headers): void => {\n if (!meta) return;\n for (const [key, val] of Object.entries(meta.headers)) {\n headers.set(key, val);\n }\n for (const cookie of meta.setCookies ?? []) {\n headers.append('set-cookie', cookie);\n }\n };\n\n // Response 对象:原样返回(但合并 meta headers)\n if (value instanceof Response) {\n if (\n meta &&\n (meta.status !== undefined ||\n Object.keys(meta.headers).length > 0 ||\n meta.setCookies.length > 0)\n ) {\n // 仅 headers 时延迟到发送层落头(避免每请求 Headers 拷贝 + Response 重建,\n // helmet 开启后 meta.headers 恒非空)。status/cookie 需要 Response 级变更,仍重建\n if (isHeadersOnlyMeta(meta)) {\n deferMetaHeaders(value, meta.headers);\n return value;\n }\n const headers = new Headers(value.headers);\n applyMeta(headers);\n return new Response(value.body, {\n status: meta.status ?? value.status,\n headers,\n });\n }\n return value;\n }\n\n // null/undefined:204 No Content\n if (value === null || value === undefined) {\n const status = meta?.status ?? 204;\n const headers = new Headers();\n applyMeta(headers);\n return new Response(null, { status, headers });\n }\n\n // ReadableStream:直接作为 body\n if (typeof ReadableStream !== 'undefined' && value instanceof ReadableStream) {\n const headers = new Headers({ 'Content-Type': 'application/octet-stream' });\n applyMeta(headers);\n return new Response(value, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // Buffer / Uint8Array:作为二进制 body\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n const headers = new Headers({ 'Content-Type': 'application/octet-stream' });\n applyMeta(headers);\n return new Response(value as BodyInit, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n if (value instanceof Uint8Array) {\n const headers = new Headers({ 'Content-Type': 'application/octet-stream' });\n applyMeta(headers);\n return new Response(value as BodyInit, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // 普通对象/数组:JSON.stringify,Content-Type: application/json\n if (isPlainObject(value) || Array.isArray(value)) {\n const body = JSON.stringify(value);\n const headers = new Headers({ 'Content-Type': 'application/json' });\n applyMeta(headers);\n return new Response(body, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // string:text/plain\n if (typeof value === 'string') {\n const headers = new Headers({ 'Content-Type': 'text/plain' });\n applyMeta(headers);\n return new Response(value, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // number/boolean:text/plain,String(value)\n if (typeof value === 'number' || typeof value === 'boolean') {\n const headers = new Headers({ 'Content-Type': 'text/plain' });\n applyMeta(headers);\n return new Response(String(value), {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // 其他类型 fallback:JSON.stringify\n const body = JSON.stringify(value);\n const headers = new Headers({ 'Content-Type': 'application/json' });\n applyMeta(headers);\n return new Response(body, {\n status: meta?.status ?? 200,\n headers,\n });\n}\n","import type { ToolMetadata } from '../ast/extractToolMetadata';\nimport type { AgentMetadata, AgentCore } from '../ast/extractAgentMetadata';\nimport type { FaapiContext } from '../runtime/contextTypes';\n\n/**\n * app 级注册表(方案 A:注册表实例化)\n *\n * 每个应用实例(`createAppBase`)持有一套独立的注册表,随 app 创建、随\n * `app.close()` 销毁——多 app 同进程(测试 / 嵌入 / 多租户)互不串台。\n * 此前注册表是模块级全局单例 + hydrate 整体替换语义,后创建的 app 会覆盖\n * 先创建的 app 的清单,任一 app close 会清空全部(详见各模块 .md)。\n *\n * 原四个模块(toolRegistry / agentRegistry / skillRegistry / agentHandle)\n * 保留同名全局函数作为**默认实例的便捷访问器**(供编程式直调 / 单元测试 /\n * 无 app 上下文的场景),内部全部路由到本模块的 `defaultRegistries`。\n * 框架自身链路(hydrate / 请求注入 / `@faapi/agent` 插件 / lifecycle 钩子)\n * 一律走 app 实例,不再读写全局默认实例。\n */\n\n// ─── Tool 注册表 ─────────────────────────────────────────────────────\n\nexport interface ToolRegistry {\n /** 全量替换(tool 清单来自编译期产物,reload 时整体重新生成) */\n hydrate(tools: ToolMetadata[]): void;\n /** 按全名查找(如 `weather.getWeather`) */\n get(name: string): ToolMetadata | undefined;\n /** 所有已注册 tool(副本) */\n list(): ToolMetadata[];\n clear(): void;\n}\n\nexport function createToolRegistry(): ToolRegistry {\n let registry = new Map<string, ToolMetadata>();\n return {\n hydrate(tools) {\n const next = new Map<string, ToolMetadata>();\n for (const tool of tools) {\n next.set(tool.name, tool);\n }\n registry = next;\n },\n get(name) {\n return registry.get(name);\n },\n list() {\n return Array.from(registry.values());\n },\n clear() {\n registry = new Map();\n },\n };\n}\n\n// ─── Agent 注册表 ────────────────────────────────────────────────────\n\n/** agent 包装为 tool 的描述符(reactLoop 据此识别 sub-agent 递归) */\nexport interface AgentToolDescriptor {\n kind: 'agent';\n name: string;\n agentName: string;\n description?: string;\n metadata: AgentCore;\n}\n\nexport interface AgentRegistry {\n /** 全量替换(与 hydrateToolRegistry 同构) */\n hydrate(agents: AgentMetadata[]): void;\n /** LLM 可见元数据(AgentCore) */\n getAgent(name: string): AgentCore | undefined;\n /** 完整元数据(含 filePath / hasRun,供加载 handler.js 执行 run) */\n getAgentEntry(name: string): AgentMetadata | undefined;\n /** 所有已注册 agent 的 LLM 可见元数据(副本) */\n listAgents(): AgentCore[];\n /** 包装为 tool 描述符 */\n asTool(name: string): AgentToolDescriptor | undefined;\n /**\n * 解析 agent 显式声明的 tool 集合。\n * 跨注册表依赖:经由构造时绑定的 tool 注册表查找(同属一套 AppRegistries,\n * 由 createAppBase 在同一启动阶段水合)\n */\n resolveAgentTools(name: string): ToolMetadata[];\n /** 解析 agent 可调用的子 agent 集合 */\n resolveSubAgents(name: string): AgentCore[];\n clear(): void;\n}\n\nexport function createAgentRegistry(tool: ToolRegistry): AgentRegistry {\n let registry = new Map<string, AgentMetadata>();\n\n const getAgent = (name: string): AgentCore | undefined => registry.get(name);\n\n return {\n hydrate(agents) {\n const next = new Map<string, AgentMetadata>();\n for (const agent of agents) {\n next.set(agent.name, agent);\n }\n registry = next;\n },\n getAgent,\n getAgentEntry(name) {\n return registry.get(name);\n },\n listAgents() {\n const merged = new Map<string, AgentCore>();\n for (const agent of registry.values()) merged.set(agent.name, agent);\n return Array.from(merged.values());\n },\n asTool(name) {\n const agent = getAgent(name);\n if (!agent) return undefined;\n return {\n kind: 'agent',\n name: `agent.${agent.name}`,\n agentName: agent.name,\n description: agent.description,\n metadata: agent,\n };\n },\n resolveAgentTools(name) {\n const agent = getAgent(name);\n if (!agent) return [];\n\n const result = new Map<string, ToolMetadata>();\n if (agent.tools) {\n for (const toolName of agent.tools) {\n const resolved = tool.get(toolName);\n if (resolved) result.set(resolved.name, resolved);\n // 未找到的 tool 名静默跳过(tool 可选可用,不强制存在)\n }\n }\n return Array.from(result.values());\n },\n resolveSubAgents(name) {\n const agent = getAgent(name);\n if (!agent || !agent.agents) return [];\n\n const result: AgentCore[] = [];\n for (const subName of agent.agents) {\n const sub = getAgent(subName);\n if (sub) result.push(sub);\n // 未注册的 agent 名跳过(agent 可选可用,不强制存在)\n }\n return result;\n },\n clear() {\n registry = new Map();\n },\n };\n}\n\n// ─── Skill 注册表 ────────────────────────────────────────────────────\n\nexport interface SkillRegistry {\n /** 全量替换(DB change stream 场景也可用 upsert 增量) */\n hydrate(skills: AgentCore[]): void;\n /** 增量注册 / 覆盖(业务方监听 DB 单条变更) */\n upsert(skill: AgentCore): void;\n remove(name: string): void;\n get(name: string): AgentCore | undefined;\n list(): AgentCore[];\n clear(): void;\n}\n\nexport function createSkillRegistry(): SkillRegistry {\n let registry = new Map<string, AgentCore>();\n return {\n hydrate(skills) {\n const next = new Map<string, AgentCore>();\n for (const skill of skills) {\n next.set(skill.name, skill);\n }\n registry = next;\n },\n upsert(skill) {\n registry.set(skill.name, skill);\n },\n remove(name) {\n registry.delete(name);\n },\n get(name) {\n return registry.get(name);\n },\n list() {\n return Array.from(registry.values());\n },\n clear() {\n registry = new Map();\n },\n };\n}\n\n// ─── Agent handle 工厂 ───────────────────────────────────────────────\n\n/** agent handle 工厂函数(由 `@faapi/agent` 插件注册) */\nexport type AgentHandleFactory = (ctx: FaapiContext) => unknown;\n\nexport interface AgentHandleStore {\n /** 注册工厂(null 清理);二次注册覆盖 */\n register(factory: AgentHandleFactory | null): void;\n /** 工厂已注册时返回 AgentHandle 实例,未注册返回 undefined */\n get(ctx: FaapiContext): unknown;\n clear(): void;\n}\n\nexport function createAgentHandleStore(): AgentHandleStore {\n let currentFactory: AgentHandleFactory | null = null;\n return {\n register(factory) {\n currentFactory = factory;\n },\n get(ctx) {\n if (currentFactory === null) return undefined;\n return currentFactory(ctx);\n },\n clear() {\n currentFactory = null;\n },\n };\n}\n\n// ─── App 级集合 ──────────────────────────────────────────────────────\n\n/** 一个 app 实例持有的全套注册表 */\nexport interface AppRegistries {\n tool: ToolRegistry;\n agent: AgentRegistry;\n skill: SkillRegistry;\n agentHandle: AgentHandleStore;\n}\n\n/** 创建一套 app 级注册表(`createAppBase` 每次调用创建独立实例) */\nexport function createAppRegistries(): AppRegistries {\n const tool = createToolRegistry();\n const agent = createAgentRegistry(tool);\n const skill = createSkillRegistry();\n const agentHandle = createAgentHandleStore();\n return { tool, agent, skill, agentHandle };\n}\n\n/**\n * 默认实例:原全局函数的退路(编程式直调 / 单元测试 / 无 app 上下文场景)。\n *\n * 框架自身链路不读写此实例——app 创建自己的 AppRegistries 并在请求链路 /\n * 插件 / lifecycle 钩子中传递。多 app 场景下默认实例无隔离语义(等同旧全局行为)。\n */\nexport const defaultRegistries: AppRegistries = createAppRegistries();\n","import type { AgentCore, AgentMetadata } from '../ast/extractAgentMetadata';\nimport { defaultRegistries } from './registries';\n\n// 类型从 registries 转发(AgentToolDescriptor 的定义源已随实例化迁入 registries)\nexport type { AgentToolDescriptor } from './registries';\n\n/**\n * agent 注册表全局访问器(默认实例便捷入口)\n *\n * 框架自身链路(`createAppBase` 水合 / `@faapi/agent` 插件 deps / `agents`\n * 参数注入)已改为读写 **app 实例级注册表**(见 [registries.md](./registries.md))——\n * agent 注册表实例在构造时绑定同套 tool 注册表实例(`resolveAgentTools` 跨表查找),\n * 多 app 同进程互不串台。\n *\n * 本模块的同名函数保留为**默认实例**的便捷访问器(语义约束不变:不 fallback 到\n * skillRegistry、Core / Entry 双查询入口),供编程式直调 / 单元测试使用。\n *\n * ## 与 skillRegistry 物理隔离\n *\n * agent 负责核心流程,skill 用于拓展——查询函数不 fallback 到 skillRegistry,\n * 实例化后依旧如此(两套注册表实例互不引用)。\n *\n * 详见 [agentRegistry.md](./agentRegistry.md)。\n */\n\n/**\n * 水合默认实例的 agent 注册表(全量替换)\n *\n * 框架路径请使用 app 实例:`ctx.registries.agent.hydrate(agents)`。\n *\n * @param agents 从 `faapi-agents.js` 水合还原的 `AgentMetadata[]`\n */\nexport function hydrateAgentRegistry(agents: AgentMetadata[]): void {\n defaultRegistries.agent.hydrate(agents);\n}\n\n/**\n * 清空默认实例的 agent 注册表\n *\n * app close 清理的是 app 自己的实例,不经过此函数。\n */\nexport function clearAgentRegistry(): void {\n defaultRegistries.agent.clear();\n}\n\n/**\n * 按名查找单个 agent 的 LLM 可见元数据(默认实例)\n *\n * 返回 [AgentCore](../ast/extractAgentMetadata.md)(不含 filePath / hasRun)。\n * 加载 handler.js 执行 `run` 函数请用 [getAgentEntry](#getAgentEntry)。\n *\n * @param name agent 名(如 `researcher`,含 `@agent` 覆盖值)\n */\nexport function getAgent(name: string): AgentCore | undefined {\n return defaultRegistries.agent.getAgent(name);\n}\n\n/**\n * 按名查找单个 agent 的完整元数据(默认实例,含 filePath / hasRun)\n */\nexport function getAgentEntry(name: string): AgentMetadata | undefined {\n return defaultRegistries.agent.getAgentEntry(name);\n}\n\n/**\n * 返回默认实例所有已注册 agent 的 LLM 可见元数据(副本,仅文件型 agent)\n */\nexport function listAgents(): AgentCore[] {\n return defaultRegistries.agent.listAgents();\n}\n\n/**\n * 把 agent 包装为 tool 描述符(默认实例)\n */\nexport function asTool(name: string) {\n return defaultRegistries.agent.asTool(name);\n}\n\n/**\n * 解析 agent 显式声明的 tool 集合(默认实例,跨查默认 tool 注册表)\n */\nexport function resolveAgentTools(name: string) {\n return defaultRegistries.agent.resolveAgentTools(name);\n}\n\n/**\n * 解析 agent 可调用的子 agent 集合(默认实例,按声明顺序)\n */\nexport function resolveSubAgents(name: string): AgentCore[] {\n return defaultRegistries.agent.resolveSubAgents(name);\n}\n","import type { FaapiContext } from '../runtime/contextTypes';\n\n/**\n * agent handle 工厂注册表(单例)\n *\n * 让 `@faapi/agent` 插件在启动时注册「请求级 agent handle 工厂」,\n * [injectParams](./injectParams.md) 在 `agent` 参数注入时调工厂拿到 `AgentHandle` 实例。\n *\n * 解耦设计:faapi 核心不依赖 `@faapi/agent`——核心只提供注册 / 查询点,\n * 工厂返回 `unknown`,具体类型由 `@faapi/agent` 的 `AgentHandle` 接口定义。\n *\n * 详见 [agentHandle.md](./agentHandle.md)。\n */\n\n/** agent handle 工厂函数(由 `@faapi/agent` 插件注册) */\nexport type AgentHandleFactory = (ctx: FaapiContext) => unknown;\n\nimport { defaultRegistries } from './registries';\n\n/**\n * agent handle 工厂全局访问器(默认实例便捷入口)\n *\n * 框架路径已改为 app 实例:`@faapi/agent` 插件在 setup 时经\n * `ctx.registries.agentHandle.register(factory)` 注册到**当前 app 的实例**,\n * 请求注入经 `ctx.registries.agentHandle.get(ctx)` 读取——多 app 同进程互不覆盖。\n * 此前模块级单值会让第二个 app 的插件注册覆盖第一个。\n *\n * 本模块的同名函数保留为默认实例便捷访问器(向后兼容)。\n *\n * 详见 [agentHandle.md](./agentHandle.md) 与 [registries.md](./registries.md)。\n */\n\n/**\n * 注册默认实例的 agent handle 工厂\n *\n * 框架路径请使用 `ctx.registries.agentHandle.register(factory)`(PluginContext 提供)。\n * 传入 `null` 等效于清理。\n */\nexport function registerAgentHandleFactory(factory: AgentHandleFactory | null): void {\n defaultRegistries.agentHandle.register(factory);\n}\n\n/**\n * 从默认实例获取 agent handle(工厂未注册时返回 undefined)\n */\nexport function getAgentHandle(ctx: FaapiContext): unknown {\n return defaultRegistries.agentHandle.get(ctx);\n}\n\n/**\n * 清空默认实例的工厂注册\n */\nexport function clearAgentHandleFactory(): void {\n defaultRegistries.agentHandle.clear();\n}\n","import type { FaapiContext } from '../runtime/contextTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { resolveInjection, type InjectionType } from './resolveInjection';\nimport { queryToObject } from '../utils/queryToObject';\nimport type { MultipartResult } from '../utils/parseMultipart';\nimport { listAgents } from './agentRegistry';\nimport { getAgentHandle } from './agentHandle';\n\n/**\n * 根据注入类型获取对应的值(内置)\n *\n * Phase 2.3 扩展 `agent` / `agents`:\n * - `agents` → `listAgents()`(所有已注册 agent 元数据列表)\n * - `agent` → `getAgentHandle(ctx)`(Phase 3.5:调 `@faapi/agent` 插件注册的工厂)\n *\n * Phase 3.5 的 `@faapi/agent` 插件通过 [agentHandle](./agentHandle.md) 工厂注册机制\n * 提供 `AgentHandle`(含可调用 `run` / `stream`)。未注册工厂时返回 `undefined`。\n */\nfunction getBuiltinInjectionValue(type: InjectionType, ctx: FaapiContext, body?: unknown): unknown {\n switch (type) {\n case 'query':\n return queryToObject(ctx.query);\n case 'params':\n return ctx.params;\n case 'headers':\n return ctx.headers;\n case 'context':\n return ctx;\n case 'cookies':\n return ctx.cookies;\n case 'ip':\n return ctx.ip;\n case 'ua':\n return ctx.ua;\n case 'body':\n return body;\n // form 与 body 共享解析结果(resolveInput 已按 Content-Type 解析 form-urlencoded)\n // 差异仅在 schema 校验(form coerce=true,由 collectRouteSchemaSources 标记)\n case 'form':\n return body;\n case 'files':\n if (body && typeof body === 'object' && 'files' in body) {\n return (body as MultipartResult).files;\n }\n return [];\n case 'fields':\n if (body && typeof body === 'object' && 'fields' in body) {\n return (body as MultipartResult).fields;\n }\n return {};\n // Phase 2.3:注入所有已注册 agent 元数据列表\n // 方案 A:优先读 app 实例注册表,无实例(编程式直调 ctx)回退默认全局实例\n case 'agents':\n return ctx.registries ? ctx.registries.agent.listAgents() : listAgents();\n // Phase 3.5:调 @faapi/agent 插件注册的工厂获取 AgentHandle\n case 'agent':\n return ctx.registries ? ctx.registries.agentHandle.get(ctx) : getAgentHandle(ctx);\n default:\n return undefined;\n }\n}\n\n/**\n * 根据注入信息,准备参数值并调用 handler(异步版本)\n *\n * 支持 async handler。内置注入优先于注入器(避免 query/body 等被覆盖)。\n * 非内置参数从注入器注册表按参数名查找,按需执行。\n */\nexport async function injectParamsAsync(\n handler: (...args: unknown[]) => unknown,\n ctx: FaapiContext,\n body?: unknown,\n injectors?: InjectorMap,\n): Promise<unknown> {\n const injections = resolveInjection(handler);\n\n if (injections.length === 0) {\n return await handler();\n }\n\n const args = await Promise.all(\n injections.map(async (injection) => {\n // 内置注入优先\n if (injection.type !== 'unknown') {\n return getBuiltinInjectionValue(injection.type, ctx, body);\n }\n // 注入器按参数名匹配\n if (injectors && injection.name in injectors) {\n // in 检查已证明存在\n return await injectors[injection.name]!(ctx);\n }\n return undefined;\n }),\n );\n\n return await handler(...args);\n}\n","import type { FaapiContext } from './contextTypes';\nimport type { ResponseMeta } from './contextTypes';\nimport type { SseWriter } from './sse';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { toResponse } from '../response/toResponse';\nimport { deferMetaHeaders, isHeadersOnlyMeta } from '../response/pendingMeta';\nimport { wrapOkResult } from '../response/responseFormatter';\nimport { injectParamsAsync } from '../injection/injectParams';\n\n/**\n * 自动包裹 handler 返回值(统一响应包装)\n *\n * 实现已移至 [response/responseFormatter.ts](../response/responseFormatter.ts) 的\n * wrapOkResult,与 ctx.ok() / formatFailResponse / formatErrorResponse 共享同一套\n * ok/fail 函数,确保响应格式在所有路径一致。\n *\n * 规则:\n * - Response 对象:不包裹(ctx.ok/ctx.fail/ctx.json 等返回的 Response 原样透传)\n * - 其他值(含 null/undefined):用 config.response.ok(或默认 (data) => ({ data })) 包裹\n *\n * ctx.ok() 返回的是 Response 对象,所以 handler 用 `return ctx.ok(data)` 或 `return data`\n * 最终响应一致(都是 ok(data) 的结果),不会双重包裹。\n *\n * 注意:null/undefined 也会被包裹为 { data: null } / { data: undefined }(JSON.stringify\n * 后者得到 \"{}\"),不再返回 204 No Content。如需 204,显式返回 Response 对象。\n */\nfunction wrapResult(result: unknown, ctx: FaapiContext): unknown {\n return wrapOkResult(result, ctx.config);\n}\n\n/**\n * 将 ctx.meta(setStatus/setHeader/setCookie 设置的响应元数据)合并到 Response\n *\n * 用于中间件返回 Response 时,确保用户通过 ctx.setStatus/setHeader 设置的\n * 状态码、响应头、cookie 能正确附加到中间件返回的 Response 上。\n */\nexport function mergeMeta(response: Response, meta: ResponseMeta): Response {\n const hasMeta =\n meta.status !== undefined || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;\n if (!hasMeta) return response;\n\n // 仅 headers 时延迟到发送层落头(零 Response 重建,见 pendingMeta.ts)\n if (isHeadersOnlyMeta(meta)) {\n deferMetaHeaders(response, meta.headers);\n return response;\n }\n\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(meta.headers)) {\n headers.set(key, value);\n }\n for (const cookie of meta.setCookies) {\n headers.append('set-cookie', cookie);\n }\n return new Response(response.body, {\n status: meta.status ?? response.status,\n headers,\n });\n}\n\n/**\n * 洋葱模型调度:将中间件链包装成 next 函数\n *\n * 每个中间件的 `await next()` 会执行下一个中间件并返回其 Response;\n * 最内层的 next 执行 handler(含参数注入)。\n *\n * 中间件返回 Response 的语义:\n * - 在 await next() 之前返回:拦截请求(不执行后续中间件和 handler)\n * - 在 await next() 之后返回:替换内层返回的响应\n * - 在 catch 块中返回:作为错误响应\n *\n * 中间件返回 void/undefined:使用 await next() 返回的内层响应。\n * 中间件不调用 next() 也不返回 Response:抛错(语义模糊,禁止使用)。\n *\n * 中间件返回 Response 时会合并 ctx.meta(setStatus/setHeader/setCookie 的设置),\n * 确保 ctx 便捷方法在中间件拦截场景下也生效。\n */\nexport async function compose(\n middlewares: FaapiMiddleware[],\n ctx: FaapiContext,\n finalHandler: () => Promise<Response>,\n): Promise<Response> {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n let index = -1;\n\n async function dispatch(i: number): Promise<Response> {\n if (i <= index) {\n throw new Error('next() called multiple times');\n }\n index = i;\n\n // 所有中间件执行完,运行 handler\n if (i >= middlewares.length) {\n return await finalHandler();\n }\n\n // i >= middlewares.length 已早退,此处索引必然存在\n const mw = middlewares[i]!;\n // next() 返回内层 Response,中间件可选择使用或替换\n let innerResponse: Response | undefined;\n const next = async (): Promise<Response> => {\n innerResponse = await dispatch(i + 1);\n return innerResponse;\n };\n\n const result = await mw(ctx, next);\n\n // 中间件返回 Response:使用它(拦截或替换),并合并 ctx.meta\n if (result instanceof Response) {\n return mergeMeta(result, meta);\n }\n // 中间件返回 void:使用 next() 返回的内层响应\n if (innerResponse !== undefined) {\n return innerResponse;\n }\n // 中间件既没返回 Response 也没调用 next():语义模糊,禁止\n throw new Error('中间件必须 await next() 或返回 Response');\n }\n\n return await dispatch(0);\n}\n\n/**\n * 调用路由 handler 并将返回值转为 Response\n *\n * 流程(洋葱模型):\n * 1. 中间件按洋葱模型执行:mw1.before → mw2.before → ... → handler → ... → mw2.after → mw1.after\n * 2. 中间件不调用 next() 即拦截请求(必须返回 Response)\n * 3. 中间件可用 try/catch 捕获内层错误\n * 4. 最内层执行注入器(按需)→ handler\n *\n * 注入器与中间件解耦:\n * - 注入器按 handler 参数名匹配,只执行需要的\n * - 注入器可读取中间件塞进 ctx 的值\n */\nexport async function invokeHandler(\n handler: (...args: unknown[]) => unknown,\n ctx: FaapiContext,\n body?: unknown,\n middlewares?: FaapiMiddleware[],\n injectors?: InjectorMap,\n): Promise<Response> {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n\n /**\n * 若 handler 调用了 ctx.sse(),优先使用 SSE Response\n * (handler 返回值被忽略,SSE 流由 writer 控制)\n *\n * 同时执行自动 close 兜底:若 handler 返回时 writer 仍未关闭,自动调用 close(),\n * 避免连接泄漏。handler 异步循环未退出、忘记 close、或抛错的场景由这里兜底。\n */\n const pickSseAndAutoClose = (): Response | null => {\n const sseWriter = (ctx as FaapiContext & { __sseWriter?: SseWriter }).__sseWriter;\n if (!sseWriter) return null;\n // 兜底:handler 未显式 close 时自动关闭\n if (!sseWriter.closed && !sseWriter.aborted) {\n sseWriter.close();\n }\n return mergeMeta(sseWriter.response, meta);\n };\n\n // 抛错时也保证 writer 被关闭(避免流泄漏)\n const autoCloseSseOnError = (): void => {\n const sseWriter = (ctx as FaapiContext & { __sseWriter?: SseWriter }).__sseWriter;\n if (sseWriter && !sseWriter.closed && !sseWriter.aborted) {\n sseWriter.close();\n }\n };\n\n // 无中间件:直接执行 handler\n if (!middlewares || middlewares.length === 0) {\n try {\n const result = await injectParamsAsync(handler, ctx, body, injectors);\n const sseResponse = pickSseAndAutoClose();\n if (sseResponse) return sseResponse;\n return toResponse(wrapResult(result, ctx), meta);\n } catch (err) {\n // handler 抛错时关闭未完成的 SSE 流,避免泄漏\n autoCloseSseOnError();\n throw err;\n }\n }\n\n // 有中间件:洋葱模型调度\n const finalHandler = async (): Promise<Response> => {\n try {\n const result = await injectParamsAsync(handler, ctx, body, injectors);\n const sseResponse = pickSseAndAutoClose();\n if (sseResponse) return sseResponse;\n return toResponse(wrapResult(result, ctx), meta);\n } catch (err) {\n // handler 抛错时关闭未完成的 SSE 流,避免泄漏\n autoCloseSseOnError();\n throw err;\n }\n };\n\n return await compose(middlewares, ctx, finalHandler);\n}\n","import type { ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { consumePendingMetaHeaders } from './pendingMeta';\n\n/**\n * 将 Web Response 写入 Node.js ServerResponse\n *\n * 使用 stream pipe 处理背压,避免高并发下内存膨胀。\n *\n * 断连语义:客户端中断(res 'error' / 提前 'close')时销毁源流并按正常完成收尾——\n * 网络中断不是服务端错误,不进入 500 兜底、不触发 onError;destroy 使底层\n * web ReadableStream 触发 cancel(),上游生产者(如 SseWriter)感知 aborted\n * 停止推送,避免数据堆积在无消费者的流 buffer 中(内存泄漏)。\n * 源流自身错误(handler 流式写失败)仍 reject,走正常错误响应路径。\n */\nexport async function sendNodeResponse(response: Response, res: ServerResponse): Promise<void> {\n // 设置状态码\n res.statusCode = response.status;\n\n // 设置 headers\n for (const [key, value] of response.headers) {\n if (key.toLowerCase() === 'set-cookie') {\n // Set-Cookie 使用 appendHeader 支持多个值\n res.appendHeader(key, value);\n } else {\n res.setHeader(key, value);\n }\n }\n // 延迟落头(headers-only meta 的零重建通道,见 pendingMeta.ts)——\n // 后写覆盖,与 Headers.set 语义一致\n const deferred = consumePendingMetaHeaders(response);\n if (deferred) {\n for (const [key, value] of Object.entries(deferred)) {\n res.setHeader(key, value);\n }\n }\n\n // 写入 body(使用 pipe 处理背压)\n if (response.body) {\n // TS 5.7 lib.dom 的 ReadableStream 与 node:stream/web 的 ReadableStream 是不同类型,cast 绕过\n const nodeStream = Readable.fromWeb(response.body as never);\n await new Promise<void>((resolve, reject) => {\n // 源流错误:真实错误 → reject(handleRequest catch 走错误响应)\n nodeStream.on('error', reject);\n\n // 客户端断开:销毁源流(触发底层 cancel)+ resolve(不当作服务端错误)\n const abort = () => {\n nodeStream.destroy();\n resolve();\n };\n res.on('error', abort);\n res.on('close', () => {\n // 正常完成也会触发 'close'(在 'finish' 之后,此时 writableEnded=true),不视为断连\n if (!res.writableEnded) abort();\n });\n\n res.on('finish', resolve);\n nodeStream.pipe(res);\n });\n return;\n }\n\n res.end();\n}\n","import { getSchemaName } from './schemaName';\nimport { InternalError } from '../errors/httpErrors';\nimport type { ValidationIssue, ValidationErrorCode } from '../errors/httpErrors';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport { isDevOnDemandEnabled } from '../cli/compileOnDemand';\n\n/**\n * 校验结果类型\n *\n * data 为校验/解析后的原始值(通常为对象;数组 body 为数组,\n * 顶层 primitive body——如 `type POSTBody = string`——为对应原始值)。\n */\nexport interface ValidationResult {\n valid: boolean;\n issues: ValidationIssue[];\n data: unknown;\n}\n\n/**\n * zod.js 模块导出格式\n *\n * 每个 handler 目录下的 zod.js 导出:\n * - `<SchemaName>Schema`:zod schema 对象(用于 safeParse 校验)\n *\n * query/params 的 schema 已在代码生成阶段用 z.preprocess 包裹了字符串转换逻辑\n * (number/boolean 字段),无需运行时再做 coerce。\n *\n * 无类型声明的方法不导出对应 Schema(值为 undefined)。\n */\ninterface SchemaModule {\n [key: string]: unknown;\n}\n\n/**\n * schema 模块缓存:schemaPath → SchemaModule\n *\n * dev 模式下 watch 触发 reloadRoutes 时通过 invalidateSchemaCache() 清空,\n * 下次请求重新 import(importWithCacheBust 会拼接时间戳绕过 ESM 缓存)。\n */\nconst moduleCache = new Map<string, SchemaModule>();\n\n/**\n * 清空 schema 模块缓存(dev watch 模式下文件变化时调用)\n */\nexport function invalidateSchemaCache(): void {\n moduleCache.clear();\n}\n\n/**\n * 加载 schema 模块(带缓存)\n *\n * 首次调用时 import zod.js 并缓存,后续直接返回缓存。\n * dev 模式下 watch 触发 invalidateSchemaCache 后,下次调用重新 import。\n */\nasync function loadSchemaModule(schemaPath: string): Promise<SchemaModule> {\n let mod = moduleCache.get(schemaPath);\n if (!mod) {\n // dev 按需模式下走 Node 原生 import 绕过 Vite SSR 缓存\n // (zod.js 是已编译产物,不需要 Vite alias 解析)\n mod = (await importWithCacheBust(schemaPath, isDevOnDemandEnabled())) as SchemaModule;\n moduleCache.set(schemaPath, mod);\n }\n return mod;\n}\n\n/**\n * 校验输入参数\n *\n * 流程:从 schemaPath import zod.js → zod safeParse\n *\n * schema 来源由调用方确保已生成:\n * - dev 模式:createApp 启动时 + watch 时调 generateSchemaFiles 生成 zod.js\n * - prd 模式:faapi build 时调 generateSchemaFiles 生成 zod.js\n *\n * coerce 说明:\n * - query/params 的 schema 在代码生成阶段已用 z.preprocess 包裹字符串转换逻辑\n * (number: \"1\" → 1,boolean: \"true\" → true),运行时直接 safeParse 即可\n * - body 是 JSON 解析的天然 JS 类型,schema 不含 preprocess\n *\n * 三种状态:\n * - Schema 导出存在:执行 zod safeParse 校验\n * - Schema 导出 undefined(无类型声明):跳过校验\n * - zod.js 文件不存在或 import 失败:抛 InternalError\n *\n * @param schemaPath zod.js 文件绝对路径\n * @param method HTTP 方法\n * @param inputType 输入类型\n * @param input 输入值\n */\nexport async function validateInput(\n schemaPath: string,\n method: string,\n inputType: 'query' | 'body' | 'params',\n input: unknown,\n): Promise<ValidationResult> {\n const schemaName = getSchemaName(method, inputType);\n const schemaKey = `${schemaName}Schema`;\n\n let mod: SchemaModule;\n try {\n mod = await loadSchemaModule(schemaPath);\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new InternalError(`Schema 模块加载失败: ${schemaPath}: ${reason}`);\n }\n\n const schema = mod[schemaKey];\n\n // 无类型声明:跳过校验,input 原样透传(数组/原始值 body 不再静默替换为 {})\n if (schema === undefined || schema === null) {\n return { valid: true, issues: [], data: input };\n }\n\n // schema 必须是带 safeParse 的 zod schema\n if (\n typeof schema !== 'object' ||\n typeof (schema as { safeParse?: unknown }).safeParse !== 'function'\n ) {\n throw new InternalError(`Schema 不是有效的 zod schema: ${schemaPath}#${schemaName}`);\n }\n\n // zod safeParse 校验:input 原样传入(schema 生成端支持数组/顶层 primitive body,\n // 预先替换数组会使命中 z.array 的合法 body 永远失败且 issue 误导为 received object)\n // query/params 的 preprocess 已在 schema 生成阶段内联\n const zodSchema = schema as { safeParse: (v: unknown) => ZodSafeParseResult };\n const result = zodSchema.safeParse(input);\n\n if (result.success) {\n // 解析后的数据原样返回(z.array schema 的 data 是数组,顶层 primitive 是原始值)\n return { valid: true, issues: [], data: result.data };\n }\n\n // 将 zod issues 转为 ValidationIssue\n const issues = mapZodIssues(result.error);\n return { valid: false, issues, data: input };\n}\n\n/**\n * zod safeParse 成功结果\n */\ninterface ZodSafeParseSuccess {\n success: true;\n data: unknown;\n}\n\n/**\n * zod safeParse 失败结果\n */\ninterface ZodSafeParseError {\n success: false;\n error: {\n issues: Array<{\n path: (string | number)[];\n code: string;\n expected?: string;\n received?: string;\n message: string;\n }>;\n };\n}\n\ntype ZodSafeParseResult = ZodSafeParseSuccess | ZodSafeParseError;\n\n/**\n * 将 zod v4 error issues 映射为框架的 ValidationIssue\n *\n * zod code → 框架 ValidationErrorCode 映射:\n * - invalid_type(received 非 undefined) / invalid_union → TYPE_MISMATCH(422)\n * - invalid_type(received === 'undefined',即缺失必填字段)→ MISSING_FIELD(400)\n * - unrecognized_keys → INVALID_FORMAT(400)\n * - invalid_value / invalid_format(v4 的 email/url/uuid/regex 等格式检查)/\n * invalid_key / invalid_element(v4 的 record/map 元素检查)/\n * too_small / too_big / invalid_intersection_types / not_multiple_of / custom\n * → INVALID_VALUE(422)\n *\n * path 数组转为 dot 路径(如 ['user', 'address', 'city'] → 'user.address.city')。\n */\nfunction mapZodIssues(error: ZodSafeParseError['error']): ValidationIssue[] {\n return error.issues.map((issue) => {\n const code = mapZodCode(issue);\n const path = issue.path.map(String).join('.') || '';\n return {\n path,\n code,\n expected: issue.expected ?? mapExpectedFromMessage(issue.message),\n received: issue.received ?? mapReceivedFromMessage(issue.message),\n message: issue.message,\n };\n });\n}\n\ninterface ZodIssueLike {\n code: string;\n expected?: string;\n received?: string;\n message: string;\n}\n\n/**\n * 映射 zod v4 issue code 到框架 ValidationErrorCode\n *\n * zod v4 已移除 invalid_string(v3 遗留),格式类检查(email/url/uuid/regex)\n * 产出 invalid_format;record/map 的键/元素检查产出 invalid_key / invalid_element。\n */\nfunction mapZodCode(issue: ZodIssueLike): ValidationErrorCode {\n switch (issue.code) {\n case 'invalid_type':\n // zod v4 对缺失必填字段产出 invalid_type,无独立 missing code;\n // received 字段缺失,\"received undefined\" 只出现在 message 中\n if (issue.received === 'undefined' || /received undefined/i.test(issue.message)) {\n return 'MISSING_FIELD';\n }\n return 'TYPE_MISMATCH';\n case 'invalid_union':\n case 'invalid_union_discriminator':\n return 'TYPE_MISMATCH';\n case 'unrecognized_keys':\n return 'INVALID_FORMAT';\n case 'invalid_value':\n case 'invalid_format':\n case 'invalid_key':\n case 'invalid_element':\n case 'too_small':\n case 'too_big':\n case 'invalid_intersection_types':\n case 'not_multiple_of':\n case 'custom':\n return 'INVALID_VALUE';\n default:\n return 'INVALID_VALUE';\n }\n}\n\n/**\n * 从 zod message 中提取期望类型(兜底)\n */\nfunction mapExpectedFromMessage(message: string): string {\n // zod v3 message 示例:\"Expected string, received number\"\n const match = message.match(/Expected\\s+(\\w+)/i);\n return match ? match[1]!.toLowerCase() : 'unknown';\n}\n\n/**\n * 从 zod message 中提取实际类型(兜底)\n */\nfunction mapReceivedFromMessage(message: string): string {\n const match = message.match(/received\\s+(\\w+)/i);\n return match ? match[1]!.toLowerCase() : 'unknown';\n}\n","import type { IncomingMessage } from 'node:http';\n\n/**\n * 从 IncomingMessage 提取客户端 IP\n *\n * 优先级:\n * 1. `trustedProxy=true` 时,`x-forwarded-for` 第一个 IP(反向代理场景,如 nginx/CDN)\n * 2. `req.socket.remoteAddress`(直连场景,默认路径)\n *\n * `x-forwarded-for` 格式:`client, proxy1, proxy2`,取第一个即客户端真实 IP。\n *\n * **默认不信任该 header**(`trustedProxy=false`):客户端直连时 XFF 可被任意伪造,\n * 污染 `ctx.ip`(影响限流/日志/风控)。部署在受信任的反向代理之后时,通过\n * `faapi.config.ts` 的 `trustedProxy: true` 显式开启。\n *\n * @param trustedProxy 是否信任反向代理头(默认 false——安全默认,直连不可伪造)\n * @returns 客户端 IP,无法获取时返回空字符串\n */\nexport function getClientIp(req: IncomingMessage, trustedProxy = false): string {\n // 1. x-forwarded-for(仅显式信任反向代理时)\n if (trustedProxy) {\n const xff = req.headers['x-forwarded-for'];\n if (typeof xff === 'string' && xff.length > 0) {\n const first = xff.split(',')[0]?.trim();\n if (first) return first;\n }\n }\n\n // 2. 直连 socket IP\n const remote = req.socket?.remoteAddress;\n if (remote) {\n // 去掉 IPv6 前缀 ::ffff:,统一返回 IPv4 形式\n if (remote.startsWith('::ffff:')) {\n return remote.slice(7);\n }\n return remote;\n }\n\n return '';\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\n\nexport interface CorsOptions {\n origin?: string | string[] | true;\n methods?: string[];\n allowedHeaders?: string[];\n exposeHeaders?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\nconst DEFAULT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];\n\n/**\n * 创建 CORS 中间件(洋葱模型)\n *\n * - origin=true: 允许所有来源(反射请求的 Origin)\n * - origin=string: 允许指定来源\n * - origin=string[]: 允许多个来源\n *\n * OPTIONS 预检请求直接返回 204,不调用 next()。\n */\nexport function cors(options: CorsOptions = {}): FaapiMiddleware {\n const {\n origin = true,\n methods = DEFAULT_METHODS,\n allowedHeaders,\n exposeHeaders,\n credentials = false,\n maxAge,\n } = options;\n\n return async (ctx, next) => {\n const reqOrigin = ctx.headers.get('origin');\n if (!reqOrigin) {\n await next();\n return;\n }\n\n // 确定允许的来源\n let allowOrigin: string | null = null;\n if (origin === true) {\n allowOrigin = reqOrigin; // 反射 Origin\n } else if (typeof origin === 'string') {\n allowOrigin = reqOrigin === origin ? origin : null;\n } else if (Array.isArray(origin)) {\n allowOrigin = origin.includes(reqOrigin) ? reqOrigin : null;\n }\n\n // 当 origin 为动态值(true 或数组)时,响应内容(是否带 ACAO)随 Origin 头变化\n // ——不匹配被拒的响应同样必须带 Vary: Origin。缺了它,CDN/浏览器按 URL 缓存\n // \"无 ACAO 的拒绝响应\"后,可能服务给后续合法 Origin 的请求(缓存污染面)\n if (origin === true || Array.isArray(origin)) {\n const existingVary = ctx.headers.get('vary');\n if (existingVary) {\n if (!existingVary.toLowerCase().includes('origin')) {\n ctx.setHeader('Vary', `${existingVary}, Origin`);\n }\n } else {\n ctx.setHeader('Vary', 'Origin');\n }\n }\n\n if (!allowOrigin) {\n await next();\n return;\n }\n\n // 设置 CORS 响应头\n ctx.setHeader('Access-Control-Allow-Origin', allowOrigin);\n\n ctx.setHeader('Access-Control-Allow-Methods', methods.join(', '));\n\n if (allowedHeaders) {\n ctx.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));\n } else {\n // 反射请求头\n const requestHeaders = ctx.headers.get('access-control-request-headers');\n if (requestHeaders) {\n ctx.setHeader('Access-Control-Allow-Headers', requestHeaders);\n }\n }\n\n if (exposeHeaders && exposeHeaders.length > 0) {\n ctx.setHeader('Access-Control-Expose-Headers', exposeHeaders.join(', '));\n }\n\n if (credentials) {\n ctx.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n\n if (maxAge !== undefined) {\n ctx.setHeader('Access-Control-Max-Age', String(maxAge));\n }\n\n // 处理 preflight 请求:直接返回 204,不调用 next()\n if (ctx.method === 'OPTIONS') {\n return new Response(null, { status: 204 });\n }\n\n await next();\n };\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\n\nexport interface HelmetOptions {\n contentSecurityPolicy?: string | false;\n xFrameOptions?: 'DENY' | 'SAMEORIGIN' | false;\n xContentTypeOptions?: boolean;\n referrerPolicy?: string | false;\n strictTransportSecurity?: string | false;\n xDnsPrefetchControl?: boolean;\n xDownloadOptions?: boolean;\n xPermittedCrossDomainPolicies?: string | false;\n crossOriginOpenerPolicy?: string | false;\n crossOriginResourcePolicy?: string | false;\n crossOriginEmbedderPolicy?: string | false;\n originAgentCluster?: boolean;\n xPoweredBy?: boolean;\n}\n\nconst DEFAULTS: Required<Omit<HelmetOptions, 'contentSecurityPolicy'>> & {\n contentSecurityPolicy: string | false;\n} = {\n contentSecurityPolicy: \"default-src 'self'\",\n xFrameOptions: 'SAMEORIGIN',\n xContentTypeOptions: true,\n referrerPolicy: 'no-referrer',\n strictTransportSecurity: 'max-age=31536000; includeSubDomains',\n xDnsPrefetchControl: true,\n xDownloadOptions: true,\n xPermittedCrossDomainPolicies: 'none',\n crossOriginOpenerPolicy: 'same-origin',\n crossOriginResourcePolicy: 'same-origin',\n crossOriginEmbedderPolicy: false,\n originAgentCluster: true,\n xPoweredBy: true,\n};\n\nexport function helmet(options: HelmetOptions = {}): FaapiMiddleware {\n const opts = { ...DEFAULTS, ...options };\n\n return async (ctx, next) => {\n if (opts.contentSecurityPolicy !== false) {\n ctx.setHeader('Content-Security-Policy', opts.contentSecurityPolicy);\n }\n if (opts.xFrameOptions !== false) {\n ctx.setHeader('X-Frame-Options', opts.xFrameOptions);\n }\n if (opts.xContentTypeOptions) {\n ctx.setHeader('X-Content-Type-Options', 'nosniff');\n }\n if (opts.referrerPolicy !== false) {\n ctx.setHeader('Referrer-Policy', opts.referrerPolicy);\n }\n if (opts.strictTransportSecurity !== false) {\n ctx.setHeader('Strict-Transport-Security', opts.strictTransportSecurity);\n }\n if (opts.xDnsPrefetchControl) {\n ctx.setHeader('X-DNS-Prefetch-Control', 'off');\n }\n if (opts.xDownloadOptions) {\n ctx.setHeader('X-Download-Options', 'noopen');\n }\n if (opts.xPermittedCrossDomainPolicies !== false) {\n ctx.setHeader('X-Permitted-Cross-Domain-Policies', opts.xPermittedCrossDomainPolicies);\n }\n if (opts.crossOriginOpenerPolicy !== false) {\n ctx.setHeader('Cross-Origin-Opener-Policy', opts.crossOriginOpenerPolicy);\n }\n if (opts.crossOriginResourcePolicy !== false) {\n ctx.setHeader('Cross-Origin-Resource-Policy', opts.crossOriginResourcePolicy);\n }\n if (opts.crossOriginEmbedderPolicy !== false) {\n ctx.setHeader('Cross-Origin-Embedder-Policy', opts.crossOriginEmbedderPolicy);\n }\n if (opts.originAgentCluster) {\n ctx.setHeader('Origin-Agent-Cluster', '?1');\n }\n if (opts.xPoweredBy) {\n ctx.setHeader('X-Powered-By', 'faapi');\n }\n\n return await next();\n };\n}\n","import zlib from 'node:zlib';\nimport { promisify } from 'node:util';\nimport type { FaapiMiddleware } from './middlewareTypes';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport { consumePendingMetaHeaders } from '../response/pendingMeta';\n\nconst gzipAsync = promisify(zlib.gzip);\nconst deflateAsync = promisify(zlib.deflate);\nconst brotliAsync = promisify(zlib.brotliCompress);\n\nexport interface CompressionOptions {\n /**\n * 最小压缩字节数,body 低于该值不压缩(小 payload 压缩后反而变大)\n * @default 1024\n */\n threshold?: number;\n}\n\nconst DEFAULT_THRESHOLD = 1024;\n\n/** 可压缩的 Content-Type(text/event-stream 除外——流式语义不应缓冲) */\nconst COMPRESSIBLE_CHECKS: Array<(contentType: string) => boolean> = [\n (ct) => ct === 'application/json',\n (ct) => ct === 'application/javascript',\n (ct) => ct === 'image/svg+xml',\n (ct) => ct.startsWith('text/') && ct !== 'text/event-stream',\n];\n\nfunction isCompressible(contentType: string): boolean {\n const ct = contentType.split(';')[0]!.trim().toLowerCase();\n if (ct === '') return false;\n return COMPRESSIBLE_CHECKS.some((check) => check(ct));\n}\n\ninterface AcceptedEncoding {\n encoding: string;\n quality: number;\n}\n\n/**\n * 解析 Accept-Encoding 头(含 q 值)\n *\n * `gzip, br;q=0.8, identity;q=0, *;q=0.5` → [{gzip,1},{br,0.8},{identity,0},{*,0.5}]\n */\nexport function parseAcceptEncoding(header: string): AcceptedEncoding[] {\n return header\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part !== '')\n .map((part) => {\n const [encoding, ...params] = part.split(';');\n let quality = 1;\n for (const param of params) {\n const trimmed = param.trim();\n if (trimmed.startsWith('q=')) {\n const q = Number(trimmed.slice(2));\n if (Number.isFinite(q)) quality = q;\n }\n }\n return { encoding: (encoding ?? '').trim().toLowerCase(), quality };\n })\n .filter((e) => e.encoding !== '' && e.quality > 0);\n}\n\n/**\n * 从已接受的编码中选择压缩算法\n *\n * 服务器偏好 br > gzip > deflate(同等 q 值下);`*` 通配按 gzip 处理。\n * 未命中返回 null(客户端不接受压缩)。\n */\nexport function selectEncoding(accepted: AcceptedEncoding[]): 'br' | 'gzip' | 'deflate' | null {\n const byName = new Map<string, number>();\n for (const { encoding, quality } of accepted) {\n // 同一编码出现多次取最高 q\n byName.set(encoding, Math.max(byName.get(encoding) ?? 0, quality));\n }\n // q=0 表示明确不接受\n for (const [encoding, quality] of byName) {\n if (quality === 0) byName.delete(encoding);\n }\n\n for (const candidate of ['br', 'gzip', 'deflate'] as const) {\n if ((byName.get(candidate) ?? 0) > 0) return candidate;\n }\n if ((byName.get('*') ?? 0) > 0) return 'gzip';\n return null;\n}\n\nasync function compressBody(\n encoding: 'br' | 'gzip' | 'deflate',\n body: string,\n): Promise<Uint8Array> {\n const buf = Buffer.from(body, 'utf-8');\n switch (encoding) {\n case 'br':\n return brotliAsync(buf);\n case 'gzip':\n return gzipAsync(buf);\n case 'deflate':\n return deflateAsync(buf);\n }\n}\n\n/** 向 meta.headers 合并 Vary 值(CORS 等中间件可能已设置 Vary: Origin,不能覆盖) */\nfunction mergeVary(meta: ResponseMeta, value: string): void {\n const existing = meta.headers['Vary'] ?? meta.headers['vary'];\n if (!existing) {\n meta.headers['Vary'] = value;\n return;\n }\n // 大小写不敏感地检查是否已包含\n if (!existing.toLowerCase().includes(value.toLowerCase())) {\n const key = meta.headers['Vary'] !== undefined ? 'Vary' : 'vary';\n meta.headers[key] = `${existing}, ${value}`;\n }\n}\n\n/**\n * 创建响应压缩中间件(内建,默认关闭,`config.compression` 显式启用)\n *\n * 位于外层中间件链最前(包住 CORS/helmet/logger/全局),`await next()` 后拿到\n * 最终 Response:满足压缩条件(可压缩类型 + 大小达标 + 客户端接受)时缓冲 body\n * 异步压缩后构造新 Response 替换;否则透传。`Vary: Accept-Encoding` 通过\n * meta.headers 合并(与 CORS 的 Vary: Origin 共存),两种路径都生效。\n */\nexport function compression(options: CompressionOptions = {}): FaapiMiddleware {\n const threshold = options.threshold ?? DEFAULT_THRESHOLD;\n\n return async (ctx, next) => {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n const response = await next();\n if (!response) return response;\n\n // 无条件协商 Vary(响应内容随 Accept-Encoding 变化,与是否压缩本响应无关)\n mergeVary(meta, 'Accept-Encoding');\n\n // 跳过条件:客户端不接受 / 不可压缩类型 / 已编码 / no-transform / 204、304 等\n const acceptEncoding = ctx.request.headers.get('accept-encoding') ?? '';\n const encoding = selectEncoding(parseAcceptEncoding(acceptEncoding));\n const contentType = response.headers.get('content-type') ?? '';\n if (\n !encoding ||\n !isCompressible(contentType) ||\n response.headers.has('content-encoding') ||\n (response.headers.get('cache-control') ?? '').includes('no-transform') ||\n response.status === 204 ||\n response.status === 304 ||\n response.body === null\n ) {\n return response;\n }\n\n // 缓冲 body(toResponse 的 JSON body 本就是字符串);SSE 已被 content-type 排除\n // 延迟落头(headers-only meta)需并入重建的 Response,否则发送层取不到\n const deferredHeaders = consumePendingMetaHeaders(response);\n\n const bodyText = await response.text();\n if (bodyText.length < threshold) {\n // 未达阈值:body 已被 text() 消费,需重建等价 Response\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n headers.set(key, value);\n }\n return new Response(bodyText, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n const compressed = await compressBody(encoding, bodyText);\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n headers.set(key, value);\n }\n headers.set('Content-Encoding', encoding);\n // Content-Length 由 Response 构造时按压缩后字节数自动设置(先删避免重复)\n headers.delete('Content-Length');\n return new Response(compressed as unknown as BodyInit, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n","import { createHash } from 'node:crypto';\nimport type { FaapiMiddleware } from './middlewareTypes';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport { consumePendingMetaHeaders } from '../response/pendingMeta';\n\nexport interface EtagOptions {\n /**\n * 生成弱 ETag(`W/\"<hash>\"`)。弱校验器允许压缩等表示差异下的 304 协商,\n * 与 compression 中间件配合正确;强 ETag 需对每个表示(编码)单独生成\n * @default true\n */\n weak?: boolean;\n}\n\nconst DEFAULTS: Required<EtagOptions> = { weak: true };\n\n/** 计算 ETag:SHA-1 指纹 base64;weak=true 生成弱校验器(W/ 前缀) */\nfunction computeEtag(body: string, weak: boolean): string {\n const hash = createHash('sha1').update(body).digest('base64');\n return weak ? `W/\"${hash}\"` : `\"${hash}\"`;\n}\n\n/** If-None-Match 比对:weak 模式用 RFC 7232 弱比较(忽略 W/ 前缀);strong 模式精确比对 */\nfunction ifNoneMatchMatches(ifNoneMatch: string, etag: string, weak: boolean): boolean {\n const normalize = (tag: string) => tag.trim().replace(/^W\\//i, '');\n if (weak) {\n const target = normalize(etag);\n return ifNoneMatch.split(',').some((tag) => {\n const trimmed = tag.trim();\n return trimmed === '*' || normalize(trimmed) === target;\n });\n }\n // 强比较:ETag 原样相等(含 W/ 前缀语义,弱 ETag 永不匹配强比较)\n return ifNoneMatch.split(',').some((tag) => tag.trim() === etag || tag.trim() === '*');\n}\n\n/**\n * 创建 ETag/304 协商中间件(内建,默认关闭,`config.etag` 显式启用)\n *\n * `await next()` 后对 GET/HEAD 2xx 响应计算弱 ETag 并写入 meta(handler 显式\n * `ctx.setETag()` 时不覆盖);请求 `If-None-Match` 弱比较命中时返回 304(无 body)。\n *\n * 中间件位置在 compression 内层:先算 ETag/304 再压缩——304 无 body 压缩自动跳过;\n * 弱 ETag 基于未压缩表示计算,语义正确。\n */\nexport function etag(options: EtagOptions = {}): FaapiMiddleware {\n const opts = { ...DEFAULTS, ...options };\n\n return async (ctx, next) => {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n const response = await next();\n if (!response) return response;\n\n // 仅协商幂等方法的 2xx 响应\n const method = ctx.request.method.toUpperCase();\n if (method !== 'GET' && method !== 'HEAD') return response;\n if (response.status < 200 || response.status >= 300) return response;\n\n // handler 已显式设置 ETag(ctx.setETag 写 meta.headers.etag)→ 控制权归 handler\n if (meta.headers['etag'] !== undefined || response.headers.has('etag')) {\n return response;\n }\n // 流式响应(SSE 等)不缓冲\n if (\n response.body === null ||\n (response.headers.get('content-type') ?? '').includes('text/event-stream')\n ) {\n return response;\n }\n\n const bodyText = await response.text();\n const etagValue = computeEtag(bodyText, opts.weak);\n\n // If-None-Match 弱比较命中 → 304(无 body,携带 ETag)\n const ifNoneMatch = ctx.request.headers.get('if-none-match');\n if (ifNoneMatch && ifNoneMatchMatches(ifNoneMatch, etagValue, opts.weak)) {\n return new Response(null, {\n status: 304,\n statusText: response.statusText,\n headers: { ETag: etagValue },\n });\n }\n\n // 200 路径:body 已被 text() 消费,重建响应;ETag 经 meta 传递(mergeMeta 兜底应用)\n meta.headers['etag'] = etagValue;\n const headers = new Headers(response.headers);\n // 延迟落头并入重建的 Response(etag 中间件位于 compression 内层,先消费)\n const deferredHeaders = consumePendingMetaHeaders(response);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n headers.set(key, value);\n }\n return new Response(bodyText, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\n\nexport type LoggerFn = (messageOrObj: string | Record<string, unknown>, message?: string) => void;\n\nexport interface LoggerOptions {\n /**\n * 自定义日志函数\n *\n * - 传入 `console.log`(默认):纯文本格式 `GET /api/users 200 12ms`\n * - 传入 pino logger:结构化日志 `logger.info({ method, path, status, durationMs }, 'request completed')`\n * - 传入 winston logger:`logger.info('GET /api/users 200 12ms', { method, path })`\n */\n log?: LoggerFn;\n}\n\n/**\n * 创建请求日志中间件(洋葱模型)\n *\n * 日志格式(文本模式):GET /api/users 200 12ms\n * 错误格式(文本模式):POST /api/users 400 45ms - Error: ...\n *\n * 结构化模式:传入 pino/winston 等 logger 实例时,会自动传递结构化字段。\n *\n * before/after 一体,闭包变量共享开始时间,无需污染 ctx。\n * 错误用 try/catch 捕获,记录后重新抛出(让上层处理)。\n * 成功时从 next() 返回的 Response 读取状态码。\n *\n * log 函数每次请求时读取(options.log ?? console.log),运行时替换 console.log 会生效。\n */\nexport function logger(options: LoggerOptions = {}): FaapiMiddleware {\n return async (ctx, next) => {\n const log = options.log ?? console.log;\n const start = Date.now();\n try {\n const response = await next();\n const duration = Date.now() - start;\n const entry = {\n method: ctx.method,\n path: ctx.path,\n status: response.status,\n durationMs: duration,\n };\n log(entry, `${ctx.method} ${ctx.path} ${response.status} ${duration}ms`);\n return response;\n } catch (err) {\n const duration = Date.now() - start;\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as { statusCode?: number })?.statusCode ?? 500;\n const entry = {\n method: ctx.method,\n path: ctx.path,\n status,\n durationMs: duration,\n error: message,\n };\n log(entry, `${ctx.method} ${ctx.path} ${status} ${duration}ms - ${message}`);\n throw err;\n }\n };\n}\n","/**\n * formatErrorResponse 的 re-export\n *\n * 实现已移至 [response/responseFormatter.ts](../response/responseFormatter.ts),\n * 与 wrapOkResult / formatFailResponse 共享同一套 ok/fail 函数,确保错误响应格式\n * 在「handler 抛错兜底」和「handler return ctx.fail()」两条路径一致。\n *\n * 保留此文件作为 errors/ 模块的入口,便于 errors 模块内部互引 +\n * 不破坏现有 import 路径(formatErrorResponse.test.ts / serverUtils.ts 等)。\n */\nexport { formatErrorResponse } from '../response/responseFormatter';\n","import type { IncomingMessage } from 'node:http';\nimport { formatErrorResponse } from '../errors/formatErrorResponse';\n\n/**\n * 将 Node.js IncomingMessage 的 headers 转为 Web Headers\n *\n * 处理数组型 header 值(如 set-cookie)和 undefined 值。\n */\nexport function nodeHttpToWebHeaders(req: IncomingMessage): Headers {\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const v of value) headers.append(key, v);\n } else {\n headers.set(key, value);\n }\n }\n return headers;\n}\n\n/**\n * 构建错误响应(兜底链)\n *\n * 1. 框架内置 formatErrorResponse 处理(handler 抛错时)——读 config.response.fail\n * 自定义包装函数,确保错误格式与 ctx.fail() 主动错误响应一致\n * 2. 内置兜底仍抛错 → 最简 500 JSON 响应\n *\n * 业务方如需进一步自定义错误响应,在全局中间件中 try/catch next() 即可。\n *\n * @param err handler 抛出的错误\n * @param config 业务方配置(用于读取 response.fail,可选——未传时走框架默认 fail 函数)\n */\nexport function buildErrorResponse(\n err: unknown,\n config?: Record<string, unknown> | undefined,\n): Response {\n try {\n return formatErrorResponse(err, config);\n } catch {\n // 极端情况:内置兜底也失败,返回最简 500\n return new Response(\n JSON.stringify({ error: { code: 'INTERNAL_ERROR', message: 'Internal Server Error' } }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n },\n );\n }\n}\n","/**\n * WebSocket Handler 类型定义与 socket 封装\n *\n * 用户在 handler.ts 中导出 WS 函数,返回事件回调对象。\n * 框架在协议升级成功后调用对应回调,传递封装后的 WsSocket。\n *\n * @see wsHandler.md 功能说明\n */\n\n/**\n * faapi 封装的 WebSocket socket 抽象\n *\n * 不直接暴露 ws 库的原生 socket,提供更安全、易用的 API。\n * send 对象时自动 JSON.stringify。\n */\nexport interface WsSocket {\n /** 发送数据(string/Buffer 直发,对象自动 JSON.stringify) */\n send(data: string | Buffer | object): void;\n /** 关闭连接 */\n close(code?: number, reason?: string | Buffer): void;\n /** 连接状态:0=connecting, 1=open, 2=closing, 3=closed */\n readonly readyState: number;\n}\n\n/**\n * WebSocket 事件回调集合\n *\n * 用户在 WS handler 中返回此对象,框架在对应事件触发时调用。\n * 所有回调可选,未提供则忽略事件。\n */\nexport interface WsEventHandlers {\n /** 连接建立时触发 */\n onOpen?: (ws: WsSocket) => void;\n /** 收到客户端消息时触发 */\n onMessage?: (ws: WsSocket, message: string | Buffer) => void;\n /** 连接关闭时触发 */\n onClose?: (ws: WsSocket, code: number, reason: string) => void;\n /** 发生错误时触发 */\n onError?: (ws: WsSocket, error: Error) => void;\n}\n\n/**\n * WebSocket 上下文(握手阶段构造)\n *\n * 与 HTTP FaapiContext 类似但精简,提供路由参数、查询参数、请求头、配置。\n * 可通过 declare module '@faapi/faapi' 增强自定义字段。\n */\nexport interface WsContext {\n /** 动态路由参数(如 [id] → params.id) */\n params: Record<string, string>;\n /** URL 查询参数 */\n query: URLSearchParams;\n /** 请求头 */\n headers: Headers;\n /** 业务配置(来自 faapi.config.ts) */\n config: Record<string, unknown>;\n /** 中间件塞入的用户信息(鉴权等) */\n user?: unknown;\n /** 允许通过 declare module 扩展 */\n [key: string]: unknown;\n}\n\n/**\n * WS handler 签名:接收 WsContext,返回事件回调对象(或无返回)\n */\nexport type WsHandler = (ctx: WsContext) => WsEventHandlers | void;\n\n/**\n * 将 ws 库的原生 WebSocket 封装为 faapi 的 WsSocket\n *\n * - send 对象时自动 JSON.stringify\n * - close/re.readyState 透传原生 socket\n */\nexport function wrapWsSocket(rawSocket: {\n send: (data: string | Buffer, cb?: (err?: Error) => void) => void;\n close: (code?: number, reason?: string | Buffer) => void;\n readonly readyState: number;\n}): WsSocket {\n return {\n send(data: string | Buffer | object): void {\n const payload =\n typeof data === 'string' || Buffer.isBuffer(data) ? data : JSON.stringify(data);\n rawSocket.send(payload);\n },\n close(code?: number, reason?: string | Buffer): void {\n rawSocket.close(code, reason);\n },\n get readyState(): number {\n return rawSocket.readyState;\n },\n };\n}\n","/**\n * WebSocket 协议升级处理\n *\n * 监听 HTTP server 的 upgrade 事件,匹配 WS 路由后调用 ws 库完成协议升级,\n * 加载 handler.ts 的 WS 导出,绑定事件回调。\n *\n * 握手阶段复用洋葱中间件链(鉴权/CORS/限流/日志等),与同目录 HTTP 路由共享中间件。\n * 中间件塞入 ctx 的值(如 ctx.user)传入 WS handler。连接建立后切到事件模型,不走中间件。\n *\n * @see wsHandler.md WS handler 约定与握手中间件链设计\n */\nimport type { IncomingMessage } from 'node:http';\nimport type { Server } from 'node:http';\nimport type { Socket } from 'node:net';\nimport fs from 'node:fs';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport type { RawData } from 'ws';\nimport path from 'node:path';\nimport type { WsRouteMatch, RoutesRef } from '../router/routeTypes';\nimport { matchWsRoute } from '../router/matchRoute';\nimport { createContext } from '../runtime/createContext';\nimport { compose, mergeMeta } from '../runtime/invokeHandler';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport { getClientIp } from '../utils/getClientIp';\nimport { nodeHttpToWebHeaders, buildErrorResponse } from './serverUtils';\nimport {\n ensureCompiled,\n ensureMiddlewaresCompiled,\n isDevOnDemandEnabled,\n getDevDist,\n prodPathToSourcePath,\n} from '../cli/compileOnDemand';\nimport { loadMergedMiddlewares } from '../middleware/loadMiddlewares';\nimport type { AppRegistries } from '../injection/registries';\nimport { consumePendingMetaHeaders } from '../response/pendingMeta';\nimport {\n wrapWsSocket,\n type WsContext,\n type WsEventHandlers,\n type WsHandler,\n} from '../runtime/wsHandler';\n\n/**\n * 从 IncomingMessage 提取 URL pathname(不含 query)\n */\nfunction getPathname(req: IncomingMessage): string {\n const url = req.url ?? '/';\n const idx = url.indexOf('?');\n return idx >= 0 ? url.slice(0, idx) : url;\n}\n\n/**\n * 加载 WS handler 并执行,返回事件回调对象(可能为空)\n *\n * 直接动态 import handler.ts 并取 `WS` 导出(不经过 loadRouteModule,\n * 因为 WS 不是 HTTP 方法,loadRouteModule 的 method 维度不适用)。\n * watch 模式下通过时间戳 query string 绕过 ESM 缓存。\n *\n * Dev 按需编译模式:先确保编译再 import,避免 import 不存在的文件污染 Vite SSR 状态。\n */\nasync function loadWsHandler(\n filePath: string,\n ctx: WsContext,\n rootDir?: string,\n): Promise<WsEventHandlers | void> {\n // Dev 按需模式:先确保编译再 import(同 loadRouteModule 逻辑)\n if (isDevOnDemandEnabled() && rootDir) {\n const dist = getDevDist();\n if (dist) {\n const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);\n if (sourcePath && fs.existsSync(sourcePath)) {\n await ensureCompiled(sourcePath, rootDir, dist);\n }\n }\n }\n\n const module = (await importWithCacheBust(filePath, isDevOnDemandEnabled())) as Record<\n string,\n unknown\n >;\n\n const handler = module['WS'];\n if (typeof handler !== 'function') {\n throw new Error(`WS export not found in ${filePath}`);\n }\n return (handler as WsHandler)(ctx);\n}\n\n/**\n * 绑定 ws 库原生事件到 faapi 的 WsEventHandlers\n *\n * 注意:wss.handleUpgrade 回调触发时 socket 已经处于 OPEN 状态,\n * 'open' 事件不会再触发,需要直接同步调用 onOpen。\n */\nfunction bindEvents(rawSocket: WebSocket, handlers: WsEventHandlers | void): void {\n if (!handlers) return;\n const ws = wrapWsSocket(rawSocket);\n\n if (handlers.onOpen) {\n // handleUpgrade 回调触发时 socket 已是 OPEN,直接同步调用\n if (rawSocket.readyState === WebSocket.OPEN) {\n handlers.onOpen!(ws);\n } else {\n rawSocket.once('open', () => handlers.onOpen!(ws));\n }\n }\n if (handlers.onMessage) {\n rawSocket.on('message', (data: RawData, isBinary: boolean) => {\n // ws 库 message 事件传 Buffer | ArrayBuffer | Buffer[],归一化为 Buffer;\n // 二进制帧透传 Buffer(utf8 解码不可逆,protobuf/msgpack 等二进制协议会损坏),\n // 文本帧转 utf8 字符串——与 onMessage 类型声明 string | Buffer 一致\n const buf = toWsBuffer(data);\n handlers.onMessage!(ws, isBinary ? buf : buf.toString('utf8'));\n });\n }\n if (handlers.onClose) {\n rawSocket.on('close', (code: number, reason: Buffer) => {\n handlers.onClose!(ws, code, reason.toString('utf8'));\n });\n }\n if (handlers.onError) {\n rawSocket.on('error', (err: Error) => {\n handlers.onError!(ws, err);\n });\n }\n}\n\n/** ws 库 RawData(Buffer | ArrayBuffer | Buffer[])归一化为 Buffer */\nfunction toWsBuffer(data: RawData): Buffer {\n if (Buffer.isBuffer(data)) return data;\n if (Array.isArray(data)) return Buffer.concat(data);\n return Buffer.from(data as ArrayBuffer);\n}\n\n/**\n * 把 Web Response 写回原始 socket(握手被中间件拦截或出错时使用)\n *\n * socket 未升级为 WebSocket 时,按 HTTP 响应格式写回并销毁。\n */\nasync function sendResponseToSocket(socket: Socket, response: Response): Promise<void> {\n const body = await response.text().catch(() => '');\n const statusLine = `HTTP/1.1 ${response.status} ${response.statusText || ''}\\r\\n`;\n const headerLines: string[] = [];\n let hasContentLength = false;\n // 延迟落头(headers-only meta)并入写回的 header 行\n const deferredHeaders = consumePendingMetaHeaders(response);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n if (key.toLowerCase() === 'content-length') hasContentLength = true;\n headerLines.push(`${key}: ${value}`);\n }\n for (const [key, value] of response.headers) {\n if (key.toLowerCase() === 'content-length') {\n hasContentLength = true;\n }\n headerLines.push(`${key}: ${value}`);\n }\n if (!hasContentLength) {\n headerLines.push(`Content-Length: ${Buffer.byteLength(body)}`);\n }\n socket.write(statusLine + headerLines.join('\\r\\n') + '\\r\\n\\r\\n' + body);\n socket.destroy();\n}\n\nexport interface AttachWsOptions {\n /** HTTP server 实例(已由 createServer 创建) */\n server: Server;\n /** 路由可变引用容器(watch 模式热替换时 routesRef.wsCurrent 被更新) */\n routesRef: RoutesRef;\n /** 项目根目录,用于解析路由文件绝对路径 */\n rootDir: string;\n /** 业务配置,注入到 WsContext.config */\n config?: Record<string, unknown>;\n /** 全局中间件(来自 faapi.config.ts,WS 握手最外层) */\n globalMiddlewares?: FaapiMiddleware[];\n /** 全局注入器(来自 faapi.config.ts,WS handler 可通过 ctx 间接访问全局依赖) */\n globalInjectors?: InjectorMap;\n /**\n * 是否信任反向代理头(X-Forwarded-For),默认 false\n *\n * true 时 `ctx.ip` 取 `x-forwarded-for` 第一个 IP;false(默认)时直取 socket 地址。\n */\n trustedProxy?: boolean;\n /** app 级注册表——经 WsContext 进入 WS 握手链路 */\n registries?: AppRegistries;\n}\n\n/**\n * 在 HTTP server 上挂载 WebSocket 升级处理\n *\n * 创建独立的 WebSocketServer(noServer 模式),监听 server 的 upgrade 事件。\n * 路由匹配成功后,走洋葱中间件链:\n * - 中间件放行(await next):finalHandler 内调用 ws.handleUpgrade 完成协议升级、绑定事件回调\n * - 中间件拦截(返回 Response):把 Response 写回 socket 后销毁,不进行协议升级\n * - 中间件抛错:由 formatErrorResponse/内置兜底生成错误 Response,写回 socket 后销毁\n *\n * 中间件链顺序:全局中间件(外)→ 目录中间件(内)→ finalHandler。\n * watch 模式下 wsRoutes 通过 routesRef 引用更新。\n */\nexport function attachWebSocket(options: AttachWsOptions): WebSocketServer {\n const {\n server,\n routesRef,\n rootDir,\n config,\n globalMiddlewares,\n trustedProxy = false,\n registries,\n } = options;\n\n const wss = new WebSocketServer({ noServer: true });\n\n server.on('upgrade', async (req: IncomingMessage, socket: Socket, head: Buffer) => {\n try {\n await handleUpgradeRequest(req, socket, head);\n } catch (err) {\n // 兜底 catch:upgrade 监听器是 async 函数,路由匹配 / header 构造 / 上下文创建等\n // 抛出的未捕获错误会变成 unhandled rejection(Node 15+ 默认崩掉进程)。\n // HTTP 路径有 catch 兜底,WS 路径保持对称——写回 500 后销毁 socket。\n console.error('[faapi] WS upgrade 处理失败:', err);\n if (!socket.destroyed) {\n try {\n if (!socket.writableEnded) {\n socket.write('HTTP/1.1 500 Internal Server Error\\r\\n\\r\\n');\n }\n } catch {\n // socket 已不可写,忽略\n }\n socket.destroy();\n }\n }\n });\n\n async function handleUpgradeRequest(\n req: IncomingMessage,\n socket: Socket,\n head: Buffer,\n ): Promise<void> {\n const currentWsRoutes = routesRef.wsCurrent;\n const pathname = getPathname(req);\n const match: WsRouteMatch | null = matchWsRoute(currentWsRoutes, pathname);\n\n if (!match) {\n // 路由不匹配:返回 404 并销毁 socket\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n const { route, params } = match;\n const headers = nodeHttpToWebHeaders(req);\n const host = req.headers.host ?? 'localhost';\n const url = `http://${host}${req.url ?? '/'}`;\n\n // 构造 Web Request 与 FaapiContext(与 HTTP 请求一致,供中间件使用)\n const request = new Request(url, { method: 'GET', headers });\n const ctx = createContext(request, params, config, getClientIp(req, trustedProxy), registries);\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n\n // 标记握手是否已完成协议升级(用于判断 socket 是否可写)\n let upgraded = false;\n\n // finalHandler:加载 WS handler + 协议升级 + 绑定事件\n const finalHandler = async (): Promise<Response> => {\n let handlers: WsEventHandlers | void;\n try {\n const absoluteFilePath = path.resolve(rootDir, route.filePath);\n // WsContext 是 FaapiContext 的结构子集,直接复用 ctx 实例\n // 中间件塞入的字段(如 ctx.user)对 WS handler 可见\n handlers = await loadWsHandler(absoluteFilePath, ctx as unknown as WsContext, rootDir);\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.error(`[faapi] WS handler 加载失败 ${route.filePath}: ${reason}`);\n return new Response('Internal Server Error', { status: 500 });\n }\n\n // 协议升级:等 wss.handleUpgrade 回调完成 bindEvents\n await new Promise<void>((resolve, reject) => {\n wss.handleUpgrade(req, socket, head, (rawSocket: WebSocket) => {\n try {\n bindEvents(rawSocket, handlers);\n wss.emit('connection', rawSocket, req);\n upgraded = true;\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n\n // 占位 Response,表示握手成功(socket 已升级,不会被写出)\n return new Response(null, { status: 200 });\n };\n\n // 执行中间件链:全局中间件(外)+ 目录中间件(内)\n let response: Response;\n try {\n // 按需加载中间件:route.middlewares 为 undefined 时从 middlewarePaths 加载(Vite 风格)\n if (route.middlewares === undefined && route.middlewarePaths) {\n // dev 按需模式:先编译中间件源码(含依赖闭包)再 import\n await ensureMiddlewaresCompiled(route.middlewarePaths, rootDir);\n const bundle = await loadMergedMiddlewares(route.middlewarePaths);\n if (bundle) {\n route.middlewares = bundle.middlewares;\n } else {\n route.middlewares = [];\n }\n }\n const dirMiddlewares = route.middlewares ?? [];\n const allMiddlewares =\n globalMiddlewares && globalMiddlewares.length > 0\n ? [...globalMiddlewares, ...dirMiddlewares]\n : dirMiddlewares;\n if (allMiddlewares.length > 0) {\n response = await compose(allMiddlewares, ctx, finalHandler);\n } else {\n response = await finalHandler();\n }\n } catch (err) {\n if (upgraded) {\n // 握手已完成(socket 已被 ws 库接管),中间件 after 阶段抛错无法改写响应\n console.error('[faapi] WS 握手后中间件抛错:', err);\n return;\n }\n // 错误兜底链:内置 formatErrorResponse 兜底\n // 业务方如需自定义错误响应,在全局中间件中 try/catch next() 即可\n response = buildErrorResponse(err);\n }\n\n // 握手成功:socket 已升级,中间件 after 阶段已执行完,无需再写\n if (upgraded) {\n return;\n }\n\n // 中间件拦截或错误:把 Response 写回 socket 后销毁\n await sendResponseToSocket(socket, mergeMeta(response, meta));\n }\n\n return wss;\n}\n","import {\n createServer as createHttpServer,\n type Server,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http';\nimport { createSecureServer as createHttp2SecureServer } from 'node:http2';\nimport { readFileSync } from 'node:fs';\nimport { Readable } from 'node:stream';\nimport path from 'node:path';\nimport type { RouteManifest, RouteMatch, WsRouteManifest, RoutesRef } from '../router/routeTypes';\nimport { matchRoute, findAllowedMethods } from '../router/matchRoute';\nimport { loadRouteModule } from '../loader/loadRouteModule';\nimport { createContextFromUrl } from '../runtime/createContext';\nimport { resolveInputFromUrl, resolveBodyForQueryMethod } from '../runtime/resolveInput';\nimport { invokeHandler, compose, mergeMeta } from '../runtime/invokeHandler';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport { sendNodeResponse } from '../response/sendNodeResponse';\nimport {\n RouteNotFoundError,\n MethodNotAllowedError,\n ValidationError,\n PayloadTooLargeError,\n} from '../errors/httpErrors';\nimport { validateInput } from '../validator/validateInput';\nimport { getInputTypeForMethod, hasBody } from '../runtime/inputType';\nimport { getClientIp } from '../utils/getClientIp';\nimport { cors, type CorsOptions } from '../middleware/cors';\nimport { helmet, type HelmetOptions } from '../middleware/helmet';\nimport { compression, type CompressionOptions } from '../middleware/compression';\nimport { etag, type EtagOptions } from '../middleware/etag';\nimport type { AppRegistries } from '../injection/registries';\nimport { logger as loggerMiddleware, type LoggerOptions } from '../middleware/logger';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { attachWebSocket } from './handleWsUpgrade';\nimport { nodeHttpToWebHeaders, buildErrorResponse } from './serverUtils';\nimport { getRuntimeSchemaPath } from '../cli/generateSchemaFiles';\nimport {\n ensureSchemaGenerated,\n ensureMiddlewaresCompiled,\n isDevOnDemandEnabled,\n getDevDist,\n} from '../cli/compileOnDemand';\nimport { loadMergedMiddlewares } from '../middleware/loadMiddlewares';\n\n/**\n * 将 Node.js IncomingMessage 转为 Web Request 对象\n *\n * 协议判断:\n * 1. 优先使用 X-Forwarded-Proto 头(反向代理场景)\n * 2. 回退到 http(HTTPS 由外部代理处理)\n */\nconst DEFAULT_BODY_LIMIT = 10 * 1024 * 1024; // 10MB\n\nfunction toWebRequest(\n req: IncomingMessage,\n bodyLimit: number = DEFAULT_BODY_LIMIT,\n requestSignal?: AbortSignal,\n): {\n request: Request;\n /** 已解析的 URL(pathname/searchParams 由调用方复用,免重复 new URL) */\n url: URL;\n} {\n // 协议判断:优先 X-Forwarded-Proto(反向代理),否则 http\n const forwardedProto = req.headers['x-forwarded-proto'];\n const protocol = Array.isArray(forwardedProto)\n ? (forwardedProto[0]?.split(',')[0]?.trim() ?? 'http')\n : (forwardedProto?.split(',')[0]?.trim() ?? 'http');\n const host = req.headers.host ?? 'localhost';\n // 全请求唯一一次 URL 解析——pathname/searchParams 由 ctx/routePipeline 复用\n const url = new URL(req.url ?? '/', `${protocol}://${host}`);\n\n const headers = nodeHttpToWebHeaders(req);\n\n const method = req.method ?? 'GET';\n\n // GET/HEAD 不应该有 body\n if (method === 'GET' || method === 'HEAD') {\n return {\n request: new Request(url.toString(), { method, headers, signal: requestSignal }),\n url,\n };\n }\n\n // content-length 快速判定:声明长度超限直接抛 PayloadTooLargeError,\n // 免去流包装 + 逐 chunk 读取(chunked 无此头,仍走 limitStreamSize 流式限流)\n const contentLength = req.headers['content-length'];\n if (contentLength !== undefined) {\n const declared = Number(Array.isArray(contentLength) ? contentLength[0] : contentLength);\n if (Number.isFinite(declared) && declared > bodyLimit) {\n throw new PayloadTooLargeError(bodyLimit);\n }\n }\n\n // 将 Node.js IncomingMessage 转为 Web ReadableStream\n // 并限制请求体大小(防止 DoS)\n const stream = Readable.toWeb(req) as ReadableStream<Uint8Array>;\n const limitedStream = limitStreamSize(stream, bodyLimit);\n return {\n request: new Request(url.toString(), {\n method,\n headers,\n body: limitedStream,\n duplex: 'half',\n signal: requestSignal,\n } as RequestInit),\n url,\n };\n}\n\n/**\n * 限制 ReadableStream 的总字节数,超过限制时通过 controller.error 抛 PayloadTooLargeError\n *\n * 错误传播路径:controller.error → Request body 读取方(resolveInput)抛 →\n * handleRequest catch → formatErrorResponse(命中 PayloadTooLargeError 分支)→ 413 响应\n *\n * 健壮性处理:\n * - reader.read() 抛错(客户端断开等)→ controller.error + releaseLock,避免泄漏\n * - 超限或异常后释放 reader lock,避免悬挂引用\n * - 取消时同步 cancel 上游 reader\n */\nfunction limitStreamSize(\n stream: ReadableStream<Uint8Array>,\n maxSize: number,\n): ReadableStream<Uint8Array> {\n let totalSize = 0;\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n let errored = false;\n\n const releaseReader = (): void => {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // 锁已释放或 reader 已 closed,忽略\n }\n reader = undefined;\n }\n };\n\n const failStream = (\n controller: ReadableStreamDefaultController<Uint8Array>,\n err: unknown,\n ): void => {\n if (errored) return;\n errored = true;\n controller.error(err instanceof Error ? err : new Error(String(err)));\n releaseReader();\n };\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n if (!reader) reader = stream.getReader();\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n releaseReader();\n return;\n }\n totalSize += value.byteLength;\n if (totalSize > maxSize) {\n // error 让流进入 errored 状态,下游 read() 会 reject\n failStream(controller, new PayloadTooLargeError(maxSize));\n return;\n }\n controller.enqueue(value);\n } catch (err) {\n // reader.read() 抛错(客户端断开、底层流异常等)\n failStream(controller, err);\n }\n },\n cancel(reason) {\n if (reader) {\n try {\n reader.cancel(reason);\n } catch {\n // 忽略上游 cancel 失败\n }\n releaseReader();\n }\n },\n });\n}\n\n/**\n * 查找路径的所有允许方法(用于 405 响应)\n *\n * 实现移至 [matchRoute](../router/matchRoute.ts)(共享路由索引:静态段 O(1) 直查,\n * 仅动态段线性扫描)。404 非热路径,但扫描器/探活探测的 404 高频场景下\n * 索引化仍有收益。\n */\n\nexport interface CreateServerOptions {\n routes: RouteManifest;\n rootDir: string;\n /** 产物输出目录(如 '.faapi' 或 'dist'),用于计算 schema 路径 */\n dist: string;\n cors?: CorsOptions | boolean;\n /** 请求错误钩子(在错误响应生成后调用,用于副作用;不修改已发出的响应) */\n onError?: (error: unknown, ctx: FaapiContext) => Promise<void> | void;\n /** 自定义业务配置(来自 faapi.config.ts,注入到 ctx.config) */\n config?: Record<string, unknown>;\n /** WebSocket 路由清单(空数组则不挂载 WS 支持) */\n wsRoutes?: WsRouteManifest;\n /** 全局中间件(来自 faapi.config.ts,对所有路由生效,最外层) */\n middlewares?: FaapiMiddleware[];\n /** 全局注入器(来自 faapi.config.ts,对所有路由 handler 参数注入生效) */\n injectors?: InjectorMap;\n /** 安全头配置 */\n helmet?: HelmetOptions | boolean;\n /** 响应压缩(gzip/deflate/br 协商),默认关闭,详见 middleware/compression.md */\n compression?: CompressionOptions | boolean;\n /** ETag/304 条件请求协商,默认关闭,详见 middleware/etag.md */\n etag?: EtagOptions | boolean;\n /** app 级注册表(tool/agent/skill/agentHandle)——经 FaapiContext 进入请求链路 */\n registries?: AppRegistries;\n /** 请求日志配置,默认启用(与 cors 一致) */\n logger?: LoggerOptions | boolean;\n /** 请求体大小限制(字节) */\n bodyLimit?: number;\n /** HTTP/2 配置,启用时需提供 SSL 证书路径 */\n http2?: Http2Options | boolean;\n /**\n * 是否信任反向代理头(X-Forwarded-For),默认 false\n *\n * true 时 `ctx.ip` 取 `x-forwarded-for` 第一个 IP(nginx/CDN 场景);\n * false(默认)时直取 socket 地址——直连部署下 XFF 可被伪造,安全默认不信任。\n */\n trustedProxy?: boolean;\n}\n\nexport interface Http2Options {\n key?: string;\n cert?: string;\n}\n\n/**\n * 创建 faapi HTTP server\n *\n * @param options 路由清单、根目录\n * @returns Node.js Server 实例\n */\nexport function createServer(options: CreateServerOptions): {\n server: Server;\n routesRef: RoutesRef;\n} {\n const {\n routes,\n rootDir,\n dist,\n cors: corsOption,\n onError,\n config,\n wsRoutes,\n middlewares: globalMiddlewares,\n injectors: globalInjectors,\n helmet: helmetOption,\n compression: compressionOption,\n etag: etagOption,\n registries,\n logger: loggerOption,\n bodyLimit = DEFAULT_BODY_LIMIT,\n http2: http2Option,\n trustedProxy = false,\n } = options;\n\n // 路由可变引用容器(watch 模式热替换时 reloadRoutes 更新 .current/.wsCurrent)\n const routesRef: RoutesRef = { current: routes, wsCurrent: wsRoutes ?? [] };\n\n // Build middleware chain from config options\n const configMiddlewares: FaapiMiddleware[] = [];\n\n // Compression — 显式启用;链最外层,包住完整链路使最终响应被压缩\n if (compressionOption) {\n const compOpts = typeof compressionOption === 'object' ? compressionOption : {};\n configMiddlewares.push(compression(compOpts));\n }\n\n // CORS\n const corsMiddleware: FaapiMiddleware | null =\n corsOption === false\n ? null\n : corsOption === true || corsOption === undefined\n ? cors()\n : cors(corsOption);\n if (corsMiddleware) configMiddlewares.push(corsMiddleware);\n\n // Helmet — enabled only when explicitly configured\n if (helmetOption) {\n const helmOpts = typeof helmetOption === 'object' ? helmetOption : {};\n configMiddlewares.push(helmet(helmOpts));\n }\n\n // Logger — 默认启用(与 cors 一致),false 禁用,LoggerOptions 自定义\n const loggerMiddlewareInst: FaapiMiddleware | null =\n loggerOption === false\n ? null\n : loggerOption === true || loggerOption === undefined\n ? loggerMiddleware()\n : loggerMiddleware(loggerOption);\n if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);\n\n // ETag — 显式启用;位于 compression 内层:先算 ETag/304 再压缩,\n // 弱 ETag 基于未压缩表示计算(304 无 body 时压缩自动跳过)\n if (etagOption) {\n const etagOpts = typeof etagOption === 'object' ? etagOption : {};\n configMiddlewares.push(etag(etagOpts));\n }\n\n // 外层中间件链启动期组装一次(CORS → helmet → logger → 全局),\n // 每请求不再重复 spread 重组数组\n const outerMiddlewares: FaapiMiddleware[] = [...configMiddlewares];\n if (globalMiddlewares && globalMiddlewares.length > 0) {\n outerMiddlewares.push(...globalMiddlewares);\n }\n\n const server = ((): Server => {\n if (http2Option) {\n const h2Opts = typeof http2Option === 'object' ? http2Option : {};\n return createHttp2SecureServer({\n key: h2Opts.key ? readFileSync(h2Opts.key) : undefined,\n cert: h2Opts.cert ? readFileSync(h2Opts.cert) : undefined,\n allowHTTP1: true,\n }) as unknown as Server;\n }\n return createHttpServer();\n })();\n\n server.on('request', (req: IncomingMessage, res: ServerResponse) => {\n // 每次请求读取最新的路由状态(支持 watch 模式热更新)\n const currentRoutes = routesRef.current;\n\n handleRequest(\n currentRoutes,\n rootDir,\n dist,\n req,\n res,\n outerMiddlewares,\n onError,\n config,\n globalInjectors,\n bodyLimit,\n trustedProxy,\n registries,\n ).catch(() => {\n res.statusCode = 500;\n res.end();\n });\n });\n\n // 挂载 WebSocket 升级处理(无条件挂载:upgrade 处理器内部对无匹配路由返回 404,\n // 空清单成本一次函数调用。若按初始清单条件挂载,dev watch 中新增第一个 WS 路由后\n // upgrade 监听器不会补挂,WS 路由永远 404)\n attachWebSocket({\n server,\n routesRef,\n rootDir,\n config,\n globalMiddlewares,\n trustedProxy,\n registries,\n });\n\n return { server, routesRef };\n}\n\n/**\n * 准备请求上下文:Node IncomingMessage → Web Request + FaapiContext\n *\n * 提取为独立函数,让 handleRequest 主流程聚焦于路由 + 中间件调度,\n * 便于单测与未来扩展(如自定义 context 字段来源)。\n *\n * URL 全请求只解析一次(toWebRequest 内),pathname/searchParams\n * 由 ctx / routePipeline 共享,避免一次请求重复 new URL 3~4 次。\n */\nfunction prepareRequest(\n req: IncomingMessage,\n config: Record<string, unknown> | undefined,\n bodyLimit: number,\n trustedProxy: boolean,\n registries?: AppRegistries,\n requestSignal?: AbortSignal,\n): {\n request: Request;\n url: URL;\n ctx: FaapiContext;\n meta: ResponseMeta;\n method: string;\n urlPath: string;\n} {\n const { request, url } = toWebRequest(req, bodyLimit, requestSignal);\n const method = request.method.toUpperCase();\n const urlPath = url.pathname;\n const ctx = createContextFromUrl(\n request,\n url,\n {},\n config,\n getClientIp(req, trustedProxy),\n registries,\n );\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n return { request, url, ctx, meta, method, urlPath };\n}\n\n/**\n * 路由匹配——命中返回 MatchResult,未命中抛 RouteNotFoundError / MethodNotAllowedError\n *\n * 抛错语义让调用方 try/catch 即可,无需在主流程里分支处理 null。\n */\nfunction resolveRouteOrThrow(routes: RouteManifest, method: string, urlPath: string): RouteMatch {\n const match = matchRoute(routes, method, urlPath);\n if (match) return match;\n\n // 检查是否有其他方法匹配该路径 → 405\n const allowedMethods = findAllowedMethods(routes, urlPath);\n if (allowedMethods.length > 0) {\n throw new MethodNotAllowedError(method, urlPath, allowedMethods);\n }\n\n // 路由未匹配 → 404\n throw new RouteNotFoundError(urlPath);\n}\n\n/**\n * 路由执行管线:作为外层中间件链的 finalHandler\n *\n * 包含:路由匹配 → handler.js 加载 → 参数解析 → zod.js 按需生成 + 校验 →\n * 中间件按需加载 → 注入器合并 → handler 调用。\n *\n * 错误抛出由外层 handleRequest 的 try/catch 接管,经 formatErrorResponse 转换为响应。\n */\n// 路由静态派生路径缓存(绝对路径 / schema 路径对固定 route 恒定,免每请求字符串运算)。\n// WeakMap 按 route 对象弱键——reloadRoutes 整体替换清单后旧对象可 GC,无需手动失效\nconst routePathCache = new WeakMap<\n RouteManifest[number],\n { absFilePath: string; schemaPath: string }\n>();\n\nfunction getRoutePaths(\n route: RouteManifest[number],\n rootDir: string,\n dist: string,\n): { absFilePath: string; schemaPath: string } {\n let cached = routePathCache.get(route);\n if (!cached) {\n cached = {\n absFilePath: path.resolve(rootDir, route.filePath),\n schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir),\n };\n routePathCache.set(route, cached);\n }\n return cached;\n}\n\nfunction createRoutePipeline(opts: {\n routes: RouteManifest;\n method: string;\n urlPath: string;\n url: URL;\n ctx: FaapiContext;\n request: Request;\n rootDir: string;\n dist: string;\n globalInjectors: InjectorMap | undefined;\n}): () => Promise<Response> {\n const { routes, method, urlPath, url, ctx, request, rootDir, dist, globalInjectors } = opts;\n return async () => {\n // 1. 路由匹配(未命中抛 RouteNotFound / MethodNotAllowed)\n const match = resolveRouteOrThrow(routes, method, urlPath);\n ctx.params = match.params;\n const { route } = match;\n const { absFilePath, schemaPath } = getRoutePaths(route, rootDir, dist);\n\n // 2. 加载 handler.js(dev 按需编译 + import,prod 直接 import)\n const routeModule = await loadRouteModule(absFilePath, route.method, rootDir);\n\n // 3. 参数解析(query / body / form / files 等)——复用已解析的 URL\n const input = await resolveInputFromUrl(route.method, request, url);\n\n // 4. schema 校验(运行时按 route.filePath 计算 zod.js 路径 + safeParse)\n const inputType = getInputTypeForMethod(route.method);\n // schemaPath 已在 getRoutePaths 中缓存\n\n // Dev 按需模式:zod.js 不存在或 stale 时触发按需生成\n if (isDevOnDemandEnabled()) {\n const devDist = getDevDist();\n if (devDist) {\n await ensureSchemaGenerated(schemaPath, route.filePath, routes, rootDir, dist);\n }\n }\n\n const result = await validateInput(schemaPath, route.method, inputType, input);\n if (!result.valid) {\n throw new ValidationError('参数校验失败', result.issues);\n }\n // body 计算与主输入分流:\n // - POST/PUT/PATCH:主输入就是 body,用校验后的值\n // - DELETE:主输入是 query(校验 DELETEQuery),body 单独解析注入——\n // 若把校验后的 query 当 body 传入,handler 声明 body 会静默拿到 query;\n // 同时请求体流不被消费,keep-alive 连接无法复用\n // - GET/HEAD:无 body\n const body =\n inputType === 'query' && hasBody(route.method)\n ? await resolveBodyForQueryMethod(request)\n : hasBody(route.method)\n ? result.data\n : undefined;\n\n // 5. 中间件按需加载(Vite 风格):route.middlewares 为 undefined 时从 middlewarePaths 加载\n // 首次请求加载后缓存到 route 上,后续请求直接复用\n if (route.middlewares === undefined && route.injectors === undefined && route.middlewarePaths) {\n // dev 按需模式:先编译中间件源码(含依赖闭包)再 import\n await ensureMiddlewaresCompiled(route.middlewarePaths, rootDir);\n const bundle = await loadMergedMiddlewares(route.middlewarePaths);\n if (bundle) {\n route.middlewares = bundle.middlewares;\n route.injectors = bundle.injectors;\n } else {\n // 标记为已加载(空中间件),避免重复加载\n route.middlewares = [];\n route.injectors = {};\n }\n }\n\n // 6. 注入器合并:全局注入器为基线,目录注入器覆盖同名\n const mergedInjectors = globalInjectors\n ? { ...globalInjectors, ...route.injectors }\n : route.injectors;\n\n // 7. handler 调用(含目录中间件洋葱模型 + 自动响应包装)\n return await invokeHandler(routeModule.handler, ctx, body, route.middlewares, mergedInjectors);\n };\n}\n\n/**\n * 发送成功响应\n */\nasync function sendSuccessResponse(response: Response, res: ServerResponse): Promise<void> {\n await sendNodeResponse(response, res);\n}\n\n/**\n * 发送错误响应 + 触发 onError 副作用\n *\n * 错误处理兜底链(参考 Fastify 语义):\n * 1. 框架内置 formatErrorResponse 兜底(handler 抛错时)——读 ctx.config.response.fail\n * 自定义包装函数,确保错误格式与 ctx.fail() 主动错误响应一致\n * 2. 内置兜底仍抛错 → 最简 500 JSON 响应\n * 3. 响应发出后 → onError 触发副作用(不修改已发出的响应)\n * 注:业务方如需进一步自定义错误响应,在全局中间件中 try/catch next() 即可\n *\n * `ctx` 可为 undefined——请求准备阶段抛错(如 content-length 超限)时尚未构造 ctx,\n * 此时错误格式用默认 fail 包装,onError 不触发(与原\"裸 500\"路径一致)。\n */\nasync function sendErrorResponse(\n err: unknown,\n meta: ResponseMeta,\n res: ServerResponse,\n onError: ((error: unknown, ctx: FaapiContext) => Promise<void> | void) | undefined,\n ctx: FaapiContext | undefined,\n): Promise<void> {\n await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx?.config), meta), res);\n\n // 响应已发出,触发 onError 副作用(日志/告警/链路追踪),自身抛错被忽略\n if (onError && ctx) {\n try {\n await onError(err, ctx);\n } catch {\n // onError 自身抛错不影响已发出的响应\n }\n }\n}\n\nasync function handleRequest(\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n req: IncomingMessage,\n res: ServerResponse,\n outerMiddlewares: FaapiMiddleware[],\n onError: ((error: unknown, ctx: FaapiContext) => Promise<void> | void) | undefined,\n config: Record<string, unknown> | undefined,\n globalInjectors: InjectorMap | undefined,\n bodyLimit: number,\n trustedProxy: boolean,\n registries?: AppRegistries,\n): Promise<void> {\n // meta/ctx 兜底:请求准备阶段抛错(如 content-length 超限的 413)时尚无 ctx\n let meta: ResponseMeta = { headers: {}, setCookies: [] };\n let ctx: FaapiContext | undefined;\n // 客户端断连信号:每请求一个 AbortController,signal 进入 Request(ctx.request.signal)\n // res 'close' 在响应正常完成(keep-alive)时也会触发,用 writableEnded 区分——\n // 响应未写完连接就断开(客户端提前断连)才 abort,正常完成不误触发\n const abortController = new AbortController();\n res.on('close', () => {\n if (!res.writableEnded) abortController.abort();\n });\n try {\n // 1. 准备请求上下文(toWebRequest + createContext)——URL 全请求只解析一次\n const prepared = prepareRequest(\n req,\n config,\n bodyLimit,\n trustedProxy,\n registries,\n abortController.signal,\n );\n ctx = prepared.ctx;\n meta = prepared.meta;\n const { request, url, method, urlPath } = prepared;\n\n // 2. 创建路由执行管线(路由匹配 + 校验 + 中间件加载 + handler 调用)\n const routePipeline = createRoutePipeline({\n routes,\n method,\n urlPath,\n url,\n ctx,\n request,\n rootDir,\n dist,\n globalInjectors,\n });\n\n // 3. 执行外层中间件链(CORS → helmet → logger → 全局 → routePipeline,\n // 数组已在 createServer 启动期组装,此处仅按需 compose)\n const response =\n outerMiddlewares.length > 0\n ? await compose(outerMiddlewares, ctx, routePipeline)\n : await routePipeline();\n // 4. 发送响应\n await sendSuccessResponse(response, res);\n } catch (err: unknown) {\n // 客户端已断开或响应已完成:网络中断/连接销毁不是服务端错误,\n // 不向已销毁的连接写 500(写入无效),也不触发 onError 误报\n if (res.destroyed || res.writableEnded) return;\n await sendErrorResponse(err, meta, res, onError, ctx);\n }\n}\n","import type { Server } from 'node:http';\nimport type { RouteManifest, WsRouteManifest } from '../router/routeTypes';\nimport type { CorsOptions } from '../middleware/cors';\nimport type { FaapiContext } from '../runtime/contextTypes';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport type { RequestHandler, UpgradeHandler } from '../config/pluginTypes';\nimport { createServer } from './createServer';\n\nexport interface StartOptions {\n port: number;\n routes: RouteManifest;\n rootDir: string;\n /** 产物输出目录(如 '.faapi' 或 'dist'),用于计算 schema 路径 */\n dist: string;\n cors?: CorsOptions | boolean;\n /** 请求错误钩子(在错误响应生成后调用,用于副作用,不修改已发出的响应) */\n onError?: (error: unknown, ctx: FaapiContext) => Promise<void> | void;\n /** 自定义业务配置(来自 faapi.config.ts,注入到 ctx.config) */\n config?: Record<string, unknown>;\n /** WebSocket 路由清单 */\n wsRoutes?: WsRouteManifest;\n /** 全局中间件(来自 faapi.config.ts,对所有路由生效,最外层) */\n middlewares?: FaapiMiddleware[];\n /** 全局注入器(来自 faapi.config.ts,对所有路由 handler 参数注入生效) */\n injectors?: InjectorMap;\n /**\n * server.listen 之前的钩子(用于加载插件、应用 handler 包装等)\n *\n * 钩子在 server 创建后、listen 之前调用,可修改 server 的 request/upgrade listener。\n */\n beforeListen?: (server: Server) => Promise<void> | void;\n}\n\n/**\n * 启动 HTTP server 并打印路由表\n *\n * 流程:createServer → beforeListen(可选,加载插件/应用包装)→ listen → 打印路由表\n */\nexport function startServer(options: StartOptions): Promise<Server> {\n const {\n port,\n routes,\n rootDir,\n dist,\n cors,\n onError,\n config,\n wsRoutes,\n middlewares,\n injectors,\n beforeListen,\n } = options;\n const { server } = createServer({\n routes,\n rootDir,\n dist,\n cors,\n onError,\n config,\n wsRoutes,\n middlewares,\n injectors,\n });\n\n return (async () => {\n // beforeListen 钩子:加载插件、应用 handler 包装等\n if (beforeListen) {\n await beforeListen(server);\n }\n\n return new Promise<Server>((resolve) => {\n server.listen(port, () => {\n const address = server.address();\n const actualPort = typeof address === 'object' && address !== null ? address.port : port;\n\n console.log('faapi dev server started');\n console.log(`- Local: http://localhost:${actualPort}`);\n console.log('- Loaded routes:');\n\n for (const route of routes) {\n const method = route.method.padEnd(6);\n console.log(` ${method}${route.urlPath} ${route.filePath}`);\n }\n\n if (wsRoutes && wsRoutes.length > 0) {\n console.log('- WebSocket routes:');\n for (const route of wsRoutes) {\n console.log(` WS ${route.urlPath} ${route.filePath}`);\n }\n }\n\n resolve(server);\n });\n });\n })();\n}\n\n/**\n * 应用 handler / upgrade 包装器到 server\n *\n * 在 server.listen 之前调用,替换 server 的 request/upgrade listener。\n * 包装器按数组顺序嵌套:finalHandler = wrap1(wrap2(originalHandler))。\n *\n * @param server 目标 server(未 listen)\n * @param handlerWrappers HTTP handler 包装器数组\n * @param upgradeWrappers WS upgrade handler 包装器数组\n */\nexport function applyPluginWrappers(\n server: Server,\n handlerWrappers: Array<(original: RequestHandler) => RequestHandler>,\n upgradeWrappers: Array<(original: UpgradeHandler | undefined) => UpgradeHandler>,\n): void {\n // 应用 HTTP handler 包装\n if (handlerWrappers.length > 0) {\n const listeners = server.listeners('request');\n const original = listeners[0] as RequestHandler | undefined;\n if (original) {\n server.removeAllListeners('request');\n let handler: RequestHandler = original;\n for (const wrap of handlerWrappers) {\n handler = wrap(handler);\n }\n server.on('request', handler);\n }\n }\n\n // 应用 WS upgrade handler 包装\n if (upgradeWrappers.length > 0) {\n const listeners = server.listeners('upgrade');\n const original = listeners[0] as UpgradeHandler | undefined;\n server.removeAllListeners('upgrade');\n let upgrade: UpgradeHandler | undefined = original;\n for (const wrap of upgradeWrappers) {\n upgrade = wrap(upgrade);\n }\n if (upgrade) {\n server.on('upgrade', upgrade);\n }\n }\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport type { FaapiPlugin, PluginDeclaration } from '../config/pluginTypes';\nimport type { RequestHandler, UpgradeHandler } from '../config/pluginTypes';\nimport { ensureCompiled, isDevOnDemandEnabled } from './compileOnDemand';\nimport { compileProjectModules } from './compileConfig';\n\n/** loadPlugins 的输入上下文(不含 wrap 能力,由 loadPlugins 内部注入) */\ntype LoadPluginsContext = Omit<\n import('../config/pluginTypes').PluginContext,\n 'wrapHandler' | 'wrapUpgradeHandler'\n>;\n\n/** loadPlugins 返回的包装器收集结果 */\nexport interface PluginLoadResult {\n /** 插件注册的 HTTP handler 包装器(按注册顺序) */\n handlerWrappers: Array<(original: RequestHandler) => RequestHandler>;\n /** 插件注册的 WS upgrade handler 包装器(按注册顺序) */\n upgradeWrappers: Array<(original: UpgradeHandler | undefined) => UpgradeHandler>;\n /** 加载失败的插件(specifier + 原因)——调用方据此汇总报告或 fail-fast */\n failures: Array<{ specifier: string; reason: string }>;\n}\n\n/**\n * 加载并执行插件列表\n *\n * 遍历 config.plugins,动态 import 插件包,调用 setup(ctx)。\n * 插件在 server 创建后、listen 之前按声明顺序执行。\n *\n * 插件可通过 ctx.wrapHandler / ctx.wrapUpgradeHandler 注册包装函数,\n * 本函数收集后返回,由调用方在 listen 之前应用。\n *\n * 错误口径(单一语义,不再静默降级):\n * - 单个插件加载/setup 失败不中断其他插件,但失败明细收集进 `failures` 并\n * 在加载完成后统一 `console.error` 汇总——鉴权/CORS 类插件静默丢失等同裸奔,\n * 必须对业务方可见\n * - 非法声明(resolveDeclaration 失败)与其他插件错误同口径收集,不崩启动\n * - `path` 声明相对项目根目录解析(`pathToFileURL(path.resolve(rootDir, path))`),\n * 此前直接 import 会相对 faapi 包自身产物解析,几乎必然失败且报错指向\n * node_modules 深处\n *\n * 本地 TS 插件(`{ path: './plugins/xxx' }`,`.ts` 源码)的加载路径:\n * - 探测源文件(原样 → `.ts`/`.js` → `/index.ts`/`/index.js`),Node ESM 不做\n * 扩展名补全,无扩展名 file URL 直接 import 必然 `Cannot find module`\n * - 产物存在且不比源码旧 → import 产物(build 固化 / dev 已编译产物复用)\n * - `.ts` 源码 + dev 按需模式 → esbuild 逐文件编译到产物目录后 import 产物\n * (src 内插件走 ensureCompiled 打平产物与 routes 同一运行时对象;src 外插件\n * 走 compileProjectModules,含依赖闭包与 specifier 重写——插件内部\n * `import '../src/xxx'` 无扩展名由此生效)\n * - `.ts` 源码 + 非按需模式(prod / 编程式)且产物 stale → 明确报错指引 `faapi build`,\n * 不静默使用旧产物(与 handler 的 prod 语义一致:产物固化,import 失败即报错)\n *\n * @param declarations 插件声明列表\n * @param ctx 插件上下文(不含 wrap 能力,由本函数注入)\n * @param rootDir 项目根目录(`path` 声明解析基准)\n * @param dist 产物目录(dev 为 `.faapi`,prod 为 `dist`——本地 TS 插件按需编译/产物复用基准)\n * @returns 包装器收集结果 + 失败清单\n */\nexport async function loadPlugins(\n declarations: PluginDeclaration[] | undefined,\n ctx: LoadPluginsContext,\n rootDir?: string,\n dist?: string,\n): Promise<PluginLoadResult> {\n const handlerWrappers: Array<(original: RequestHandler) => RequestHandler> = [];\n const upgradeWrappers: Array<(original: UpgradeHandler | undefined) => UpgradeHandler> = [];\n const failures: Array<{ specifier: string; reason: string }> = [];\n\n if (!declarations || declarations.length === 0) {\n return { handlerWrappers, upgradeWrappers, failures };\n }\n\n // 注入 wrap 能力到 ctx\n const fullCtx = {\n ...ctx,\n wrapHandler: (fn: (original: RequestHandler) => RequestHandler) => {\n handlerWrappers.push(fn);\n },\n wrapUpgradeHandler: (fn: (original: UpgradeHandler | undefined) => UpgradeHandler) => {\n upgradeWrappers.push(fn);\n },\n };\n\n const loaded = new Set<string>();\n\n for (const decl of declarations) {\n // 非法声明与其他插件错误同口径:收集进 failures,不崩启动\n let specifier: string;\n let options: unknown;\n let enable: boolean | undefined;\n try {\n ({ specifier, options, enable } = resolveDeclaration(decl));\n } catch (err) {\n failures.push({\n specifier: JSON.stringify(decl),\n reason: err instanceof Error ? err.message : String(err),\n });\n continue;\n }\n\n // enable 检查\n if (enable === false) continue;\n\n // 去重\n if (loaded.has(specifier)) {\n console.warn(`! Plugin already loaded: ${specifier}, skipping`);\n continue;\n }\n loaded.add(specifier);\n\n try {\n const mod = await importPluginModule(specifier, rootDir ?? process.cwd(), dist);\n const plugin: FaapiPlugin = mod.default ?? (mod as unknown as FaapiPlugin);\n\n if (typeof plugin.setup !== 'function') {\n failures.push({ specifier, reason: 'plugin has no setup function' });\n continue;\n }\n\n await plugin.setup({ ...fullCtx, options });\n console.log(`- Plugin loaded: ${plugin.name ?? specifier}`);\n } catch (err) {\n failures.push({\n specifier,\n reason: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // 失败汇总:单一出口,业务方启动日志一眼可见(鉴权类插件静默丢失不可接受)\n if (failures.length > 0) {\n console.error(\n `[faapi] ${failures.length} plugin(s) failed to load:\\n` +\n failures.map((f) => ` - ${f.specifier}: ${f.reason}`).join('\\n'),\n );\n }\n\n return { handlerWrappers, upgradeWrappers, failures };\n}\n\n/** TS 源文件后缀(需 esbuild 编译才能保证加载) */\nconst TS_EXTS = /\\.(ts|tsx|mts|cts)$/;\n/** JS 源文件后缀(Node ESM 可直接加载) */\nconst JS_EXTS = /\\.(js|mjs|cjs|jsx)$/;\n\n/**\n * 探测本地插件源文件\n *\n * 探测顺序:specifier 原样(已带扩展名)→ `.ts` → `.js` → `/index.ts` → `/index.js`。\n * Node ESM 对 file URL 不做扩展名补全,无扩展名声明必须由框架探测到实际文件。\n *\n * @param specifier 插件声明路径(相对项目根目录或绝对路径)\n * @param baseDir 项目根目录\n * @returns 源文件绝对路径,或 null(未找到)\n */\nexport function resolveLocalPluginSource(specifier: string, baseDir: string): string | null {\n const base = path.isAbsolute(specifier) ? specifier : path.resolve(baseDir, specifier);\n if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs.existsSync(base)) return base;\n for (const ext of ['.ts', '.js']) {\n if (fs.existsSync(base + ext)) return base + ext;\n }\n for (const indexExt of ['/index.ts', '/index.js']) {\n if (fs.existsSync(base + indexExt)) return base + indexExt;\n }\n return null;\n}\n\n/**\n * 从源文件绝对路径推算产物绝对路径\n *\n * - src 内源文件:打平 src/ 前缀(与 compileDevRoutes/compileBuildRoutes 产物同一,\n * 运行时对象同一,instanceof 跨边界生效)\n * - src 外源文件:保留相对 rootDir 的路径结构(compileProjectModules 以 rootDir 为\n * outbase,产物在 `<dist>/` 下保留结构)\n */\nfunction mirrorProductPath(sourcePath: string, baseDir: string, distDir: string): string {\n let rel = path.relative(baseDir, sourcePath).replace(/\\\\/g, '/');\n if (rel.startsWith('src/')) rel = rel.slice(4);\n return path.resolve(distDir, rel.replace(TS_EXTS, '.js'));\n}\n\n/** 插件模块形态:default 导出优先 */\ntype PluginModule = { default?: FaapiPlugin } & Record<string, unknown>;\n\n/** 产物是否比源码旧(stale) */\nfunction isSourceNewer(sourcePath: string, productPath: string): boolean {\n try {\n return fs.statSync(sourcePath).mtimeMs > fs.statSync(productPath).mtimeMs;\n } catch {\n return true;\n }\n}\n\n/**\n * 加载插件模块\n *\n * - 包名(非相对/绝对路径):原样 import,Node 按包解析\n * - 相对/绝对路径:探测源文件与产物,按「产物 fresh → 源码编译/直载」决策,\n * 详见 loadPlugins 注释的「本地 TS 插件的加载路径」\n *\n * @throws 找不到可加载目标或 prod 模式产物 stale 时抛错(带修复指引),进 failures 汇总\n */\nasync function importPluginModule(\n specifier: string,\n baseDir: string,\n dist: string | undefined,\n): Promise<PluginModule> {\n const isRelative = specifier.startsWith('./') || specifier.startsWith('../');\n if (!isRelative && !path.isAbsolute(specifier)) {\n return import(specifier);\n }\n\n const distDir = dist ? path.resolve(baseDir, dist) : null;\n const sourcePath = resolveLocalPluginSource(specifier, baseDir);\n const productPath =\n sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;\n\n // 产物可用:存在 且(源码不存在——部署机只发产物,或 产物不比源码旧)\n if (\n productPath !== null &&\n fs.existsSync(productPath) &&\n (sourcePath === null || !isSourceNewer(sourcePath, productPath))\n ) {\n return import(pathToFileURL(productPath).href);\n }\n\n // TS 源码:dev 按需编译后 import 产物\n if (sourcePath && TS_EXTS.test(sourcePath)) {\n if (!isDevOnDemandEnabled() || !distDir) {\n throw new Error(\n `Local plugin \"${specifier}\" has no up-to-date build artifact (dist is stale or missing). ` +\n `Run \"faapi build\" first, or use \"faapi dev\" for on-demand compilation.`,\n );\n }\n const relFromRoot = path.relative(baseDir, sourcePath).replace(/\\\\/g, '/');\n if (relFromRoot.startsWith('src/')) {\n // src 内插件:打平产物与 routes 同一(同一运行时对象)\n await ensureCompiled(sourcePath, baseDir, dist!);\n } else {\n // src 外插件:编译入口 + 依赖闭包(含插件 import 的 src 内模块)\n await compileProjectModules([sourcePath], baseDir, dist!);\n }\n if (productPath && fs.existsSync(productPath)) {\n return import(pathToFileURL(productPath).href);\n }\n // 编译已完成但产物仍缺失:落到统一报错(理论不可达,防御性兜底)\n }\n\n // JS 源码:Node 可直接加载,无需产物\n if (sourcePath && JS_EXTS.test(sourcePath)) {\n return import(pathToFileURL(sourcePath).href);\n }\n\n // 找不到可加载目标:给明确修复指引(此前仅报 Cannot find module,无法定位原因)\n const candidates = [\n `${specifier}.ts`,\n `${specifier}/index.ts`,\n `${specifier}.js`,\n `${specifier}/index.js`,\n ].map((c) => (path.isAbsolute(c) ? c : path.join(baseDir, c)));\n throw new Error(\n `Cannot find local plugin \"${specifier}\" (resolved from ${baseDir}). ` +\n `Expected one of:\\n ${candidates.join('\\n ')}\\n` +\n `Create the plugin file (export default { name, setup(ctx) {...} }) or fix the path.`,\n );\n}\n\n/**\n * 解析插件声明为统一格式\n */\nfunction resolveDeclaration(decl: PluginDeclaration): {\n specifier: string;\n options?: unknown;\n enable?: boolean;\n} {\n if (typeof decl === 'string') {\n return { specifier: decl };\n }\n\n if (Array.isArray(decl)) {\n const [specifier, options] = decl;\n return { specifier, options };\n }\n\n if ('package' in decl) {\n return { specifier: decl.package, options: decl.options, enable: decl.enable };\n }\n\n if ('path' in decl) {\n return { specifier: decl.path, options: decl.options, enable: decl.enable };\n }\n\n throw new Error(`Invalid plugin declaration: ${JSON.stringify(decl)}`);\n}\n","import type { Server } from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { PassThrough, Readable } from 'node:stream';\nimport type { RouteManifest, WsRouteManifest, RoutesRef } from '../router/routeTypes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { detectRouteConflicts } from '../router/detectRouteConflicts';\nimport { createServer } from '../server/createServer';\nimport { applyPluginWrappers } from '../server/startServer';\nimport { loadConfig } from '../config/loadConfig';\nimport { hydrateRoutes, type SerializedRouteManifest } from './generateRoutes';\nimport { hydrateTools, type SerializedToolRecord } from './generateToolArtifacts';\nimport { hydrateAgents, type SerializedAgentRecord } from './generateAgentArtifacts';\nimport { loadPlugins } from './loadPlugins';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport {\n createAppRegistries,\n defaultRegistries,\n type AppRegistries,\n} from '../injection/registries';\nimport type { ToolMetadata } from '../ast/extractToolMetadata';\nimport type { AgentMetadata } from '../ast/extractAgentMetadata';\nimport type { FaapiConfig } from '../config/configTypes';\nimport { ROUTE_PATTERNS } from '../utils/prodPaths';\n\nexport interface InjectOptions {\n method?: string;\n path?: string;\n headers?: Record<string, string>;\n query?: Record<string, string>;\n body?: unknown;\n}\n\nexport interface InjectResponse {\n status: number;\n headers: Headers;\n body: unknown;\n}\n\n/** 默认产物目录(prod 模式,对应 `faapi build` 默认输出到 `dist`) */\nconst DEFAULT_DIST = 'dist';\n/** 默认端口 */\nconst DEFAULT_PORT = 3000;\n/** 路由清单文件名(build/dev 启动时生成,必需产物) */\nconst ROUTES_FILE = 'faapi-routes.js';\n/** tool 清单文件名(build/dev 启动时生成,可选产物——无 tool 的项目不生成) */\nconst TOOLS_FILE = 'faapi-tools.js';\n/** agent 清单文件名(build/dev 启动时生成,可选产物——无 agent 的项目不生成) */\nconst AGENTS_FILE = 'faapi-agents.js';\n/** 路由源码目录(写死为 src,路由 .ts 文件位于 src/api/ 下) */\n\n/**\n * 加载 faapi-tools.js 并水合到 toolRegistry(单例)\n *\n * 与路由清单不同,tool 是可选能力——纯 API 项目可能没有 `faapi-tools.js`,\n * 此时不报错,返回空数组,toolRegistry 保持空。\n *\n * dev 按需模式下 `reloadTools` 也会调用此函数重新水合(`setLoadTimestamp` 已在外层设置)。\n *\n * @returns 水合后的 ToolMetadata[](供调用方日志/调试)\n */\nexport async function loadAndHydrateTools(\n rootDir: string,\n dist: string,\n registries: AppRegistries = defaultRegistries,\n): Promise<ToolMetadata[]> {\n const toolsPath = path.resolve(rootDir, dist, TOOLS_FILE);\n if (!fs.existsSync(toolsPath)) {\n return [];\n }\n const serialized = (await importWithCacheBust(toolsPath)) as unknown as {\n tools: SerializedToolRecord[];\n };\n const hydrated = hydrateTools(serialized.tools ?? []);\n registries.tool.hydrate(hydrated);\n return hydrated;\n}\n\n/**\n * 加载 faapi-agents.js 并水合到 agentRegistry(单例)\n *\n * 与 `loadAndHydrateTools` 对称——agent 是可选能力,纯 API 项目可能没有 `faapi-agents.js`,\n * 此时不报错,返回空数组,agentRegistry 保持空。\n *\n * dev 按需模式下 `reloadAgents` 也会调用此函数重新水合(`setLoadTimestamp` 已在外层设置)。\n *\n * @returns 水合后的 AgentMetadata[](供调用方日志/调试)\n */\nexport async function loadAndHydrateAgents(\n rootDir: string,\n dist: string,\n registries: AppRegistries = defaultRegistries,\n): Promise<AgentMetadata[]> {\n const agentsPath = path.resolve(rootDir, dist, AGENTS_FILE);\n if (!fs.existsSync(agentsPath)) {\n return [];\n }\n const serialized = (await importWithCacheBust(agentsPath)) as unknown as {\n agents: SerializedAgentRecord[];\n };\n const hydrated = hydrateAgents(serialized.agents ?? []);\n registries.agent.hydrate(hydrated);\n return hydrated;\n}\n\n/**\n * 当前 app 单例的 globalThis key\n *\n * 用 `Symbol.for` 创建全局 symbol,确保跨模块实例共享同一个 key——\n * Next.js 16 默认用 Turbopack 作为 `next dev` 的 bundler,Turbopack dev server runtime\n * 与主进程的 Node.js 原生 module cache 是两套独立缓存,用模块级变量无法跨实例共享,\n * 必须借助 `globalThis`。\n *\n * 注:生产模式(`node dist/main` + `next build`)不受此问题影响——`next build` 产物是\n * 普通 JS 文件,运行时通过 Node.js 原生 require 加载,与主进程共享 module cache。\n * 此机制主要解决 dev 模式下 RSC 调用 `getApp()` 的问题,对生产模式无副作用。\n */\nconst APP_INSTANCE_KEY = Symbol.for('faapi.app.instance');\n\n/**\n * 读取当前 app 单例(从 globalThis 取,跨模块实例共享)\n *\n * 注意:单例仅指向\"最近一次创建且未关闭的 app\"。测试场景下创建多个临时 app 时,\n * 单例会被覆盖,但 close 时只有当单例仍指向当前 app 才置 null,避免被后续 app 误清。\n */\nfunction getCurrentApp(): AppBase | null {\n return (globalThis as Record<symbol, AppBase | undefined>)[APP_INSTANCE_KEY] ?? null;\n}\n\n/** 设置/清除当前 app 单例(写入 globalThis,跨模块实例共享) */\nfunction setCurrentApp(app: AppBase | null): void {\n if (app === null) {\n delete (globalThis as Record<symbol, AppBase | undefined>)[APP_INSTANCE_KEY];\n } else {\n (globalThis as Record<symbol, AppBase | undefined>)[APP_INSTANCE_KEY] = app;\n }\n}\n\n/** 默认优雅关闭信号 handler 是否已安装(写入 globalThis,跨模块实例共享) */\nconst SHUTDOWN_INSTALLED_KEY = Symbol.for('faapi.defaultShutdownInstalled');\n\n/**\n * 注册默认优雅关闭信号(SIGTERM/SIGINT,进程级仅注册一次)\n *\n * 收到信号时关闭当前 app 单例(app.close() 内部完成 drain + onClose 钩子 +\n * 注册表清理)后退出。faapi 单进程单 app 设计——单例即唯一运行中的 app;\n * 测试场景多次 listen 不会堆积 process 监听器(globalThis 标记防重装)。\n */\nfunction registerDefaultShutdownHandlers(): void {\n const g = globalThis as Record<symbol, unknown>;\n if (g[SHUTDOWN_INSTALLED_KEY]) return;\n g[SHUTDOWN_INSTALLED_KEY] = true;\n\n const shutdown = (signal: string): void => {\n console.log(`\\n- Received ${signal}, shutting down...`);\n void (async () => {\n const app = getCurrentApp();\n if (app) await app.close();\n process.exit(0);\n })();\n };\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('SIGINT', () => shutdown('SIGINT'));\n}\n\n/**\n * 获取当前 faapi app 单例\n *\n * 用于在无法直接拿到 app 引用的场景(如 Next.js Server Component)中访问 app。\n *\n * 通过 `globalThis` 共享单例,确保 Next.js 16 Turbopack dev server runtime 加载的\n * `@faapi/faapi` 模块实例与主进程 `faapi dev` 设置单例的实例能读到同一个 app 引用。\n *\n * @returns 当前 app 实例\n * @throws 未初始化时抛错(需先调 `createProdApp()` / `createDevApp()`,或 `faapi dev` / `node dist/main` 启动)\n *\n * @example\n * ```ts\n * // Next.js RSC 中调用 faapi API(同进程,跳过 HTTP loopback)\n * import { getApp } from '@faapi/faapi';\n * import { headers } from 'next/headers';\n *\n * const app = getApp();\n * const h = await headers();\n * const res = await app.inject({\n * method: 'GET',\n * path: '/api/user',\n * headers: { cookie: h.get('cookie') ?? '', authorization: h.get('authorization') ?? '' },\n * });\n * const data = res.body; // 已解析\n * ```\n */\nexport function getApp(): AppBase {\n const app = getCurrentApp();\n if (!app) {\n throw new Error(\n '[faapi] No app instance. Call createProdApp() / createDevApp() first, or run `faapi dev` / `node dist/main`.',\n );\n }\n return app;\n}\n\n/** FaapiConfig 的内置 key 集合(排除自定义业务配置) */\nconst FAAPI_CONFIG_KEYS = new Set([\n 'cors',\n 'lifecycle',\n 'middlewares',\n 'injectors',\n 'extendContext',\n 'plugins',\n 'helmet',\n 'compression',\n 'etag',\n 'bodyLimit',\n 'logger',\n 'http2',\n 'trustedProxy',\n 'response',\n]);\n\nfunction isFaapiConfigKey(key: string): boolean {\n return FAAPI_CONFIG_KEYS.has(key);\n}\n\nexport interface CreateAppOptions {\n /** 项目根目录,默认 process.cwd() */\n rootDir?: string;\n /** 产物输出目录(如 dist 或 .faapi),覆盖环境变量 FAAPI_DIST,默认 'dist' */\n dist?: string;\n /** 端口号,也可在 listen() 时传入;默认环境变量 PORT 或 3000 */\n port?: number;\n}\n\n/** 应用基础接口(dev/prod 共用,不含 reloadRoutes) */\nexport interface AppBase {\n /** Node.js Server 实例(listen 后可用,close 后置 null) */\n server: Server | null;\n /** app 级注册表(tool/agent/skill/agentHandle 实例,close 时清理) */\n registries: AppRegistries;\n /** 排序后的路由清单 */\n routes: RouteManifest;\n /** WebSocket 路由清单 */\n wsRoutes: WsRouteManifest;\n /** 项目根目录 */\n rootDir: string;\n /** 启动 HTTP server,打印路由表,执行 onReady 钩子 */\n listen(port?: number): Promise<Server>;\n /** 关闭 server,执行 onClose 钩子 */\n close(): Promise<void>;\n /**\n * 无服务器测试注入\n *\n * 构建一个模拟请求直接走完整请求链路(CORS / helmet / logger / 全局中间件 / 路由匹配 /\n * schema 校验 / 目录中间件 / handler),不绑定端口,返回已解析的 `{ status, headers, body }`。\n *\n * `listen()` 前后均可调用——`listen()` 后调用常用于 Next.js Server Component 等同进程场景\n * (配合 `getApp()` 拿到 app 实例)。\n */\n inject(options?: InjectOptions): Promise<InjectResponse>;\n}\n\n/**\n * 内部上下文(供 dev 扩展 reloadRoutes 使用)\n *\n * prod 模式不使用此上下文——createProdApp 直接返回 AppBase。\n */\nexport interface AppContext {\n /** 项目根目录 */\n rootDir: string;\n /** app 级注册表(tool/agent/skill/agentHandle 实例,随 app 生命周期) */\n registries: AppRegistries;\n /** 产物目录 */\n dist: string;\n /** 扫描 patterns(scanRoutes 用) */\n patterns: string[];\n /** Node.js Server 实例(未 listen) */\n server: Server;\n /** 路由可变引用容器(createServer 闭包和 reloadRoutes 共享) */\n routesRef: RoutesRef;\n /** 配置(原始 FaapiConfig 或 null) */\n config: FaapiConfig | null;\n /**\n * 更新路由引用(app.routes/wsRoutes + routesRef + 闭包变量)\n *\n * dev reloadRoutes 调用此方法把新扫描的路由同步到 app 和 server。\n */\n updateRoutes(routes: RouteManifest, wsRoutes: WsRouteManifest): void;\n}\n\n/**\n * 创建应用基础实例(dev/prod 共用逻辑)\n *\n * 完成:配置加载 → 路由清单水合 → 创建 server → 插件加载。\n * 返回 AppBase(listen/close)+ AppContext(供 dev 扩展 reloadRoutes)。\n *\n * dist 由 `process.env.FAAPI_DIST` 决定:\n * - `faapi dev` 启动时固定设为 `.faapi` → 读 dev 产物\n * - `node <dist>/main` 不设 → 默认 `dist`,读 prod 产物\n *\n * 不负责编译 TypeScript——编译由 `faapi dev`(esbuild → `.faapi/`)和 `faapi build`(→ `dist/`)负责。\n * 不负责生成路由清单——`faapi dev`/`faapi build` 启动时生成 `faapi-routes.js`,createAppBase 直接水合。\n */\nexport async function createAppBase(options?: CreateAppOptions): Promise<{\n app: AppBase;\n ctx: AppContext;\n}> {\n const rootDir = options?.rootDir ?? process.cwd();\n const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;\n\n // 校验产物存在性\n const routesPath = path.resolve(rootDir, dist, ROUTES_FILE);\n if (!fs.existsSync(routesPath)) {\n throw new Error(\n `[faapi] ${dist}/${ROUTES_FILE} 不存在,请先执行 \\`faapi build\\`(或 \\`faapi dev\\`)生成产物。`,\n );\n }\n\n // 加载配置(统一读 <dist>/faapi-config.js)\n const config = await loadConfig(rootDir, dist);\n\n // 水合路由清单(统一路径,无 dev/prod 分支)\n const serialized = (await importWithCacheBust(routesPath)) as unknown as SerializedRouteManifest;\n const hydrated = await hydrateRoutes(serialized);\n let sorted = sortRoutes(hydrated.routes);\n let wsRoutes = hydrated.wsRoutes;\n\n // 检测路由冲突\n const conflicts = detectRouteConflicts(sorted);\n if (conflicts.length > 0) {\n for (const conflict of conflicts) {\n console.warn(`! 路由冲突: ${conflict.method} ${conflict.urlPath}`);\n for (const file of conflict.files) {\n console.warn(` - ${file}`);\n }\n }\n }\n\n // app 级注册表(方案 A 实例化):每个 app 持有独立实例,随 app 创建/销毁,\n // 多 app 同进程互不串台。框架链路(请求注入 / 插件 / lifecycle)只读写此实例\n const registries = createAppRegistries();\n\n // 水合 tool 清单(可选产物——无 tool 的项目跳过,tool 注册表保持空)\n const tools = await loadAndHydrateTools(rootDir, dist, registries);\n\n // 水合 agent 清单(可选产物——无 agent 的项目跳过,agent 注册表保持空)\n const agents = await loadAndHydrateAgents(rootDir, dist, registries);\n\n // 自定义业务配置(排除内置 key)\n const pluginConfig: Record<string, unknown> = config\n ? Object.fromEntries(Object.entries(config).filter(([k]) => !isFaapiConfigKey(k)))\n : {};\n\n // 创建 server(不 listen)\n const { server, routesRef } = createServer({\n routes: sorted,\n rootDir,\n dist,\n cors: config?.cors ?? true,\n onError: config?.lifecycle?.onError,\n config: (config as Record<string, unknown> | null) ?? undefined,\n wsRoutes,\n middlewares: config?.middlewares,\n injectors: config?.injectors,\n helmet: config?.helmet,\n compression: config?.compression,\n etag: config?.etag,\n logger: config?.logger,\n bodyLimit: config?.bodyLimit,\n http2: config?.http2,\n trustedProxy: config?.trustedProxy,\n registries,\n });\n\n // 加载插件 + 应用 handler/upgrade 包装器(dist 供本地 TS 插件按需编译/产物复用)\n const { handlerWrappers, upgradeWrappers } = await loadPlugins(\n config?.plugins,\n {\n rootDir,\n registries,\n routes: sorted,\n getRoutes: () => sorted,\n server,\n config: pluginConfig,\n },\n rootDir,\n dist,\n );\n applyPluginWrappers(server, handlerWrappers, upgradeWrappers);\n\n // 关闭状态标记(避免重复关闭)\n let closed = false;\n\n const app: AppBase = {\n server: null,\n registries,\n routes: sorted,\n wsRoutes,\n rootDir,\n\n async listen(listenPort?: number): Promise<Server> {\n // 端口优先级:listen() 参数 > options.port > 环境变量 PORT > 默认 3000\n const envPort = process.env.PORT ? Number(process.env.PORT) : undefined;\n const actualPort = listenPort ?? options?.port ?? envPort ?? DEFAULT_PORT;\n\n // onBoot 生命周期钩子(listen 前):启动校验 / DB 迁移等\"失败即不该暴露端口\"的逻辑。\n // 此时 server 已创建但未监听、路由/tool/agent 清单已水合、插件已加载。\n // 抛错 → listen() 以原始错误 reject,server.listen 不会被调用,端口不暴露。\n if (config?.lifecycle?.onBoot) {\n try {\n await config.lifecycle.onBoot({ rootDir, routes: sorted, server, registries });\n console.log('- onBoot hook executed');\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[faapi] onBoot hook failed: ${message}`);\n throw err;\n }\n }\n\n return new Promise<Server>((resolve, reject) => {\n // listen 阶段错误(端口占用等)转为 Promise reject,避免未监听 'error'\n // 事件以裸堆栈崩掉进程;成功后解除,运行期错误语义不变\n const onListenError = (err: Error): void => {\n if ((err as NodeJS.ErrnoException).code === 'EADDRINUSE') {\n reject(\n new Error(\n `Port ${actualPort} is already in use. ` +\n `Is another faapi instance running? Change the port via the PORT env var.`,\n ),\n );\n return;\n }\n reject(err);\n };\n server.once('error', onListenError);\n server.listen(actualPort, async () => {\n server.off('error', onListenError);\n const address = server.address();\n const p = typeof address === 'object' && address !== null ? address.port : actualPort;\n\n console.log('faapi server started');\n console.log(`- Local: http://localhost:${p}`);\n console.log('- Loaded routes:');\n for (const route of sorted) {\n console.log(` ${route.method.padEnd(6)}${route.urlPath} ${route.filePath}`);\n }\n if (wsRoutes.length > 0) {\n console.log('- WebSocket routes:');\n for (const route of wsRoutes) {\n console.log(` WS ${route.urlPath} ${route.filePath}`);\n }\n }\n if (tools.length > 0) {\n console.log(`- Loaded ${tools.length} tool(s):`);\n for (const tool of tools) {\n console.log(` ${tool.name} ${tool.filePath}`);\n }\n }\n if (agents.length > 0) {\n console.log(`- Loaded ${agents.length} agent(s):`);\n for (const agent of agents) {\n const exports = agent.hasRun ? 'run' : '-';\n console.log(` ${agent.name} [${exports}] ${agent.filePath}`);\n }\n }\n\n // 注册默认优雅关闭信号(进程级仅注册一次,faapi 单进程单 app 设计)\n registerDefaultShutdownHandlers();\n\n // onReady 生命周期钩子\n if (config?.lifecycle?.onReady) {\n await config.lifecycle.onReady({ rootDir, routes: sorted, server, registries });\n console.log('- onReady hook executed');\n }\n\n app.server = server;\n resolve(server);\n });\n });\n },\n\n async inject(injectOpts?: InjectOptions): Promise<InjectResponse> {\n const {\n method = 'GET',\n path: reqPath = '/',\n headers: reqHeaders = {},\n query,\n body,\n } = injectOpts ?? {};\n\n const queryStr = query\n ? '?' +\n new URLSearchParams(Object.entries(query).map(([k, v]) => [k, String(v)])).toString()\n : '';\n\n return new Promise<InjectResponse>((resolve, reject) => {\n // mockRes 需为 Writable Stream(PassThrough)以支持 sendNodeResponse 的 pipe 路径\n // (handler 返回值被自动包裹后产生 JSON body,走 nodeStream.pipe(res))\n const chunks: Buffer[] = [];\n const mockRes = new PassThrough() as PassThrough & {\n statusCode: number;\n _headers: Record<string, string>;\n setHeader(name: string, value: string): void;\n appendHeader(name: string, value: string): void;\n writeHead(status: number, headers?: Record<string, string>): void;\n };\n mockRes.statusCode = 200;\n mockRes._headers = {};\n mockRes.setHeader = function (name: string, value: string) {\n this._headers[name.toLowerCase()] = value;\n };\n mockRes.appendHeader = function (name: string, value: string) {\n const key = name.toLowerCase();\n const existing = this._headers[key];\n this._headers[key] = existing ? `${existing}, ${value}` : value;\n };\n mockRes.writeHead = function (status: number, headers?: Record<string, string>) {\n this.statusCode = status;\n if (headers) {\n Object.assign(this._headers, headers);\n }\n };\n\n mockRes.on('data', (chunk: Buffer) => chunks.push(chunk));\n mockRes.on('error', reject);\n mockRes.on('finish', () => {\n const body = Buffer.concat(chunks);\n let parsed: unknown;\n try {\n parsed = JSON.parse(body.toString());\n } catch {\n parsed = body.toString();\n }\n resolve({\n status: mockRes.statusCode,\n headers: new Headers(mockRes._headers as Record<string, string>),\n body: parsed,\n });\n });\n\n const listeners = server.listeners('request');\n const handler = listeners[listeners.length - 1];\n if (typeof handler !== 'function') {\n reject(new Error('No request handler found'));\n return;\n }\n\n const mockReq: Readable & {\n method?: string;\n url?: string;\n headers?: Record<string, string | undefined>;\n socket?: { remoteAddress?: string };\n } = Readable.from(body !== undefined ? [Buffer.from(JSON.stringify(body))] : []);\n mockReq.method = method;\n mockReq.url = `${reqPath}${queryStr}`;\n mockReq.headers = {\n ...reqHeaders,\n host: 'localhost',\n 'content-type': body !== undefined ? 'application/json' : undefined,\n };\n mockReq.socket = { remoteAddress: '127.0.0.1' };\n\n handler(\n mockReq as unknown as import('node:http').IncomingMessage,\n mockRes as unknown as import('node:http').ServerResponse,\n );\n });\n },\n\n async close(): Promise<void> {\n if (closed) return;\n closed = true;\n\n const s = server as unknown as {\n closeIdleConnections?: () => void;\n closeAllConnections?: () => void;\n };\n\n // 停止接受新连接 + 断开空闲 keep-alive 连接;在途请求继续执行直至完成(drain)。\n // 此前 closeAllConnections 在等待之前调用,会直接掐断在途请求(硬关,非优雅停机)\n s.closeIdleConnections?.();\n\n if (config?.lifecycle?.onClose) {\n await config.lifecycle.onClose({ rootDir, routes: sorted, server, registries });\n }\n\n // 注册表(方案 A 实例化):清理的是 app 自己的实例——多 app 同进程\n // 天然隔离,无需所有权守卫;全局默认实例不被 app 生命周期触碰\n registries.tool.clear();\n registries.agent.clear();\n registries.skill.clear();\n registries.agentHandle.clear();\n\n // server 未 listen 时直接清理状态(避免 ERR_SERVER_NOT_RUNNING 错误)\n if (!server.listening) {\n app.server = null;\n // 清理单例(仅当单例仍指向当前 app 时,避免被后续 app 误清)\n if (getCurrentApp() === app) setCurrentApp(null);\n return;\n }\n\n const drained = new Promise<void>((resolve) => {\n server.close((err) => {\n if (err) console.error('Error closing server:', err);\n resolve();\n });\n });\n\n // drain:等在途请求完成。SSE/WS 长连接永不主动结束,超时后强制断开\n // (默认 10s,FAAPI_SHUTDOWN_TIMEOUT_MS 环境变量可调)\n const drainTimeoutMs = Number(process.env.FAAPI_SHUTDOWN_TIMEOUT_MS ?? 10_000);\n if (\n typeof s.closeAllConnections === 'function' &&\n Number.isFinite(drainTimeoutMs) &&\n drainTimeoutMs >= 0\n ) {\n const forceClose = new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n s.closeAllConnections?.();\n resolve();\n }, drainTimeoutMs);\n // 停机定时器不应阻止进程自然退出\n timer.unref?.();\n });\n await Promise.race([drained, forceClose]);\n // 强制断开后 close 回调随即触发;短兜底等待收尾完成\n const tail = new Promise<void>((resolve) => setTimeout(resolve, 250).unref?.());\n await Promise.race([drained, tail]);\n } else {\n await drained;\n }\n\n app.server = null;\n // 清理单例(仅当单例仍指向当前 app 时,避免被后续 app 误清)\n if (getCurrentApp() === app) setCurrentApp(null);\n },\n };\n\n // 设置单例(覆盖之前的实例;测试场景下多次创建会覆盖,close 时只清自己)\n // 通过 globalThis 存储,确保 Next.js Turbopack dev server runtime 加载的模块实例也能读到\n setCurrentApp(app);\n\n /** 更新路由引用(app + routesRef + 闭包变量) */\n const ctx: AppContext = {\n rootDir,\n registries,\n dist,\n patterns: ROUTE_PATTERNS,\n server,\n routesRef,\n config,\n updateRoutes(newRoutes: RouteManifest, newWsRoutes: WsRouteManifest): void {\n sorted = newRoutes;\n wsRoutes = newWsRoutes;\n app.routes = newRoutes;\n app.wsRoutes = newWsRoutes;\n routesRef.current = newRoutes;\n routesRef.wsCurrent = newWsRoutes;\n },\n };\n\n return { app, ctx };\n}\n","import type { AppBase, CreateAppOptions } from './createAppCore';\nimport { createAppBase, loadAndHydrateTools, loadAndHydrateAgents } from './createAppCore';\nimport { scanRoutes } from '../router/scanRoutes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { scanTools } from '../tools/scanTools';\nimport { TOOL_PATTERNS } from '../tools/scanTools';\nimport { generateToolArtifacts } from './generateToolArtifacts';\nimport { scanAgents } from '../agents/scanAgents';\nimport { DEFAULT_AGENT_PATTERNS } from '../agents/scanAgents';\nimport { generateAgentArtifacts } from './generateAgentArtifacts';\nimport { invalidateMiddlewareCache } from '../middleware/loadMiddlewares';\nimport { invalidateProgramCache } from '../ast/createProgram';\nimport { invalidateSchemaCache } from '../validator/validateInput';\nimport { setLoadTimestamp } from '../utils/importWithCacheBust';\nimport {\n clearCompiledFiles,\n clearGeneratedSchemas,\n deleteSchemaFiles,\n isDevOnDemandEnabled,\n} from './compileOnDemand';\n\n/** dev 应用接口(AppBase + reloadRoutes/reloadTools/reloadAgents 热替换) */\nexport interface DevApp extends AppBase {\n /** 重新水合路由清单 + 清 schema 缓存 + 更新 server 路由引用(dev 热替换用) */\n reloadRoutes(): Promise<void>;\n /** 重新扫描 tools + 重生成 faapi-tools.js + 清缓存(dev 热替换用) */\n reloadTools(): Promise<void>;\n /** 重新扫描 agents + 重生成 faapi-agents.js + 清缓存(dev 热替换用) */\n reloadAgents(): Promise<void>;\n}\n\n/**\n * dev 模式应用启动 API\n *\n * 在 createAppBase(共享逻辑)基础上增加 `reloadRoutes` 热替换能力,供 `faapi dev` watcher 调用。\n *\n * 与 createProdApp 的区别:\n * - dev:含 reloadRoutes(重新扫描路由 + 重新生成 schema + 清缓存 + 更新 server 路由引用)\n * - prod:精简,无 reloadRoutes(产物已固化,运行时不重建)\n *\n * 由 `devCommand` 直接调用,devCommand 持有 app 引用并传给 watcher。\n *\n * @example\n * ```ts\n * // devCommand 内部\n * const app = await createDevApp();\n * await app.listen();\n * startWatcher({ rootDir, app, devDist });\n * ```\n */\nexport async function createDevApp(options?: CreateAppOptions): Promise<DevApp> {\n const { app, ctx } = await createAppBase(options);\n\n const devApp = app as DevApp;\n\n devApp.reloadRoutes = async (): Promise<void> => {\n // 更新模块加载时间戳(ESM import 绕过缓存)\n setLoadTimestamp(Date.now());\n // 清理缓存(中间件/schema 已被 watcher 重新生成)\n invalidateMiddlewareCache();\n invalidateProgramCache();\n invalidateSchemaCache();\n // 清按需编译缓存(让被修改的 handler.js 重新编译)\n clearCompiledFiles();\n // 重新扫描路由\n // 不走 faapi-routes.js 重新 import——ESM 模块缓存难以可靠绕过,直接 scanRoutes 更稳定\n const reScanned = await scanRoutes(ctx.rootDir, ctx.patterns, ctx.dist);\n const sorted = sortRoutes(reScanned.routes);\n\n if (isDevOnDemandEnabled()) {\n // 按需模式:删除 stale zod.js(类型引用变化等),后台批量预生成 + 请求兜底\n // 不在 reload 路径上 await 生成——保持热替换轻快\n await deleteSchemaFiles(sorted, ctx.rootDir, ctx.dist);\n clearGeneratedSchemas();\n // 后台预生成全量 zod.js:此前只删不生成,每次保存后所有路由的首个请求都要\n // 在请求路径上同步付全项目 Program 创建 + schema 生成的代价(p99 尖刺)。\n // 预生成不阻塞 reload 与请求;请求路径的 ensureSchemaGenerated(mtime 缓存 +\n // in-flight mutex + 原子写)与之自然协同——已生成命中 mtime 跳过,并发生成\n // 各自原子落盘内容一致\n void (async (): Promise<void> => {\n try {\n const { generateSchemaFiles } = await import('./generateSchemaFiles');\n await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);\n } catch (err) {\n // 后台失败不崩进程——请求路径的按需生成仍会兜底\n console.error('[faapi] Background schema regeneration failed:', err);\n }\n })();\n } else {\n // 非按需模式(兼容旧路径):全量重新生成 zod.js\n const { generateSchemaFiles } = await import('./generateSchemaFiles');\n await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);\n }\n\n // 更新 app 和 server 路由引用\n ctx.updateRoutes(sorted, reScanned.wsRoutes);\n };\n\n devApp.reloadTools = async (): Promise<void> => {\n // 更新模块加载时间戳(ESM import 绕过缓存,让 faapi-tools.js 重新读取)\n setLoadTimestamp(Date.now());\n // 清 Program 缓存(tool 源码可能变化,AST 需重新分析)\n invalidateProgramCache();\n // 重新扫描 tools(零 import,仅读源码 + 正则提取函数名)\n const tools = await scanTools(ctx.rootDir, TOOL_PATTERNS);\n // 重生成 faapi-tools.js(含 AST 增强:description / inputTypeName)\n // 按需模式跳过 zod.js 生成——首次请求时按需生成(与 reloadRoutes 的策略一致)\n await generateToolArtifacts(tools, ctx.rootDir, ctx.dist, {\n skipSchema: isDevOnDemandEnabled(),\n });\n // 重新水合 faapi-tools.js 到 toolRegistry(reload 后需更新注册表)\n await loadAndHydrateTools(ctx.rootDir, ctx.dist, ctx.registries);\n };\n\n devApp.reloadAgents = async (): Promise<void> => {\n // 更新模块加载时间戳(ESM import 绕过缓存,让 faapi-agents.js 重新读取)\n setLoadTimestamp(Date.now());\n // 清 Program 缓存(agent 源码可能变化,AST 需重新分析)\n invalidateProgramCache();\n // 重新扫描 agents(零 import,仅读源码 + 正则检测 config/run 导出)\n const agents = await scanAgents(ctx.rootDir, DEFAULT_AGENT_PATTERNS);\n // 重生成 faapi-agents.js(含 AST 增强:description / @agent 覆盖 / config 块字段)\n // agent 不生成 zod.js(无输入参数),无 skipSchema 选项\n await generateAgentArtifacts(agents, ctx.rootDir, ctx.dist);\n // 重新水合 faapi-agents.js 到 agentRegistry(reload 后需更新注册表)\n await loadAndHydrateAgents(ctx.rootDir, ctx.dist, ctx.registries);\n };\n\n return devApp;\n}\n","import path from 'node:path';\nimport { compileConfig } from './compileConfig';\nimport { serializeRoutes, writeRoutesModule } from './generateRoutes';\nimport { scanRoutes } from '../router/scanRoutes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { scanTools } from '../tools/scanTools';\nimport { TOOL_PATTERNS } from '../tools/scanTools';\nimport { generateToolArtifacts } from './generateToolArtifacts';\nimport { scanAgents } from '../agents/scanAgents';\nimport { DEFAULT_AGENT_PATTERNS } from '../agents/scanAgents';\nimport { generateAgentArtifacts } from './generateAgentArtifacts';\nimport { loadConfig } from '../config/loadConfig';\nimport { loadEnv } from './loadEnv';\nimport { startWatcher } from './watcher';\nimport { createDevApp } from './createDevApp';\nimport { setDevOnDemandEnabled, setDevDist } from './compileOnDemand';\nimport { ROUTE_PATTERNS } from '../utils/prodPaths';\n\n/** dev 模式产物目录(固定为 .faapi,不可修改) */\nconst DEV_DIST = '.faapi';\n/** 路由清单文件名 */\nconst ROUTES_FILE = 'faapi-routes.js';\n/** 路由源码目录(写死为 src,路由 .ts 文件位于 src/api/ 下) */\n\n/** dev 命令选项(来自 CLI 参数) */\nexport interface DevCommandOptions {\n port?: number;\n}\n\n/**\n * `faapi dev` 命令:编译配置 → 生成路由清单 → 启动 dev 应用 → 启动 watcher\n *\n * **Vite 风格按需编译**(阶段 2+3):启动时只编译配置和路由清单,**不全量编译 handler.js / zod.js**。\n * - handler.js 在首次请求时由 `loadRouteModule` / `loadWsHandler` 触发按需编译\n * - zod.js 在首次请求时由 `ensureSchemaGenerated` 触发按需生成\n *\n * 与 `faapi build`(产线构建)为两套独立代码路径,仅共享工具级函数。\n *\n * 框架元信息通过 CLI 选项或环境变量传入(不放在 faapi.config.ts 内):\n * - `--port` / `PORT`:服务端口,默认 3000\n * - `FAAPI_DIST`:dev 模式由 devCommand 固定设为 `.faapi`\n *\n * 产物(与 `faapi build` 一致,仅目录不同:dev 用 `.faapi/`,build 用 `dist/`):\n * 1. `.faapi/faapi-config.js` — 配置合并产物(compileConfig 生成)\n * 2. `.faapi/faapi-routes.js` — 路由清单(serializeRoutes 生成)\n * 3. `.faapi/` 下各 handler 目录的 `zod.js` — schema 模块(**按需生成**,首次请求触发)\n * 4. `.faapi/` 下的 handler `*.js` — **按需编译**(首次请求时触发,非启动时全量)\n *\n * 流程:\n * 1. 兜底 NODE_ENV(未显式设置时)+ 加载 .env 系列文件到 process.env(loadEnv)\n * 2. 设置 dev 环境标记 + `FAAPI_DIST=.faapi` + 启用按需编译模式\n * 3. 编译配置产物(compileConfig)—— config 引用的项目模块也由 compileConfig 编译\n * 4. 生成路由清单(scanRoutes 读源码 + 正则提取方法名,零 import)\n * 5. 调用 createDevApp() + listen() 启动 dev 应用(含 reloadRoutes 热替换能力)\n * 6. 启动 watcher(增量编译 + 重生成 config + 调 app.reloadRoutes 热替换)\n *\n * **与旧版(启动全量编译)的差异**:\n * - 旧版:步骤 4 之前调 `compileDevRoutes` + `generateSchemaFiles` 全量编译所有 .ts 和生成 zod.js → 启动慢\n * - 新版:跳过全量编译和 schema 生成,handler.js / zod.js 按需生成 → 启动快,首请求有单文件编译延迟(~50ms)\n */\nexport async function devCommand(options?: DevCommandOptions): Promise<void> {\n const rootDir = process.cwd();\n\n // 1. 兜底 NODE_ENV(未显式设置时)+ 加载 .env 系列文件到 process.env\n // loadEnv 读 NODE_ENV 决定加载 .env.{env},需在 loadEnv 之前设置\n if (!process.env.NODE_ENV) process.env.NODE_ENV = 'development';\n loadEnv(rootDir);\n\n // 2. 设置 dist(固定为 .faapi,不可修改)+ 启用按需编译模式\n const devDist = DEV_DIST;\n process.env.FAAPI_DIST = devDist;\n setDevOnDemandEnabled(true);\n setDevDist(devDist);\n console.log('- Development mode (on-demand compile)');\n\n // 3. 编译配置产物(compileConfig 内部编译 config 源 + 引用的项目模块)\n console.log('- Compiling config...');\n await compileConfig({ rootDir, dist: devDist });\n const _config = await loadConfig(rootDir, devDist);\n\n // 4. 生成路由清单 + schema 文件(scanRoutes 不 import,仅读源码 + 正则提取方法名)\n console.log('- Generating route manifest (schema generated on demand)...');\n await generateRouteArtifacts(rootDir, ROUTE_PATTERNS, devDist);\n\n // 5. 生成 tool 清单(scanTools 不 import,仅读源码 + 正则提取函数名)\n // 按需模式跳过 zod.js 生成——首次请求时按需生成(与路由 schema 策略一致)\n console.log('- Generating tool manifest...');\n await generateToolArtifactsForDev(rootDir, devDist);\n\n // 6. 生成 agent 清单(scanAgents 不 import,仅读源码 + 正则检测 config/run)\n // agent 不生成 zod.js(无输入参数),无 skipSchema 选项\n // 无 agent 文件时 scanAgents 返回空列表,generateAgentArtifacts 写入空清单\n console.log('- Generating agent manifest...');\n await generateAgentArtifactsForDev(rootDir, devDist);\n\n // 7. 启动 dev 应用(createDevApp + listen,含 reloadRoutes/reloadTools/reloadAgents 热替换能力)\n console.log('- Starting dev app...');\n const app = await createDevApp({ rootDir, port: options?.port });\n await app.listen();\n\n // 8. 启动 watcher(文件变化时增量编译 + 重生成 config + 调 app.reloadRoutes/reloadTools/reloadAgents)\n startWatcher({ rootDir, app, devDist });\n}\n\n/**\n * 生成路由产物:faapi-routes.js(仅路由清单,不含 zod.js)\n *\n * zod.js 按需生成:首次请求时由 `ensureSchemaGenerated` 触发(阶段 3)。\n * 与 `faapi build` 不同——build 阶段全量生成 zod.js,dev 阶段按需生成。\n */\nexport async function generateRouteArtifacts(\n rootDir: string,\n patterns: string[],\n dist: string,\n): Promise<void> {\n // 扫描路由(读源码 + 正则提取方法名,零 import)\n const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, dist);\n const sorted = sortRoutes(routes);\n\n // 生成路由清单(zod.js 按需生成,不在启动时全量生成)\n const routesPath = path.resolve(rootDir, dist, ROUTES_FILE);\n const serialized = serializeRoutes(sorted, wsRoutes, rootDir, dist);\n await writeRoutesModule(serialized, routesPath);\n}\n\n/**\n * 生成 tool 产物:faapi-tools.js(仅 tool 清单,不含 zod.js)\n *\n * 与 `generateRouteArtifacts` 对称——dev 按需模式跳过 zod.js 生成,\n * 首次请求时按需生成(tool zod.js 的按需生成由 toolRegistry 在 Phase 1.6+ 接入)。\n * 无 tool 文件时 scanTools 返回空列表,generateToolArtifacts 写入空清单。\n */\nexport async function generateToolArtifactsForDev(rootDir: string, dist: string): Promise<void> {\n const tools = await scanTools(rootDir, TOOL_PATTERNS);\n await generateToolArtifacts(tools, rootDir, dist, { skipSchema: true });\n}\n\n/**\n * 生成 agent 产物:faapi-agents.js(仅 agent 清单,不含 zod.js)\n *\n * 与 `generateToolArtifactsForDev` 对称——agent 不生成 zod.js(无输入参数,\n * config 块字段在 AST 阶段已提取为字面量)。无 agent 文件时 scanAgents 返回空列表,\n * generateAgentArtifacts 写入空清单。\n */\nexport async function generateAgentArtifactsForDev(rootDir: string, dist: string): Promise<void> {\n const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);\n await generateAgentArtifacts(agents, rootDir, dist);\n}\n","import { compileSourceFiles, type CompileResult } from './compileSourceFiles';\n\nexport type { CompileResult };\n\n/**\n * build 编译选项\n */\nexport interface CompileBuildOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(build 模式为 `dist`) */\n dist: string;\n /**\n * 增量编译:传入要编译的文件列表(绝对路径)。\n * 不传则全量编译 src 下所有 .ts(排除测试文件和声明文件)。\n */\n files?: string[];\n /** 是否输出 esbuild 日志(build 模式默认静默) */\n logLevel?: 'silent' | 'info';\n}\n\n/**\n * build 模式编译 TypeScript:逐文件编译 + 编译期常量替换 + 死分支删除\n *\n * 共享实现见 [compileSourceFiles](./compileSourceFiles.ts);build 差异仅一点:\n * 启用 `define` + `minifySyntax`,在编译期把 `process.env.NODE_ENV` 替换为\n * `\"production\"` 并删除 `if (false) {...}` 死分支。两者在 `bundle: false` 下均生效\n * (单文件级别优化,不需要跨文件分析)。\n *\n * **为什么不用 bundle 模式**:bundle 模式会把 import 的项目模块 inline 进产物,\n * 导致 `faapi.config.ts` 中的 `instanceof` 对项目自定义错误类失效\n * (config 和 routes 各自打包出独立的项目类副本)。\n * 逐文件编译保证每个源文件对应唯一一份产物,config 和 routes 共享同一运行时对象。\n *\n * **tree shaking 不可用**:`bundle: false` 不分析跨文件引用图,未引用的 export 不会被删除。\n * 这恰好符合设计意图——保留所有 export,让 config 和 routes 共享同一运行时对象。\n *\n * 框架采用零入口设计——用户无需编写 main.ts,dev/prod 启动由 CLI 内部编排。\n *\n * @example\n * await compileBuildRoutes({ rootDir, dist: 'dist' });\n */\nexport async function compileBuildRoutes(options: CompileBuildOptions): Promise<CompileResult> {\n return compileSourceFiles({ ...options, production: true });\n}\n","import { scanRoutes } from '../router/scanRoutes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { detectRouteConflicts } from '../router/detectRouteConflicts';\nimport { scanTools } from '../tools/scanTools';\nimport { TOOL_PATTERNS } from '../tools/scanTools';\nimport { generateToolArtifacts } from './generateToolArtifacts';\nimport { scanAgents } from '../agents/scanAgents';\nimport { DEFAULT_AGENT_PATTERNS } from '../agents/scanAgents';\nimport { generateAgentArtifacts } from './generateAgentArtifacts';\nimport { generateSchemaFiles } from './generateSchemaFiles';\nimport { serializeRoutes, writeRoutesModule } from './generateRoutes';\nimport { compileBuildRoutes } from './compileBuildRoutes';\nimport { ROUTE_PATTERNS } from '../utils/prodPaths';\nimport { compileConfig, compileProjectModules } from './compileConfig';\nimport { loadConfig } from '../config/loadConfig';\nimport { resolveLocalPluginSource } from './loadPlugins';\nimport type { PluginDeclaration } from '../config/pluginTypes';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { isInsideDir, toRealPath } from '../utils/prodPaths';\n\n/** build 模式默认产物目录 */\nconst DEFAULT_DIST = 'dist';\n/** 路由源码目录(写死为 src,路由 .ts 文件位于 src/api/ 下) */\n\n/**\n * 构建命令选项\n */\nexport interface BuildOptions {\n /** 项目根目录,默认 process.cwd() */\n rootDir?: string;\n /** 产物输出目录,默认 dist */\n dist?: string;\n}\n\n/**\n * 执行构建命令\n *\n * `--dist` 直接作为产物输出目录(默认 `dist`),与 Next.js `distDir` 语义一致。\n * 运行时只加载 .js 产物,不依赖 tsx。\n *\n * 应用行为配置从 faapi.config.ts 读取。\n *\n * 框架采用零入口设计——用户无需编写 main.ts,build 阶段自动生成 `<dist>/main.js` 启动入口,\n * 运行时 `node <dist>/main` 直接启动服务,无需 `faapi start` 命令。\n *\n * 端口不通过 build 选项指定——`main.js` 中 `listen()` 无参,运行时由 `PORT` 环境变量\n * 或默认值 3000 决定(与 `next build` 不支持 `--port` 的设计一致)。\n *\n * 流程:\n * 0. 编译配置产物(compileConfig)→ loadConfig 读应用行为配置\n * 1. 编译 TypeScript(逐文件编译,与 dev 一致,打平 src/ 前缀)\n * 2. 重新编译配置文件(确保使用最新源码)\n * 3. 扫描路由(从 <dist> 产物,import .js 拿方法名)\n * 4. 生成 schema 模块(AST 从源码 .ts)\n * 5. 生成路由清单\n * 6. 生成启动入口 <dist>/main.js(import createProdApp + loadEnv + listen)\n *\n * **统一编译模式**:build 与 dev 都采用 `bundle: false` 逐文件编译,差异仅由 `dist` 驱动,\n * 不存在 `if (isDev)` 控制流分支。逐文件编译保证每个源文件对应唯一一份产物,\n * config 和 routes 共享同一运行时对象(`instanceof` 跨边界生效)。\n */\nexport async function buildCommand(options?: BuildOptions): Promise<void> {\n const rootDir = options?.rootDir ?? process.cwd();\n const outdir = options?.dist ?? DEFAULT_DIST;\n\n // 清空输出目录(Vite emptyOutDir 语义):删除路由后 dist 残留旧 handler.js /\n // zod.js / map 文件,体积膨胀且误导排查。防误删保护:outdir 必须严格位于\n // rootDir 内(两侧 realpath 归一化,tmpdir 符号链接不误判)且不等于 rootDir\n const absOut = path.resolve(rootDir, outdir);\n const realRoot = toRealPath(path.resolve(rootDir));\n if (isInsideDir(toRealPath(absOut), realRoot)) {\n fs.rmSync(absOut, { recursive: true, force: true });\n } else {\n console.warn(\n `! Output directory \"${outdir}\" is outside the project root, skipping clean (stale artifacts may remain)`,\n );\n }\n\n // 加载 config\n // build 时无产物,先 compileConfig 生成临时产物到 outdir,再用 loadConfig 读\n await compileConfig({ rootDir, dist: outdir });\n const _config = await loadConfig(rootDir, outdir);\n\n // CJS 项目告警:产物为 ESM(main.js 用 import 语句),缺 type:module 时\n // node dist/main 报 \"Cannot use import statement outside a module\",用户无法关联原因\n const pkgPath = path.resolve(rootDir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { type?: string };\n if (pkg.type !== 'module') {\n console.warn(\n '! package.json is missing \"type\": \"module\" — build output is ESM and `node dist/main` will fail without it',\n );\n }\n } catch {\n // package.json 解析失败不阻断构建\n }\n }\n\n console.log('faapi build started');\n console.log(`- Root: ${rootDir}`);\n console.log(`- Source: src/`);\n console.log(`- Output: ${outdir}`);\n\n // 1. 编译 TypeScript(逐文件编译,与 dev 一致)\n console.log('\\n[1/8] Compiling TypeScript (bundle: false)...');\n const result = await compileBuildRoutes({\n rootDir,\n dist: outdir,\n logLevel: 'silent',\n });\n console.log(` Compiled ${result.compiledFiles.length} file(s)`);\n if (result.compiledFiles.length === 0) {\n console.warn(' ! No source files found, nothing to build');\n return;\n }\n\n // 2. 配置产物已在步骤 0 编译(compileConfig 内部有 mtime 缓存,无源码变化\n // 时重复调用只会命中缓存——此前重复调用还打印 \"Written to\" 撒谎日志)\n\n // 2.5 编译本地 TS 插件(config.plugins 的 path 声明):源文件 + src 外依赖闭包\n // 编译到 `<dist>/` 下(保留相对结构),运行时 loadPlugins 直接 import 产物。\n // 插件引用的 src 内模块已由步骤 1 全量编译,无需重复。\n const localPluginSources = extractLocalPluginSources(_config?.plugins, rootDir);\n if (localPluginSources.length > 0) {\n console.log('\\n[2.5/8] Compiling local plugins...');\n const outside = localPluginSources.filter((p) => {\n const rel = path.relative(rootDir, p).replace(/\\\\/g, '/');\n return !rel.startsWith('src/');\n });\n if (outside.length > 0) {\n await compileProjectModules(outside, rootDir, outdir);\n }\n console.log(` Compiled ${localPluginSources.length} local plugin(s)`);\n }\n\n // 3. 扫描路由(扫描源码 .ts 文件列表,但 import 产物 .js 拿方法名)\n console.log('\\n[3/8] Scanning routes...');\n const { routes, wsRoutes } = await scanRoutes(rootDir, ROUTE_PATTERNS, outdir);\n const sorted = sortRoutes(routes);\n console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);\n\n // 检测路由冲突\n const conflicts = detectRouteConflicts(sorted);\n if (conflicts.length > 0) {\n console.warn('! 检测到路由冲突:');\n for (const conflict of conflicts) {\n console.warn(` ${conflict.method} ${conflict.urlPath}`);\n for (const file of conflict.files) {\n console.warn(` - ${file}`);\n }\n }\n }\n\n // 4. 生成 schema 文件\n console.log('\\n[4/8] Generating schema...');\n await generateSchemaFiles(sorted, rootDir, outdir);\n console.log(` Schema: zod.js files under ${path.resolve(rootDir, outdir)}`);\n\n // 5. 生成路由清单(prd 启动时直接读取,不再 scanRoutes)\n console.log('\\n[5/8] Generating routes manifest...');\n const routesPath = path.resolve(rootDir, outdir, 'faapi-routes.js');\n const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);\n await writeRoutesModule(serialized, routesPath);\n console.log(` Written to ${routesPath}`);\n\n // 6. 生成 tool 清单 + schema(scanTools 读源码 + 正则提取函数名,generateToolArtifacts 做 AST 增强)\n // 与路由对称——生成 faapi-tools.js(tool 清单)+ 每个 tool handler 的 zod.js\n // 无 tool 文件时 scanTools 返回空列表,generateToolArtifacts 写入空清单\n console.log('\\n[6/8] Generating tool manifest and schema...');\n const tools = await scanTools(rootDir, TOOL_PATTERNS);\n const toolMeta = await generateToolArtifacts(tools, rootDir, outdir);\n console.log(` Found ${toolMeta.length} tool(s)`);\n console.log(` Tool manifest: ${path.resolve(rootDir, outdir, 'faapi-tools.js')}`);\n\n // 7. 生成 agent 清单(scanAgents 读源码 + 正则检测 config/run,generateAgentArtifacts 做 AST 增强)\n // agent 不生成 zod.js(无输入参数,config 块字段在 AST 阶段已提取为字面量)\n // 无 agent 文件时 scanAgents 返回空列表,generateAgentArtifacts 写入空清单\n console.log('\\n[7/8] Generating agent manifest...');\n const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);\n const agentMeta = await generateAgentArtifacts(agents, rootDir, outdir);\n console.log(` Found ${agentMeta.length} agent(s)`);\n console.log(` Agent manifest: ${path.resolve(rootDir, outdir, 'faapi-agents.js')}`);\n\n // 8. 生成启动入口 main.js(零入口设计:用户无需编写 main.ts)\n // 内部 import @faapi/faapi 的 createProdApp + loadEnv + listen\n // 运行时 `node <dist>/main` 直接启动:loadEnv 加载 .env → createProdApp 水合产物 → listen\n // --dist 选项写入 main.js(非默认 dist 时),端口由运行时 PORT 环境变量决定\n console.log('\\n[8/8] Generating entry file...');\n const mainPath = path.resolve(rootDir, outdir, 'main.js');\n // 非默认 dist 时写入 createProdApp 参数,让 prod 启动时能定位到产物目录\n // JSON.stringify 生成合法 JS 字符串字面量:Windows 反斜杠路径(.\\build 的 \\b 是退格转义)\n // 与含引号路径不再损坏\n const createProdAppArgs =\n options?.dist && options.dist !== DEFAULT_DIST ? `{ dist: ${JSON.stringify(outdir)} }` : '';\n const mainContent = `// 由 faapi build 自动生成,请勿手动编辑\nimport { createProdApp, loadEnv } from '@faapi/faapi';\n\n// 兜底 NODE_ENV(未显式设置时)+ 加载 .env 系列文件到 process.env\nif (!process.env.NODE_ENV) process.env.NODE_ENV = 'production';\nloadEnv(process.cwd());\n\nconst app = await createProdApp(${createProdAppArgs});\nawait app.listen();\n`;\n await fs.promises.writeFile(mainPath, mainContent, 'utf-8');\n console.log(` Written to ${mainPath}`);\n\n console.log('\\nfaapi build completed');\n}\n\n/**\n * 从 plugins 声明提取本地 TS/JS 插件源文件(build 端编译入口)\n *\n * 只取相对/绝对路径声明(`./x`、`../x`、绝对路径);包名声明跳过(运行时 Node 解析)。\n * 探测不到源文件的声明跳过——运行时 loadPlugins 会报带修复指引的失败,build 不在此\n * 替代报错(文件可能在运行时才出现,或用户笔误由运行时日志定位)。\n */\nfunction extractLocalPluginSources(\n declarations: PluginDeclaration[] | undefined,\n rootDir: string,\n): string[] {\n if (!declarations || declarations.length === 0) return [];\n const sources: string[] = [];\n const seen = new Set<string>();\n for (const decl of declarations) {\n let specifier: string | undefined;\n if (typeof decl === 'string') specifier = decl;\n else if (Array.isArray(decl)) specifier = decl[0];\n else if ('path' in decl) specifier = decl.path;\n if (!specifier) continue;\n if (\n !specifier.startsWith('./') &&\n !specifier.startsWith('../') &&\n !path.isAbsolute(specifier)\n ) {\n continue; // 包名声明\n }\n const source = resolveLocalPluginSource(specifier, rootDir);\n if (source && !seen.has(source)) {\n seen.add(source);\n sources.push(source);\n }\n }\n return sources;\n}\n","import { EventEmitter } from 'events';\n\nfunction toArr(any) {\n\treturn any == null ? [] : Array.isArray(any) ? any : [any];\n}\n\nfunction toVal(out, key, val, opts) {\n\tvar x, old=out[key], nxt=(\n\t\t!!~opts.string.indexOf(key) ? (val == null || val === true ? '' : String(val))\n\t\t: typeof val === 'boolean' ? val\n\t\t: !!~opts.boolean.indexOf(key) ? (val === 'false' ? false : val === 'true' || (out._.push((x = +val,x * 0 === 0) ? x : val),!!val))\n\t\t: (x = +val,x * 0 === 0) ? x : val\n\t);\n\tout[key] = old == null ? nxt : (Array.isArray(old) ? old.concat(nxt) : [old, nxt]);\n}\n\nfunction mri2 (args, opts) {\n\targs = args || [];\n\topts = opts || {};\n\n\tvar k, arr, arg, name, val, out={ _:[] };\n\tvar i=0, j=0, idx=0, len=args.length;\n\n\tconst alibi = opts.alias !== void 0;\n\tconst strict = opts.unknown !== void 0;\n\tconst defaults = opts.default !== void 0;\n\n\topts.alias = opts.alias || {};\n\topts.string = toArr(opts.string);\n\topts.boolean = toArr(opts.boolean);\n\n\tif (alibi) {\n\t\tfor (k in opts.alias) {\n\t\t\tarr = opts.alias[k] = toArr(opts.alias[k]);\n\t\t\tfor (i=0; i < arr.length; i++) {\n\t\t\t\t(opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i=opts.boolean.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.boolean[i]] || [];\n\t\tfor (j=arr.length; j-- > 0;) opts.boolean.push(arr[j]);\n\t}\n\n\tfor (i=opts.string.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.string[i]] || [];\n\t\tfor (j=arr.length; j-- > 0;) opts.string.push(arr[j]);\n\t}\n\n\tif (defaults) {\n\t\tfor (k in opts.default) {\n\t\t\tname = typeof opts.default[k];\n\t\t\tarr = opts.alias[k] = opts.alias[k] || [];\n\t\t\tif (opts[name] !== void 0) {\n\t\t\t\topts[name].push(k);\n\t\t\t\tfor (i=0; i < arr.length; i++) {\n\t\t\t\t\topts[name].push(arr[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst keys = strict ? Object.keys(opts.alias) : [];\n\n\tfor (i=0; i < len; i++) {\n\t\targ = args[i];\n\n\t\tif (arg === '--') {\n\t\t\tout._ = out._.concat(args.slice(++i));\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (j=0; j < arg.length; j++) {\n\t\t\tif (arg.charCodeAt(j) !== 45) break; // \"-\"\n\t\t}\n\n\t\tif (j === 0) {\n\t\t\tout._.push(arg);\n\t\t} else if (arg.substring(j, j + 3) === 'no-') {\n\t\t\tname = arg.substring(j + 3);\n\t\t\tif (strict && !~keys.indexOf(name)) {\n\t\t\t\treturn opts.unknown(arg);\n\t\t\t}\n\t\t\tout[name] = false;\n\t\t} else {\n\t\t\tfor (idx=j+1; idx < arg.length; idx++) {\n\t\t\t\tif (arg.charCodeAt(idx) === 61) break; // \"=\"\n\t\t\t}\n\n\t\t\tname = arg.substring(j, idx);\n\t\t\tval = arg.substring(++idx) || (i+1 === len || (''+args[i+1]).charCodeAt(0) === 45 || args[++i]);\n\t\t\tarr = (j === 2 ? [name] : name);\n\n\t\t\tfor (idx=0; idx < arr.length; idx++) {\n\t\t\t\tname = arr[idx];\n\t\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown('-'.repeat(j) + name);\n\t\t\t\ttoVal(out, name, (idx + 1 < arr.length) || val, opts);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (defaults) {\n\t\tfor (k in opts.default) {\n\t\t\tif (out[k] === void 0) {\n\t\t\t\tout[k] = opts.default[k];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (alibi) {\n\t\tfor (k in out) {\n\t\t\tarr = opts.alias[k] || [];\n\t\t\twhile (arr.length > 0) {\n\t\t\t\tout[arr.shift()] = out[k];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\nconst removeBrackets = (v) => v.replace(/[<[].+/, \"\").trim();\nconst findAllBrackets = (v) => {\n const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;\n const SQUARE_BRACKET_RE_GLOBAL = /\\[([^\\]]+)\\]/g;\n const res = [];\n const parse = (match) => {\n let variadic = false;\n let value = match[1];\n if (value.startsWith(\"...\")) {\n value = value.slice(3);\n variadic = true;\n }\n return {\n required: match[0].startsWith(\"<\"),\n value,\n variadic\n };\n };\n let angledMatch;\n while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) {\n res.push(parse(angledMatch));\n }\n let squareMatch;\n while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) {\n res.push(parse(squareMatch));\n }\n return res;\n};\nconst getMriOptions = (options) => {\n const result = {alias: {}, boolean: []};\n for (const [index, option] of options.entries()) {\n if (option.names.length > 1) {\n result.alias[option.names[0]] = option.names.slice(1);\n }\n if (option.isBoolean) {\n if (option.negated) {\n const hasStringTypeOption = options.some((o, i) => {\n return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === \"boolean\";\n });\n if (!hasStringTypeOption) {\n result.boolean.push(option.names[0]);\n }\n } else {\n result.boolean.push(option.names[0]);\n }\n }\n }\n return result;\n};\nconst findLongest = (arr) => {\n return arr.sort((a, b) => {\n return a.length > b.length ? -1 : 1;\n })[0];\n};\nconst padRight = (str, length) => {\n return str.length >= length ? str : `${str}${\" \".repeat(length - str.length)}`;\n};\nconst camelcase = (input) => {\n return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {\n return p1 + p2.toUpperCase();\n });\n};\nconst setDotProp = (obj, keys, val) => {\n let i = 0;\n let length = keys.length;\n let t = obj;\n let x;\n for (; i < length; ++i) {\n x = t[keys[i]];\n t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(\".\") || !(+keys[i + 1] > -1) ? {} : [];\n }\n};\nconst setByType = (obj, transforms) => {\n for (const key of Object.keys(transforms)) {\n const transform = transforms[key];\n if (transform.shouldTransform) {\n obj[key] = Array.prototype.concat.call([], obj[key]);\n if (typeof transform.transformFunction === \"function\") {\n obj[key] = obj[key].map(transform.transformFunction);\n }\n }\n }\n};\nconst getFileName = (input) => {\n const m = /([^\\\\\\/]+)$/.exec(input);\n return m ? m[1] : \"\";\n};\nconst camelcaseOptionName = (name) => {\n return name.split(\".\").map((v, i) => {\n return i === 0 ? camelcase(v) : v;\n }).join(\".\");\n};\nclass CACError extends Error {\n constructor(message) {\n super(message);\n this.name = this.constructor.name;\n if (typeof Error.captureStackTrace === \"function\") {\n Error.captureStackTrace(this, this.constructor);\n } else {\n this.stack = new Error(message).stack;\n }\n }\n}\n\nclass Option {\n constructor(rawName, description, config) {\n this.rawName = rawName;\n this.description = description;\n this.config = Object.assign({}, config);\n rawName = rawName.replace(/\\.\\*/g, \"\");\n this.negated = false;\n this.names = removeBrackets(rawName).split(\",\").map((v) => {\n let name = v.trim().replace(/^-{1,2}/, \"\");\n if (name.startsWith(\"no-\")) {\n this.negated = true;\n name = name.replace(/^no-/, \"\");\n }\n return camelcaseOptionName(name);\n }).sort((a, b) => a.length > b.length ? 1 : -1);\n this.name = this.names[this.names.length - 1];\n if (this.negated && this.config.default == null) {\n this.config.default = true;\n }\n if (rawName.includes(\"<\")) {\n this.required = true;\n } else if (rawName.includes(\"[\")) {\n this.required = false;\n } else {\n this.isBoolean = true;\n }\n }\n}\n\nconst processArgs = process.argv;\nconst platformInfo = `${process.platform}-${process.arch} node-${process.version}`;\n\nclass Command {\n constructor(rawName, description, config = {}, cli) {\n this.rawName = rawName;\n this.description = description;\n this.config = config;\n this.cli = cli;\n this.options = [];\n this.aliasNames = [];\n this.name = removeBrackets(rawName);\n this.args = findAllBrackets(rawName);\n this.examples = [];\n }\n usage(text) {\n this.usageText = text;\n return this;\n }\n allowUnknownOptions() {\n this.config.allowUnknownOptions = true;\n return this;\n }\n ignoreOptionDefaultValue() {\n this.config.ignoreOptionDefaultValue = true;\n return this;\n }\n version(version, customFlags = \"-v, --version\") {\n this.versionNumber = version;\n this.option(customFlags, \"Display version number\");\n return this;\n }\n example(example) {\n this.examples.push(example);\n return this;\n }\n option(rawName, description, config) {\n const option = new Option(rawName, description, config);\n this.options.push(option);\n return this;\n }\n alias(name) {\n this.aliasNames.push(name);\n return this;\n }\n action(callback) {\n this.commandAction = callback;\n return this;\n }\n isMatched(name) {\n return this.name === name || this.aliasNames.includes(name);\n }\n get isDefaultCommand() {\n return this.name === \"\" || this.aliasNames.includes(\"!\");\n }\n get isGlobalCommand() {\n return this instanceof GlobalCommand;\n }\n hasOption(name) {\n name = name.split(\".\")[0];\n return this.options.find((option) => {\n return option.names.includes(name);\n });\n }\n outputHelp() {\n const {name, commands} = this.cli;\n const {\n versionNumber,\n options: globalOptions,\n helpCallback\n } = this.cli.globalCommand;\n let sections = [\n {\n body: `${name}${versionNumber ? `/${versionNumber}` : \"\"}`\n }\n ];\n sections.push({\n title: \"Usage\",\n body: ` $ ${name} ${this.usageText || this.rawName}`\n });\n const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;\n if (showCommands) {\n const longestCommandName = findLongest(commands.map((command) => command.rawName));\n sections.push({\n title: \"Commands\",\n body: commands.map((command) => {\n return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;\n }).join(\"\\n\")\n });\n sections.push({\n title: `For more info, run any command with the \\`--help\\` flag`,\n body: commands.map((command) => ` $ ${name}${command.name === \"\" ? \"\" : ` ${command.name}`} --help`).join(\"\\n\")\n });\n }\n let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];\n if (!this.isGlobalCommand && !this.isDefaultCommand) {\n options = options.filter((option) => option.name !== \"version\");\n }\n if (options.length > 0) {\n const longestOptionName = findLongest(options.map((option) => option.rawName));\n sections.push({\n title: \"Options\",\n body: options.map((option) => {\n return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? \"\" : `(default: ${option.config.default})`}`;\n }).join(\"\\n\")\n });\n }\n if (this.examples.length > 0) {\n sections.push({\n title: \"Examples\",\n body: this.examples.map((example) => {\n if (typeof example === \"function\") {\n return example(name);\n }\n return example;\n }).join(\"\\n\")\n });\n }\n if (helpCallback) {\n sections = helpCallback(sections) || sections;\n }\n console.log(sections.map((section) => {\n return section.title ? `${section.title}:\n${section.body}` : section.body;\n }).join(\"\\n\\n\"));\n }\n outputVersion() {\n const {name} = this.cli;\n const {versionNumber} = this.cli.globalCommand;\n if (versionNumber) {\n console.log(`${name}/${versionNumber} ${platformInfo}`);\n }\n }\n checkRequiredArgs() {\n const minimalArgsCount = this.args.filter((arg) => arg.required).length;\n if (this.cli.args.length < minimalArgsCount) {\n throw new CACError(`missing required args for command \\`${this.rawName}\\``);\n }\n }\n checkUnknownOptions() {\n const {options, globalCommand} = this.cli;\n if (!this.config.allowUnknownOptions) {\n for (const name of Object.keys(options)) {\n if (name !== \"--\" && !this.hasOption(name) && !globalCommand.hasOption(name)) {\n throw new CACError(`Unknown option \\`${name.length > 1 ? `--${name}` : `-${name}`}\\``);\n }\n }\n }\n }\n checkOptionValue() {\n const {options: parsedOptions, globalCommand} = this.cli;\n const options = [...globalCommand.options, ...this.options];\n for (const option of options) {\n const value = parsedOptions[option.name.split(\".\")[0]];\n if (option.required) {\n const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));\n if (value === true || value === false && !hasNegated) {\n throw new CACError(`option \\`${option.rawName}\\` value is missing`);\n }\n }\n }\n }\n}\nclass GlobalCommand extends Command {\n constructor(cli) {\n super(\"@@global@@\", \"\", {}, cli);\n }\n}\n\nvar __assign = Object.assign;\nclass CAC extends EventEmitter {\n constructor(name = \"\") {\n super();\n this.name = name;\n this.commands = [];\n this.rawArgs = [];\n this.args = [];\n this.options = {};\n this.globalCommand = new GlobalCommand(this);\n this.globalCommand.usage(\"<command> [options]\");\n }\n usage(text) {\n this.globalCommand.usage(text);\n return this;\n }\n command(rawName, description, config) {\n const command = new Command(rawName, description || \"\", config, this);\n command.globalCommand = this.globalCommand;\n this.commands.push(command);\n return command;\n }\n option(rawName, description, config) {\n this.globalCommand.option(rawName, description, config);\n return this;\n }\n help(callback) {\n this.globalCommand.option(\"-h, --help\", \"Display this message\");\n this.globalCommand.helpCallback = callback;\n this.showHelpOnExit = true;\n return this;\n }\n version(version, customFlags = \"-v, --version\") {\n this.globalCommand.version(version, customFlags);\n this.showVersionOnExit = true;\n return this;\n }\n example(example) {\n this.globalCommand.example(example);\n return this;\n }\n outputHelp() {\n if (this.matchedCommand) {\n this.matchedCommand.outputHelp();\n } else {\n this.globalCommand.outputHelp();\n }\n }\n outputVersion() {\n this.globalCommand.outputVersion();\n }\n setParsedInfo({args, options}, matchedCommand, matchedCommandName) {\n this.args = args;\n this.options = options;\n if (matchedCommand) {\n this.matchedCommand = matchedCommand;\n }\n if (matchedCommandName) {\n this.matchedCommandName = matchedCommandName;\n }\n return this;\n }\n unsetMatchedCommand() {\n this.matchedCommand = void 0;\n this.matchedCommandName = void 0;\n }\n parse(argv = processArgs, {\n run = true\n } = {}) {\n this.rawArgs = argv;\n if (!this.name) {\n this.name = argv[1] ? getFileName(argv[1]) : \"cli\";\n }\n let shouldParse = true;\n for (const command of this.commands) {\n const parsed = this.mri(argv.slice(2), command);\n const commandName = parsed.args[0];\n if (command.isMatched(commandName)) {\n shouldParse = false;\n const parsedInfo = __assign(__assign({}, parsed), {\n args: parsed.args.slice(1)\n });\n this.setParsedInfo(parsedInfo, command, commandName);\n this.emit(`command:${commandName}`, command);\n }\n }\n if (shouldParse) {\n for (const command of this.commands) {\n if (command.name === \"\") {\n shouldParse = false;\n const parsed = this.mri(argv.slice(2), command);\n this.setParsedInfo(parsed, command);\n this.emit(`command:!`, command);\n }\n }\n }\n if (shouldParse) {\n const parsed = this.mri(argv.slice(2));\n this.setParsedInfo(parsed);\n }\n if (this.options.help && this.showHelpOnExit) {\n this.outputHelp();\n run = false;\n this.unsetMatchedCommand();\n }\n if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {\n this.outputVersion();\n run = false;\n this.unsetMatchedCommand();\n }\n const parsedArgv = {args: this.args, options: this.options};\n if (run) {\n this.runMatchedCommand();\n }\n if (!this.matchedCommand && this.args[0]) {\n this.emit(\"command:*\");\n }\n return parsedArgv;\n }\n mri(argv, command) {\n const cliOptions = [\n ...this.globalCommand.options,\n ...command ? command.options : []\n ];\n const mriOptions = getMriOptions(cliOptions);\n let argsAfterDoubleDashes = [];\n const doubleDashesIndex = argv.indexOf(\"--\");\n if (doubleDashesIndex > -1) {\n argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);\n argv = argv.slice(0, doubleDashesIndex);\n }\n let parsed = mri2(argv, mriOptions);\n parsed = Object.keys(parsed).reduce((res, name) => {\n return __assign(__assign({}, res), {\n [camelcaseOptionName(name)]: parsed[name]\n });\n }, {_: []});\n const args = parsed._;\n const options = {\n \"--\": argsAfterDoubleDashes\n };\n const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;\n let transforms = Object.create(null);\n for (const cliOption of cliOptions) {\n if (!ignoreDefault && cliOption.config.default !== void 0) {\n for (const name of cliOption.names) {\n options[name] = cliOption.config.default;\n }\n }\n if (Array.isArray(cliOption.config.type)) {\n if (transforms[cliOption.name] === void 0) {\n transforms[cliOption.name] = Object.create(null);\n transforms[cliOption.name][\"shouldTransform\"] = true;\n transforms[cliOption.name][\"transformFunction\"] = cliOption.config.type[0];\n }\n }\n }\n for (const key of Object.keys(parsed)) {\n if (key !== \"_\") {\n const keys = key.split(\".\");\n setDotProp(options, keys, parsed[key]);\n setByType(options, transforms);\n }\n }\n return {\n args,\n options\n };\n }\n runMatchedCommand() {\n const {args, options, matchedCommand: command} = this;\n if (!command || !command.commandAction)\n return;\n command.checkUnknownOptions();\n command.checkOptionValue();\n command.checkRequiredArgs();\n const actionArgs = [];\n command.args.forEach((arg, index) => {\n if (arg.variadic) {\n actionArgs.push(args.slice(index));\n } else {\n actionArgs.push(args[index]);\n }\n });\n actionArgs.push(options);\n return command.commandAction.apply(this, actionArgs);\n }\n}\n\nconst cac = (name = \"\") => new CAC(name);\n\nexport default cac;\nexport { CAC, Command, cac };\n","#!/usr/bin/env node\n\nimport { cac } from 'cac';\nimport { createRequire } from 'node:module';\n\n/**\n * 从最近的 package.json 读版本号\n *\n * src/cli/index.ts 需向上两级到包根,dist/index.js 一级——逐级探测,\n * 命中第一个存在的 package.json(publishConfig 的 files 只发 dist,\n * 探测在 dist 内即命中包根)\n */\nfunction resolveVersion(): string {\n const require = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json']) {\n try {\n const { version } = require(rel) as { version: string };\n if (version) return version;\n } catch {\n // 继续向上一级探测\n }\n }\n return '0.0.0';\n}\n\n/**\n * 创建 faapi CLI 实例(导出供测试调用 `cli.parse(argv)` 显式传参)。\n *\n * cac 在模块加载时捕获 `process.argv` 快照(`const processArgs = process.argv`),\n * `cli.parse()` 无参时用该快照。生产环境 CLI 入口由 shebang 触发,`process.argv` 已就绪;\n * 测试环境需显式传 argv 才能精确控制命令分发。\n */\nexport function createCli() {\n const version = resolveVersion();\n\n const cli = cac('faapi');\n\n cli\n .command('')\n .alias('dev')\n .option('--port <number>', '服务端口(默认 3000)')\n .action(async (options) => {\n const { devCommand } = await import('./devCommand.js');\n await runCommand(() => devCommand(options));\n });\n\n cli\n .command('build', 'Build for production')\n .option('--dist <dir>', '产物输出目录,默认 dist')\n .action(async (options) => {\n const { buildCommand } = await import('./buildCommand.js');\n await runCommand(() => buildCommand(options));\n });\n\n cli.help();\n cli.version(version);\n return cli;\n}\n\n/**\n * 命令顶层错误处理:编译/配置/加载错误以一行友好摘要输出(exit code 1),\n * 不再裸堆栈糊屏。设置 FAAPI_DEBUG=1 时附加完整堆栈供排查。\n */\nasync function runCommand(run: () => Promise<void>): Promise<void> {\n try {\n await run();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`\\nfaapi: ${message}`);\n if (process.env.FAAPI_DEBUG && err instanceof Error && err.stack) {\n console.error(err.stack);\n }\n process.exitCode = 1;\n }\n}\n\n// shebang 入口:直接执行时 parse(用 process.argv 快照)\ncreateCli().parse();\n"],"mappings":";;;;;;;;;;;;AAQO,SAAS,aAAa,WAAmB,QAAuC;AACrF,QAAM,aAAuB,CAAC;AAE9B,aAAW,CAAC,SAAS,OAAO,KAAK,OAAO,QAAQ,OAAO,KAAK,GAAG;AAC7D,UAAM,gBAAgB,QAAQ,QAAQ,GAAG;AAEzC,QAAI,kBAAkB,IAAI;AAExB,UAAI,cAAc,SAAS;AACzB,mBAAW,KAAK,GAAG,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AAGA,UAAM,SAAS,QAAQ,MAAM,GAAG,aAAa;AAC7C,UAAM,SAAS,QAAQ,MAAM,gBAAgB,CAAC;AAE9C,QACE,UAAU,WAAW,MAAM,KAC3B,UAAU,SAAS,MAAM,KACzB,UAAU,UAAU,OAAO,SAAS,OAAO,QAC3C;AACA,YAAM,WAAW,UAAU,MAAM,OAAO,QAAQ,UAAU,SAAS,OAAO,MAAM;AAChF,iBAAW,UAAU,SAAS;AAC5B,mBAAW,KAAK,OAAO,QAAQ,KAAK,QAAQ,CAAC;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAvCA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAcR,SAAS,eAAe,UAAkB,MAAsB;AACrE,MAAI,MAAM,SAAS,QAAQ,OAAO,GAAG;AAErC,MAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,UAAM,IAAI,MAAM,CAAC;AAAA,EACnB;AACA,QAAM,SAAS,IAAI,QAAQ,SAAS,KAAK;AACzC,SAAO,OAAO,WAAW,GAAG,IAAI,GAAG,IAAI,SAAS,GAAG,IAAI,IAAI,MAAM;AACnE;AAKO,SAAS,gBAAgB,UAA0B;AACxD,MAAI,SAAS,SAAS,KAAK,EAAG,QAAO,SAAS,MAAM,GAAG,EAAE,IAAI;AAC7D,MAAI,SAAS,SAAS,MAAM,EAAG,QAAO,SAAS,MAAM,GAAG,EAAE,IAAI;AAC9D,MAAI,SAAS,SAAS,MAAM,EAAG,QAAO,SAAS,MAAM,GAAG,EAAE,IAAI;AAC9D,SAAO;AACT;AAOO,SAAS,WAAW,GAAmB;AAC5C,MAAI;AACF,WAAO,GAAG,aAAa,CAAC;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,YAAY,UAAkB,KAAsB;AAClE,QAAM,MAAM,KAAK,SAAS,KAAK,QAAQ;AACvC,SAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,CAAC,KAAK,WAAW,GAAG;AACpE;AAzDA,IAIa,SAGA;AAPb;AAAA;AAAA;AAIO,IAAM,UAAU;AAGhB,IAAM,iBAAiB,CAAC,iBAAiB;AAAA;AAAA;;;ACPhD,OAAO,QAAQ;AACf,OAAOA,WAAU;AACjB,OAAOC,SAAQ;AA8BR,SAAS,aAAa,SAA6C;AACxE,QAAM,eAAeD,MAAK,QAAQ,SAAS,eAAe;AAC1D,MAAI,CAACC,IAAG,WAAW,YAAY,EAAG,QAAO;AAGzC,MAAI;AACJ,MAAI;AACF,cAAUA,IAAG,SAAS,YAAY,EAAE;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,SAAS,cAAc,IAAI,YAAY;AAC7C,MAAI,UAAU,OAAO,YAAY,SAAS;AACxC,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,aAAa,GAAG,eAAe,cAAc,GAAG,IAAI,QAAQ;AAClE,MAAI,WAAW,SAAS,CAAC,WAAW,OAAQ,QAAO;AAGnD,QAAM,SAAS,GAAG,2BAA2B,WAAW,QAAQ,GAAG,KAAK,OAAO;AAG/E,QAAM,UAAU,OAAO,QAAQ,WAAW;AAE1C,QAAM,WAAW,OAAO,QAAQ;AAChC,QAAM,SAAqC,YACtC,MAAM;AAEL,UAAM,QAAkC,CAAC;AACzC,eAAW,CAAC,SAAS,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACzD,YAAM,OAAO,IAAI,QAAQ,IAAI,CAAC,MAAMD,MAAK,QAAQ,SAAS,CAAC,CAAC;AAAA,IAC9D;AACA,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B,GAAG,IACH;AAEJ,gBAAc,IAAI,cAAc,EAAE,SAAS,OAAO,CAAC;AACnD,SAAO;AACT;AAvEA,IA8BM;AA9BN;AAAA;AAAA;AA8BA,IAAM,gBAAgB,oBAAI,IAAgC;AAAA;AAAA;;;AC7B1D,OAAOE,WAAU;AACjB,OAAOC,SAAQ;AAWf,SAAS,iBAAiB,YAAoB,UAA0B;AACtE,QAAM,cAAcD,MAAK,QAAQ,QAAQ;AACzC,MAAI,MAAMA,MAAK,SAAS,aAAa,UAAU;AAC/C,QAAM,IAAI,MAAMA,MAAK,GAAG,EAAE,KAAK,GAAG;AAClC,MAAI,CAAC,IAAI,WAAW,GAAG,EAAG,OAAM,OAAO;AACvC,SAAO,gBAAgB,GAAG;AAC5B;AAeA,SAAS,yBAAyB,YAAoB,SAAyB;AAC7E,QAAM,YAAY,WAAWA,MAAK,QAAQ,SAAS,OAAO,CAAC;AAC3D,QAAM,aAAa,WAAW,UAAU;AACxC,MAAI,MAAMA,MAAK,SAAS,WAAW,UAAU;AAC7C,QAAM,IAAI,MAAMA,MAAK,GAAG,EAAE,KAAK,GAAG;AAClC,MAAI,CAAC,IAAI,WAAW,GAAG,EAAG,OAAM,OAAO;AACvC,SAAO,gBAAgB,GAAG;AAC5B;AAcA,SAAS,yBAAyB,UAAkB,SAAiB,aAA6B;AAGhG,QAAM,cAAcA,MACjB,SAAS,WAAWA,MAAK,QAAQ,OAAO,CAAC,GAAG,WAAW,QAAQ,CAAC,EAChE,MAAMA,MAAK,GAAG,EACd,KAAK,GAAG;AACX,QAAM,kBAAkBA,MAAK,MAAM,QAAQ,gBAAgB,WAAW,CAAC;AACvE,MAAI,oBAAoB,IAAK,QAAO;AACpC,QAAM,SAAS,YAAY,QAAQ,SAAS,EAAE;AAC9C,MAAI,MAAMA,MAAK,MAAM,SAAS,iBAAiB,MAAM;AACrD,MAAI,CAAC,IAAI,WAAW,GAAG,EAAG,OAAM,OAAO;AACvC,SAAO;AACT;AA+CO,SAAS,yBAAyB,UAAkB,WAAkC;AAC3F,QAAM,cAAcA,MAAK,QAAQ,QAAQ;AACzC,QAAM,OAAOA,MAAK,QAAQ,aAAa,SAAS;AAGhD,MAAI,UAAU,KAAK,CAAC,QAAQ,UAAU,SAAS,GAAG,CAAC,GAAG;AACpD,WAAOC,IAAG,WAAW,IAAI,IAAI,OAAO;AAAA,EACtC;AAGA,MAAI,kBAAkB,KAAK,SAAS,GAAG;AACrC,WAAOA,IAAG,WAAW,IAAI,IAAI,OAAO;AAAA,EACtC;AAGA,aAAW,OAAO,aAAa;AAC7B,UAAM,OAAO,OAAO;AACpB,QAAIA,IAAG,WAAW,IAAI,EAAG,QAAO;AAAA,EAClC;AAGA,aAAW,YAAY,YAAY;AACjC,UAAM,OAAO,OAAO;AACpB,QAAIA,IAAG,WAAW,IAAI,EAAG,QAAO;AAAA,EAClC;AAEA,SAAO;AACT;AAiCO,SAAS,kBACd,QACA,SACQ;AAER,QAAMC,WAAU;AAIhB,QAAM,YAAY,SAAS,UAAU,WAAWF,MAAK,QAAQ,QAAQ,SAAS,OAAO,CAAC,IAAI;AAC1F,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,OAAO;AACX,YAAM,OAAO,EAAE,QAAQ,6BAA6B,GAAG,CAAC,SAAS;AAC/D,YAAI;AACJ,YAAI;AACF,mBAASC,IAAG,aAAa,KAAK,MAAM,MAAM;AAAA,QAC5C,QAAQ;AACN,iBAAO;AAAA,QACT;AACA,cAAM,WAAW,KAAK;AAEtB,cAAM,wBAAwB,YAAY,CAAC,YAAY,UAAU,SAAS,IAAI;AAC9E,YAAI,WAAW;AACf,cAAM,YAAY,OAAO,QAAQC,UAAS,CAAC,MAAM,QAAQ,OAAO,cAAc;AAE5E,cACE,UAAU,WAAW,GAAG,KACxB,UAAU,WAAW,OAAO,KAC5B,UAAU,WAAW,OAAO,GAC5B;AACA,mBAAO;AAAA,UACT;AAGA,cAAI,UAAU,WAAW,IAAI,KAAK,UAAU,WAAW,KAAK,GAAG;AAC7D,kBAAM,WAAW,yBAAyB,UAAU,SAAS;AAC7D,gBAAI,UAAU;AAEZ,kBAAI,UAAU,KAAK,CAAC,QAAQ,UAAU,SAAS,GAAG,CAAC,GAAG;AACpD,uBAAO;AAAA,cACT;AAGA,kBAAI,aAAa,yBAAyB,YAAY,UAAU,SAAS,GAAG;AAC1E,2BAAW;AACX,uBAAO,GAAG,MAAM,GAAG,KAAK,GAAG;AAAA,kBACzB;AAAA,kBACA,QAAS;AAAA,kBACT,yBAAyB,UAAU,QAAS,OAAQ;AAAA,gBACtD,CAAC,GAAG,KAAK;AAAA,cACX;AACA,yBAAW;AACX,qBAAO,GAAG,MAAM,GAAG,KAAK,GAAG,iBAAiB,UAAU,QAAQ,CAAC,GAAG,KAAK;AAAA,YACzE;AACA,mBAAO;AAAA,UACT;AAGA,gBAAM,aAAa,aAAa,WAAW,MAAM;AACjD,qBAAW,aAAa,YAAY;AAClC,uBAAW,OAAO,aAAa;AAC7B,oBAAM,OAAO,YAAY;AACzB,kBAAID,IAAG,WAAW,IAAI,GAAG;AACvB,2BAAW;AAEX,oBAAI,aAAa,yBAAyB,YAAY,MAAM,SAAS,GAAG;AACtE,yBAAO,GAAG,MAAM,GAAG,KAAK,GAAG;AAAA,oBACzB;AAAA,oBACA,QAAS;AAAA,oBACT,yBAAyB,MAAM,QAAS,OAAQ;AAAA,kBAClD,CAAC,GAAG,KAAK;AAAA,gBACX;AACA,uBAAO,GAAG,MAAM,GAAG,KAAK,GAAG,iBAAiB,MAAM,QAAQ,CAAC,GAAG,KAAK;AAAA,cACrE;AAAA,YACF;AACA,uBAAW,YAAY,YAAY;AACjC,oBAAM,OAAO,YAAY;AACzB,kBAAIA,IAAG,WAAW,IAAI,GAAG;AACvB,2BAAW;AACX,oBAAI,aAAa,yBAAyB,YAAY,MAAM,SAAS,GAAG;AACtE,yBAAO,GAAG,MAAM,GAAG,KAAK,GAAG;AAAA,oBACzB;AAAA,oBACA,QAAS;AAAA,oBACT,yBAAyB,MAAM,QAAS,OAAQ;AAAA,kBAClD,CAAC,GAAG,KAAK;AAAA,gBACX;AACA,uBAAO,GAAG,MAAM,GAAG,KAAK,GAAG,iBAAiB,MAAM,QAAQ,CAAC,GAAG,KAAK;AAAA,cACrE;AAAA,YACF;AAAA,UACF;AACA,iBAAO;AAAA,QACT,CAAC;AACD,YAAI,CAAC,SAAU,QAAO;AACtB,eAAO,EAAE,UAAU,WAAW,QAAQ,UAAU;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAYO,SAAS,kBAAkB,SAA2B;AAC3D,QAAM,WAAW,aAAa,OAAO;AACrC,SAAO,CAAC,kBAAkB,YAAY,EAAE,SAAS,KAAK,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;AACjF;AAhSA,IAqFM,WAKA,aAKA;AA/FN;AAAA;AAAA;AAGA;AACA;AACA;AAgFA,IAAM,YAAY,CAAC,OAAO,QAAQ,MAAM;AAKxC,IAAM,cAAc,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM;AAKjE,IAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA;;;ACtGA,OAAOE,WAAU;AACjB,OAAOC,SAAQ;AAiBf,SAAS,wBAAwB,QAA0B;AACzD,QAAM,aAAuB,CAAC;AAC9B,MAAI;AACJ,UAAQ,YAAY;AACpB,UAAQ,QAAQ,QAAQ,KAAK,MAAM,OAAO,MAAM;AAC9C,eAAW,KAAK,MAAM,CAAC,CAAE;AAAA,EAC3B;AACA,SAAO;AACT;AAQA,SAAS,wBAAwB,KAAa,WAAkC;AAC9E,SAAO,yBAAyBD,MAAK,KAAK,KAAK,oBAAoB,GAAG,SAAS;AACjF;AAwBA,eAAsB,uBACpB,YACA,SAC4D;AAE5D,QAAM,YAAY,WAAWA,MAAK,QAAQ,SAAS,KAAK,CAAC;AACzD,QAAM,WAAW,aAAa,OAAO;AACrC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,cAAc,oBAAI,IAAY;AACpC,QAAM,eAAe,oBAAI,IAAY;AAErC,iBAAe,QAAQ,UAAiC;AACtD,QAAI,QAAQ,IAAI,QAAQ,EAAG;AAC3B,YAAQ,IAAI,QAAQ;AAEpB,QAAI;AACJ,QAAI;AACF,eAAS,MAAMC,IAAG,SAAS,SAAS,UAAU,MAAM;AAAA,IACtD,QAAQ;AACN;AAAA,IACF;AAEA,eAAW,aAAa,wBAAwB,MAAM,GAAG;AAEvD,UAAI,kBAAkB,KAAK,SAAS,EAAG;AAEvC,UAAI;AACJ,UAAI,UAAU,WAAW,IAAI,KAAK,UAAU,WAAW,KAAK,GAAG;AAC7D,mBAAW,yBAAyB,UAAU,SAAS;AAAA,MACzD,WAAW,UAAU;AAEnB,mBAAW;AACX,mBAAW,aAAa,aAAa,WAAW,QAAQ,GAAG;AACzD,gBAAM,SAAS,wBAAwB,SAAS,SAAS;AACzD,cAAI,QAAQ;AACV,uBAAW;AACX;AAAA,UACF;AAAA,QACF;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,MACb;AAEA,UAAI,CAAC,SAAU;AAKf,UAAI,CAAC,YAAY,WAAW,QAAQ,GAAG,WAAWD,MAAK,QAAQ,OAAO,CAAC,CAAC,EAAG;AAE3E,UAAI,YAAY,WAAW,QAAQ,GAAG,SAAS,GAAG;AAChD,oBAAY,IAAI,QAAQ;AAAA,MAC1B,OAAO;AACL,qBAAa,IAAI,QAAQ;AAAA,MAC3B;AACA,YAAM,QAAQ,QAAQ;AAAA,IACxB;AAAA,EACF;AAEA,aAAW,SAAS,YAAY;AAC9B,UAAM,QAAQ,KAAK;AAAA,EACrB;AAEA,SAAO;AAAA,IACL,aAAa,MAAM,KAAK,WAAW;AAAA,IACnC,cAAc,MAAM,KAAK,YAAY;AAAA,EACvC;AACF;AA/HA,IAUM;AAVN;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAKA,IAAM,UAAU;AAAA;AAAA;;;ACVhB,OAAOE,WAAU;AACjB,OAAOC,SAAQ;AAgBf,SAAS,eAAe,SAAgC;AACtD,aAAW,KAAK,mBAAmB;AACjC,QAAIA,IAAG,WAAWD,MAAK,KAAK,SAAS,CAAC,CAAC,GAAG;AACxC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,aAAa,UAA0B;AAC9C,SAAO,gBAAgB,QAAQ;AACjC;AAsCA,eAAe,aAAa,OAAkD;AAC5E,MAAI;AACF,UAAMC,IAAG,SAAS,KAAK,MAAM,UAAU;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,CAAC,UAAU,OAAO,KAAK,MAAM,QAAQ;AAC9C,QAAI;AACF,YAAMC,QAAO,MAAMD,IAAG,SAAS,KAAK,QAAQ;AAC5C,UAAIC,MAAK,YAAY,QAAS,QAAO;AAAA,IACvC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAiBA,SAAS,+BAAuC;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,OAAO;AAEX,YAAM,UAAU,EAAE,QAAQ,aAAa,GAAG,CAAC,UAAU;AAAA,QACnD,MAAM,KAAK;AAAA,QACX,UAAU;AAAA,MACZ,EAAE;AAAA,IACJ;AAAA,EACF;AACF;AAiBA,eAAsB,sBACpB,aACA,SACA,MAC4D;AAC5D,QAAM,EAAE,aAAa,aAAa,IAAI,MAAM,uBAAuB,aAAa,OAAO;AAEvF,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,eAAe,kBAAkB,OAAO;AAC9C,QAAM,UAAUF,MAAK,QAAQ,SAAS,IAAI;AAG1C,QAAM,QAAQ,MAAM;AAAA,IAClB,aAAa,CAAC,GAAG,aAAa,GAAG,YAAY;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,IACV,SAAS;AAAA,IACT,UAAU;AAAA,EACZ,CAAC;AAGD,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,aAAaA,MAAK,QAAQ,SAAS,KAAK;AAC9C,UAAM,QAAQ,MAAM;AAAA,MAClB,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,aAAa,aAAa;AACrC;AAuBA,eAAsB,cAAc,SAA6D;AAC/F,QAAM,EAAE,SAAS,KAAK,IAAI;AAE1B,QAAM,iBAAiB,eAAe,OAAO;AAC7C,MAAI,CAAC,gBAAgB;AAEnB,WAAO,EAAE,WAAW,OAAO,YAAY,IAAI,SAAS,MAAM;AAAA,EAC5D;AAEA,QAAM,UAAUA,MAAK,QAAQ,SAAS,IAAI;AAC1C,QAAMC,IAAG,SAAS,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAEpD,QAAM,aAAaD,MAAK,QAAQ,SAAS,iBAAiB;AAC1D,QAAM,WAAW,GAAG,WAAW,OAAO,CAAC,KAAK,IAAI;AAGhD,QAAM,SAAS,mBAAmB,IAAI,QAAQ;AAC9C,MAAI,UAAW,MAAM,aAAa,MAAM,GAAI;AAC1C,WAAO,EAAE,WAAW,MAAM,YAAY,OAAO,YAAY,SAAS,KAAK;AAAA,EACzE;AACA,qBAAmB,OAAO,QAAQ;AAGlC,QAAM,oBAA8B,CAACA,MAAK,QAAQ,SAAS,cAAc,CAAC;AAG1E,QAAM,EAAE,aAAa,aAAa,cAAc,eAAe,IAAI,MAAM;AAAA,IACvE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAIA,QAAM,aAAa,uBAAuB,aAAa,cAAc,CAAC;AACtE,QAAM,gBAAgB;AAEtB,QAAM,YAAY,CAAC,YAAY,aAAa,EAAE,KAAK,IAAI;AAEvD,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,QAAQ,MAAM;AAAA,IAClB,OAAO,EAAE,UAAU,WAAW,YAAY,SAAS,QAAQ,KAAK;AAAA,IAChE,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,IACV,SAAS,CAAC,6BAA6B,CAAC;AAAA,IACxC,UAAU;AAAA,EACZ,CAAC;AAKD,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,eAAeA,MAAK,QAAQ,SAAS,eAAe;AAC1D,QAAM,kBAAkB;AAAA,IACtB,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAIC,IAAG,WAAW,YAAY,IAAI,CAAC,YAAY,IAAI,CAAC;AAAA,EACtD;AACA,aAAW,YAAY,iBAAiB;AACtC,QAAI;AACF,YAAMC,QAAO,MAAMD,IAAG,SAAS,KAAK,QAAQ;AAC5C,aAAO,IAAI,UAAUC,MAAK,OAAO;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,qBAAmB,IAAI,UAAU,EAAE,QAAQ,WAAW,CAAC;AAEvD,SAAO,EAAE,WAAW,MAAM,YAAY,SAAS,MAAM;AACvD;AA9QA,IAUM,mBAwDA;AAlEN;AAAA;AAAA;AAGA;AACA;AACA;AAKA,IAAM,oBAAoB,CAAC,mBAAmB,iBAAiB;AAwD/D,IAAM,qBAAqB,oBAAI,IAAqC;AAAA;AAAA;;;AClEpE,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AAef,eAAsB,gBAAgB,YAAoB,SAAgC;AACxF,QAAMA,IAAG,SAAS,MAAMD,MAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACrE,QAAM,MAAM,GAAG,UAAU,QAAQ,QAAQ,GAAG,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACnF,QAAMC,IAAG,SAAS,UAAU,KAAK,SAAS,OAAO;AACjD,QAAMA,IAAG,SAAS,OAAO,KAAK,UAAU;AAC1C;AArBA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAoDV,SAAS,gBACd,QACA,UACA,SACA,OAAe,QACU;AACzB,QAAM,YAAY,CAChB,UAC4E;AAC5E,UAAM,kBAAkB,uBAAuB,MAAM,UAAU,SAAS,IAAI;AAC5E,UAAM,aAAa;AAAA,MACjB,SAAS,MAAM;AAAA,MACf,UAAU,eAAe,MAAM,UAAU,IAAI;AAAA,MAC7C,YAAY,MAAM;AAAA,MAClB,WAAW,MAAM;AAAA,MACjB,YAAY,MAAM;AAAA,MAClB;AAAA,IACF;AACA,QAAI,YAAY,OAAO;AACrB,MAAC,WAAqC,SAAU,MAAsB;AAAA,IACxE;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO,IAAI,SAAS;AAAA,IAC5B,UAAU,SAAS,IAAI,SAAS;AAAA,EAClC;AACF;AAYA,SAAS,uBAAuB,eAAuB,SAAiB,MAAwB;AAC9F,QAAM,WAAWA,MAAK,QAAQ,aAAa;AAC3C,QAAM,eAAeA,MAAK,QAAQ,OAAO;AAEzC,QAAM,QAAkB,CAAC;AACzB,MAAI,aAAaA,MAAK,QAAQ,SAAS,QAAQ;AAC/C,SAAO,MAAM;AAEX,UAAM,WAAWA,MAAK,KAAK,YAAY,gBAAgB;AACvD,UAAM,WAAWA,MAAK,KAAK,YAAY,gBAAgB;AACvD,UAAM,YAAYA,MAAK,QAAQ,SAAS,QAAQ;AAChD,UAAM,YAAYA,MAAK,QAAQ,SAAS,QAAQ;AAChD,UAAM,YAAYD,IAAG,WAAW,SAAS,IACrC,YACAA,IAAG,WAAW,SAAS,IACrB,YACA;AACN,QAAI,WAAW;AAEb,YAAM,YAAYC,MAAK,SAAS,SAAS,SAAS;AAClD,YAAM,cAAcA,MAAK,QAAQ,SAAS,eAAe,WAAW,IAAI,CAAC;AACzE,YAAM,KAAK,WAAW;AAAA,IACxB;AACA,QAAI,eAAe,aAAc;AAGjC,UAAM,YAAYA,MAAK,QAAQ,UAAU;AACzC,QAAI,cAAc,WAAY;AAC9B,iBAAa;AAAA,EACf;AAGA,QAAM,QAAQ;AACd,SAAO;AACT;AAOA,eAAsB,kBACpB,UACA,YACe;AAEf,QAAM,UAAU;AAAA,wBACM,KAAK,UAAU,SAAS,QAAQ,MAAM,CAAC,CAAC;AAAA,0BACtC,KAAK,UAAU,SAAS,UAAU,MAAM,CAAC,CAAC;AAAA;AAGlE,QAAM,gBAAgB,YAAY,OAAO;AAC3C;AASA,eAAsB,cACpB,UAC+D;AAC/D,QAAM,eAAe,CAAC,gBAAoD;AAAA,IACxE,QAAQ,WAAW;AAAA,IACnB,SAAS,WAAW;AAAA,IACpB,UAAU,WAAW;AAAA,IACrB,YAAY,WAAW;AAAA,IACvB,WAAW,WAAW;AAAA,IACtB,YAAY,WAAW;AAAA,IACvB,iBAAiB,WAAW;AAAA,EAC9B;AAEA,QAAM,iBAAiB,CAAC,gBAAwD;AAAA,IAC9E,SAAS,WAAW;AAAA,IACpB,UAAU,WAAW;AAAA,IACrB,YAAY,WAAW;AAAA,IACvB,WAAW,WAAW;AAAA,IACtB,YAAY,WAAW;AAAA,IACvB,iBAAiB,WAAW;AAAA,EAC9B;AAEA,QAAM,SAAS,SAAS,OAAO,IAAI,YAAY;AAC/C,QAAM,WAAW,SAAS,SAAS,IAAI,cAAc;AACrD,SAAO,EAAE,QAAQ,SAAS;AAC5B;AAnLA;AAAA;AAAA;AASA;AACA;AAAA;AAAA;;;ACVA,IAAa,cAIP;AAJN;AAAA;AAAA;AAAO,IAAM,eAAe,CAAC,OAAO,QAAQ,OAAO,SAAS,UAAU,QAAQ,SAAS;AAIvF,IAAM,kBAAuC,IAAI,IAAI,YAAY;AAAA;AAAA;;;ACG1D,SAAS,cAAcC,QAAsB;AAClD,MAAI,CAACA,OAAM,QAAO;AAGlB,MAAI,SAASA,OAAK,QAAQ,OAAO,GAAG;AAGpC,WAAS,OAAO,QAAQ,QAAQ,GAAG;AAGnC,WAAS,OAAO,QAAQ,QAAQ,EAAE;AAGlC,MAAI,UAAU,CAAC,OAAO,WAAW,GAAG,GAAG;AACrC,aAAS,MAAM;AAAA,EACjB;AAEA,SAAO;AACT;AAzBA;AAAA;AAAA;AAAA;AAAA;;;ACQO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,QAAQ,QAAQ,MAAM,YAAY;AACxC,MAAI,OAAO;AACT,WAAO,MAAM,MAAM,CAAC;AAAA,EACtB;AACA,SAAO;AACT;AASO,SAAS,kBAAkB,SAA2B;AAC3D,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAW,QAAQ,MAAM,GAAG;AAClC,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,WAAW,MAAM,GAAG;AAE9B,aAAO,KAAK,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC9B,WAAW,QAAQ,WAAW,GAAG,GAAG;AAClC,aAAO,KAAK,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,kBAAkB,SAA0B;AAC1D,SAAO,iBAAiB,KAAK,OAAO;AACtC;AAOO,SAAS,aAAa,SAA0B;AACrD,SAAO,WAAW,KAAK,OAAO;AAChC;AAiBO,SAAS,kBAAkB,UAA0B;AAE1D,QAAM,gBAAgB,SAAS,WAAW,MAAM,IAAI,SAAS,MAAM,CAAC,IAAI;AAGxE,QAAM,iBAAiB,cAAc,YAAY,GAAG;AACpD,QAAM,UAAU,mBAAmB,KAAK,KAAK,cAAc,MAAM,GAAG,cAAc;AAElF,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,QACd,MAAM,GAAG,EACT,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,EAC9B,IAAI,qBAAqB;AAG5B,SAAO,cAAc,SAAS,KAAK,GAAG,CAAC;AACzC;AA1FA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,SAAS,qBAAqB;AAUvB,SAAS,iBAAiBC,MAAkB;AACjD,WAASA;AACX;AAmBA,SAAS,wBAAoD;AAC3D,QAAM,KAAM,WAA0D;AACtE,MAAI,OAAO,IAAI,iBAAiB,WAAY,QAAO;AACnD,SAAO,GAAG,aAAa,KAAK,EAAE;AAChC;AAoBA,eAAsB,oBACpB,UACA,gBAAgB,OACkB;AAElC,QAAM,eAAe,sBAAsB;AAC3C,MAAI,cAAc;AAChB,QAAI,eAAe;AAGjB,UAAIC,OAAM,cAAc,QAAQ,EAAE;AAClC,MAAAA,QAAO,MAAM,KAAK,IAAI,CAAC;AACvB,aAAQ,MAAM,OAAOA;AAAA,IACvB;AACA,WAAQ,MAAM,aAAa,QAAQ;AAAA,EACrC;AAGA,MAAI,MAAM,cAAc,QAAQ,EAAE;AAClC,MAAI,WAAW,QAAW;AACxB,WAAO,MAAM,MAAM;AAAA,EACrB;AACA,SAAQ,MAAM,OAAO;AACvB;AA9EA,IAQI;AARJ;AAAA;AAAA;AAAA;AAAA;;;AC0BO,SAAS,4BAAkC;AAChD,kBAAgB,MAAM;AACtB,WAAS,MAAM;AACjB;AAKO,SAAS,qBAAqB,SAAqD;AACxF,SAAO,gBAAgB,IAAI,OAAO;AACpC;AAKO,SAAS,qBAAqB,SAAiB,QAAsC;AAC1F,kBAAgB,IAAI,SAAS,MAAM;AACrC;AAgBA,eAAsB,oBAAoB,UAAmD;AAC3F,MAAI;AACJ,MAAI;AACF,aAAU,MAAM,oBAAoB,QAAQ;AAAA,EAC9C,SAAS,KAAK;AAEZ,YAAQ;AAAA,MACN,2CAA2C,QAAQ;AAAA,MACnD,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW;AAAA,IACtD;AACA,WAAO,EAAE,aAAa,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EAC1C;AAGA,QAAM,cAAe,OAAO,WAAW,OAAO,eAAe,CAAC;AAC9D,MAAI,CAAC,MAAM,QAAQ,WAAW,GAAG;AAC/B,YAAQ,KAAK,kFAAqC,QAAQ,EAAE;AAC5D,WAAO,EAAE,aAAa,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EAC1C;AAEA,QAAM,mBAAmB,YAAY,OAAO,CAAC,MAAe;AAC1D,QAAI,OAAO,MAAM,YAAY;AAC3B,cAAQ,KAAK,mHAA8B,OAAO,CAAC,EAAE;AACrD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,CAAC;AAGD,QAAM,YAAa,OAAO,aAAa,CAAC;AACxC,MAAI,OAAO,cAAc,YAAY,cAAc,MAAM;AACvD,YAAQ,KAAK,6EAAgC,QAAQ,EAAE;AACvD,WAAO,EAAE,aAAa,kBAAkB,WAAW,CAAC,EAAE;AAAA,EACxD;AAGA,QAAM,iBAA8B,CAAC;AACrC,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,SAAS,GAAG;AACxD,QAAI,OAAO,aAAa,YAAY;AAClC,cAAQ,KAAK,8BAAe,IAAI,mDAAW;AAC3C;AAAA,IACF;AACA,mBAAe,IAAI,IAAI;AAAA,EACzB;AAEA,SAAO,EAAE,aAAa,kBAAkB,WAAW,eAAe;AACpE;AAcA,eAAsB,sBACpB,iBAC6C;AAC7C,MAAI,gBAAgB,WAAW,EAAG,QAAO;AAEzC,QAAM,oBAAuC,CAAC;AAC9C,QAAM,kBAA+B,CAAC;AAEtC,aAAW,aAAa,iBAAiB;AACvC,QAAI,SAA6C,qBAAqB,SAAS;AAC/E,QAAI,WAAW,QAAW;AAExB,UAAI,UAAU,SAAS,IAAI,SAAS;AACpC,UAAI,CAAC,SAAS;AACZ,kBAAU,oBAAoB,SAAS,EAAE,KAAK,CAAC,WAAW;AACxD,+BAAqB,WAAW,MAAM;AACtC,iBAAO;AAAA,QACT,CAAC;AACD,iBAAS,IAAI,WAAW,OAAO;AAAA,MACjC;AACA,eAAS,MAAM;AAAA,IACjB;AACA,sBAAkB,KAAK,GAAG,OAAO,WAAW;AAC5C,eAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AAC/D,sBAAgB,IAAI,IAAI;AAAA,IAC1B;AAAA,EACF;AAEA,MAAI,kBAAkB,WAAW,KAAK,OAAO,KAAK,eAAe,EAAE,WAAW,GAAG;AAC/E,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,aAAa,mBAAmB,WAAW,gBAAgB;AACtE;AAxJA,IAeM,iBAMA;AArBN;AAAA;AAAA;AAEA;AAaA,IAAM,kBAAkB,oBAAI,IAAoC;AAMhE,IAAM,WAAW,oBAAI,IAA6C;AAAA;AAAA;;;ACrBlE,OAAO,QAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AA6Bf,SAAS,yBAAyB,QAA6B;AAC7D,QAAM,QAAQ,oBAAI,IAAY;AAC9B,MAAI;AACJ,uBAAqB,YAAY;AACjC,UAAQ,QAAQ,qBAAqB,KAAK,MAAM,OAAO,MAAM;AAC3D,UAAM,IAAI,MAAM,CAAC,CAAE;AAAA,EACrB;AACA,SAAO;AACT;AAmBA,SAAS,uBAAuB,eAAuB,SAAiB,MAAyB;AAC/F,QAAM,WAAWD,MAAK,QAAQ,aAAa;AAC3C,QAAM,eAAeA,MAAK,QAAQ,OAAO;AAEzC,QAAM,QAAkB,CAAC;AACzB,MAAI,aAAaA,MAAK,QAAQ,SAAS,QAAQ;AAC/C,SAAO,MAAM;AACX,QAAI,MAAM;AAER,YAAM,WAAWA,MAAK,KAAK,YAAY,gBAAgB;AACvD,YAAM,WAAWA,MAAK,KAAK,YAAY,gBAAgB;AACvD,YAAM,YAAYA,MAAK,QAAQ,SAAS,QAAQ;AAChD,YAAM,YAAYA,MAAK,QAAQ,SAAS,QAAQ;AAChD,YAAM,YAAYC,IAAG,WAAW,SAAS,IACrC,YACAA,IAAG,WAAW,SAAS,IACrB,YACA;AACN,UAAI,WAAW;AAEb,cAAM,YAAYD,MAAK,SAAS,SAAS,SAAS;AAClD,cAAM,cAAcA,MAAK,QAAQ,SAAS,eAAe,WAAW,IAAI,CAAC;AACzE,cAAM,KAAK,WAAW;AAAA,MACxB;AAAA,IACF,OAAO;AAEL,iBAAW,OAAO,CAAC,OAAO,KAAK,GAAG;AAChC,cAAM,SAASA,MAAK,KAAK,YAAY,cAAc,GAAG,EAAE;AACxD,cAAM,YAAYA,MAAK,QAAQ,SAAS,MAAM;AAC9C,YAAIC,IAAG,WAAW,SAAS,GAAG;AAC5B,gBAAM,KAAK,SAAS;AACpB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAe,aAAc;AACjC,UAAM,YAAYD,MAAK,QAAQ,UAAU;AACzC,QAAI,cAAc,WAAY;AAC9B,iBAAa;AAAA,EACf;AAGA,QAAM,QAAQ;AACd,SAAO;AACT;AAwCA,eAAsB,WACpB,SACA,UACA,MAC+D;AAC/D,QAAM,QAAQ,MAAM,GAAG,UAAU;AAAA,IAC/B,KAAK;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,SAAwB,CAAC;AAC/B,QAAM,WAA4B,CAAC;AAEnC,aAAW,QAAQ,OAAO;AACxB,UAAM,iBAAiB,KAAK,QAAQ,OAAO,GAAG;AAC9C,UAAM,WAAW,eAAe,MAAM,GAAG,EAAE,IAAI;AAI/C,QAAI,aAAa,gBAAgB,aAAa,cAAc;AAC1D,YAAM,UAAUA,MAAK,QAAQ,SAAS,cAAc;AACpD,YAAM,UAAU,kBAAkB,cAAc;AAChD,YAAM,aAAa,kBAAkB,OAAO;AAC5C,YAAM,YAAY,WAAW,SAAS;AACtC,YAAM,aAAa,eAAe,MAAM,GAAG,EAAE,KAAK,iBAAiB;AAGnE,UAAI;AACJ,UAAI;AACJ,UAAI,MAAM;AAER,0BAAkB,uBAAuB,gBAAgB,SAAS,IAAI;AAAA,MACxE,OAAO;AAEL,cAAM,UAAU,uBAAuB,gBAAgB,OAAO;AAC9D,2BAAmB,MAAM,sBAAsB,OAAO;AAAA,MACxD;AAGA,YAAM,SAAS,MAAMC,IAAG,SAAS,SAAS,SAAS,MAAM,EAAE,MAAM,MAAM,EAAE;AACzE,YAAM,cAAc,yBAAyB,MAAM;AACnD,YAAM,UAAU,aAAa,OAAO,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC;AAE7D,iBAAW,UAAU,SAAS;AAC5B,eAAO,KAAK;AAAA,UACV;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,YAAY,cAAc;AAAA,UAC1B;AAAA,UACA,aAAa,kBAAkB;AAAA,UAC/B,WAAW,kBAAkB;AAAA,QAC/B,CAAC;AAAA,MACH;AAGA,UAAI,YAAY,IAAI,IAAI,GAAG;AACzB,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,YAAY,cAAc;AAAA,UAC1B;AAAA,UACA,aAAa,kBAAkB;AAAA,UAC/B,WAAW,kBAAkB;AAAA,QAC/B,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,SAAS;AAC5B;AA1NA,IAuBM;AAvBN;AAAA;AAAA;AAIA;AACA;AACA;AAGA;AAcA,IAAM,uBAAuB,IAAI;AAAA,MAC/B,OAAO;AAAA,MACP;AAAA,IACF;AAAA;AAAA;;;ACjBO,SAAS,WAAW,QAAsC;AAC/D,SAAO,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM;AAEhC,QAAI,EAAE,cAAc,EAAE,WAAW;AAC/B,aAAO,EAAE,YAAY,IAAI;AAAA,IAC3B;AAGA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,IAAI;AAAA,IAC5B;AAGA,UAAM,YAAY,EAAE,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE;AACvD,UAAM,YAAY,EAAE,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE;AACvD,QAAI,cAAc,WAAW;AAC3B,aAAO,YAAY;AAAA,IACrB;AAGA,WAAO,EAAE,QAAQ,cAAc,EAAE,OAAO;AAAA,EAC1C,CAAC;AACH;AA/BA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AA8Cf,SAAS,6BAA6B,QAA6B;AACjE,QAAM,QAAQ,oBAAI,IAAY;AAC9B,MAAI;AACJ,iBAAe,YAAY;AAC3B,UAAQ,QAAQ,eAAe,KAAK,MAAM,OAAO,MAAM;AACrD,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,CAAC,iBAAiB,IAAI,IAAI,GAAG;AAC/B,YAAM,IAAI,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AASA,SAAS,4BAA4B,SAAyB;AAC5D,QAAM,YAAY,QAAQ,YAAY,GAAG;AACzC,QAAM,UAAU,cAAc,KAAK,KAAK,QAAQ,MAAM,GAAG,SAAS;AAClE,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,MAAM,GAAG,EAAE,KAAK,GAAG;AACpC;AAeA,SAAS,wBAAwB,UAA0B;AACzD,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAG9C,QAAM,aAAa,WAAW,MAAM,sBAAsB;AAC1D,MAAI,YAAY;AACd,WAAO,4BAA4B,WAAW,CAAC,CAAE;AAAA,EACnD;AAGA,SAAO;AACT;AAKA,SAAS,cAAc,WAAmB,cAA8B;AACtE,SAAO,YAAY,GAAG,SAAS,IAAI,YAAY,KAAK;AACtD;AAqBA,eAAsB,UAAU,SAAiB,UAA+C;AAC9F,QAAM,QAAQ,MAAMF,IAAG,UAAU;AAAA,IAC/B,KAAK;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,QAA0B,CAAC;AAEjC,QAAM,OAAO,oBAAI,IAAoB;AAErC,aAAW,QAAQ,OAAO;AACxB,UAAM,iBAAiB,KAAK,QAAQ,OAAO,GAAG;AAC9C,UAAM,WAAW,eAAe,MAAM,GAAG,EAAE,IAAI;AAG/C,QAAI,aAAa,gBAAgB,aAAa,cAAc;AAC1D;AAAA,IACF;AAEA,UAAM,UAAUC,MAAK,QAAQ,SAAS,cAAc;AACpD,UAAM,SAAS,MAAMC,IAAG,SAAS,SAAS,SAAS,MAAM,EAAE,MAAM,MAAM,EAAE;AACzE,UAAM,cAAc,6BAA6B,MAAM;AAEvD,UAAM,YAAY,wBAAwB,cAAc;AAExD,eAAW,UAAU,aAAa;AAChC,YAAM,WAAW,cAAc,WAAW,MAAM;AAGhD,YAAM,WAAW,KAAK,IAAI,QAAQ;AAClC,UAAI,UAAU;AACZ,cAAM,IAAI;AAAA,UACR,mBAAmB,QAAQ,sBAAsB,QAAQ,QAAQ,cAAc;AAAA,QACjF;AAAA,MACF;AAEA,WAAK,IAAI,UAAU,cAAc;AAEjC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,cAAc;AAAA,QACd,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AA/KA,IAWa,eAoBP,gBAYA;AA3CN;AAAA;AAAA;AAWO,IAAM,gBAAgB,CAAC,mBAAmB;AAoBjD,IAAM,iBAAiB,IAAI;AAAA,MACzB,OAAO;AAAA,MACP;AAAA,IACF;AASA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,WAAW,UAAU,KAAK,CAAC;AAAA;AAAA;;;AC3C7D,OAAOC,SAAQ;AAKR,SAAS,kBAAkB,MAAwB;AACxD,MAAI,CAACA,IAAG,iBAAiB,IAAI,EAAG,QAAO;AACvC,QAAM,YAAYA,IAAG,aAAa,IAAI;AACtC,SAAO,CAAC,CAAC,WAAW,KAAK,CAAC,MAAM,EAAE,SAASA,IAAG,WAAW,aAAa;AACxE;AAQO,SAAS,iBAAiB,MAAqC;AACpE,QAAM,UAAUA,IACb,wBAAwB,IAAI,EAC5B,OAAO,CAAC,UAA6BA,IAAG,QAAQ,KAAK,CAAC;AACzD,MAAI,QAAQ,SAAS,EAAG,QAAO,QAAQ,CAAC;AAExC,QAAM,aAAc,KAA2C;AAC/D,MAAI,cAAc,WAAW,SAAS,EAAG,QAAO,WAAW,CAAC;AAE5D,SAAO;AACT;AAQO,SAAS,mBAAmB,OAAiD;AAClF,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,MAAM,YAAY,SAAU,QAAO;AAC9C,QAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,SAAO,WAAW;AACpB;AASO,SAAS,qBACd,OACA,SACoB;AACpB,MAAI,CAAC,SAAS,CAAC,MAAM,KAAM,QAAO;AAClC,aAAW,OAAO,MAAM,MAAM;AAC5B,QAAI,IAAI,QAAQ,SAAS,QAAS;AAElC,QAAI,OAAO,IAAI,YAAY,SAAU,QAAO;AAC5C,UAAM,OAAO,IAAI,QAAQ,KAAK;AAC9B,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,UAAU,KAAK,QAAQ,YAAY,EAAE,EAAE,KAAK;AAClD,WAAO,WAAW;AAAA,EACpB;AACA,SAAO;AACT;AAhEA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,SAAQ;AAiGR,SAAS,oBACd,SACA,UACA,cACA,UACqB;AACrB,QAAM,aAAa,QAAQ,cAAc,QAAQ;AACjD,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,QAAQ,qBAAqB,YAAY,YAAY;AAC3D,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,EAAE,IAAI,WAAW,IAAI;AAC3B,QAAM,QAAQ,iBAAiB,UAAU;AACzC,QAAM,cAAc,mBAAmB,KAAK;AAC5C,QAAM,mBAAmB,qBAAqB,OAAO,MAAM;AAC3D,QAAM,gBAAgB,sBAAsB,IAAI,UAAU;AAE1D,SAAO;AAAA,IACL,MAAM,oBAAoB,SAAS;AAAA,IACnC;AAAA,IACA;AAAA,IACA,UAAU,SAAS;AAAA,IACnB;AAAA,EACF;AACF;AAuBA,SAAS,qBACP,YACA,cACsB;AACtB,MAAI,SAA+B;AAEnC,EAAAA,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,QAAI,OAAQ;AAGZ,QACEA,IAAG,sBAAsB,IAAI,KAC7B,kBAAkB,IAAI,KACtB,KAAK,MAAM,SAAS,cACpB;AACA,eAAS,EAAE,IAAI,MAAM,YAAY,KAAK;AACtC;AAAA,IACF;AAGA,QAAIA,IAAG,oBAAoB,IAAI,KAAK,kBAAkB,IAAI,GAAG;AAC3D,iBAAW,QAAQ,KAAK,gBAAgB,cAAc;AACpD,YAAI,OAAQ;AACZ,cAAM,WAAWA,IAAG,aAAa,KAAK,IAAI,IACtC,KAAK,KAAK,OACV,KAAK,KAAK,QAAQ,UAAU;AAChC,YAAI,aAAa,gBAAgB,CAAC,KAAK,YAAa;AACpD,YAAIA,IAAG,gBAAgB,KAAK,WAAW,KAAKA,IAAG,qBAAqB,KAAK,WAAW,GAAG;AAErF,mBAAS,EAAE,IAAI,KAAK,aAAa,YAAY,KAAK;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAaA,SAAS,sBAAsB,IAAkB,YAA+C;AAC9F,QAAM,aAAa,GAAG,WAAW,CAAC;AAClC,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI,CAAC,WAAW,KAAM,QAAO;AAC7B,MAAI,CAACA,IAAG,oBAAoB,WAAW,IAAI,EAAG,QAAO;AACrD,SAAO,WAAW,KAAK,SAAS,QAAQ,UAAU;AACpD;AAxMA;AAAA;AAAA;AACA;AAAA;AAAA;;;ACDA,OAAOC,SAAQ;AACf,OAAOC,UAAQ;AACf,OAAOC,YAAU;AAmCV,SAAS,yBAA+B;AAC7C,eAAa,MAAM;AACnB,gBAAc,MAAM;AACtB;AAQA,SAAS,aAAa,UAAiC;AACrD,MAAI,MAAMA,OAAK,QAAQ,QAAQ;AAC/B,QAAM,OAAOA,OAAK,MAAM,GAAG,EAAE;AAC7B,SAAO,MAAM;AACX,UAAM,YAAYA,OAAK,KAAK,KAAK,eAAe;AAChD,QAAID,KAAG,WAAW,SAAS,GAAG;AAC5B,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,SAASC,OAAK,QAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;AAgBA,SAAS,cAAc,cAAsC;AAC3D,QAAM,SAAS,cAAc,IAAI,YAAY;AAC7C,MAAI,OAAQ,QAAO;AAEnB,QAAM,SAAyB,EAAE,WAAW,CAAC,EAAE;AAE/C,MAAI;AACF,UAAM,aAAaF,IAAG,eAAe,cAAc,CAAC,MAAMC,KAAG,aAAa,GAAG,OAAO,CAAC;AACrF,QAAI,WAAW,OAAO;AACpB,oBAAc,IAAI,cAAc,MAAM;AACtC,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,WAAW,UAAU,CAAC;AACrC,UAAM,WAAWC,OAAK,QAAQ,YAAY;AAI1C,UAAM,SAASF,IAAG;AAAA,MAChB;AAAA,MACAA,IAAG;AAAA,MACH;AAAA;AAAA,MACsB;AAAA,MACtB;AAAA,IACF;AAEA,WAAO,YAAY,OAAO;AAC1B,QAAI,OAAO,QAAQ,WAAW,QAAW;AACvC,aAAO,SAAS,OAAO,QAAQ;AAAA,IACjC;AACA,QAAI,OAAO,QAAQ,qBAAqB,QAAW;AACjD,aAAO,mBAAmB,OAAO,QAAQ;AAAA,IAC3C;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,gBAAc,IAAI,cAAc,MAAM;AACtC,SAAO;AACT;AAoBO,SAAS,cAAc,UAA8B;AAC1D,QAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,aAAa,CAAC,QAAQ,GAAG,aAAa,QAAQ,CAAC;AAE/D,eAAa,IAAI,UAAU,OAAO;AAClC,SAAO;AACT;AAuBO,SAAS,eAAe,WAA8C;AAC3E,QAAM,SAAS,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;AACrC,QAAM,SAAS,oBAAI,IAAwB;AAG3C,QAAM,SAAS,oBAAI,IAAuD;AAC1E,QAAM,kBAA4B,CAAC;AACnC,aAAW,YAAY,QAAQ;AAC7B,UAAM,eAAe,aAAa,QAAQ;AAC1C,QAAI,CAAC,cAAc;AACjB,sBAAgB,KAAK,QAAQ;AAC7B;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,IAAI,YAAY;AACrC,QAAI,OAAO;AACT,YAAM,MAAM,KAAK,QAAQ;AAAA,IAC3B,OAAO;AACL,aAAO,IAAI,cAAc,EAAE,cAAc,OAAO,CAAC,QAAQ,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF;AAEA,aAAW,EAAE,cAAc,MAAM,KAAK,OAAO,OAAO,GAAG;AAOrD,UAAM,WAAW,WAAW,YAAY;AACxC,QAAI,UAAU,aAAa,IAAI,QAAQ;AACvC,UAAM,YACJ,YAAY,UAAa,MAAM,MAAM,CAAC,MAAM,QAAS,cAAc,CAAC,MAAM,MAAS;AACrF,QAAI,CAAC,WAAW,CAAC,WAAW;AAC1B,gBAAU,aAAa,OAAO,YAAY;AAC1C,mBAAa,IAAI,UAAU,OAAO;AAAA,IACpC;AACA,eAAW,YAAY,OAAO;AAC5B,aAAO,IAAI,UAAU,OAAO;AAAA,IAC9B;AAAA,EACF;AAGA,aAAW,YAAY,iBAAiB;AACtC,WAAO,IAAI,UAAU,cAAc,QAAQ,CAAC;AAAA,EAC9C;AAEA,SAAO;AACT;AASA,SAAS,aAAa,YAAsB,cAAyC;AAEnF,QAAM,UAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,QAAQA,IAAG,aAAa;AAAA,IACxB,QAAQA,IAAG,WAAW;AAAA,IACtB,kBAAkBA,IAAG,qBAAqB;AAAA,IAC1C,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAGA,QAAM,YAAY,CAAC,GAAG,UAAU;AAEhC,MAAI,cAAc;AAChB,UAAM,YAAY,cAAc,YAAY;AAC5C,QAAI,UAAU,WAAW,QAAW;AAClC,cAAQ,SAAS,UAAU;AAAA,IAC7B;AACA,QAAI,UAAU,qBAAqB,QAAW;AAC5C,cAAQ,mBAAmB,UAAU;AAAA,IACvC;AACA,QAAI,UAAU,UAAU,SAAS,GAAG;AAElC,iBAAW,YAAY,UAAU,WAAW;AAC1C,YAAI,CAAC,UAAU,SAAS,QAAQ,GAAG;AACjC,oBAAU,KAAK,QAAQ;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAOA,IAAG,cAAc,WAAW,OAAO;AAC5C;AAlQA,IAUM,cAgBA;AA1BN;AAAA;AAAA;AAUA,IAAM,eAAe,oBAAI,IAAwB;AAgBjD,IAAM,gBAAgB,oBAAI,IAA4B;AAAA;AAAA;;;AC1BtD,OAAOG,SAAQ;AAqBR,SAAS,kBAAkB,SAAkC;AAClE,mBAAiB;AACnB;AAuJO,SAAS,gBACd,UACA,SACA,UAAuB,oBAAI,IAAI,GAC/B,WAAqC,oBAAI,IAAI,GAChC;AACb,QAAM,OAAO,SAAS;AAGtB,UAAQ,MAAM;AAAA,IACZ,KAAKA,IAAG,WAAW;AACjB,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAKA,IAAG,WAAW;AACjB,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B,KAAKA,IAAG,WAAW;AACjB,aAAO,EAAE,MAAM,UAAU;AAAA,IAC3B,KAAKA,IAAG,WAAW;AAEjB,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,IACF,KAAKA,IAAG,WAAW;AAEjB,YAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,wDAA0B;AAAA,IAChF,KAAKA,IAAG,WAAW;AACjB,aAAO,EAAE,MAAM,OAAO;AAAA,IACxB,KAAKA,IAAG,WAAW;AACjB,aAAO,EAAE,MAAM,YAAY;AAAA,IAC7B,KAAKA,IAAG,WAAW;AAEjB,aAAO,EAAE,MAAM,MAAM;AAAA,IACvB,KAAKA,IAAG,WAAW;AACjB,YAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,uFAA2B;AAAA,IACjF,KAAKA,IAAG,WAAW;AACjB,YAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,uDAAe;AAAA,IACrE,KAAKA,IAAG,WAAW;AACjB,YAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,wDAAgB;AAAA,IACtE,KAAKA,IAAG,WAAW;AACjB,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,EACJ;AAGA,MAAIA,IAAG,kBAAkB,QAAQ,GAAG;AAClC,UAAM,UAAU,SAAS;AACzB,QAAIA,IAAG,gBAAgB,OAAO,GAAG;AAC/B,aAAO,EAAE,MAAM,WAAW,OAAO,QAAQ,KAAK;AAAA,IAChD;AACA,QAAIA,IAAG,iBAAiB,OAAO,GAAG;AAChC,aAAO,EAAE,MAAM,WAAW,OAAO,OAAO,QAAQ,IAAI,EAAE;AAAA,IACxD;AACA,QAAI,QAAQ,SAASA,IAAG,WAAW,aAAa;AAC9C,aAAO,EAAE,MAAM,WAAW,OAAO,KAAK;AAAA,IACxC;AACA,QAAI,QAAQ,SAASA,IAAG,WAAW,cAAc;AAC/C,aAAO,EAAE,MAAM,WAAW,OAAO,MAAM;AAAA,IACzC;AACA,QAAI,QAAQ,SAASA,IAAG,WAAW,aAAa;AAC9C,aAAO,EAAE,MAAM,OAAO;AAAA,IACxB;AACA,UAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,wDAAW;AAAA,EACjE;AAGA,MAAIA,IAAG,gBAAgB,QAAQ,GAAG;AAChC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,gBAAgB,SAAS,aAAa,SAAS,SAAS,QAAQ;AAAA,IAC3E;AAAA,EACF;AAGA,MAAIA,IAAG,gBAAgB,QAAQ,GAAG;AAChC,UAAM,WAA2B,SAAS,SAAS,IAAI,CAAC,MAAM;AAE5D,UAAIA,IAAG,eAAe,CAAC,GAAG;AACxB,cAAM,QAAQ,gBAAgB,EAAE,MAAM,SAAS,SAAS,QAAQ;AAEhE,YAAI,MAAM,SAAS,SAAS;AAC1B,iBAAO,EAAE,MAAM,MAAM,SAAS,UAAU,OAAO,MAAM,KAAK;AAAA,QAC5D;AAEA,eAAO,EAAE,MAAM,OAAO,UAAU,OAAO,MAAM,KAAK;AAAA,MACpD;AAEA,UAAIA,IAAG,mBAAmB,CAAC,GAAG;AAC5B,eAAO;AAAA,UACL,MAAM,gBAAgB,EAAE,MAAM,SAAS,SAAS,QAAQ;AAAA,UACxD,UAAU,CAAC,CAAC,EAAE;AAAA,UACd,MAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAIA,IAAG,mBAAmB,CAAC,GAAG;AAC5B,eAAO;AAAA,UACL,MAAM,gBAAgB,EAAE,MAAM,SAAS,SAAS,QAAQ;AAAA,UACxD,UAAU;AAAA,UACV,MAAM;AAAA,QACR;AAAA,MACF;AAEA,aAAO;AAAA,QACL,MAAM,gBAAgB,GAAG,SAAS,SAAS,QAAQ;AAAA,QACnD,UAAU;AAAA,QACV,MAAM;AAAA,MACR;AAAA,IACF,CAAC;AACD,WAAO,EAAE,MAAM,SAAS,SAAS;AAAA,EACnC;AAGA,MAAIA,IAAG,gBAAgB,QAAQ,GAAG;AAChC,UAAM,UAAU,SAAS,MAAM,IAAI,CAAC,MAAM,gBAAgB,GAAG,SAAS,SAAS,QAAQ,CAAC;AACxF,WAAO,EAAE,MAAM,SAAS,QAAQ;AAAA,EAClC;AAGA,MAAIA,IAAG,uBAAuB,QAAQ,GAAG;AACvC,UAAM,UAAU,oBAAI,IAA0B;AAC9C,eAAW,KAAK,SAAS,OAAO;AAC9B,YAAM,WAAW,gBAAgB,GAAG,SAAS,SAAS,QAAQ;AAC9D,UAAI,SAAS,SAAS,UAAU;AAG9B,cAAM,IAAI;AAAA,UACR,SAAS,QAAQ;AAAA,UACjB,uEAAqB,SAAS,IAAI;AAAA,QACpC;AAAA,MACF;AACA,iBAAW,QAAQ,SAAS,YAAY;AACtC,cAAM,WAAW,QAAQ,IAAI,KAAK,IAAI;AACtC,YAAI,CAAC,UAAU;AACb,kBAAQ,IAAI,KAAK,MAAM,IAAI;AAC3B;AAAA,QACF;AAGA,YACE,KAAK,UAAU,SAAS,IAAI,MAAM,KAAK,UAAU,KAAK,IAAI,KAC1D,SAAS,aAAa,KAAK,UAC3B;AACA,gBAAM,sBAAsB;AAAA,YAC1B;AAAA,YACA,SAAS,QAAQ;AAAA,YACjB,uEAAgB,KAAK,IAAI;AAAA,UAC3B;AAAA,QACF;AACA,YAAI,CAAC,SAAS,aAAa,UAAU,KAAK,aAAa,QAAQ;AAC7D,kBAAQ,IAAI,KAAK,MAAM,IAAI;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,MAAM,UAAU,YAAY,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE;AAAA,EAC7D;AAGA,MAAIA,IAAG,kBAAkB,QAAQ,GAAG;AAClC,WAAO,mBAAmB,UAAU,SAAS,SAAS,QAAQ;AAAA,EAChE;AAGA,MAAIA,IAAG,mBAAmB,QAAQ,KAAK,SAAS,aAAaA,IAAG,WAAW,cAAc;AACvF,WAAO,aAAa,UAAU,OAAO;AAAA,EACvC;AAIA,MAAIA,IAAG,mBAAmB,QAAQ,KAAK,SAAS,aAAaA,IAAG,WAAW,iBAAiB;AAC1F,WAAO,gBAAgB,SAAS,MAAM,SAAS,SAAS,QAAQ;AAAA,EAClE;AAGA,MAAIA,IAAG,oBAAoB,QAAQ,GAAG;AACpC,WAAO,qBAAqB,UAAU,SAAS,SAAS,QAAQ;AAAA,EAClE;AAKA,MAAIA,IAAG,8BAA8B,QAAQ,GAAG;AAC9C,WAAO;AAAA,MACL;AAAA,QACE,SAAS,MAAM,SAAS,QAAQ;AAAA,QAChC,UAAU,SAAS;AAAA,QACnB,eAAe,SAAS;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,sBAAsB,GAAG,UAAU,SAAS,QAAQ,GAAG,kDAAU;AACzE;AAKA,SAAS,mBACP,UACA,SACA,UAAuB,oBAAI,IAAI,GAC/B,WAAqC,oBAAI,IAAI,GAChC;AACb,QAAM,aAA6B,CAAC;AAEpC,MAAI;AAEJ,aAAW,UAAU,SAAS,SAAS;AAErC,QACEA,IAAG,kBAAkB,MAAM,KAC3BA,IAAG,yBAAyB,MAAM,KAClCA,IAAG,yBAAyB,MAAM,GAClC;AACA,YAAM,sBAAsB;AAAA,QAC1B;AAAA,QACA,OAAO,QAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,QAAIA,IAAG,oBAAoB,MAAM,KAAK,OAAO,MAAM;AACjD,YAAM,OAAO,OAAO,KAAK,QAAQ;AACjC,YAAM,WAAW,CAAC,CAAC,OAAO;AAC1B,YAAM,OAAO,OAAO,OAChB,gBAAgB,OAAO,MAAM,SAAS,SAAS,QAAQ,IACvD,EAAE,MAAM,MAAe;AAC3B,YAAM,cAAc,4BAA4B,QAAQ,IAAI;AAC5D,0BAAoB,aAAa,MAAM,IAAI;AAC3C,iBAAW;AAAA,QACT,YAAY,SAAS,IAAI,EAAE,MAAM,MAAM,UAAU,YAAY,IAAI,EAAE,MAAM,MAAM,SAAS;AAAA,MAC1F;AAAA,IACF;AAEA,QAAIA,IAAG,4BAA4B,MAAM,GAAG;AAC1C,iBAAW,OAAO,OACd,gBAAgB,OAAO,MAAM,SAAS,SAAS,QAAQ,IACvD,EAAE,MAAM,MAAe;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,aAAa,SAChB,EAAE,MAAM,UAAU,YAAY,SAAS,IACvC,EAAE,MAAM,UAAU,WAAW;AACnC;AAQA,SAAS,mBAAmB,MAAoC;AAC9D,MAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU;AAC7D,WAAO,CAAC,KAAK,KAAK;AAAA,EACpB;AACA,MAAI,KAAK,SAAS,SAAS;AACzB,UAAM,OAAiB,CAAC;AACxB,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,SAAS,aAAa,OAAO,OAAO,UAAU,UAAU;AACjE,aAAK,KAAK,OAAO,KAAK;AAAA,MACxB,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAWA,SAAS,uBAAuB,UAAuB,SAA2C;AAChG,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,QAAQ,oBAAoB,QAAQ;AAEjD,MAAI,KAAK,QAAQ,GAAG;AAClB,UAAM,OAAiB,CAAC;AACxB,eAAW,UAAU,KAAK,OAAO;AAC/B,UAAI,OAAO,gBAAgB,GAAG;AAC5B,aAAK,KAAK,OAAO,KAAK;AAAA,MACxB,WAAW,OAAO,gBAAgB,GAAG;AACnC,aAAK,KAAK,OAAO,OAAO,KAAK,CAAC;AAAA,MAChC,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,gBAAgB,GAAG;AAC1B,WAAO,CAAC,KAAK,KAAK;AAAA,EACpB;AACA,MAAI,KAAK,gBAAgB,GAAG;AAC1B,WAAO,CAAC,OAAO,KAAK,KAAK,CAAC;AAAA,EAC5B;AAEA,SAAO;AACT;AAOA,SAAS,aAAa,UAA+B,SAAuC;AAC1F,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,uDAAyB;AAAA,EAC/E;AACA,QAAM,OAAO,QAAQ,oBAAoB,QAAQ;AAEjD,MAAI,KAAK,QAAQ,GAAG;AAClB,UAAM,UAAyB,CAAC;AAChC,eAAW,UAAU,KAAK,OAAO;AAC/B,UAAI,OAAO,gBAAgB,GAAG;AAC5B,gBAAQ,KAAK,EAAE,MAAM,WAAW,OAAO,OAAO,MAAM,CAAC;AAAA,MACvD,WAAW,OAAO,gBAAgB,GAAG;AACnC,gBAAQ,KAAK,EAAE,MAAM,WAAW,OAAO,OAAO,MAAM,CAAC;AAAA,MACvD,OAAO;AACL,cAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,4EAAqB;AAAA,MAC3E;AAAA,IACF;AACA,WAAO,EAAE,MAAM,SAAS,QAAQ;AAAA,EAClC;AAEA,MAAI,KAAK,gBAAgB,GAAG;AAC1B,WAAO,EAAE,MAAM,WAAW,OAAO,KAAK,MAAM;AAAA,EAC9C;AACA,MAAI,KAAK,gBAAgB,GAAG;AAC1B,WAAO,EAAE,MAAM,WAAW,OAAO,KAAK,MAAM;AAAA,EAC9C;AAEA,QAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,wFAAuB;AAC7E;AAoBA,SAAS,qBACP,UACA,SACA,UAAuB,oBAAI,IAAI,GAC/B,WAAqC,oBAAI,IAAI,GAChC;AACb,QAAM,WAAW,SAAS,SAAS,QAAQ;AAG3C,QAAM,QAAQ,SAAS,IAAI,QAAQ;AACnC,MAAI,OAAO;AACT,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,QAAQ;AACvB,WAAO,EAAE,MAAM,OAAO;AAAA,EACxB;AAGA,OACG,aAAa,WAAW,aAAa,oBACtC,SAAS,eAAe,WAAW,GACnC;AACA,UAAM,CAAC,GAAG,IAAI,SAAS;AACvB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,gBAAgB,KAAM,SAAS,SAAS,QAAQ;AAAA,IAC3D;AAAA,EACF;AAGA,MAAI,aAAa,YAAY,SAAS,eAAe,WAAW,GAAG;AACjE,UAAM,CAAC,QAAQ,QAAQ,IAAI,SAAS;AACpC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,gBAAgB,QAAS,SAAS,SAAS,QAAQ;AAAA,MACxD,OAAO,gBAAgB,UAAW,SAAS,SAAS,QAAQ;AAAA,IAC9D;AAAA,EACF;AAGA,OACG,aAAa,aAAa,aAAa,cAAc,aAAa,eACnE,SAAS,eAAe,WAAW,GACnC;AACA,UAAM,QAAQ,gBAAgB,SAAS,cAAc,CAAC,GAAI,SAAS,SAAS,QAAQ;AACpF,QAAI,MAAM,SAAS,YAAY,aAAa,WAAW;AAErD,aAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY,MAAM,WAAW,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU,KAAK,EAAE;AAAA,MACpE;AAAA,IACF;AACA,QAAI,MAAM,SAAS,YAAY,aAAa,YAAY;AAEtD,aAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY,MAAM,WAAW,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU,MAAM,EAAE;AAAA,MACrE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAGA,OAAK,aAAa,UAAU,aAAa,WAAW,SAAS,eAAe,WAAW,GAAG;AACxF,UAAM,YAAY,gBAAgB,SAAS,cAAc,CAAC,GAAI,SAAS,SAAS,QAAQ;AACxF,QAAI,UAAU,SAAS,UAAU;AAC/B,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB,GAAG,QAAQ,gFAAoB,UAAU,IAAI;AAAA,MAC/C;AAAA,IACF;AAGA,UAAM,cAAc,SAAS,cAAc,CAAC;AAC5C,QAAI,OAAO,mBAAmB,gBAAgB,aAAa,SAAS,SAAS,QAAQ,CAAC;AACtF,QAAI,SAAS,MAAM;AACjB,aAAO,uBAAuB,aAAa,OAAO;AAAA,IACpD;AACA,QAAI,SAAS,MAAM;AACjB,YAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,GAAG,QAAQ,wEAAiB;AAAA,IAClF;AAEA,UAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,UAAM,aACJ,aAAa,SACT,UAAU,WAAW,OAAO,CAAC,MAAM,OAAO,IAAI,EAAE,IAAI,CAAC,IACrD,UAAU,WAAW,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,IAAI,CAAC;AAE5D,WAAO,EAAE,MAAM,UAAU,WAAW;AAAA,EACtC;AAGA,MAAI,aAAa,OAAO;AACtB,QAAI,CAAC,SAAS,iBAAiB,SAAS,cAAc,WAAW,GAAG;AAClE,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AACA,UAAM,CAAC,QAAQ,QAAQ,IAAI,SAAS;AACpC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,gBAAgB,QAAS,SAAS,SAAS,QAAQ;AAAA,MACxD,OAAO,gBAAgB,UAAW,SAAS,SAAS,QAAQ;AAAA,IAC9D;AAAA,EACF;AAGA,MAAI,aAAa,OAAO;AACtB,QAAI,CAAC,SAAS,iBAAiB,SAAS,cAAc,WAAW,GAAG;AAClE,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AACA,UAAM,CAAC,MAAM,IAAI,SAAS;AAC1B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,gBAAgB,QAAS,SAAS,SAAS,QAAQ;AAAA,IAC9D;AAAA,EACF;AAGA,MAAI,aAAa,aAAa,aAAa,WAAW;AACpD,UAAM,IAAI;AAAA,MACR,SAAS,QAAQ;AAAA,MACjB,GAAG,QAAQ;AAAA,IACb;AAAA,EACF;AAGA,MAAI,aAAa,WAAW;AAC1B,UAAM,IAAI;AAAA,MACR,SAAS,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,aAAa,YAAY;AAC3B,UAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,0DAA4B;AAAA,EAClF;AAGA,MAAI,QAAQ,IAAI,QAAQ,GAAG;AACzB,WAAO,EAAE,MAAM,OAAO,MAAM,SAAS;AAAA,EACvC;AACA,UAAQ,IAAI,QAAQ;AAGpB,MAAI,SAAS;AACX,UAAM,SACJA,IAAG,aAAa,SAAS,QAAQ,KAAKA,IAAG,gBAAgB,SAAS,QAAQ,IACtE,QAAQ,oBAAoB,SAAS,QAAQ,IAC7C;AAEN,QAAI,QAAQ;AACV,YAAM,cAAc,OAAO,eAAe,CAAC;AAC3C,UAAI,aAAa;AAEf,YAAIA,IAAG,uBAAuB,WAAW,GAAG;AAC1C,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAIA,IAAG,uBAAuB,WAAW,GAAG;AAC1C,gBAAM,eAAe;AAAA,YACnB,YAAY;AAAA,YACZ,SAAS;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,iBAAO,gBAAgB,YAAY,MAAM,SAAS,SAAS,YAAY;AAAA,QACzE;AAEA,YAAIA,IAAG,kBAAkB,WAAW,GAAG;AACrC,iBAAO,uBAAuB,WAAW;AAAA,QAC3C;AAIA,YAAIA,IAAG,kBAAkB,WAAW,KAAKA,IAAG,eAAe,WAAW,GAAG;AACvE,gBAAM,WAAW;AAAA,YACf;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,SAAS;AAAA,UACX;AACA,cAAI,SAAU,QAAO;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,2DAAc,QAAQ,GAAG;AAC/E;AAUA,SAAS,mBACP,gBACA,eACA,eACA,SACA,SACA,WAC0B;AAC1B,MAAI,CAAC,kBAAkB,eAAe,WAAW,EAAG,QAAO;AAE3D,QAAM,WAAW,IAAI,IAAI,aAAa;AACtC,WAAS,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;AAC9C,UAAM,QAAQ,eAAe,CAAC;AAC9B,QAAI,CAAC,MAAO;AACZ,UAAM,MAAM,gBAAgB,CAAC;AAC7B,QAAI,KAAK;AACP,eAAS,IAAI,MAAM,KAAK,MAAM,gBAAgB,KAAK,SAAS,SAAS,aAAa,CAAC;AAAA,IACrF,WAAW,MAAM,SAAS;AACxB,eAAS,IAAI,MAAM,KAAK,MAAM,gBAAgB,MAAM,SAAS,SAAS,SAAS,QAAQ,CAAC;AAAA,IAC1F,OAAO;AACL,YAAM,IAAI;AAAA,QACR,UAAU,QAAQ;AAAA,QAClB,6BAAS,MAAM,KAAK,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAoBA,SAAS,mBACP,UACA,QACA,SACA,SACA,WAAqC,oBAAI,IAAI,GAC7C,eACoB;AACpB,QAAM,WAAW,SAAS,SAAS,QAAQ;AAG3C,MAAI;AACF,UAAM,UAAU,QAAQ,iBAAiB,MAAM;AAC/C,QAAI,WAAW,QAAQ,gBAAgB,QAAQ,aAAa,SAAS,GAAG;AACtE,YAAM,OAAO,QAAQ,aAAa,CAAC;AACnC,UAAIA,IAAG,uBAAuB,IAAI,GAAG;AACnC,eAAO,4BAA4B,MAAM,SAAS,SAAS,UAAU,aAAa;AAAA,MACpF;AACA,UAAIA,IAAG,uBAAuB,IAAI,GAAG;AACnC,cAAM,eAAe;AAAA,UACnB,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,gBAAgB,KAAK,MAAM,SAAS,SAAS,YAAY;AAAA,MAClE;AACA,UAAIA,IAAG,kBAAkB,IAAI,GAAG;AAC9B,eAAO,uBAAuB,IAAI;AAAA,MACpC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAKA,QAAM,UAAU;AAChB,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,SAAS,QAAQ,eAAe;AACtC,aAAW,cAAc,QAAQ;AAE/B,QACE,WAAW,SAAS,SAAS,gBAAgB,KAC7C,WAAW,SAAS,SAAS,iBAAiB,GAC9C;AACA;AAAA,IACF;AAEA,UAAM,QAAQ,iBAAiB,YAAY,QAAQ;AACnD,QAAI,OAAO;AACT,UAAI,MAAM,SAAS,aAAa;AAC9B,eAAO,4BAA4B,MAAM,MAAM,SAAS,SAAS,UAAU,aAAa;AAAA,MAC1F;AACA,UAAI,MAAM,SAAS,aAAa;AAC9B,cAAM,eAAe;AAAA,UACnB,MAAM,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,gBAAgB,MAAM,KAAK,MAAM,SAAS,SAAS,YAAY;AAAA,MACxE;AACA,UAAI,MAAM,SAAS,QAAQ;AACzB,eAAO,uBAAuB,MAAM,IAAI;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAeO,SAAS,iBAAiB,YAA2B,UAAuC;AACjG,MAAI,QAA6B;AACjC,EAAAA,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,QAAI,MAAO;AACX,QAAIA,IAAG,uBAAuB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAClE,cAAQ,EAAE,MAAM,aAAa,KAAK;AAAA,IACpC,WAAWA,IAAG,uBAAuB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AACzE,cAAQ,EAAE,MAAM,aAAa,KAAK;AAAA,IACpC,WAAWA,IAAG,kBAAkB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AACpE,cAAQ,EAAE,MAAM,QAAQ,KAAK;AAAA,IAC/B;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAWO,SAAS,uBAAuB,MAAuC;AAC5E,QAAM,UAAyB,CAAC;AAChC,MAAI,mBAAmB;AAEvB,aAAW,UAAU,KAAK,SAAS;AACjC,QAAI,OAAO,aAAa;AACtB,UAAIA,IAAG,gBAAgB,OAAO,WAAW,GAAG;AAC1C,gBAAQ,KAAK,EAAE,MAAM,WAAW,OAAO,OAAO,YAAY,KAAK,CAAC;AAAA,MAClE,WAAWA,IAAG,iBAAiB,OAAO,WAAW,GAAG;AAClD,cAAM,MAAM,OAAO,OAAO,YAAY,IAAI;AAC1C,gBAAQ,KAAK,EAAE,MAAM,WAAW,OAAO,IAAI,CAAC;AAC5C,2BAAmB,MAAM;AAAA,MAC3B,OAAO;AACL,cAAM,IAAI;AAAA,UACR,KAAK,KAAK;AAAA,UACV,sBAAY,OAAO,KAAK,QAAQ,CAAC;AAAA,QACnC;AAAA,MACF;AAAA,IACF,OAAO;AAEL,cAAQ,KAAK,EAAE,MAAM,WAAW,OAAO,iBAAiB,CAAC;AACzD;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,SAAS,QAAQ;AAClC;AAYO,SAAS,4BACd,MACA,SACA,UAAuB,oBAAI,IAAI,GAC/B,gBAA0C,oBAAI,IAAI,GAClD,eACa;AACb,QAAM,aAA6B,CAAC;AACpC,QAAM,UAAU,oBAAI,IAA0B;AAE9C,MAAI;AAGJ,QAAM,WAAW;AAAA,IACf,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,aAAW,kBAAkB,KAAK,mBAAmB,CAAC,GAAG;AACvD,QAAI,eAAe,UAAUA,IAAG,WAAW,gBAAgB;AACzD,iBAAW,QAAQ,eAAe,OAAO;AACvC,cAAM,aAAa,gBAAgB,MAAM,SAAS,SAAS,QAAQ;AACnE,YAAI,WAAW,SAAS,UAAU;AAChC,qBAAW,QAAQ,WAAW,YAAY;AACxC,oBAAQ,IAAI,KAAK,MAAM,IAAI;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,aAAW,UAAU,KAAK,SAAS;AAEjC,QACEA,IAAG,kBAAkB,MAAM,KAC3BA,IAAG,yBAAyB,MAAM,KAClCA,IAAG,yBAAyB,MAAM,GAClC;AACA,YAAM,sBAAsB;AAAA,QAC1B;AAAA,QACA,OAAO,QAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF;AACA,QAAIA,IAAG,oBAAoB,MAAM,KAAK,OAAO,MAAM;AACjD,YAAM,OAAO,OAAO,KAAK,QAAQ;AACjC,YAAM,WAAW,CAAC,CAAC,OAAO;AAC1B,YAAM,OAAO,OAAO,OAChB,gBAAgB,OAAO,MAAM,SAAS,SAAS,QAAQ,IACvD,EAAE,MAAM,MAAe;AAC3B,YAAM,cAAc,4BAA4B,QAAQ,IAAI;AAC5D,0BAAoB,aAAa,MAAM,IAAI;AAC3C,cAAQ;AAAA,QACN;AAAA,QACA,YAAY,SAAS,IAAI,EAAE,MAAM,MAAM,UAAU,YAAY,IAAI,EAAE,MAAM,MAAM,SAAS;AAAA,MAC1F;AAAA,IACF;AAEA,QAAIA,IAAG,4BAA4B,MAAM,GAAG;AAC1C,iBAAW,OAAO,OACd,gBAAgB,OAAO,MAAM,SAAS,SAAS,QAAQ,IACvD,EAAE,MAAM,MAAe;AAAA,IAC7B;AAAA,EACF;AAEA,aAAW,QAAQ,QAAQ,OAAO,GAAG;AACnC,eAAW,KAAK,IAAI;AAAA,EACtB;AAEA,SAAO,aAAa,SAChB,EAAE,MAAM,UAAU,YAAY,SAAS,IACvC,EAAE,MAAM,UAAU,WAAW;AACnC;AA4CA,SAAS,4BACP,MACA,WACkB;AAElB,QAAM,SAASA,IACZ,wBAAwB,IAAI,EAC5B,OAAO,CAAC,UAA6BA,IAAG,QAAQ,KAAK,CAAC;AAEzD,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AAEjC,QAAM,cAAgC,CAAC;AAEvC,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAM,KAAM;AACjB,eAAW,OAAO,MAAM,MAAM;AAC5B,YAAM,aAAa,cAAc,KAAK,SAAS;AAC/C,UAAI,WAAY,aAAY,KAAK,UAAU;AAAA,IAC7C;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,kBAAkB,KAAsC;AAC/D,QAAM,UAAU,IAAI;AACpB,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,SAAO;AACT;AASA,SAAS,cAAc,KAAkB,WAA0C;AACjF,QAAM,UAAU,IAAI,QAAQ;AAE5B,UAAQ,SAAS;AAAA;AAAA,IAEf,KAAK;AAAA,IACL,KAAK,OAAO;AACV,YAAM,QAAQ,iBAAiB,KAAK,WAAW,OAAO;AACtD,aAAO,EAAE,MAAM,SAAS,MAAM;AAAA,IAChC;AAAA;AAAA,IAGA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK,UAAU;AACb,YAAM,QAAQ,iBAAiB,KAAK,WAAW,OAAO;AACtD,aAAO,EAAE,MAAM,SAAS,MAAM;AAAA,IAChC;AAAA;AAAA,IAGA,KAAK;AAAA,IACL,KAAK,WAAW;AACd,YAAM,OAAO,kBAAkB,GAAG;AAClC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,sBAAsB,WAAW,IAAI,OAAO,mEAA2B;AAAA,MACnF;AACA,YAAM,QAAQ,kBAAkB,KAAK,KAAK,GAAG,SAAS;AACtD,aAAO,EAAE,MAAM,SAAS,SAAS,MAAM,SAAS,OAAO,MAAM,MAAM;AAAA,IACrE;AAAA;AAAA,IAGA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,MAAM,QAAQ;AAAA;AAAA,IAGzB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,MAAM,QAAQ;AAAA,IAEzB;AACE,aAAO;AAAA,EACX;AACF;AASA,SAAS,iBAAiB,KAAkB,WAAmB,SAAyB;AACtF,QAAM,OAAO,kBAAkB,GAAG;AAClC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,sBAAsB,WAAW,IAAI,OAAO,yDAAY;AAAA,EACpE;AACA,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,MAAM,OAAO,OAAO;AAC1B,MAAI,CAAC,OAAO,SAAS,GAAG,GAAG;AACzB,UAAM,IAAI,sBAAsB,WAAW,IAAI,OAAO,8BAAU,OAAO,wCAAU;AAAA,EACnF;AACA,SAAO;AACT;AAQA,SAAS,kBAAkB,MAAc,WAAwD;AAE/F,QAAM,QAAQ,wBAAwB,KAAK,IAAI;AAC/C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,sBAAsB,WAAW,uBAAQ,IAAI,4CAAwB;AAAA,EACjF;AACA,QAAM,CAAC,EAAE,SAAS,KAAK,IAAI;AAC3B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,sBAAsB,WAAW,uBAAQ,IAAI,2CAAkB;AAAA,EAC3E;AACA,SAAO,QAAQ,EAAE,SAAS,MAAM,IAAI,EAAE,QAAQ;AAChD;AAWA,SAAS,oBACP,aACA,MACA,WACM;AACN,MAAI,YAAY,WAAW,EAAG;AAE9B,aAAW,cAAc,aAAa;AACpC,UAAM,OAAO,WAAW;AAExB,QAAI,wBAAwB,IAAI,IAAI,GAAG;AACrC,UAAI,KAAK,SAAS,UAAU;AAC1B,cAAM,IAAI;AAAA,UACR;AAAA,UACA,IAAI,IAAI,qFAAyB,KAAK,IAAI;AAAA,QAC5C;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,wBAAwB,IAAI,IAAI,GAAG;AACrC,UAAI,KAAK,SAAS,YAAY,KAAK,SAAS,SAAS;AACnD,cAAM,IAAI;AAAA,UACR;AAAA,UACA,IAAI,IAAI,kGAAiC,KAAK,IAAI;AAAA,QACpD;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,+BAA+B,IAAI,IAAI,GAAG;AAC5C,UAAI,KAAK,SAAS,UAAU;AAC1B,cAAM,IAAI;AAAA,UACR;AAAA,UACA,IAAI,IAAI,qFAAyB,KAAK,IAAI;AAAA,QAC5C;AAAA,MACF;AACA;AAAA,IACF;AAAA,EACF;AACF;AA5uCA,IAcI,gBAwBS,uBAy+BP,yBAaA,yBASA;AAriCN;AAAA;AAAA;AAcA,IAAI,iBAAoC;AAwBjC,IAAM,wBAAN,MAAM,+BAA8B,MAAM;AAAA,MAC/C,YACkB,UACA,QAChB,SACgB,UAChB;AACA;AAAA,UACE,yCAAW,QAAQ,MAAM,MAAM,MAC5B,WAAW,KAAK,SAAS,IAAI,IAAI,SAAS,IAAI,IAAI,SAAS,MAAM,MAAM;AAAA,UAC1E;AAAA,QACF;AATgB;AACA;AAEA;AAOhB,aAAK,OAAO;AAAA,MACd;AAAA,MAXkB;AAAA,MACA;AAAA,MAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgBlB,OAAO,GAAG,MAAe,UAAkB,QAAuC;AAChF,cAAM,aAAa,KAAK,cAAc;AACtC,YAAI,CAAC,YAAY;AACf,iBAAO,IAAI,uBAAsB,UAAU,MAAM;AAAA,QACnD;AACA,cAAM,EAAE,MAAM,UAAU,IAAI,WAAW,8BAA8B,KAAK,SAAS,CAAC;AACpF,eAAO,IAAI,uBAAsB,UAAU,QAAQ,QAAW;AAAA,UAC5D,MAAM,WAAW;AAAA,UACjB,MAAM,OAAO;AAAA,UACb,QAAQ,YAAY;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF;AAw8BA,IAAM,0BAA0B,oBAAI,IAA4B;AAAA,MAC9D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAKD,IAAM,0BAA0B,oBAAI,IAA4B;AAAA,MAC9D;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAKD,IAAM,iCAAiC,oBAAI,IAA4B;AAAA,MACrE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA;AAAA;;;AC1iCD,OAAOC,SAAQ;AAwCR,SAAS,gBACd,SACA,UACA,UACwB;AACxB,QAAM,aAAa,QAAQ,cAAc,QAAQ;AACjD,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,UAAU,QAAQ,eAAe;AAIvC,oBAAkB,OAAO;AACzB,MAAI;AAEF,QAAI,SAAiC;AAErC,IAAAA,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,UAAI,OAAQ;AAEZ,UAAIA,IAAG,uBAAuB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAClE,cAAM,UAAU,oBAAI,IAAY;AAChC,gBAAQ,IAAI,QAAQ;AACpB,cAAM,cAAc;AAAA,UAAgB;AAAA,UAAU;AAAA,UAAU,MACtD,4BAA4B,MAAM,SAAS,OAAO;AAAA,QACpD;AACA,iBAAS;AAAA,UACP,MAAM;AAAA,UACN,YAAY,YAAY,SAAS,WAAW,YAAY,aAAa,CAAC;AAAA,UACtE;AAAA,QACF;AACA;AAAA,MACF;AAGA,UAAIA,IAAG,uBAAuB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAClE,cAAM,UAAU,oBAAI,IAAY;AAChC,gBAAQ,IAAI,QAAQ;AACpB,cAAM,cAAc;AAAA,UAAgB;AAAA,UAAU;AAAA,UAAU,MACtD,gBAAgB,KAAK,MAAM,SAAS,OAAO;AAAA,QAC7C;AACA,iBAAS;AAAA,UACP,MAAM;AAAA,UACN,YAAY,YAAY,SAAS,WAAW,YAAY,aAAa,CAAC;AAAA,UACtE;AAAA,QACF;AACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,OAAQ,QAAO;AAGnB,eAAW,MAAM,QAAQ,eAAe,GAAG;AACzC,UAAI,OAAO,WAAY;AAEvB,UAAI,GAAG,SAAS,SAAS,gBAAgB,KAAK,GAAG,SAAS,SAAS,iBAAiB,GAAG;AACrF;AAAA,MACF;AACA,YAAM,QAAQ,iBAAiB,IAAI,QAAQ;AAC3C,UAAI,CAAC,MAAO;AAEZ,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,IAAI,QAAQ;AACpB,YAAM,cAAc,gBAAgB,UAAU,UAAU,MAAM;AAC5D,YAAI,MAAM,SAAS,aAAa;AAC9B,iBAAO,4BAA4B,MAAM,MAAM,SAAS,OAAO;AAAA,QACjE;AACA,YAAI,MAAM,SAAS,aAAa;AAC9B,iBAAO,gBAAgB,MAAM,KAAK,MAAM,SAAS,OAAO;AAAA,QAC1D;AACA,eAAO,uBAAuB,MAAM,IAAI;AAAA,MAC1C,CAAC;AACD,aAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY,YAAY,SAAS,WAAW,YAAY,aAAa,CAAC;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT,UAAE;AACA,sBAAkB,IAAI;AAAA,EACxB;AACF;AAwFO,SAAS,uBAAuB,SAAqB,UAAoC;AAC9F,QAAM,QAAQ,oBAAI,IAAoC;AACtD,SAAO;AAAA,IACL,QAAQ,MAAsC;AAC5C,UAAI,MAAM,IAAI,IAAI,EAAG,QAAO,MAAM,IAAI,IAAI;AAC1C,YAAM,OAAO,gBAAgB,SAAS,UAAU,IAAI;AACpD,YAAM,IAAI,MAAM,IAAI;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAOA,SAAS,gBAAmB,UAAkB,UAAkB,IAAgB;AAC9E,MAAI;AACF,WAAO,GAAG;AAAA,EACZ,SAAS,KAAK;AACZ,QAAI,eAAe,uBAAuB;AAExC,YAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAC9C,YAAM,WAAW,IAAI;AAAA,QACnB,IAAI;AAAA,QACJ,GAAG,IAAI,MAAM,uBAAQ,QAAQ,mBAAS,QAAQ;AAAA,QAC9C,EAAE,OAAO,IAAI;AAAA,QACb,IAAI;AAAA,MACN;AACA,YAAM;AAAA,IACR;AACA,UAAM;AAAA,EACR;AACF;AAtPA;AAAA;AAAA;AACA;AAAA;AAAA;;;ACSO,SAAS,sBAAsB,QAAkC;AACtE,QAAM,QAAQ,OAAO,YAAY;AACjC,MAAI,UAAU,SAAS,UAAU,YAAY,UAAU,QAAQ;AAC7D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAQO,SAAS,QAAQ,QAAyB;AAC/C,QAAM,QAAQ,OAAO,YAAY;AACjC,SAAO,UAAU,UAAU,UAAU,SAAS,UAAU,WAAW,UAAU;AAC/E;AA3BA;AAAA;AAAA;AAAA;AAAA;;;ACMO,SAAS,cAAc,QAAgB,WAAgD;AAC5F,SAAO,GAAG,OAAO,YAAY,CAAC,GAAG,UAAU,OAAO,CAAC,EAAE,YAAY,IAAI,UAAU,MAAM,CAAC,CAAC;AACzF;AARA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,SAAQ;AAoFR,SAAS,iBAAiB,IAAsD;AACrF,QAAM,SAAS,eAAe,IAAI,EAAE;AACpC,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,GAAG,SAAS;AAC1B,QAAM,SAAS,qBAAqB,KAAK;AAEzC,QAAM,QAAQ,OAAO,IAAI,CAAC,UAAU;AAClC,UAAM,OAAO,eAAe,MAAM,IAAI,KAAK;AAC3C,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,SAAS;AAAA;AAAA,IACX;AAAA,EACF,CAAC;AAED,iBAAe,IAAI,IAAI,KAAK;AAC5B,SAAO;AACT;AAWA,SAAS,qBAAqB,OAAwC;AAEpE,QAAM,aAAaA,IAAG;AAAA,IACpB;AAAA,IACA;AAAA,IACAA,IAAG,aAAa;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,aAAuB,CAAC;AAE9B,WAAS,MAAM,MAAqB;AAElC,QAAIA,IAAG,sBAAsB,IAAI,KAAK,KAAK,WAAW,SAAS,GAAG;AAChE,iBAAW,SAAS,KAAK,YAAY;AACnC,yBAAiB,OAAO,UAAU;AAAA,MACpC;AACA;AAAA,IACF;AAGA,SAAKA,IAAG,gBAAgB,IAAI,KAAKA,IAAG,qBAAqB,IAAI,MAAM,KAAK,WAAW,SAAS,GAAG;AAC7F,iBAAW,SAAS,KAAK,YAAY;AACnC,yBAAiB,OAAO,UAAU;AAAA,MACpC;AACA;AAAA,IACF;AAEA,IAAAA,IAAG,aAAa,MAAM,KAAK;AAAA,EAC7B;AAEA,QAAM,UAAU;AAEhB,SAAO,WAAW,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE;AAC5C;AAYA,SAAS,iBAAiB,OAAgC,OAAuB;AAC/E,QAAM,OAAO,MAAM;AAGnB,MAAIA,IAAG,aAAa,IAAI,GAAG;AACzB,UAAM,KAAK,KAAK,IAAI;AACpB;AAAA,EACF;AAGA,MAAIA,IAAG,uBAAuB,IAAI,GAAG;AACnC,eAAW,WAAW,KAAK,UAAU;AACnC,UAAIA,IAAG,iBAAiB,OAAO,GAAG;AAChC,cAAM,WAAW,QAAQ;AACzB,YAAIA,IAAG,aAAa,QAAQ,GAAG;AAC7B,gBAAM,KAAK,SAAS,IAAI;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAGA,MAAIA,IAAG,sBAAsB,IAAI,GAAG;AAClC,eAAW,WAAW,KAAK,UAAU;AACnC,UAAI,WAAWA,IAAG,iBAAiB,OAAO,GAAG;AAC3C,cAAM,WAAW,QAAQ;AACzB,YAAIA,IAAG,aAAa,QAAQ,GAAG;AAC7B,gBAAM,KAAK,SAAS,IAAI;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AACF;AAnMA,IAgDa,gBAwBP;AAxEN;AAAA;AAAA;AAgDO,IAAM,iBAAgD;AAAA,MAC3D,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,KAAK;AAAA;AAAA,MACL,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA;AAAA,MACP,QAAQ;AAAA;AAAA,IACV;AASA,IAAM,iBAAiB,oBAAI,QAAgC;AAAA;AAAA;;;ACxE3D,OAAOC,SAAQ;AAmDR,SAAS,6BACd,YACA,cACe;AACf,QAAM,SAAsB,CAAC;AAG7B,EAAAA,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,QAAIA,IAAG,sBAAsB,IAAI,KAAK,KAAK,MAAM,SAAS,cAAc;AAEtE,iBAAW,SAAS,KAAK,YAAY;AACnC,cAAM,YAAY,aAAa,OAAO,UAAU;AAChD,eAAO,KAAK,SAAS;AAAA,MACvB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,EAAE,OAAO;AAClB;AAKA,SAAS,aAAa,OAAgC,YAAsC;AAC1F,QAAM,OAAO,MAAM,KAAK,QAAQ,UAAU;AAC1C,QAAM,OAAO,eAAe,IAAI,KAAK;AAErC,QAAM,SAAoB,EAAE,MAAM,KAAK;AAGvC,MAAI,MAAM,MAAM;AACd,QAAIA,IAAG,oBAAoB,MAAM,IAAI,GAAG;AAEtC,aAAO,WAAW,MAAM,KAAK,SAAS,QAAQ,UAAU;AAAA,IAC1D,WAAWA,IAAG,kBAAkB,MAAM,IAAI,GAAG;AAE3C,aAAO,SAAS,cAAc,MAAM,MAAM,UAAU;AAAA,IACtD;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,cAAc,UAA8B,YAA2C;AAC9F,QAAM,SAAyB,CAAC;AAEhC,aAAW,UAAU,SAAS,SAAS;AACrC,QAAIA,IAAG,oBAAoB,MAAM,KAAK,OAAO,QAAQA,IAAG,aAAa,OAAO,IAAI,GAAG;AACjF,YAAM,WAAW,OAAO,KAAK;AAC7B,YAAM,WAAW,CAAC,CAAC,OAAO;AAC1B,YAAM,WAAW,OAAO,MAAM,QAAQ,UAAU,KAAK;AAErD,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAnHA;AAAA;AAAA;AAEA;AAAA;AAAA;;;ACQA,OAAOC,YAAU;AA2CV,SAAS,0BACd,QACA,SAKA;AAGA,QAAM,gBAAgB,oBAAI,IAAuD;AACjF,aAAW,SAAS,QAAQ;AAC1B,UAAM,WAAW,UAAUA,OAAK,QAAQ,SAAS,MAAM,QAAQ,IAAI,MAAM;AACzE,QAAI,QAAQ,cAAc,IAAI,QAAQ;AACtC,QAAI,CAAC,OAAO;AACV,cAAQ,EAAE,SAAS,MAAM,SAAS,SAAS,oBAAI,IAAI,EAAE;AACrD,oBAAc,IAAI,UAAU,KAAK;AAAA,IACnC;AACA,UAAM,QAAQ,IAAI,MAAM,MAAM;AAAA,EAChC;AAGA,QAAM,gBAAgB,eAAe,CAAC,GAAG,cAAc,KAAK,CAAC,CAAC;AAG9D,QAAM,kBAAkB,oBAAI,IAA8B;AAC1D,aAAW,YAAY,cAAc,KAAK,GAAG;AAC3C,oBAAgB,IAAI,UAAU,uBAAuB,cAAc,IAAI,QAAQ,GAAI,QAAQ,CAAC;AAAA,EAC9F;AAGA,QAAM,UAA+B,CAAC;AACtC,aAAW,CAAC,UAAU,KAAK,KAAK,eAAe;AAC7C,UAAM,UAAU,cAAc,IAAI,QAAQ;AAC1C,UAAM,aAAa,QAAQ,cAAc,QAAQ;AACjD,QAAI,CAAC,WAAY;AACjB,UAAM,WAAW,gBAAgB,IAAI,QAAQ;AAC7C,eAAW,UAAU,MAAM,SAAS;AAClC,YAAM,YAAY,sBAAsB,MAAM;AAC9C,YAAM,aAAa,cAAc,QAAQ,SAAS;AAElD,YAAM,OAAO,6BAA6B,YAAY,MAAM;AAK5D,YAAM,QACJ,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,MAC3C,cAAc,SAAS,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI;AACvE,YAAM,SAAS,OAAO,SAAS;AAE/B,YAAM,WAAW,OAAO,WAAW,SAAS,QAAQ,MAAM,QAAQ,IAAI;AACtE,cAAQ,KAAK;AAAA,QACX,SAAS,MAAM;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,UAAU;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,gBAAgB;AACpC;AApHA;AAAA;AAAA;AAAA;AACA;AAKA;AACA;AACA;AAAA;AAAA;;;ACwCO,SAAS,kBAAkB,MAAmB,KAA2B;AAC9E,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH;AAAA,IAEF,KAAK;AACH,wBAAkB,KAAK,SAAS,GAAG;AACnC;AAAA,IAEF,KAAK;AACH,iBAAW,MAAM,KAAK,UAAU;AAC9B,0BAAkB,GAAG,MAAM,GAAG;AAAA,MAChC;AACA;AAAA,IAEF,KAAK;AACH,iBAAW,QAAQ,KAAK,YAAY;AAClC,0BAAkB,KAAK,MAAM,GAAG;AAAA,MAClC;AACA;AAAA,IAEF,KAAK;AACH,iBAAW,UAAU,KAAK,SAAS;AACjC,0BAAkB,QAAQ,GAAG;AAAA,MAC/B;AACA;AAAA,IAEF,KAAK;AACH,wBAAkB,KAAK,KAAK,GAAG;AAC/B,wBAAkB,KAAK,OAAO,GAAG;AACjC;AAAA,IAEF,KAAK;AACH,wBAAkB,KAAK,KAAK,GAAG;AAC/B,wBAAkB,KAAK,OAAO,GAAG;AACjC;AAAA,IAEF,KAAK;AACH,wBAAkB,KAAK,SAAS,GAAG;AACnC;AAAA,IAEF,KAAK,OAAO;AACV,UAAI,IAAI,WAAW,IAAI,KAAK,IAAI,EAAG;AAEnC,UAAI,WAAW,IAAI,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAC7C,YAAM,WAAW,IAAI,YAAY,KAAK,IAAI;AAC1C,UAAI,UAAU;AACZ,YAAI,WAAW,IAAI,KAAK,MAAM,QAAQ;AACtC,0BAAkB,UAAU,GAAG;AAC/B;AAAA,MACF;AAIA,YAAM,IAAI;AAAA,QACR,KAAK;AAAA,QACL,iEAAe,KAAK,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,2BACd,MACA,KACA,aACQ;AACR,QAAM,OAAO,eAAe,MAAM,GAAG;AAGrC,QAAM,kBACJ,eAAe,YAAY,SAAS,IAAI,iBAAiB,MAAM,aAAa,KAAK,IAAI,IAAI;AAG3F,MAAI,IAAI,WAAW,KAAK,SAAS,YAAY,KAAK,SAAS,YAAY;AACrE,WAAO,qBAAqB,KAAK,MAAM,eAAe;AAAA,EACxD;AAIA,MACE,IAAI,UACJ,KAAK,SAAS,cACb,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,UAAU,YACzD;AACA,WAAO;AAAA,MACL,OAAO,KAAK,UAAU,WAAW,WAAW;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAcA,SAAS,iBACP,UACA,aACA,UACQ;AACR,QAAM,SAAS,YAAY,IAAI,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE;AAChF,SAAO,GAAG,QAAQ,GAAG,MAAM;AAC7B;AAQA,SAAS,qBAAqB,YAA4B,WAAwC;AAChG,UAAQ,WAAW,MAAM;AAAA,IACvB,KAAK;AACH,aAAO,QAAQ,WAAW,KAAK;AAAA,IACjC,KAAK;AACH,aAAO,QAAQ,WAAW,KAAK;AAAA,IACjC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,QAAQ,WAAW,KAAK;AAAA,IACjC,KAAK;AACH,aAAO,QAAQ,WAAW,KAAK;AAAA,IACjC,KAAK;AACH,aAAO,WAAW,WAAW,KAAK;AAAA,IACpC,KAAK,SAAS;AACZ,YAAM,QAAQ,WAAW,SAAS;AAElC,aAAO,qBAAqB,KAAK,UAAU,WAAW,OAAO,CAAC,GAAG,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC,KAAK,EAAE;AAAA,IAC5G;AAAA,IACA,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAKA,SAAS,eAAe,MAAmB,KAA6B;AACtE,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAEH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,aAAa,KAAK,UAAU,KAAK,KAAK,CAAC;AAAA,IAChD,KAAK;AACH,aAAO,WAAW,2BAA2B,KAAK,SAAS,GAAG,CAAC;AAAA,IACjE,KAAK;AACH,aAAO,wBAAwB,KAAK,UAAU,GAAG;AAAA,IACnD,KAAK;AACH,aAAO,yBAAyB,MAAM,GAAG;AAAA,IAC3C,KAAK;AACH,aAAO,wBAAwB,KAAK,SAAS,GAAG;AAAA,IAClD,KAAK;AAIH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,YAAY,2BAA2B,KAAK,KAAK,GAAG,CAAC,KAAK,2BAA2B,KAAK,OAAO,GAAG,CAAC;AAAA,IAC9G,KAAK;AAGH,aAAO,iCAAiC,2BAA2B,KAAK,KAAK,GAAG,CAAC,KAAK,2BAA2B,KAAK,OAAO,GAAG,CAAC;AAAA,IACnI,KAAK;AAGH,aAAO,iCAAiC,2BAA2B,KAAK,SAAS,GAAG,CAAC;AAAA,IACvF,KAAK;AAGH,UAAI,KAAK,SAAS,IAAI,eAAe;AACnC,eAAO,GAAG,IAAI,eAAe;AAAA,MAC/B;AACA,aAAO,GAAG,KAAK,IAAI;AAAA,EACvB;AACF;AAuEO,SAAS,4BAAoC;AAClD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AASO,SAAS,kBAAkB,MAAuB;AACvD,SACE,KAAK,SAAS,cAAc,KAC5B,KAAK,SAAS,eAAe,KAC7B,KAAK,SAAS,WAAW,KACzB,KAAK,SAAS,WAAW;AAE7B;AAQA,SAAS,qBAAqB,MAA4B,OAAuB;AAC/E,MAAI,SAAS,UAAU;AACrB,WAAO,8BAA8B,KAAK;AAAA,EAC5C;AACA,SAAO,+BAA+B,KAAK;AAC7C;AAUA,SAAS,wBAAwB,UAA0B,KAA6B;AACtF,QAAM,aAAuB,CAAC;AAC9B,QAAM,gBAA2B,CAAC;AAClC,MAAI,iBAAiB;AACrB,MAAI,cAAc;AAElB,aAAW,MAAM,UAAU;AACzB,QAAI,GAAG,MAAM;AACX,uBAAiB,2BAA2B,GAAG,MAAM,GAAG;AACxD,oBAAc;AAAA,IAChB,WAAW,CAAC,aAAa;AACvB,iBAAW,KAAK,2BAA2B,GAAG,MAAM,GAAG,CAAC;AACxD,oBAAc,KAAK,GAAG,QAAQ;AAAA,IAChC;AAAA,EACF;AAKA,MAAI,gBAAgB;AAClB,UAAM,oBAAoB,WAAW;AAAA,MAAI,CAAC,MAAM,MAC9C,cAAc,CAAC,IAAI,GAAG,IAAI,gBAAgB;AAAA,IAC5C;AACA,WAAO,YAAY,kBAAkB,KAAK,IAAI,CAAC,WAAW,cAAc;AAAA,EAC1E;AAGA,QAAM,cAAc,cAAc,KAAK,CAAC,MAAM,CAAC;AAC/C,MAAI,CAAC,aAAa;AAChB,WAAO,YAAY,WAAW,KAAK,IAAI,CAAC;AAAA,EAC1C;AAIA,QAAM,WAAqB,CAAC;AAC5B,WAAS,MAAM,WAAW,QAAQ,OAAO,GAAG,OAAO;AAIjD,UAAM,UAAU,cAAc,MAAM,GAAG;AACvC,QAAI,QAAQ,SAAS,KAAK,QAAQ,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG;AAEjD;AAAA,IACF;AACA,UAAM,SAAS,WAAW,MAAM,GAAG,GAAG;AACtC,aAAS,KAAK,YAAY,OAAO,KAAK,IAAI,CAAC,IAAI;AAAA,EACjD;AAGA,WAAS,QAAQ;AACjB,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,SAAS,CAAC;AAAA,EACnB;AACA,SAAO,YAAY,SAAS,KAAK,IAAI,CAAC;AACxC;AASA,SAAS,yBACP,MACA,KACQ;AACR,QAAM,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS;AAC3C,UAAM,OAAO,2BAA2B,KAAK,MAAM,KAAK,KAAK,WAAW;AACxE,UAAM,YAAY,KAAK,WAAW,GAAG,IAAI,gBAAgB;AACzD,WAAO,GAAG,KAAK,UAAU,KAAK,IAAI,CAAC,KAAK,SAAS;AAAA,EACnD,CAAC;AACD,QAAM,aAAa,cAAc,OAAO,KAAK,IAAI,CAAC;AAClD,SAAO,KAAK,aAAa,SACrB,GAAG,UAAU,aAAa,2BAA2B,KAAK,UAAU,GAAG,CAAC,MACxE;AACN;AAQA,SAAS,wBAAwB,SAAwB,KAA6B;AACpF,QAAM,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AACrD,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM;AAGvD,MAAI,WAAW,QAAQ,WAAW,GAAG;AACnC,WAAO,GAAG,2BAA2B,QAAQ,CAAC,GAAI,GAAG,CAAC;AAAA,EACxD;AAGA,MAAI,SAAS;AACX,UAAMC,cAAa,QAAQ,IAAI,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC,EAAE,KAAK,IAAI;AACnF,WAAO,YAAYA,WAAU;AAAA,EAC/B;AAGA,QAAM,aAAa,QAAQ,IAAI,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC,EAAE,KAAK,IAAI;AACnF,SAAO,YAAY,UAAU;AAC/B;AAQA,SAAS,6BACP,MACA,MACA,KACQ;AACR,QAAM,OAAO,2BAA2B,MAAM,GAAG;AACjD,QAAM,SAAS,YAAY,MAAM,oBAAI,IAAI,CAAC,IAAI,CAAC,CAAC;AAChD,MAAI,QAAQ;AAEV,WAAO,SAAS,IAAI,yBAAyB,IAAI;AAAA,EACnD;AACA,SAAO,SAAS,IAAI,YAAY,IAAI;AACtC;AAKA,SAAS,YAAY,MAAmB,SAA+B;AACrE,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,QAAQ,IAAI,KAAK,IAAI;AAAA,IAC9B,KAAK;AACH,aAAO,YAAY,KAAK,SAAS,OAAO;AAAA,IAC1C,KAAK;AACH,aAAO,KAAK,SAAS,KAAK,CAAC,OAAO,YAAY,GAAG,MAAM,OAAO,CAAC;AAAA,IACjE,KAAK;AACH,aAAO,KAAK,WAAW,KAAK,CAAC,SAAS,YAAY,KAAK,MAAM,OAAO,CAAC;AAAA,IACvE,KAAK;AACH,aAAO,KAAK,QAAQ,KAAK,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAAA,IACzD,KAAK;AACH,aAAO,YAAY,KAAK,KAAK,OAAO,KAAK,YAAY,KAAK,OAAO,OAAO;AAAA,IAC1E,KAAK;AACH,aAAO,YAAY,KAAK,KAAK,OAAO,KAAK,YAAY,KAAK,OAAO,OAAO;AAAA,IAC1E,KAAK;AACH,aAAO,YAAY,KAAK,SAAS,OAAO;AAAA,IAC1C;AACE,aAAO;AAAA,EACX;AACF;AAiBO,SAAS,wBACd,UACA,aACA,YACA,SAAS,OACD;AACR,QAAM,EAAE,uBAAuB,iBAAiB,IAAI;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,0BAA0B;AACrC,QAAM,KAAK,EAAE;AAEb,aAAW,EAAE,YAAY,KAAK,uBAAuB;AACnD,UAAM,KAAK,WAAW;AAAA,EACxB;AACA,MAAI,sBAAsB,SAAS,EAAG,OAAM,KAAK,EAAE;AAEnD,QAAM,KAAK,gBAAgB;AAE3B,SAAO,MAAM,KAAK,IAAI;AACxB;AAkBO,SAAS,6BACd,UACA,aACA,YACA,SAAS,OACa;AACtB,QAAM,MAAM,IAAI,eAAe,WAAW;AAC1C,QAAM,OAAO,cAAc,SAAS;AACpC,MAAI,gBAAgB,SAAS;AAC7B,MAAI,kBAAkB;AACtB,MAAI,SAAS;AAGb,oBAAkB,SAAS,aAAa,GAAG;AAC3C,MAAI,WAAW,OAAO,SAAS,IAAI;AAEnC,QAAM,wBAAwB,CAAC,GAAG,IAAI,UAAU,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO;AAAA,IACpE,MAAM;AAAA,IACN,aAAa,6BAA6B,GAAG,MAAM,GAAG;AAAA,EACxD,EAAE;AAGF,QAAM,YAAY,2BAA2B,SAAS,aAAa,GAAG;AACtE,QAAM,aAAa,YAAY,SAAS,aAAa,oBAAI,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;AAC7E,QAAM,mBAAmB,aACrB,gBAAgB,IAAI,yBAAyB,SAAS,OACtD,gBAAgB,IAAI,YAAY,SAAS;AAE7C,SAAO,EAAE,uBAAuB,iBAAiB;AACnD;AAtnBA,IAoBM,gBAyQO,sBAaA,uBAkBA,mBAcA,mBAMA;AAhVb;AAAA;AAAA;AAAA;AAoBA,IAAM,iBAAN,MAAqB;AAAA;AAAA,MAEV,aAAa,oBAAI,IAAyB;AAAA;AAAA,MAE1C;AAAA;AAAA,MAET,gBAAgB;AAAA;AAAA,MAEhB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOlB,SAAS;AAAA,MAET,YAAY,aAA2B;AACrC,aAAK,cAAc;AAAA,MACrB;AAAA,IACF;AAqPO,IAAM,uBACX;AAYK,IAAM,wBACX;AAiBK,IAAM,oBACX;AAaK,IAAM,oBACX;AAKK,IAAM,mBAAmB;AAAA;AAAA;;;AChVhC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAOC,YAAU;AAyBV,SAAS,oBAAoB,YAAoB,MAAc,SAAyB;AAC7F,MAAI,MAAM,WAAW,QAAQ,OAAO,GAAG;AAEvC,MAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,UAAM,IAAI,MAAM,CAAC;AAAA,EACnB;AAEA,QAAM,MAAM,IAAI,YAAY,GAAG;AAC/B,QAAM,SAAS,OAAO,IAAI,IAAI,MAAM,GAAG,GAAG,IAAI;AAC9C,SAAOA,OAAK,QAAQ,SAAS,MAAM,QAAQ,QAAQ;AACrD;AAmBO,SAAS,qBAAqB,UAAkB,MAAc,SAAyB;AAC5F,MAAI,MAAM,SAAS,QAAQ,OAAO,GAAG;AAErC,MAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,UAAM,IAAI,MAAM,CAAC;AAAA,EACnB,WAES,IAAI,WAAW,GAAG,IAAI,GAAG,GAAG;AACnC,UAAM,IAAI,MAAM,KAAK,SAAS,CAAC;AAAA,EACjC;AAEA,QAAM,MAAM,IAAI,YAAY,GAAG;AAC/B,QAAM,SAAS,OAAO,IAAI,IAAI,MAAM,GAAG,GAAG,IAAI;AAC9C,SAAOA,OAAK,QAAQ,SAAS,MAAM,QAAQ,QAAQ;AACrD;AAcO,SAAS,qBAAqB,QAAwB;AAC3D,MAAI,CAAC,OAAQ,QAAO,KAAK,gBAAgB;AACzC,QAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE;AAChD,SAAO,GAAG,MAAM,OAAO,KAAK,CAAC,GAAG,gBAAgB;AAClD;AAgCO,SAAS,yBACd,SACA,aACA,mBACQ;AACR,QAAM,QAAkB,CAAC,0BAA0B;AAKnD,QAAM,wBAAkC,CAAC;AACzC,QAAM,iBAAiB,oBAAI,IAAY;AAGvC,QAAM,eAAyB,CAAC;AAChC,aAAW,UAAU,SAAS;AAC5B,UAAM,EAAE,YAAY,SAAS,IAAI;AACjC,QAAI,CAAC,UAAU;AAEb;AAAA,IACF;AAKA,UAAM,SAAS,OAAO,UAAU,oBAAoB,KAAK,UAAU;AAGnE,UAAM,EAAE,uBAAuB,OAAO,iBAAiB,IAAI;AAAA,MACzD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,EAAE,MAAM,YAAY,KAAK,OAAO;AACzC,UAAI,eAAe,IAAI,IAAI,EAAG;AAC9B,qBAAe,IAAI,IAAI;AACvB,4BAAsB,KAAK,WAAW;AAAA,IACxC;AAEA,iBAAa,KAAK,CAAC,MAAM,UAAU,IAAI,kBAAkB,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,EACzE;AAEA,MAAI,sBAAsB,SAAS,GAAG;AACpC,UAAM,KAAK,GAAG,qBAAqB;AACnC,UAAM,KAAK,EAAE;AAAA,EACf;AAKA,QAAM,gBAAgB,CAAC,GAAG,uBAAuB,GAAG,YAAY,EAAE,KAAK,IAAI;AAC3E,MAAI,qBAAqB,kBAAkB,aAAa,GAAG;AACzD,UAAM;AAAA,MACJ,sEAAsE,iBAAiB;AAAA,IACzF;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,KAAK,GAAG,YAAY;AAE1B,SAAO,MAAM,KAAK,IAAI,EAAE,QAAQ,QAAQ,IAAI;AAC9C;AAwBA,eAAsB,oBACpB,QACA,SACA,MACe;AACf,MAAI,OAAO,WAAW,EAAG;AAEzB,QAAM,EAAE,SAAS,gBAAgB,IAAI,0BAA0B,QAAQ,OAAO;AAG9E,QAAM,gBAAgB,oBAAI,IAAiC;AAC3D,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,cAAc,IAAI,OAAO,QAAQ;AAC5C,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AACR,oBAAc,IAAI,OAAO,UAAU,IAAI;AAAA,IACzC;AACA,SAAK,KAAK,MAAM;AAAA,EAClB;AAGA,QAAM,cAAwD,CAAC;AAC/D,aAAW,CAAC,UAAU,WAAW,KAAK,eAAe;AAEnD,UAAM,UAAUA,OAAK,SAAS,SAAS,QAAQ,EAAE,QAAQ,OAAO,GAAG;AACnE,UAAM,aAAa,oBAAoB,SAAS,MAAM,OAAO;AAG7D,QAAI,YAAY;AAChB,QAAI,UAAU,WAAW,MAAM,GAAG;AAChC,kBAAY,UAAU,MAAM,CAAC;AAAA,IAC/B;AACA,UAAM,SAAS,UAAU,YAAY,GAAG;AACxC,UAAM,YAAY,UAAU,IAAI,UAAU,MAAM,GAAG,MAAM,IAAI;AAC7D,UAAM,oBAAoB,qBAAqB,SAAS;AAExD,UAAM,WAAW,gBAAgB,IAAI,QAAQ;AAC7C,UAAM,SAAS;AAAA,MACb;AAAA,MACA,CAAC,SAAS,UAAU,QAAQ,IAAI,GAAG;AAAA,MACnC;AAAA,IACF;AACA,gBAAY,KAAK,EAAE,YAAY,OAAO,CAAC;AAAA,EACzC;AAGA,QAAM,gBAAgB,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI;AAChE,MAAI,kBAAkB,aAAa,GAAG;AACpC,UAAM,cAAcA,OAAK,QAAQ,SAAS,MAAM,gBAAgB;AAChE,UAAM,gBAAgB,aAAa,0BAA0B,CAAC;AAAA,EAChE;AAGA,QAAM,QAAQ;AAAA,IACZ,YAAY,IAAI,CAAC,EAAE,YAAY,OAAO,MAAM,gBAAgB,YAAY,MAAM,CAAC;AAAA,EACjF;AACF;AAKA,eAAe,gBAAgB,YAAoB,QAA+B;AAEhF,QAAM,gBAAgB,YAAY,MAAM;AAC1C;AA5QA;AAAA;AAAA;AACA;AAIA;AACA;AAAA;AAAA;;;ACNA,OAAOC,YAAU;AACjB,SAAS,kBAAkB;AAuFpB,SAAS,eACd,OACA,OAAe,QACS;AACxB,SAAO,MAAM,IAAI,CAAC,OAAO;AAAA,IACvB,MAAM,EAAE;AAAA,IACR,cAAc,EAAE;AAAA,IAChB,aAAa,EAAE;AAAA,IACf,eAAe,EAAE;AAAA,IACjB,UAAU,eAAe,EAAE,UAAU,IAAI;AAAA,EAC3C,EAAE;AACJ;AAQA,eAAsB,iBACpB,UACA,YACe;AACf,QAAM,UAAU;AAAA,uBACK,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA;AAEtD,QAAM,gBAAgB,YAAY,OAAO;AAC3C;AAQO,SAAS,aAAa,UAAkD;AAC7E,SAAO,SAAS,IAAI,CAAC,OAAO;AAAA,IAC1B,MAAM,EAAE;AAAA,IACR,cAAc,EAAE;AAAA,IAChB,aAAa,EAAE,eAAe;AAAA,IAC9B,eAAe,EAAE,iBAAiB;AAAA,IAClC,UAAU,EAAE;AAAA,EACd,EAAE;AACJ;AAkBA,SAAS,yBACP,OACA,SAIA;AAEA,QAAM,cAAc,oBAAI,IAA4B;AACpD,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,cAAe;AACzB,UAAM,UAAUA,OAAK,QAAQ,SAAS,KAAK,QAAQ;AACnD,QAAI,OAAO,YAAY,IAAI,OAAO;AAClC,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AACR,kBAAY,IAAI,SAAS,IAAI;AAAA,IAC/B;AACA,SAAK,KAAK,IAAI;AAAA,EAChB;AAGA,QAAM,gBAAgB,eAAe,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC;AAC5D,QAAM,kBAAkB,oBAAI,IAA8B;AAC1D,aAAW,YAAY,YAAY,KAAK,GAAG;AACzC,oBAAgB,IAAI,UAAU,uBAAuB,cAAc,IAAI,QAAQ,GAAI,QAAQ,CAAC;AAAA,EAC9F;AAGA,QAAM,UAA8B,CAAC;AACrC,aAAW,CAAC,UAAU,SAAS,KAAK,aAAa;AAC/C,UAAM,UAAU,cAAc,IAAI,QAAQ;AAC1C,eAAW,QAAQ,WAAW;AAE5B,YAAM,gBAAgB,KAAK;AAC3B,YAAM,WAAW,gBAAgB,SAAS,UAAU,aAAa;AACjE,cAAQ,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX;AAAA,QACA,YAAY;AAAA;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,gBAAgB;AACpC;AAuBO,SAAS,6BACd,SACA,aACA,mBACQ;AACR,QAAM,QAAkB,CAAC,0BAA0B;AAGnD,QAAM,eAAyB,CAAC;AAChC,aAAW,UAAU,SAAS;AAC5B,UAAM,EAAE,YAAY,SAAS,IAAI;AACjC,QAAI,CAAC,UAAU;AAEb;AAAA,IACF;AAGA,UAAM,SAAS;AAEf,UAAM,QAAQ,CAAC,MAAM,OAAO,IAAI,WAAM,UAAU,EAAE;AAGlD,UAAM,aAAa,wBAAwB,UAAU,aAAa,YAAY,MAAM,EAAE;AAAA,MACpF;AAAA,MACA;AAAA,IACF;AACA,UAAM,KAAK,UAAU;AACrB,UAAM,KAAK,EAAE;AACb,iBAAa,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,EACpC;AAGA,QAAM,gBAAgB,aAAa,KAAK,IAAI;AAC5C,MAAI,qBAAqB,kBAAkB,aAAa,GAAG;AACzD,UAAM;AAAA,MACJ,sEAAsE,iBAAiB;AAAA,IACzF;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,KAAK,GAAG,YAAY;AAE1B,SAAO,MAAM,KAAK,IAAI,EAAE,QAAQ,QAAQ,IAAI;AAC9C;AAaA,eAAe,qBAAqB,eAAuB,SAAgC;AACzF,MAAI,CAAC,kBAAkB,aAAa,EAAG;AACvC,QAAM,cAAcA,OAAK,QAAQ,SAAS,gBAAgB;AAC1D,MAAI,WAAW,WAAW,EAAG;AAC7B,QAAM,gBAAgB,aAAa,0BAA0B,CAAC;AAChE;AAsBA,eAAsB,sBACpB,OACA,SACA,MACA,SACyB;AAEzB,QAAM,WAA2B,CAAC;AAClC,QAAM,gBAAgB,eAAe,MAAM,IAAI,CAAC,MAAMA,OAAK,QAAQ,SAAS,EAAE,QAAQ,CAAC,CAAC;AACxF,aAAW,YAAY,OAAO;AAC5B,UAAM,UAAUA,OAAK,QAAQ,SAAS,SAAS,QAAQ;AACvD,UAAM,UAAU,cAAc,IAAI,OAAO;AACzC,UAAM,SAAS,oBAAoB,SAAS,SAAS,SAAS,cAAc;AAAA,MAC1E,MAAM,SAAS;AAAA,MACf,UAAU,SAAS;AAAA,IACrB,CAAC;AACD,QAAI,QAAQ;AACV,eAAS,KAAK,MAAM;AAAA,IACtB;AAAA,EACF;AAGA,QAAM,aAAa,eAAe,UAAU,IAAI;AAChD,QAAM,YAAYA,OAAK,QAAQ,SAAS,MAAM,UAAU;AACxD,QAAM,iBAAiB,YAAY,SAAS;AAG5C,MAAI,SAAS,YAAY;AACvB,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,SAAS,gBAAgB,IAAI,yBAAyB,UAAU,OAAO;AAC/E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAGA,QAAM,gBAAgB,oBAAI,IAAgC;AAC1D,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,cAAc,IAAI,OAAO,QAAQ;AAC5C,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AACR,oBAAc,IAAI,OAAO,UAAU,IAAI;AAAA,IACzC;AACA,SAAK,KAAK,MAAM;AAAA,EAClB;AAGA,QAAM,cAAwD,CAAC;AAC/D,aAAW,CAAC,UAAU,WAAW,KAAK,eAAe;AACnD,UAAM,UAAUA,OAAK,SAAS,SAAS,QAAQ,EAAE,QAAQ,OAAO,GAAG;AACnE,UAAM,aAAa,oBAAoB,SAAS,MAAM,OAAO;AAC7D,UAAM,WAAW,gBAAgB,IAAI,QAAQ;AAG7C,QAAI,YAAY;AAChB,QAAI,UAAU,WAAW,MAAM,GAAG;AAChC,kBAAY,UAAU,MAAM,CAAC;AAAA,IAC/B;AACA,UAAM,SAAS,UAAU,YAAY,GAAG;AACxC,UAAM,YAAY,UAAU,IAAI,UAAU,MAAM,GAAG,MAAM,IAAI;AAC7D,UAAM,oBAAoB,qBAAqB,SAAS;AAExD,UAAM,SAAS;AAAA,MACb;AAAA,MACA,CAAC,SAAS,UAAU,QAAQ,IAAI,GAAG;AAAA,MACnC;AAAA,IACF;AACA,gBAAY,KAAK,EAAE,YAAY,OAAO,CAAC;AAAA,EACzC;AAGA,QAAM,gBAAgB,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI;AAChE,QAAM,UAAUA,OAAK,QAAQ,SAAS,IAAI;AAC1C,QAAM,qBAAqB,eAAe,OAAO;AAGjD,QAAM,QAAQ;AAAA,IACZ,YAAY,IAAI,CAAC,EAAE,YAAY,OAAO,MAAM,oBAAoB,YAAY,MAAM,CAAC;AAAA,EACrF;AAEA,SAAO;AACT;AAKA,eAAe,oBAAoB,YAAoB,QAA+B;AACpF,QAAM,gBAAgB,YAAY,MAAM;AAC1C;AA1YA,IAiEM;AAjEN;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AAOA;AAMA;AA2CA,IAAM,aAAa;AAAA;AAAA;;;ACjEnB,OAAOC,SAAQ;AACf,OAAOC,YAAU;AACjB,OAAOC,UAAQ;AA0Cf,SAAS,yBAAyB,UAA0B;AAC1D,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAC9C,QAAM,QAAQ,WAAW,MAAM,uCAAuC;AACtE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,uBAAuB,QAAQ;AAAA,IACjC;AAAA,EACF;AACA,SAAO,MAAM,CAAC;AAChB;AAQA,SAAS,mBAAmB,QAAqC;AAC/D,SAAO;AAAA,IACL,QAAQ,cAAc,KAAK,MAAM;AAAA,EACnC;AACF;AAiBA,eAAsB,WAAW,SAAiB,UAAgD;AAChG,QAAM,QAAQ,MAAMF,IAAG,UAAU;AAAA,IAC/B,KAAK;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,SAA4B,CAAC;AAEnC,QAAM,OAAO,oBAAI,IAAoB;AAErC,aAAW,QAAQ,OAAO;AACxB,UAAM,iBAAiB,KAAK,QAAQ,OAAO,GAAG;AAC9C,UAAM,WAAW,eAAe,MAAM,GAAG,EAAE,IAAI;AAG/C,QAAI,aAAa,gBAAgB,aAAa,cAAc;AAC1D;AAAA,IACF;AAEA,UAAM,UAAUC,OAAK,QAAQ,SAAS,cAAc;AACpD,UAAM,SAAS,MAAMC,KAAG,SAAS,SAAS,SAAS,MAAM,EAAE,MAAM,MAAM,EAAE;AACzE,UAAM,EAAE,OAAO,IAAI,mBAAmB,MAAM;AAC5C,UAAM,OAAO,yBAAyB,cAAc;AAGpD,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,QAAI,UAAU;AACZ,YAAM,IAAI;AAAA,QACR,oBAAoB,IAAI,sBAAsB,QAAQ,QAAQ,cAAc;AAAA,MAC9E;AAAA,IACF;AACA,SAAK,IAAI,MAAM,cAAc;AAE7B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,UAAU;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AA5HA,IAca,wBAkBP;AAhCN;AAAA;AAAA;AAcO,IAAM,yBAAyB,CAAC,yBAAyB;AAkBhE,IAAM,gBAAgB;AAAA;AAAA;;;AChCtB,OAAOC,SAAQ;AAkHR,SAAS,qBACd,SACA,UACA,UACsB;AACtB,QAAM,aAAa,QAAQ,cAAc,QAAQ;AACjD,MAAI,CAAC,WAAY,QAAO;AAGxB,QAAM,cAAc,iBAAiB,UAAU;AAC/C,MAAI,aAA6B;AACjC,MAAI,gBAAmD;AAEvD,MAAI,aAAa;AACf,iBAAa,YAAY;AACzB,oBAAgB,YAAY;AAAA,EAC9B,WAAW,SAAS,QAAQ;AAE1B,UAAM,UAAU,cAAc,UAAU;AACxC,QAAI,SAAS;AACX,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,QAAM,QAAQ,aAAa,iBAAiB,UAAU,IAAI;AAC1D,QAAM,cAAc,mBAAmB,KAAK;AAC5C,QAAM,oBAAoB,qBAAqB,OAAO,OAAO;AAG7D,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI,eAAe;AACjB,UAAM,SAAS,oBAAoB,aAAa;AAChD,mBAAe,OAAO;AACtB,YAAQ,OAAO;AACf,aAAS,OAAO;AAChB,YAAQ,OAAO;AACf,eAAW,OAAO;AAAA,EACpB;AAEA,SAAO;AAAA,IACL,MAAM,qBAAqB,SAAS;AAAA,IACpC;AAAA,IACA,UAAU,SAAS;AAAA,IACnB,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAaA,SAAS,iBAAiB,YAA+C;AACvE,MAAI,SAA6B;AAEjC,EAAAA,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,QAAI,OAAQ;AAGZ,QAAIA,IAAG,oBAAoB,IAAI,KAAK,kBAAkB,IAAI,GAAG;AAC3D,iBAAW,QAAQ,KAAK,gBAAgB,cAAc;AACpD,YAAI,OAAQ;AACZ,cAAM,WAAWA,IAAG,aAAa,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO;AAC/D,YAAI,aAAa,YAAY,CAAC,KAAK,YAAa;AAGhD,YAAIA,IAAG,0BAA0B,KAAK,WAAW,GAAG;AAClD,mBAAS,EAAE,YAAY,MAAM,eAAe,KAAK,YAAY;AAAA,QAC/D,WAESA,IAAG,gBAAgB,KAAK,WAAW,GAAG;AAC7C,gBAAM,YAAY,uBAAuB,KAAK,WAAW;AACzD,mBAAS,EAAE,YAAY,MAAM,eAAe,UAAU;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAGA,QAAIA,IAAG,sBAAsB,IAAI,KAAK,kBAAkB,IAAI,KAAK,KAAK,MAAM,SAAS,UAAU;AAC7F,YAAM,YAAY,uBAAuB,IAAI;AAC7C,eAAS,EAAE,YAAY,MAAM,eAAe,UAAU;AAAA,IACxD;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAQA,SAAS,cAAc,YAA2C;AAChE,MAAI,SAAyB;AAE7B,EAAAA,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,QAAI,OAAQ;AAGZ,QAAIA,IAAG,sBAAsB,IAAI,KAAK,kBAAkB,IAAI,KAAK,KAAK,MAAM,SAAS,OAAO;AAC1F,eAAS;AACT;AAAA,IACF;AAGA,QAAIA,IAAG,oBAAoB,IAAI,KAAK,kBAAkB,IAAI,GAAG;AAC3D,iBAAW,QAAQ,KAAK,gBAAgB,cAAc;AACpD,YAAI,OAAQ;AACZ,cAAM,WAAWA,IAAG,aAAa,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO;AAC/D,YAAI,aAAa,SAAS,CAAC,KAAK,YAAa;AAC7C,YAAIA,IAAG,gBAAgB,KAAK,WAAW,KAAKA,IAAG,qBAAqB,KAAK,WAAW,GAAG;AACrF,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAWA,SAAS,uBACP,IACmC;AACnC,QAAM,OAAO,GAAG;AAChB,MAAI,CAAC,KAAM,QAAO;AAGlB,MAAIA,IAAG,0BAA0B,IAAI,GAAG;AACtC,WAAO;AAAA,EACT;AAGA,MAAIA,IAAG,QAAQ,IAAI,GAAG;AACpB,eAAW,QAAQ,KAAK,YAAY;AAClC,UACEA,IAAG,kBAAkB,IAAI,KACzB,KAAK,cACLA,IAAG,0BAA0B,KAAK,UAAU,GAC5C;AACA,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAcA,SAAS,oBAAoB,QAM3B;AACA,QAAM,SAMF,CAAC;AAEL,aAAW,QAAQ,OAAO,YAAY;AAEpC,QAAI,CAACA,IAAG,qBAAqB,IAAI,EAAG;AAEpC,UAAM,WAAW,gBAAgB,KAAK,IAAI;AAC1C,QAAI,CAAC,SAAU;AAEf,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,eAAO,eAAe,mBAAmB,KAAK,WAAW;AACzD;AAAA,MACF,KAAK;AACH,eAAO,QAAQ,wBAAwB,KAAK,WAAW;AACvD;AAAA,MACF,KAAK;AACH,eAAO,SAAS,wBAAwB,KAAK,WAAW;AACxD;AAAA,MACF,KAAK;AACH,eAAO,QAAQ,mBAAmB,KAAK,WAAW;AAClD;AAAA,MACF,KAAK;AACH,eAAO,WAAW,mBAAmB,KAAK,WAAW;AACrD;AAAA,IACJ;AAAA,EACF;AAEA,SAAO;AACT;AAWA,SAAS,gBAAgB,MAAsC;AAC7D,MAAIA,IAAG,aAAa,IAAI,EAAG,QAAO,KAAK;AACvC,MAAIA,IAAG,gBAAgB,IAAI,EAAG,QAAO,KAAK;AAC1C,SAAO;AACT;AASA,SAAS,mBAAmB,MAAyC;AACnE,MAAIA,IAAG,gBAAgB,IAAI,EAAG,QAAO,KAAK;AAC1C,SAAO;AACT;AAQA,SAAS,mBAAmB,MAAyC;AACnE,MAAIA,IAAG,iBAAiB,IAAI,GAAG;AAC7B,UAAM,MAAM,OAAO,KAAK,IAAI;AAC5B,WAAO,OAAO,MAAM,GAAG,IAAI,SAAY;AAAA,EACzC;AACA,SAAO;AACT;AAUA,SAAS,wBAAwB,MAA2C;AAC1E,MAAI,CAACA,IAAG,yBAAyB,IAAI,EAAG,QAAO;AAC/C,QAAM,SAAmB,CAAC;AAC1B,aAAW,WAAW,KAAK,UAAU;AACnC,QAAI,CAACA,IAAG,gBAAgB,OAAO,EAAG,QAAO;AACzC,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AACA,SAAO;AACT;AAhZA;AAAA;AAAA;AACA;AAAA;AAAA;;;ACDA,OAAOC,YAAU;AAwDV,SAAS,gBACd,QACA,OAAe,QACU;AACzB,SAAO,OAAO,IAAI,CAAC,OAAO;AAAA,IACxB,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,IACf,QAAQ,EAAE;AAAA,IACV,cAAc,EAAE;AAAA,IAChB,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,IACV,OAAO,EAAE;AAAA,IACT,UAAU,EAAE;AAAA,IACZ,UAAU,eAAe,EAAE,UAAU,IAAI;AAAA,EAC3C,EAAE;AACJ;AAQA,eAAsB,kBACpB,UACA,YACe;AACf,QAAM,UAAU;AAAA,wBACM,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA;AAEvD,QAAM,gBAAgB,YAAY,OAAO;AAC3C;AAQO,SAAS,cAAc,UAAoD;AAChF,SAAO,SAAS,IAAI,CAAC,OAAO;AAAA,IAC1B,MAAM,EAAE;AAAA,IACR,aAAa,EAAE,eAAe;AAAA,IAC9B,UAAU,EAAE;AAAA,IACZ,QAAQ,EAAE;AAAA,IACV,cAAc,EAAE,gBAAgB;AAAA,IAChC,OAAO,EAAE,SAAS;AAAA,IAClB,QAAQ,EAAE,UAAU;AAAA,IACpB,OAAO,EAAE,SAAS;AAAA,IAClB,UAAU,EAAE,YAAY;AAAA,EAC1B,EAAE;AACJ;AA0BA,eAAsB,uBACpB,QACA,SACA,MAC0B;AAE1B,QAAM,WAA4B,CAAC;AACnC,QAAM,gBAAgB,eAAe,OAAO,IAAI,CAAC,MAAMA,OAAK,QAAQ,SAAS,EAAE,QAAQ,CAAC,CAAC;AACzF,aAAW,YAAY,QAAQ;AAC7B,UAAM,UAAUA,OAAK,QAAQ,SAAS,SAAS,QAAQ;AACvD,UAAM,UAAU,cAAc,IAAI,OAAO;AACzC,UAAM,SAAS,qBAAqB,SAAS,SAAS;AAAA,MACpD,MAAM,SAAS;AAAA,MACf,UAAU,SAAS;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB,CAAC;AACD,QAAI,QAAQ;AACV,eAAS,KAAK,MAAM;AAAA,IACtB;AAAA,EACF;AAGA,QAAM,aAAa,gBAAgB,UAAU,IAAI;AACjD,QAAM,aAAaA,OAAK,QAAQ,SAAS,MAAM,WAAW;AAC1D,QAAM,kBAAkB,YAAY,UAAU;AAE9C,SAAO;AACT;AAhKA,IA4CM;AA5CN;AAAA;AAAA;AAGA;AACA;AACA;AACA;AAsCA,IAAM,cAAc;AAAA;AAAA;;;AC5CpB,OAAOC,YAAU;AACjB,OAAOC,UAAQ;AA2Bf,eAAsB,WACpB,SACA,MACsC;AACtC,QAAM,oBAAoBD,OAAK,QAAQ,SAAS,MAAM,mBAAmB;AAEzE,MAAIC,KAAG,WAAW,iBAAiB,GAAG;AACpC,UAAM,SAAU,MAAM,oBAAoB,iBAAiB;AAG3D,WAAO,OAAO,WAAW,CAAC;AAAA,EAC5B;AAGA,QAAM,kBACJA,KAAG,WAAWD,OAAK,KAAK,SAAS,iBAAiB,CAAC,KACnDC,KAAG,WAAWD,OAAK,KAAK,SAAS,iBAAiB,CAAC;AAErD,MAAI,iBAAiB;AACnB,UAAM,IAAI;AAAA,MACR,WAAW,IAAI,IAAI,mBAAmB;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AArDA,IAQM;AARN;AAAA;AAAA;AAEA;AAMA,IAAM,sBAAsB;AAAA;AAAA;;;ACR5B,OAAOE,UAAQ;AACf,OAAOC,YAAU;AAOjB,SAAS,aAAqB;AAC5B,SAAO,QAAQ,IAAI,YAAY;AACjC;AAUA,SAAS,YAAY,KAAuB;AAC1C,SAAO,CAAC,QAAQ,cAAc,QAAQ,GAAG,IAAI,QAAQ,GAAG,QAAQ;AAClE;AAiBA,SAAS,aAAa,SAAiB,UAA0D;AAC/F,QAAM,SAAiC,CAAC;AAExC,QAAM,QAAQ,QAAQ,QAAQ,SAAS,IAAI,EAAE,MAAM,IAAI;AAEvD,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAK;AAE1B,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AAIzC,UAAM,QAAQ,sDAAsD,KAAK,OAAO;AAChF,QAAI,CAAC,MAAO;AAGZ,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,WAAW,MAAM,CAAC;AACxB,UAAM,QAAQ,WAAW,UAAU,EAAE,GAAG,UAAU,GAAG,OAAO,CAAC;AAC7D,WAAO,GAAG,IAAI;AAAA,EAChB;AAEA,SAAO;AACT;AASA,SAAS,WAAW,KAAa,KAAqC;AACpE,MAAI,QAAQ,GAAI,QAAO;AAGvB,MAAI,IAAI,CAAC,MAAM,KAAK;AAClB,UAAM,MAAM,IAAI,QAAQ,KAAK,CAAC;AAC9B,WAAO,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,GAAG,GAAG;AAAA,EACrD;AAGA,MAAI,IAAI,CAAC,MAAM,KAAK;AAClB,UAAM,QAAQ,uBAAuB,KAAK,GAAG;AAE7C,UAAM,QAAQ,QAAQ,MAAM,CAAC,IAAK,IAAI,MAAM,CAAC;AAC7C,WAAO,qBAAqB,OAAO,GAAG;AAAA,EACxC;AAIA,QAAM,eAAe,kBAAkB,KAAK,GAAG;AAC/C,QAAM,QAAQ,eAAe,aAAa,CAAC,IAAK;AAChD,SAAO,MAAM,KAAK;AACpB;AAQA,SAAS,qBAAqB,KAAa,KAAqC;AAE9E,QAAM,UAAU,IAAI,QAAQ,UAAU,CAAC,GAAG,OAAe;AACvD,YAAQ,IAAI;AAAA,MACV,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF,CAAC;AAGD,SAAO,QAAQ;AAAA,IACb;AAAA,IACA,CAAC,GAAG,QAAQ,UAAU;AACpB,YAAM,UAAU,UAAU;AAC1B,aAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK;AAAA,IACjD;AAAA,EACF;AACF;AAoBO,SAAS,QAAQ,SAAuB;AAC7C,QAAM,MAAM,WAAW;AACvB,QAAM,QAAQ,YAAY,GAAG;AAG7B,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAWA,OAAK,KAAK,SAAS,IAAI;AACxC,QAAI,CAACD,KAAG,WAAW,QAAQ,EAAG;AAC9B,UAAM,UAAUA,KAAG,aAAa,UAAU,OAAO;AACjD,UAAM,SAAS,aAAa,SAAS,MAAM;AAC3C,WAAO,OAAO,QAAQ,MAAM;AAAA,EAC9B;AAGA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,QAAQ,IAAI,GAAG,MAAM,QAAW;AAClC,cAAQ,IAAI,GAAG,IAAI;AAAA,IACrB;AAAA,EACF;AACF;AAxKA;AAAA;AAAA;AAAA;AAAA;;;ACAA,SAAS,MAAM,OAAO,SAAS,gBAAgB;AAC/C,SAAS,gBAAgB;AACzB,SAAS,WAAW,UAAU,YAAY,WAAW,QAAQ,OAAO,OAAO,YAAY;AA6NhF,SAAS,SAAS,MAAM,UAAU,CAAC,GAAG;AAEzC,MAAI,OAAO,QAAQ,aAAa,QAAQ;AACxC,MAAI,SAAS;AACT,WAAO,WAAW;AACtB,MAAI;AACA,YAAQ,OAAO;AACnB,MAAI,CAAC,MAAM;AACP,UAAM,IAAI,MAAM,qEAAqE;AAAA,EACzF,WACS,OAAO,SAAS,UAAU;AAC/B,UAAM,IAAI,UAAU,0EAA0E;AAAA,EAClG,WACS,QAAQ,CAAC,UAAU,SAAS,IAAI,GAAG;AACxC,UAAM,IAAI,MAAM,6CAA6C,UAAU,KAAK,IAAI,CAAC,EAAE;AAAA,EACvF;AACA,UAAQ,OAAO;AACf,SAAO,IAAI,eAAe,OAAO;AACrC;AAjPA,IAGa,YAMP,gBAWA,sBACA,oBACA,WAMA,WAKA,YAKA,mBACA,mBACA,SACA,iBAgBO;AAzDb;AAAA;AAAA;AAGO,IAAM,aAAa;AAAA,MACtB,WAAW;AAAA,MACX,UAAU;AAAA,MACV,eAAe;AAAA,MACf,iBAAiB;AAAA,IACrB;AACA,IAAM,iBAAiB;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,CAAC,eAAe;AAAA,MAC5B,iBAAiB,CAAC,eAAe;AAAA,MACjC,MAAM,WAAW;AAAA,MACjB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,IACnB;AACA,WAAO,OAAO,cAAc;AAC5B,IAAM,uBAAuB;AAC7B,IAAM,qBAAqB,oBAAI,IAAI,CAAC,UAAU,SAAS,UAAU,SAAS,oBAAoB,CAAC;AAC/F,IAAM,YAAY;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACf;AACA,IAAM,YAAY,oBAAI,IAAI;AAAA,MACtB,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACf,CAAC;AACD,IAAM,aAAa,oBAAI,IAAI;AAAA,MACvB,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACf,CAAC;AACD,IAAM,oBAAoB,CAAC,UAAU,mBAAmB,IAAI,MAAM,IAAI;AACtE,IAAM,oBAAoB,QAAQ,aAAa;AAC/C,IAAM,UAAU,CAAC,eAAe;AAChC,IAAM,kBAAkB,CAAC,WAAW;AAChC,UAAI,WAAW;AACX,eAAO;AACX,UAAI,OAAO,WAAW;AAClB,eAAO;AACX,UAAI,OAAO,WAAW,UAAU;AAC5B,cAAM,KAAK,OAAO,KAAK;AACvB,eAAO,CAAC,UAAU,MAAM,aAAa;AAAA,MACzC;AACA,UAAI,MAAM,QAAQ,MAAM,GAAG;AACvB,cAAM,UAAU,OAAO,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AAChD,eAAO,CAAC,UAAU,QAAQ,KAAK,CAAC,MAAM,MAAM,aAAa,CAAC;AAAA,MAC9D;AACA,aAAO;AAAA,IACX;AAEO,IAAM,iBAAN,cAA6B,SAAS;AAAA,MACzC,YAAY,UAAU,CAAC,GAAG;AACtB,cAAM;AAAA,UACF,YAAY;AAAA,UACZ,aAAa;AAAA,UACb,eAAe,QAAQ;AAAA,QAC3B,CAAC;AACD,cAAM,OAAO,EAAE,GAAG,gBAAgB,GAAG,QAAQ;AAC7C,cAAM,EAAE,MAAM,KAAK,IAAI;AACvB,aAAK,cAAc,gBAAgB,KAAK,UAAU;AAClD,aAAK,mBAAmB,gBAAgB,KAAK,eAAe;AAC5D,cAAM,aAAa,KAAK,QAAQ,QAAQ;AAExC,YAAI,mBAAmB;AACnB,eAAK,QAAQ,CAACE,WAAS,WAAWA,QAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,QAC5D,OACK;AACD,eAAK,QAAQ;AAAA,QACjB;AACA,aAAK,YAAY,KAAK,SAAS,eAAe;AAC9C,aAAK,YAAY,OAAO,UAAU,IAAI,IAAI,IAAI;AAC9C,aAAK,aAAa,OAAO,WAAW,IAAI,IAAI,IAAI;AAChD,aAAK,mBAAmB,SAAS,WAAW;AAC5C,aAAK,QAAQ,SAAS,IAAI;AAC1B,aAAK,YAAY,CAAC,KAAK;AACvB,aAAK,aAAa,KAAK,YAAY,WAAW;AAC9C,aAAK,aAAa,EAAE,UAAU,QAAQ,eAAe,KAAK,UAAU;AAEpE,aAAK,UAAU,CAAC,KAAK,YAAY,MAAM,CAAC,CAAC;AACzC,aAAK,UAAU;AACf,aAAK,SAAS;AAAA,MAClB;AAAA,MACA,MAAM,MAAM,OAAO;AACf,YAAI,KAAK;AACL;AACJ,aAAK,UAAU;AACf,YAAI;AACA,iBAAO,CAAC,KAAK,aAAa,QAAQ,GAAG;AACjC,kBAAM,MAAM,KAAK;AACjB,kBAAM,MAAM,OAAO,IAAI;AACvB,gBAAI,OAAO,IAAI,SAAS,GAAG;AACvB,oBAAM,EAAE,MAAAA,QAAM,MAAM,IAAI;AACxB,oBAAM,QAAQ,IAAI,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,WAAW,KAAK,aAAa,QAAQA,MAAI,CAAC;AAClF,oBAAM,UAAU,MAAM,QAAQ,IAAI,KAAK;AACvC,yBAAW,SAAS,SAAS;AACzB,oBAAI,CAAC;AACD;AACJ,oBAAI,KAAK;AACL;AACJ,sBAAM,YAAY,MAAM,KAAK,cAAc,KAAK;AAChD,oBAAI,cAAc,eAAe,KAAK,iBAAiB,KAAK,GAAG;AAC3D,sBAAI,SAAS,KAAK,WAAW;AACzB,yBAAK,QAAQ,KAAK,KAAK,YAAY,MAAM,UAAU,QAAQ,CAAC,CAAC;AAAA,kBACjE;AACA,sBAAI,KAAK,WAAW;AAChB,yBAAK,KAAK,KAAK;AACf;AAAA,kBACJ;AAAA,gBACJ,YACU,cAAc,UAAU,KAAK,eAAe,KAAK,MACvD,KAAK,YAAY,KAAK,GAAG;AACzB,sBAAI,KAAK,YAAY;AACjB,yBAAK,KAAK,KAAK;AACf;AAAA,kBACJ;AAAA,gBACJ;AAAA,cACJ;AAAA,YACJ,OACK;AACD,oBAAM,SAAS,KAAK,QAAQ,IAAI;AAChC,kBAAI,CAAC,QAAQ;AACT,qBAAK,KAAK,IAAI;AACd;AAAA,cACJ;AACA,mBAAK,SAAS,MAAM;AACpB,kBAAI,KAAK;AACL;AAAA,YACR;AAAA,UACJ;AAAA,QACJ,SACO,OAAO;AACV,eAAK,QAAQ,KAAK;AAAA,QACtB,UACA;AACI,eAAK,UAAU;AAAA,QACnB;AAAA,MACJ;AAAA,MACA,MAAM,YAAYA,QAAM,OAAO;AAC3B,YAAI;AACJ,YAAI;AACA,kBAAQ,MAAM,QAAQA,QAAM,KAAK,UAAU;AAAA,QAC/C,SACO,OAAO;AACV,eAAK,SAAS,KAAK;AAAA,QACvB;AACA,eAAO,EAAE,OAAO,OAAO,MAAAA,OAAK;AAAA,MAChC;AAAA,MACA,MAAM,aAAa,QAAQA,QAAM;AAC7B,YAAI;AACJ,cAAMC,YAAW,KAAK,YAAY,OAAO,OAAO;AAChD,YAAI;AACA,gBAAM,WAAW,SAAS,MAAMD,QAAMC,SAAQ,CAAC;AAC/C,kBAAQ,EAAE,MAAM,UAAU,KAAK,OAAO,QAAQ,GAAG,UAAU,UAAAA,UAAS;AACpE,gBAAM,KAAK,UAAU,IAAI,KAAK,YAAY,SAAS,MAAM,KAAK,MAAM,QAAQ;AAAA,QAChF,SACO,KAAK;AACR,eAAK,SAAS,GAAG;AACjB;AAAA,QACJ;AACA,eAAO;AAAA,MACX;AAAA,MACA,SAAS,KAAK;AACV,YAAI,kBAAkB,GAAG,KAAK,CAAC,KAAK,WAAW;AAC3C,eAAK,KAAK,QAAQ,GAAG;AAAA,QACzB,OACK;AACD,eAAK,QAAQ,GAAG;AAAA,QACpB;AAAA,MACJ;AAAA,MACA,MAAM,cAAc,OAAO;AAGvB,YAAI,CAAC,SAAS,KAAK,cAAc,OAAO;AACpC,iBAAO;AAAA,QACX;AACA,cAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,YAAI,MAAM,OAAO;AACb,iBAAO;AACX,YAAI,MAAM,YAAY;AAClB,iBAAO;AACX,YAAI,SAAS,MAAM,eAAe,GAAG;AACjC,gBAAM,OAAO,MAAM;AACnB,cAAI;AACA,kBAAM,gBAAgB,MAAM,SAAS,IAAI;AACzC,kBAAM,qBAAqB,MAAM,MAAM,aAAa;AACpD,gBAAI,mBAAmB,OAAO,GAAG;AAC7B,qBAAO;AAAA,YACX;AACA,gBAAI,mBAAmB,YAAY,GAAG;AAClC,oBAAM,MAAM,cAAc;AAC1B,kBAAI,KAAK,WAAW,aAAa,KAAK,KAAK,OAAO,KAAK,CAAC,MAAM,MAAM;AAChE,sBAAM,iBAAiB,IAAI,MAAM,+BAA+B,IAAI,gBAAgB,aAAa,GAAG;AAEpG,+BAAe,OAAO;AACtB,uBAAO,KAAK,SAAS,cAAc;AAAA,cACvC;AACA,qBAAO;AAAA,YACX;AAAA,UACJ,SACO,OAAO;AACV,iBAAK,SAAS,KAAK;AACnB,mBAAO;AAAA,UACX;AAAA,QACJ;AAAA,MACJ;AAAA,MACA,eAAe,OAAO;AAClB,cAAM,QAAQ,SAAS,MAAM,KAAK,UAAU;AAC5C,eAAO,SAAS,KAAK,oBAAoB,CAAC,MAAM,YAAY;AAAA,MAChE;AAAA,IACJ;AAAA;AAAA;;;ACxNA,SAAS,WAAW,aAAa,SAAS,gBAAgB;AAC1D,SAAS,MAAM,QAAAC,OAAM,SAAAC,QAAO,YAAY,kBAAkB;AAC1D,YAAY,aAAa;AACzB,SAAS,QAAQ,cAAc;AA+G/B,SAAS,sBAAsBC,QAAM,SAAS,UAAU,YAAY,SAAS;AACzE,QAAM,cAAc,CAAC,UAAU,WAAW;AACtC,aAASA,MAAI;AACb,YAAQ,UAAU,QAAQ,EAAE,aAAaA,OAAK,CAAC;AAG/C,QAAI,UAAUA,WAAS,QAAQ;AAC3B,uBAAyB,gBAAQA,QAAM,MAAM,GAAG,eAAuB,aAAKA,QAAM,MAAM,CAAC;AAAA,IAC7F;AAAA,EACJ;AACA,MAAI;AACA,WAAO,SAASA,QAAM;AAAA,MAClB,YAAY,QAAQ;AAAA,IACxB,GAAG,WAAW;AAAA,EAClB,SACO,OAAO;AACV,eAAW,KAAK;AAChB,WAAO;AAAA,EACX;AACJ;AArIA,IAIa,UACA,SACA,WACA,UAEP,IACO,WACA,SACA,SACA,WACA,QACA,QAWP,IACA,qBACA,aACA,eACA,SACA,SACA,cAEA,kBAkCA,cAEA,SAQA,eAOA,WASA,YASA,YACA,kBAkCA,kBAgBA,oBAsEA,sBAUA,wBAyDO;AAnSb;AAAA;AAAA;AAIO,IAAM,WAAW;AACjB,IAAM,UAAU;AAChB,IAAM,YAAY;AAClB,IAAM,WAAW,MAAM;AAAA,IAAE;AAEhC,IAAM,KAAK,QAAQ;AACZ,IAAM,YAAY,OAAO;AACzB,IAAM,UAAU,OAAO;AACvB,IAAM,UAAU,OAAO;AACvB,IAAM,YAAY,OAAO;AACzB,IAAM,SAAS,OAAO,MAAM;AAC5B,IAAM,SAAS;AAAA,MAClB,KAAK;AAAA,MACL,OAAO;AAAA,MACP,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,KAAK;AAAA,MACL,OAAO;AAAA,IACX;AACA,IAAM,KAAK;AACX,IAAM,sBAAsB;AAC5B,IAAM,cAAc,EAAE,OAAAD,QAAO,MAAAD,MAAK;AAClC,IAAM,gBAAgB;AACtB,IAAM,UAAU;AAChB,IAAM,UAAU;AAChB,IAAM,eAAe,CAAC,eAAe,SAAS,OAAO;AAErD,IAAM,mBAAmB,oBAAI,IAAI;AAAA,MAC7B;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAM;AAAA,MAAK;AAAA,MAAO;AAAA,MAAO;AAAA,MAAY;AAAA,MAAW;AAAA,MAAS;AAAA,MACrF;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAY;AAAA,MAAM;AAAA,MAAO;AAAA,MAAO;AAAA,MAAM;AAAA,MAC1E;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAM;AAAA,MAAO;AAAA,MAAM;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MACxD;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAS;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACvF;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAY;AAAA,MAAO;AAAA,MACrF;AAAA,MAAS;AAAA,MAAO;AAAA,MAAO;AAAA,MACvB;AAAA,MAAa;AAAA,MAAa;AAAA,MAAa;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MACpE;AAAA,MAAO;AAAA,MAAO;AAAA,MAAM;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAW;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAC1E;AAAA,MAAM;AAAA,MAAM;AAAA,MAAO;AAAA,MAAW;AAAA,MAAM;AAAA,MACpC;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAC5D;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACnD;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAM;AAAA,MAAO;AAAA,MAAQ;AAAA,MAC1C;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACrF;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAS;AAAA,MACxB;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MACtC;AAAA,MAAO;AAAA,MAAO;AAAA,MAAW;AAAA,MACzB;AAAA,MAAK;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACtD;AAAA,MAAS;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAC/E;AAAA,MAAQ;AAAA,MAAO;AAAA,MACf;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACjF;AAAA,MACA;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAa;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MACpF;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAU;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACnF;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACrB;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAChF;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAC1C;AAAA,MAAO;AAAA,MACP;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAM;AAAA,MAChF;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAS;AAAA,MAAO;AAAA,MACtC;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAQ;AAAA,MACnF;AAAA,MAAS;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAC9B;AAAA,MAAK;AAAA,MAAO;AAAA,IAChB,CAAC;AACD,IAAM,eAAe,CAAC,aAAa,iBAAiB,IAAY,gBAAQ,QAAQ,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC;AAExG,IAAM,UAAU,CAAC,KAAK,OAAO;AACzB,UAAI,eAAe,KAAK;AACpB,YAAI,QAAQ,EAAE;AAAA,MAClB,OACK;AACD,WAAG,GAAG;AAAA,MACV;AAAA,IACJ;AACA,IAAM,gBAAgB,CAAC,MAAM,MAAM,SAAS;AACxC,UAAI,YAAY,KAAK,IAAI;AACzB,UAAI,EAAE,qBAAqB,MAAM;AAC7B,aAAK,IAAI,IAAI,YAAY,oBAAI,IAAI,CAAC,SAAS,CAAC;AAAA,MAChD;AACA,gBAAU,IAAI,IAAI;AAAA,IACtB;AACA,IAAM,YAAY,CAAC,SAAS,CAAC,QAAQ;AACjC,YAAM,MAAM,KAAK,GAAG;AACpB,UAAI,eAAe,KAAK;AACpB,YAAI,MAAM;AAAA,MACd,OACK;AACD,eAAO,KAAK,GAAG;AAAA,MACnB;AAAA,IACJ;AACA,IAAM,aAAa,CAAC,MAAM,MAAM,SAAS;AACrC,YAAM,YAAY,KAAK,IAAI;AAC3B,UAAI,qBAAqB,KAAK;AAC1B,kBAAU,OAAO,IAAI;AAAA,MACzB,WACS,cAAc,MAAM;AACzB,eAAO,KAAK,IAAI;AAAA,MACpB;AAAA,IACJ;AACA,IAAM,aAAa,CAAC,QAAS,eAAe,MAAM,IAAI,SAAS,IAAI,CAAC;AACpE,IAAM,mBAAmB,oBAAI,IAAI;AAkCjC,IAAM,mBAAmB,CAAC,UAAU,cAAc,MAAM,MAAM,SAAS;AACnE,YAAM,OAAO,iBAAiB,IAAI,QAAQ;AAC1C,UAAI,CAAC;AACD;AACJ,cAAQ,KAAK,YAAY,GAAG,CAAC,aAAa;AACtC,iBAAS,MAAM,MAAM,IAAI;AAAA,MAC7B,CAAC;AAAA,IACL;AASA,IAAM,qBAAqB,CAACE,QAAM,UAAU,SAAS,aAAa;AAC9D,YAAM,EAAE,UAAU,YAAY,WAAW,IAAI;AAC7C,UAAI,OAAO,iBAAiB,IAAI,QAAQ;AACxC,UAAI;AACJ,UAAI,CAAC,QAAQ,YAAY;AACrB,kBAAU,sBAAsBA,QAAM,SAAS,UAAU,YAAY,UAAU;AAC/E,YAAI,CAAC;AACD;AACJ,eAAO,QAAQ,MAAM,KAAK,OAAO;AAAA,MACrC;AACA,UAAI,MAAM;AACN,sBAAc,MAAM,eAAe,QAAQ;AAC3C,sBAAc,MAAM,SAAS,UAAU;AACvC,sBAAc,MAAM,SAAS,UAAU;AAAA,MAC3C,OACK;AACD,kBAAU;AAAA,UAAsBA;AAAA,UAAM;AAAA,UAAS,iBAAiB,KAAK,MAAM,UAAU,aAAa;AAAA,UAAG;AAAA;AAAA,UACrG,iBAAiB,KAAK,MAAM,UAAU,OAAO;AAAA,QAAC;AAC9C,YAAI,CAAC;AACD;AACJ,gBAAQ,GAAG,GAAG,OAAO,OAAO,UAAU;AAClC,gBAAM,eAAe,iBAAiB,KAAK,MAAM,UAAU,OAAO;AAClE,cAAI;AACA,iBAAK,kBAAkB;AAE3B,cAAI,aAAa,MAAM,SAAS,SAAS;AACrC,gBAAI;AACA,oBAAM,KAAK,MAAM,KAAKA,QAAM,GAAG;AAC/B,oBAAM,GAAG,MAAM;AACf,2BAAa,KAAK;AAAA,YACtB,SACO,KAAK;AAAA,YAEZ;AAAA,UACJ,OACK;AACD,yBAAa,KAAK;AAAA,UACtB;AAAA,QACJ,CAAC;AACD,eAAO;AAAA,UACH,WAAW;AAAA,UACX,aAAa;AAAA,UACb,aAAa;AAAA,UACb;AAAA,QACJ;AACA,yBAAiB,IAAI,UAAU,IAAI;AAAA,MACvC;AAIA,aAAO,MAAM;AACT,mBAAW,MAAM,eAAe,QAAQ;AACxC,mBAAW,MAAM,SAAS,UAAU;AACpC,mBAAW,MAAM,SAAS,UAAU;AACpC,YAAI,WAAW,KAAK,SAAS,GAAG;AAG5B,eAAK,QAAQ,MAAM;AAEnB,2BAAiB,OAAO,QAAQ;AAChC,uBAAa,QAAQ,UAAU,IAAI,CAAC;AAEpC,eAAK,UAAU;AACf,iBAAO,OAAO,IAAI;AAAA,QACtB;AAAA,MACJ;AAAA,IACJ;AAIA,IAAM,uBAAuB,oBAAI,IAAI;AAUrC,IAAM,yBAAyB,CAACA,QAAM,UAAU,SAAS,aAAa;AAClE,YAAM,EAAE,UAAU,WAAW,IAAI;AACjC,UAAI,OAAO,qBAAqB,IAAI,QAAQ;AAG5C,YAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAI,UAAU,MAAM,aAAa,QAAQ,cAAc,MAAM,WAAW,QAAQ,WAAW;AAOvF,oBAAY,QAAQ;AACpB,eAAO;AAAA,MACX;AACA,UAAI,MAAM;AACN,sBAAc,MAAM,eAAe,QAAQ;AAC3C,sBAAc,MAAM,SAAS,UAAU;AAAA,MAC3C,OACK;AAID,eAAO;AAAA,UACH,WAAW;AAAA,UACX,aAAa;AAAA,UACb;AAAA,UACA,SAAS,UAAU,UAAU,SAAS,CAAC,MAAM,SAAS;AAClD,oBAAQ,KAAK,aAAa,CAACC,gBAAe;AACtC,cAAAA,YAAW,GAAG,QAAQ,UAAU,EAAE,MAAM,KAAK,CAAC;AAAA,YAClD,CAAC;AACD,kBAAM,YAAY,KAAK;AACvB,gBAAI,KAAK,SAAS,KAAK,QAAQ,YAAY,KAAK,WAAW,cAAc,GAAG;AACxE,sBAAQ,KAAK,WAAW,CAACC,cAAaA,UAASF,QAAM,IAAI,CAAC;AAAA,YAC9D;AAAA,UACJ,CAAC;AAAA,QACL;AACA,6BAAqB,IAAI,UAAU,IAAI;AAAA,MAC3C;AAIA,aAAO,MAAM;AACT,mBAAW,MAAM,eAAe,QAAQ;AACxC,mBAAW,MAAM,SAAS,UAAU;AACpC,YAAI,WAAW,KAAK,SAAS,GAAG;AAC5B,+BAAqB,OAAO,QAAQ;AACpC,sBAAY,QAAQ;AACpB,eAAK,UAAU,KAAK,UAAU;AAC9B,iBAAO,OAAO,IAAI;AAAA,QACtB;AAAA,MACJ;AAAA,IACJ;AAIO,IAAM,gBAAN,MAAoB;AAAA,MACvB,YAAY,KAAK;AACb,aAAK,MAAM;AACX,aAAK,oBAAoB,CAAC,UAAU,IAAI,aAAa,KAAK;AAAA,MAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,iBAAiBA,QAAM,UAAU;AAC7B,cAAM,OAAO,KAAK,IAAI;AACtB,cAAM,YAAoB,gBAAQA,MAAI;AACtC,cAAMG,YAAmB,iBAASH,MAAI;AACtC,cAAM,SAAS,KAAK,IAAI,eAAe,SAAS;AAChD,eAAO,IAAIG,SAAQ;AACnB,cAAM,eAAuB,gBAAQH,MAAI;AACzC,cAAM,UAAU;AAAA,UACZ,YAAY,KAAK;AAAA,QACrB;AACA,YAAI,CAAC;AACD,qBAAW;AACf,YAAI;AACJ,YAAI,KAAK,YAAY;AACjB,gBAAM,YAAY,KAAK,aAAa,KAAK;AACzC,kBAAQ,WAAW,aAAa,aAAaG,SAAQ,IAAI,KAAK,iBAAiB,KAAK;AACpF,mBAAS,uBAAuBH,QAAM,cAAc,SAAS;AAAA,YACzD;AAAA,YACA,YAAY,KAAK,IAAI;AAAA,UACzB,CAAC;AAAA,QACL,OACK;AACD,mBAAS,mBAAmBA,QAAM,cAAc,SAAS;AAAA,YACrD;AAAA,YACA,YAAY,KAAK;AAAA,YACjB,YAAY,KAAK,IAAI;AAAA,UACzB,CAAC;AAAA,QACL;AACA,eAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,YAAY,MAAM,OAAO,YAAY;AACjC,YAAI,KAAK,IAAI,QAAQ;AACjB;AAAA,QACJ;AACA,cAAMI,WAAkB,gBAAQ,IAAI;AACpC,cAAMD,YAAmB,iBAAS,IAAI;AACtC,cAAM,SAAS,KAAK,IAAI,eAAeC,QAAO;AAE9C,YAAI,YAAY;AAEhB,YAAI,OAAO,IAAID,SAAQ;AACnB;AACJ,cAAM,WAAW,OAAOH,QAAM,aAAa;AACvC,cAAI,CAAC,KAAK,IAAI,UAAU,qBAAqB,MAAM,CAAC;AAChD;AACJ,cAAI,CAAC,YAAY,SAAS,YAAY,GAAG;AACrC,gBAAI;AACA,oBAAMK,YAAW,MAAMP,MAAK,IAAI;AAChC,kBAAI,KAAK,IAAI;AACT;AAEJ,oBAAM,KAAKO,UAAS;AACpB,oBAAM,KAAKA,UAAS;AACpB,kBAAI,CAAC,MAAM,MAAM,MAAM,OAAO,UAAU,SAAS;AAC7C,qBAAK,IAAI,MAAM,GAAG,QAAQ,MAAMA,SAAQ;AAAA,cAC5C;AACA,mBAAK,WAAW,WAAW,cAAc,UAAU,QAAQA,UAAS,KAAK;AACrE,qBAAK,IAAI,WAAWL,MAAI;AACxB,4BAAYK;AACZ,sBAAMC,UAAS,KAAK,iBAAiB,MAAM,QAAQ;AACnD,oBAAIA;AACA,uBAAK,IAAI,eAAeN,QAAMM,OAAM;AAAA,cAC5C,OACK;AACD,4BAAYD;AAAA,cAChB;AAAA,YACJ,SACO,OAAO;AAEV,mBAAK,IAAI,QAAQD,UAASD,SAAQ;AAAA,YACtC;AAAA,UAEJ,WACS,OAAO,IAAIA,SAAQ,GAAG;AAE3B,kBAAM,KAAK,SAAS;AACpB,kBAAM,KAAK,SAAS;AACpB,gBAAI,CAAC,MAAM,MAAM,MAAM,OAAO,UAAU,SAAS;AAC7C,mBAAK,IAAI,MAAM,GAAG,QAAQ,MAAM,QAAQ;AAAA,YAC5C;AACA,wBAAY;AAAA,UAChB;AAAA,QACJ;AAEA,cAAM,SAAS,KAAK,iBAAiB,MAAM,QAAQ;AAEnD,YAAI,EAAE,cAAc,KAAK,IAAI,QAAQ,kBAAkB,KAAK,IAAI,aAAa,IAAI,GAAG;AAChF,cAAI,CAAC,KAAK,IAAI,UAAU,GAAG,KAAK,MAAM,CAAC;AACnC;AACJ,eAAK,IAAI,MAAM,GAAG,KAAK,MAAM,KAAK;AAAA,QACtC;AACA,eAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,MAAM,eAAe,OAAO,WAAWH,QAAM,MAAM;AAC/C,YAAI,KAAK,IAAI,QAAQ;AACjB;AAAA,QACJ;AACA,cAAM,OAAO,MAAM;AACnB,cAAM,MAAM,KAAK,IAAI,eAAe,SAAS;AAC7C,YAAI,CAAC,KAAK,IAAI,QAAQ,gBAAgB;AAElC,eAAK,IAAI,gBAAgB;AACzB,cAAI;AACJ,cAAI;AACA,uBAAW,MAAM,WAAWA,MAAI;AAAA,UACpC,SACO,GAAG;AACN,iBAAK,IAAI,WAAW;AACpB,mBAAO;AAAA,UACX;AACA,cAAI,KAAK,IAAI;AACT;AACJ,cAAI,IAAI,IAAI,IAAI,GAAG;AACf,gBAAI,KAAK,IAAI,cAAc,IAAI,IAAI,MAAM,UAAU;AAC/C,mBAAK,IAAI,cAAc,IAAI,MAAM,QAAQ;AACzC,mBAAK,IAAI,MAAM,GAAG,QAAQA,QAAM,MAAM,KAAK;AAAA,YAC/C;AAAA,UACJ,OACK;AACD,gBAAI,IAAI,IAAI;AACZ,iBAAK,IAAI,cAAc,IAAI,MAAM,QAAQ;AACzC,iBAAK,IAAI,MAAM,GAAG,KAAKA,QAAM,MAAM,KAAK;AAAA,UAC5C;AACA,eAAK,IAAI,WAAW;AACpB,iBAAO;AAAA,QACX;AAEA,YAAI,KAAK,IAAI,cAAc,IAAI,IAAI,GAAG;AAClC,iBAAO;AAAA,QACX;AACA,aAAK,IAAI,cAAc,IAAI,MAAM,IAAI;AAAA,MACzC;AAAA,MACA,YAAY,WAAW,YAAY,IAAI,QAAQ,KAAK,OAAO,WAAW;AAElE,oBAAoB,aAAK,WAAW,EAAE;AACtC,oBAAY,KAAK,IAAI,UAAU,WAAW,WAAW,GAAI;AACzD,YAAI,CAAC;AACD;AACJ,cAAM,WAAW,KAAK,IAAI,eAAe,GAAG,IAAI;AAChD,cAAM,UAAU,oBAAI,IAAI;AACxB,YAAI,SAAS,KAAK,IAAI,UAAU,WAAW;AAAA,UACvC,YAAY,CAAC,UAAU,GAAG,WAAW,KAAK;AAAA,UAC1C,iBAAiB,CAAC,UAAU,GAAG,UAAU,KAAK;AAAA,QAClD,CAAC;AACD,YAAI,CAAC;AACD;AACJ,eACK,GAAG,UAAU,OAAO,UAAU;AAC/B,cAAI,KAAK,IAAI,QAAQ;AACjB,qBAAS;AACT;AAAA,UACJ;AACA,gBAAM,OAAO,MAAM;AACnB,cAAIA,SAAe,aAAK,WAAW,IAAI;AACvC,kBAAQ,IAAI,IAAI;AAChB,cAAI,MAAM,MAAM,eAAe,KAC1B,MAAM,KAAK,eAAe,OAAO,WAAWA,QAAM,IAAI,GAAI;AAC3D;AAAA,UACJ;AACA,cAAI,KAAK,IAAI,QAAQ;AACjB,qBAAS;AACT;AAAA,UACJ;AAIA,cAAI,SAAS,UAAW,CAAC,UAAU,CAAC,SAAS,IAAI,IAAI,GAAI;AACrD,iBAAK,IAAI,gBAAgB;AAEzB,YAAAA,SAAe,aAAK,KAAa,iBAAS,KAAKA,MAAI,CAAC;AACpD,iBAAK,aAAaA,QAAM,YAAY,IAAI,QAAQ,CAAC;AAAA,UACrD;AAAA,QACJ,CAAC,EACI,GAAG,GAAG,OAAO,KAAK,iBAAiB;AACxC,eAAO,IAAI,QAAQ,CAACO,UAAS,WAAW;AACpC,cAAI,CAAC;AACD,mBAAO,OAAO;AAClB,iBAAO,KAAK,SAAS,MAAM;AACvB,gBAAI,KAAK,IAAI,QAAQ;AACjB,uBAAS;AACT;AAAA,YACJ;AACA,kBAAM,eAAe,YAAY,UAAU,MAAM,IAAI;AACrD,YAAAA,SAAQ,MAAS;AAIjB,qBACK,YAAY,EACZ,OAAO,CAAC,SAAS;AAClB,qBAAO,SAAS,aAAa,CAAC,QAAQ,IAAI,IAAI;AAAA,YAClD,CAAC,EACI,QAAQ,CAAC,SAAS;AACnB,mBAAK,IAAI,QAAQ,WAAW,IAAI;AAAA,YACpC,CAAC;AACD,qBAAS;AAET,gBAAI;AACA,mBAAK,YAAY,WAAW,OAAO,IAAI,QAAQ,KAAK,OAAO,SAAS;AAAA,UAC5E,CAAC;AAAA,QACL,CAAC;AAAA,MACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYA,MAAM,WAAW,KAAK,OAAO,YAAY,OAAO,QAAQ,IAAIC,WAAU;AAClE,cAAM,YAAY,KAAK,IAAI,eAAuB,gBAAQ,GAAG,CAAC;AAC9D,cAAM,UAAU,UAAU,IAAY,iBAAS,GAAG,CAAC;AACnD,YAAI,EAAE,cAAc,KAAK,IAAI,QAAQ,kBAAkB,CAAC,UAAU,CAAC,SAAS;AACxE,eAAK,IAAI,MAAM,GAAG,SAAS,KAAK,KAAK;AAAA,QACzC;AAEA,kBAAU,IAAY,iBAAS,GAAG,CAAC;AACnC,aAAK,IAAI,eAAe,GAAG;AAC3B,YAAI;AACJ,YAAI;AACJ,cAAM,SAAS,KAAK,IAAI,QAAQ;AAChC,aAAK,UAAU,QAAQ,SAAS,WAAW,CAAC,KAAK,IAAI,cAAc,IAAIA,SAAQ,GAAG;AAC9E,cAAI,CAAC,QAAQ;AACT,kBAAM,KAAK,YAAY,KAAK,YAAY,IAAI,QAAQ,KAAK,OAAO,SAAS;AACzE,gBAAI,KAAK,IAAI;AACT;AAAA,UACR;AACA,mBAAS,KAAK,iBAAiB,KAAK,CAAC,SAASC,WAAU;AAEpD,gBAAIA,UAASA,OAAM,YAAY;AAC3B;AACJ,iBAAK,YAAY,SAAS,OAAO,IAAI,QAAQ,KAAK,OAAO,SAAS;AAAA,UACtE,CAAC;AAAA,QACL;AACA,eAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUA,MAAM,aAAaT,QAAM,YAAY,SAAS,OAAO,QAAQ;AACzD,cAAM,QAAQ,KAAK,IAAI;AACvB,YAAI,KAAK,IAAI,WAAWA,MAAI,KAAK,KAAK,IAAI,QAAQ;AAC9C,gBAAM;AACN,iBAAO;AAAA,QACX;AACA,cAAM,KAAK,KAAK,IAAI,iBAAiBA,MAAI;AACzC,YAAI,SAAS;AACT,aAAG,aAAa,CAAC,UAAU,QAAQ,WAAW,KAAK;AACnD,aAAG,YAAY,CAAC,UAAU,QAAQ,UAAU,KAAK;AAAA,QACrD;AAEA,YAAI;AACA,gBAAM,QAAQ,MAAM,YAAY,GAAG,UAAU,EAAE,GAAG,SAAS;AAC3D,cAAI,KAAK,IAAI;AACT;AACJ,cAAI,KAAK,IAAI,WAAW,GAAG,WAAW,KAAK,GAAG;AAC1C,kBAAM;AACN,mBAAO;AAAA,UACX;AACA,gBAAM,SAAS,KAAK,IAAI,QAAQ;AAChC,cAAI;AACJ,cAAI,MAAM,YAAY,GAAG;AACrB,kBAAM,UAAkB,gBAAQA,MAAI;AACpC,kBAAM,aAAa,SAAS,MAAM,WAAWA,MAAI,IAAIA;AACrD,gBAAI,KAAK,IAAI;AACT;AACJ,qBAAS,MAAM,KAAK,WAAW,GAAG,WAAW,OAAO,YAAY,OAAO,QAAQ,IAAI,UAAU;AAC7F,gBAAI,KAAK,IAAI;AACT;AAEJ,gBAAI,YAAY,cAAc,eAAe,QAAW;AACpD,mBAAK,IAAI,cAAc,IAAI,SAAS,UAAU;AAAA,YAClD;AAAA,UACJ,WACS,MAAM,eAAe,GAAG;AAC7B,kBAAM,aAAa,SAAS,MAAM,WAAWA,MAAI,IAAIA;AACrD,gBAAI,KAAK,IAAI;AACT;AACJ,kBAAM,SAAiB,gBAAQ,GAAG,SAAS;AAC3C,iBAAK,IAAI,eAAe,MAAM,EAAE,IAAI,GAAG,SAAS;AAChD,iBAAK,IAAI,MAAM,GAAG,KAAK,GAAG,WAAW,KAAK;AAC1C,qBAAS,MAAM,KAAK,WAAW,QAAQ,OAAO,YAAY,OAAOA,QAAM,IAAI,UAAU;AACrF,gBAAI,KAAK,IAAI;AACT;AAEJ,gBAAI,eAAe,QAAW;AAC1B,mBAAK,IAAI,cAAc,IAAY,gBAAQA,MAAI,GAAG,UAAU;AAAA,YAChE;AAAA,UACJ,OACK;AACD,qBAAS,KAAK,YAAY,GAAG,WAAW,OAAO,UAAU;AAAA,UAC7D;AACA,gBAAM;AACN,cAAI;AACA,iBAAK,IAAI,eAAeA,QAAM,MAAM;AACxC,iBAAO;AAAA,QACX,SACO,OAAO;AACV,cAAI,KAAK,IAAI,aAAa,KAAK,GAAG;AAC9B,kBAAM;AACN,mBAAOA;AAAA,UACX;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA;AAAA;;;ACnnBA,SAAS,QAAQ,cAAc;AAC/B,SAAS,QAAAU,OAAM,WAAAC,gBAAe;AAC9B,SAAS,gBAAAC,qBAAoB;AAC7B,YAAYC,cAAa;AAYzB,SAAS,OAAO,MAAM;AAClB,SAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AAC7C;AAEA,SAAS,cAAc,SAAS;AAC5B,MAAI,OAAO,YAAY;AACnB,WAAO;AACX,MAAI,OAAO,YAAY;AACnB,WAAO,CAAC,WAAW,YAAY;AACnC,MAAI,mBAAmB;AACnB,WAAO,CAAC,WAAW,QAAQ,KAAK,MAAM;AAC1C,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACjD,WAAO,CAAC,WAAW;AACf,UAAI,QAAQ,SAAS;AACjB,eAAO;AACX,UAAI,QAAQ,WAAW;AACnB,cAAMC,YAAmB,kBAAS,QAAQ,MAAM,MAAM;AACtD,YAAI,CAACA,WAAU;AACX,iBAAO;AAAA,QACX;AACA,eAAO,CAACA,UAAS,WAAW,IAAI,KAAK,CAAS,oBAAWA,SAAQ;AAAA,MACrE;AACA,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO,MAAM;AACjB;AACA,SAASC,eAAcC,QAAM;AACzB,MAAI,OAAOA,WAAS;AAChB,UAAM,IAAI,MAAM,iBAAiB;AACrC,EAAAA,SAAe,mBAAUA,MAAI;AAC7B,EAAAA,SAAOA,OAAK,QAAQ,OAAO,GAAG;AAC9B,MAAI,UAAU;AACd,MAAIA,OAAK,WAAW,IAAI;AACpB,cAAU;AACd,QAAMC,mBAAkB;AACxB,SAAOD,OAAK,MAAMC,gBAAe;AAC7B,IAAAD,SAAOA,OAAK,QAAQC,kBAAiB,GAAG;AAC5C,MAAI;AACA,IAAAD,SAAO,MAAMA;AACjB,SAAOA;AACX;AACA,SAAS,cAAc,UAAU,YAAY,OAAO;AAChD,QAAMA,SAAOD,eAAc,UAAU;AACrC,WAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;AAClD,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,QAAQC,QAAM,KAAK,GAAG;AACtB,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AACA,SAAS,SAAS,UAAU,YAAY;AACpC,MAAI,YAAY,MAAM;AAClB,UAAM,IAAI,UAAU,kCAAkC;AAAA,EAC1D;AAEA,QAAM,gBAAgB,OAAO,QAAQ;AACrC,QAAM,WAAW,cAAc,IAAI,CAAC,YAAY,cAAc,OAAO,CAAC;AACtE,MAAI,cAAc,MAAM;AACpB,WAAO,CAACE,aAAY,UAAU;AAC1B,aAAO,cAAc,UAAUA,aAAY,KAAK;AAAA,IACpD;AAAA,EACJ;AACA,SAAO,cAAc,UAAU,UAAU;AAC7C;AAusBO,SAAS,MAAM,OAAO,UAAU,CAAC,GAAG;AACvC,QAAM,UAAU,IAAI,UAAU,OAAO;AACrC,UAAQ,IAAI,KAAK;AACjB,SAAO;AACX;AA5xBA,IAOM,OACA,aACA,SACA,UACA,aACA,eACA,iBACA,QACA,aAIA,iBA+DA,YASA,QAgBA,qBAEA,kBAQA,iBAMA,WAIA,UAkDA,eACA,eACO,aAsCA,WAokBN;AA7xBP,IAAAC,YAAA;AAAA;AAAA;AAKA;AACA;AACA,IAAM,QAAQ;AACd,IAAM,cAAc;AACpB,IAAM,UAAU;AAChB,IAAM,WAAW;AACjB,IAAM,cAAc;AACpB,IAAM,gBAAgB;AACtB,IAAM,kBAAkB;AACxB,IAAM,SAAS;AACf,IAAM,cAAc;AAIpB,IAAM,kBAAkB,CAAC,YAAY,OAAO,YAAY,YAAY,YAAY,QAAQ,EAAE,mBAAmB;AA+D7G,IAAM,aAAa,CAAC,WAAW;AAC3B,YAAM,QAAQ,OAAO,MAAM,EAAE,KAAK;AAClC,UAAI,CAAC,MAAM,MAAM,CAAC,MAAM,OAAO,MAAM,WAAW,GAAG;AAC/C,cAAM,IAAI,UAAU,sCAAsC,KAAK,EAAE;AAAA,MACrE;AACA,aAAO,MAAM,IAAI,mBAAmB;AAAA,IACxC;AAGA,IAAM,SAAS,CAAC,WAAW;AACvB,UAAI,MAAM,OAAO,QAAQ,eAAe,KAAK;AAC7C,UAAI,UAAU;AACd,UAAI,IAAI,WAAW,WAAW,GAAG;AAC7B,kBAAU;AAAA,MACd;AACA,aAAO,IAAI,MAAM,eAAe,GAAG;AAC/B,cAAM,IAAI,QAAQ,iBAAiB,KAAK;AAAA,MAC5C;AACA,UAAI,SAAS;AACT,cAAM,QAAQ;AAAA,MAClB;AACA,aAAO;AAAA,IACX;AAGA,IAAM,sBAAsB,CAACH,WAAS,OAAe,mBAAU,OAAOA,MAAI,CAAC,CAAC;AAE5E,IAAM,mBAAmB,CAAC,MAAM,OAAO,CAACA,WAAS;AAC7C,UAAI,OAAOA,WAAS,UAAU;AAC1B,eAAO,oBAA4B,oBAAWA,MAAI,IAAIA,SAAe,cAAK,KAAKA,MAAI,CAAC;AAAA,MACxF,OACK;AACD,eAAOA;AAAA,MACX;AAAA,IACJ;AACA,IAAM,kBAAkB,CAACA,QAAM,QAAQ;AACnC,UAAY,oBAAWA,MAAI,GAAG;AAC1B,eAAOA;AAAA,MACX;AACA,aAAe,cAAK,KAAKA,MAAI;AAAA,IACjC;AACA,IAAM,YAAY,OAAO,OAAO,oBAAI,IAAI,CAAC;AAIzC,IAAM,WAAN,MAAe;AAAA,MACX,YAAY,KAAK,eAAe;AAC5B,aAAK,OAAO;AACZ,aAAK,iBAAiB;AACtB,aAAK,QAAQ,oBAAI,IAAI;AAAA,MACzB;AAAA,MACA,IAAI,MAAM;AACN,cAAM,EAAE,MAAM,IAAI;AAClB,YAAI,CAAC;AACD;AACJ,YAAI,SAAS,WAAW,SAAS;AAC7B,gBAAM,IAAI,IAAI;AAAA,MACtB;AAAA,MACA,MAAM,OAAO,MAAM;AACf,cAAM,EAAE,MAAM,IAAI;AAClB,YAAI,CAAC;AACD;AACJ,cAAM,OAAO,IAAI;AACjB,YAAI,MAAM,OAAO;AACb;AACJ,cAAM,MAAM,KAAK;AACjB,YAAI;AACA,gBAAML,SAAQ,GAAG;AAAA,QACrB,SACO,KAAK;AACR,cAAI,KAAK,gBAAgB;AACrB,iBAAK,eAAuB,iBAAQ,GAAG,GAAW,kBAAS,GAAG,CAAC;AAAA,UACnE;AAAA,QACJ;AAAA,MACJ;AAAA,MACA,IAAI,MAAM;AACN,cAAM,EAAE,MAAM,IAAI;AAClB,YAAI,CAAC;AACD;AACJ,eAAO,MAAM,IAAI,IAAI;AAAA,MACzB;AAAA,MACA,cAAc;AACV,cAAM,EAAE,MAAM,IAAI;AAClB,YAAI,CAAC;AACD,iBAAO,CAAC;AACZ,eAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAAA,MAC7B;AAAA,MACA,UAAU;AACN,aAAK,MAAM,MAAM;AACjB,aAAK,OAAO;AACZ,aAAK,iBAAiB;AACtB,aAAK,QAAQ;AACb,eAAO,OAAO,IAAI;AAAA,MACtB;AAAA,IACJ;AACA,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACf,IAAM,cAAN,MAAkB;AAAA,MACrB,YAAYK,QAAM,QAAQ,KAAK;AAC3B,aAAK,MAAM;AACX,cAAM,YAAYA;AAClB,aAAK,OAAOA,SAAOA,OAAK,QAAQ,aAAa,EAAE;AAC/C,aAAK,YAAY;AACjB,aAAK,gBAAwB,iBAAQ,SAAS;AAC9C,aAAK,WAAW,CAAC;AACjB,aAAK,SAAS,QAAQ,CAAC,UAAU;AAC7B,cAAI,MAAM,SAAS;AACf,kBAAM,IAAI;AAAA,QAClB,CAAC;AACD,aAAK,iBAAiB;AACtB,aAAK,aAAa,SAAS,gBAAgB;AAAA,MAC/C;AAAA,MACA,UAAU,OAAO;AACb,eAAe,cAAK,KAAK,WAAmB,kBAAS,KAAK,WAAW,MAAM,QAAQ,CAAC;AAAA,MACxF;AAAA,MACA,WAAW,OAAO;AACd,cAAM,EAAE,MAAM,IAAI;AAClB,YAAI,SAAS,MAAM,eAAe;AAC9B,iBAAO,KAAK,UAAU,KAAK;AAC/B,cAAM,eAAe,KAAK,UAAU,KAAK;AAEzC,eAAO,KAAK,IAAI,aAAa,cAAc,KAAK,KAAK,KAAK,IAAI,oBAAoB,KAAK;AAAA,MAC3F;AAAA,MACA,UAAU,OAAO;AACb,eAAO,KAAK,IAAI,aAAa,KAAK,UAAU,KAAK,GAAG,MAAM,KAAK;AAAA,MACnE;AAAA,IACJ;AASO,IAAM,YAAN,cAAwBJ,cAAa;AAAA;AAAA,MAExC,YAAY,QAAQ,CAAC,GAAG;AACpB,cAAM;AACN,aAAK,SAAS;AACd,aAAK,WAAW,oBAAI,IAAI;AACxB,aAAK,gBAAgB,oBAAI,IAAI;AAC7B,aAAK,aAAa,oBAAI,IAAI;AAC1B,aAAK,WAAW,oBAAI,IAAI;AACxB,aAAK,gBAAgB,oBAAI,IAAI;AAC7B,aAAK,WAAW,oBAAI,IAAI;AACxB,aAAK,iBAAiB,oBAAI,IAAI;AAC9B,aAAK,kBAAkB,oBAAI,IAAI;AAC/B,aAAK,cAAc;AACnB,aAAK,gBAAgB;AACrB,cAAM,MAAM,MAAM;AAClB,cAAM,UAAU,EAAE,oBAAoB,KAAM,cAAc,IAAI;AAC9D,cAAM,OAAO;AAAA;AAAA,UAET,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,wBAAwB;AAAA,UACxB,UAAU;AAAA,UACV,gBAAgB;AAAA,UAChB,gBAAgB;AAAA,UAChB,YAAY;AAAA;AAAA,UAEZ,QAAQ;AAAA;AAAA,UACR,GAAG;AAAA;AAAA,UAEH,SAAS,MAAM,UAAU,OAAO,MAAM,OAAO,IAAI,OAAO,CAAC,CAAC;AAAA,UAC1D,kBAAkB,QAAQ,OAAO,UAAU,OAAO,QAAQ,WAAW,EAAE,GAAG,SAAS,GAAG,IAAI,IAAI;AAAA,QAClG;AAEA,YAAI;AACA,eAAK,aAAa;AAEtB,YAAI,KAAK,WAAW;AAChB,eAAK,SAAS,CAAC,KAAK;AAIxB,cAAM,UAAU,QAAQ,IAAI;AAC5B,YAAI,YAAY,QAAW;AACvB,gBAAM,WAAW,QAAQ,YAAY;AACrC,cAAI,aAAa,WAAW,aAAa;AACrC,iBAAK,aAAa;AAAA,mBACb,aAAa,UAAU,aAAa;AACzC,iBAAK,aAAa;AAAA;AAElB,iBAAK,aAAa,CAAC,CAAC;AAAA,QAC5B;AACA,cAAM,cAAc,QAAQ,IAAI;AAChC,YAAI;AACA,eAAK,WAAW,OAAO,SAAS,aAAa,EAAE;AAEnD,YAAI,aAAa;AACjB,aAAK,aAAa,MAAM;AACpB;AACA,cAAI,cAAc,KAAK,aAAa;AAChC,iBAAK,aAAa;AAClB,iBAAK,gBAAgB;AAErB,oBAAQ,SAAS,MAAM,KAAK,KAAK,OAAG,KAAK,CAAC;AAAA,UAC9C;AAAA,QACJ;AACA,aAAK,WAAW,IAAI,SAAS,KAAK,KAAK,OAAG,KAAK,GAAG,IAAI;AACtD,aAAK,eAAe,KAAK,QAAQ,KAAK,IAAI;AAC1C,aAAK,UAAU;AACf,aAAK,iBAAiB,IAAI,cAAc,IAAI;AAE5C,eAAO,OAAO,IAAI;AAAA,MACtB;AAAA,MACA,gBAAgB,SAAS;AACrB,YAAI,gBAAgB,OAAO,GAAG;AAE1B,qBAAW,WAAW,KAAK,eAAe;AACtC,gBAAI,gBAAgB,OAAO,KACvB,QAAQ,SAAS,QAAQ,QACzB,QAAQ,cAAc,QAAQ,WAAW;AACzC;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ;AACA,aAAK,cAAc,IAAI,OAAO;AAAA,MAClC;AAAA,MACA,mBAAmB,SAAS;AACxB,aAAK,cAAc,OAAO,OAAO;AAEjC,YAAI,OAAO,YAAY,UAAU;AAC7B,qBAAW,WAAW,KAAK,eAAe;AAItC,gBAAI,gBAAgB,OAAO,KAAK,QAAQ,SAAS,SAAS;AACtD,mBAAK,cAAc,OAAO,OAAO;AAAA,YACrC;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,IAAI,QAAQ,UAAU,WAAW;AAC7B,cAAM,EAAE,IAAI,IAAI,KAAK;AACrB,aAAK,SAAS;AACd,aAAK,gBAAgB;AACrB,YAAI,QAAQ,WAAW,MAAM;AAC7B,YAAI,KAAK;AACL,kBAAQ,MAAM,IAAI,CAACI,WAAS;AACxB,kBAAM,UAAU,gBAAgBA,QAAM,GAAG;AAEzC,mBAAO;AAAA,UACX,CAAC;AAAA,QACL;AACA,cAAM,QAAQ,CAACA,WAAS;AACpB,eAAK,mBAAmBA,MAAI;AAAA,QAChC,CAAC;AACD,aAAK,eAAe;AACpB,YAAI,CAAC,KAAK;AACN,eAAK,cAAc;AACvB,aAAK,eAAe,MAAM;AAC1B,gBAAQ,IAAI,MAAM,IAAI,OAAOA,WAAS;AAClC,gBAAM,MAAM,MAAM,KAAK,eAAe,aAAaA,QAAM,CAAC,WAAW,QAAW,GAAG,QAAQ;AAC3F,cAAI;AACA,iBAAK,WAAW;AACpB,iBAAO;AAAA,QACX,CAAC,CAAC,EAAE,KAAK,CAAC,YAAY;AAClB,cAAI,KAAK;AACL;AACJ,kBAAQ,QAAQ,CAAC,SAAS;AACtB,gBAAI;AACA,mBAAK,IAAY,iBAAQ,IAAI,GAAW,kBAAS,YAAY,IAAI,CAAC;AAAA,UAC1E,CAAC;AAAA,QACL,CAAC;AACD,eAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA,MAIA,QAAQ,QAAQ;AACZ,YAAI,KAAK;AACL,iBAAO;AACX,cAAM,QAAQ,WAAW,MAAM;AAC/B,cAAM,EAAE,IAAI,IAAI,KAAK;AACrB,cAAM,QAAQ,CAACA,WAAS;AAEpB,cAAI,CAAS,oBAAWA,MAAI,KAAK,CAAC,KAAK,SAAS,IAAIA,MAAI,GAAG;AACvD,gBAAI;AACA,cAAAA,SAAe,cAAK,KAAKA,MAAI;AACjC,YAAAA,SAAe,iBAAQA,MAAI;AAAA,UAC/B;AACA,eAAK,WAAWA,MAAI;AACpB,eAAK,gBAAgBA,MAAI;AACzB,cAAI,KAAK,SAAS,IAAIA,MAAI,GAAG;AACzB,iBAAK,gBAAgB;AAAA,cACjB,MAAAA;AAAA,cACA,WAAW;AAAA,YACf,CAAC;AAAA,UACL;AAGA,eAAK,eAAe;AAAA,QACxB,CAAC;AACD,eAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA,MAIA,QAAQ;AACJ,YAAI,KAAK,eAAe;AACpB,iBAAO,KAAK;AAAA,QAChB;AACA,aAAK,SAAS;AAEd,aAAK,mBAAmB;AACxB,cAAM,UAAU,CAAC;AACjB,aAAK,SAAS,QAAQ,CAAC,eAAe,WAAW,QAAQ,CAAC,WAAW;AACjE,gBAAM,UAAU,OAAO;AACvB,cAAI,mBAAmB;AACnB,oBAAQ,KAAK,OAAO;AAAA,QAC5B,CAAC,CAAC;AACF,aAAK,SAAS,QAAQ,CAAC,WAAW,OAAO,QAAQ,CAAC;AAClD,aAAK,eAAe;AACpB,aAAK,cAAc;AACnB,aAAK,gBAAgB;AACrB,aAAK,SAAS,QAAQ,CAAC,WAAW,OAAO,QAAQ,CAAC;AAClD,aAAK,SAAS,MAAM;AACpB,aAAK,SAAS,MAAM;AACpB,aAAK,SAAS,MAAM;AACpB,aAAK,cAAc,MAAM;AACzB,aAAK,WAAW,MAAM;AACtB,aAAK,gBAAgB,QAAQ,SACvB,QAAQ,IAAI,OAAO,EAAE,KAAK,MAAM,MAAS,IACzC,QAAQ,QAAQ;AACtB,eAAO,KAAK;AAAA,MAChB;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,aAAa;AACT,cAAM,YAAY,CAAC;AACnB,aAAK,SAAS,QAAQ,CAAC,OAAO,QAAQ;AAClC,gBAAM,MAAM,KAAK,QAAQ,MAAc,kBAAS,KAAK,QAAQ,KAAK,GAAG,IAAI;AACzE,gBAAM,QAAQ,OAAO;AACrB,oBAAU,KAAK,IAAI,MAAM,YAAY,EAAE,KAAK;AAAA,QAChD,CAAC;AACD,eAAO;AAAA,MACX;AAAA,MACA,YAAY,OAAO,MAAM;AACrB,aAAK,KAAK,OAAO,GAAG,IAAI;AACxB,YAAI,UAAU,OAAG;AACb,eAAK,KAAK,OAAG,KAAK,OAAO,GAAG,IAAI;AAAA,MACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAWA,MAAM,MAAM,OAAOA,QAAM,OAAO;AAC5B,YAAI,KAAK;AACL;AACJ,cAAM,OAAO,KAAK;AAClB,YAAI;AACA,UAAAA,SAAe,mBAAUA,MAAI;AACjC,YAAI,KAAK;AACL,UAAAA,SAAe,kBAAS,KAAK,KAAKA,MAAI;AAC1C,cAAM,OAAO,CAACA,MAAI;AAClB,YAAI,SAAS;AACT,eAAK,KAAK,KAAK;AACnB,cAAM,MAAM,KAAK;AACjB,YAAI;AACJ,YAAI,QAAQ,KAAK,KAAK,eAAe,IAAIA,MAAI,IAAI;AAC7C,aAAG,aAAa,oBAAI,KAAK;AACzB,iBAAO;AAAA,QACX;AACA,YAAI,KAAK,QAAQ;AACb,cAAI,UAAU,OAAG,QAAQ;AACrB,iBAAK,gBAAgB,IAAIA,QAAM,CAAC,OAAO,GAAG,IAAI,CAAC;AAC/C,uBAAW,MAAM;AACb,mBAAK,gBAAgB,QAAQ,CAAC,OAAOA,WAAS;AAC1C,qBAAK,KAAK,GAAG,KAAK;AAClB,qBAAK,KAAK,OAAG,KAAK,GAAG,KAAK;AAC1B,qBAAK,gBAAgB,OAAOA,MAAI;AAAA,cACpC,CAAC;AAAA,YACL,GAAG,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,GAAG;AACtD,mBAAO;AAAA,UACX;AACA,cAAI,UAAU,OAAG,OAAO,KAAK,gBAAgB,IAAIA,MAAI,GAAG;AACpD,oBAAQ,OAAG;AACX,iBAAK,gBAAgB,OAAOA,MAAI;AAAA,UACpC;AAAA,QACJ;AACA,YAAI,QAAQ,UAAU,OAAG,OAAO,UAAU,OAAG,WAAW,KAAK,eAAe;AACxE,gBAAM,UAAU,CAAC,KAAKI,WAAU;AAC5B,gBAAI,KAAK;AACL,sBAAQ,OAAG;AACX,mBAAK,CAAC,IAAI;AACV,mBAAK,YAAY,OAAO,IAAI;AAAA,YAChC,WACSA,QAAO;AAEZ,kBAAI,KAAK,SAAS,GAAG;AACjB,qBAAK,CAAC,IAAIA;AAAA,cACd,OACK;AACD,qBAAK,KAAKA,MAAK;AAAA,cACnB;AACA,mBAAK,YAAY,OAAO,IAAI;AAAA,YAChC;AAAA,UACJ;AACA,eAAK,kBAAkBJ,QAAM,IAAI,oBAAoB,OAAO,OAAO;AACnE,iBAAO;AAAA,QACX;AACA,YAAI,UAAU,OAAG,QAAQ;AACrB,gBAAM,cAAc,CAAC,KAAK,UAAU,OAAG,QAAQA,QAAM,EAAE;AACvD,cAAI;AACA,mBAAO;AAAA,QACf;AACA,YAAI,KAAK,cACL,UAAU,WACT,UAAU,OAAG,OAAO,UAAU,OAAG,WAAW,UAAU,OAAG,SAAS;AACnE,gBAAM,WAAW,KAAK,MAAc,cAAK,KAAK,KAAKA,MAAI,IAAIA;AAC3D,cAAII;AACJ,cAAI;AACA,YAAAA,SAAQ,MAAMV,MAAK,QAAQ;AAAA,UAC/B,SACO,KAAK;AAAA,UAEZ;AAEA,cAAI,CAACU,UAAS,KAAK;AACf;AACJ,eAAK,KAAKA,MAAK;AAAA,QACnB;AACA,aAAK,YAAY,OAAO,IAAI;AAC5B,eAAO;AAAA,MACX;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,aAAa,OAAO;AAChB,cAAM,OAAO,SAAS,MAAM;AAC5B,YAAI,SACA,SAAS,YACT,SAAS,cACR,CAAC,KAAK,QAAQ,0BAA2B,SAAS,WAAW,SAAS,WAAY;AACnF,eAAK,KAAK,OAAG,OAAO,KAAK;AAAA,QAC7B;AACA,eAAO,SAAS,KAAK;AAAA,MACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,UAAU,YAAYJ,QAAM,SAAS;AACjC,YAAI,CAAC,KAAK,WAAW,IAAI,UAAU,GAAG;AAClC,eAAK,WAAW,IAAI,YAAY,oBAAI,IAAI,CAAC;AAAA,QAC7C;AACA,cAAM,SAAS,KAAK,WAAW,IAAI,UAAU;AAC7C,YAAI,CAAC;AACD,gBAAM,IAAI,MAAM,kBAAkB;AACtC,cAAM,aAAa,OAAO,IAAIA,MAAI;AAClC,YAAI,YAAY;AACZ,qBAAW;AACX,iBAAO;AAAA,QACX;AAEA,YAAI;AACJ,cAAM,QAAQ,MAAM;AAChB,gBAAM,OAAO,OAAO,IAAIA,MAAI;AAC5B,gBAAM,QAAQ,OAAO,KAAK,QAAQ;AAClC,iBAAO,OAAOA,MAAI;AAClB,uBAAa,aAAa;AAC1B,cAAI;AACA,yBAAa,KAAK,aAAa;AACnC,iBAAO;AAAA,QACX;AACA,wBAAgB,WAAW,OAAO,OAAO;AACzC,cAAM,MAAM,EAAE,eAAe,OAAO,OAAO,EAAE;AAC7C,eAAO,IAAIA,QAAM,GAAG;AACpB,eAAO;AAAA,MACX;AAAA,MACA,kBAAkB;AACd,eAAO,KAAK;AAAA,MAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,kBAAkBA,QAAM,WAAW,OAAO,SAAS;AAC/C,cAAM,MAAM,KAAK,QAAQ;AACzB,YAAI,OAAO,QAAQ;AACf;AACJ,cAAM,eAAe,IAAI;AACzB,YAAI;AACJ,YAAI,WAAWA;AACf,YAAI,KAAK,QAAQ,OAAO,CAAS,oBAAWA,MAAI,GAAG;AAC/C,qBAAmB,cAAK,KAAK,QAAQ,KAAKA,MAAI;AAAA,QAClD;AACA,cAAM,MAAM,oBAAI,KAAK;AACrB,cAAM,SAAS,KAAK;AACpB,iBAAS,mBAAmB,UAAU;AAClC,iBAAO,UAAU,CAAC,KAAK,YAAY;AAC/B,gBAAI,OAAO,CAAC,OAAO,IAAIA,MAAI,GAAG;AAC1B,kBAAI,OAAO,IAAI,SAAS;AACpB,wBAAQ,GAAG;AACf;AAAA,YACJ;AACA,kBAAMK,OAAM,OAAO,oBAAI,KAAK,CAAC;AAC7B,gBAAI,YAAY,QAAQ,SAAS,SAAS,MAAM;AAC5C,qBAAO,IAAIL,MAAI,EAAE,aAAaK;AAAA,YAClC;AACA,kBAAM,KAAK,OAAO,IAAIL,MAAI;AAC1B,kBAAM,KAAKK,OAAM,GAAG;AACpB,gBAAI,MAAM,WAAW;AACjB,qBAAO,OAAOL,MAAI;AAClB,sBAAQ,QAAW,OAAO;AAAA,YAC9B,OACK;AACD,+BAAiB,WAAW,oBAAoB,cAAc,OAAO;AAAA,YACzE;AAAA,UACJ,CAAC;AAAA,QACL;AACA,YAAI,CAAC,OAAO,IAAIA,MAAI,GAAG;AACnB,iBAAO,IAAIA,QAAM;AAAA,YACb,YAAY;AAAA,YACZ,YAAY,MAAM;AACd,qBAAO,OAAOA,MAAI;AAClB,2BAAa,cAAc;AAC3B,qBAAO;AAAA,YACX;AAAA,UACJ,CAAC;AACD,2BAAiB,WAAW,oBAAoB,YAAY;AAAA,QAChE;AAAA,MACJ;AAAA;AAAA;AAAA;AAAA,MAIA,WAAWA,QAAM,OAAO;AACpB,YAAI,KAAK,QAAQ,UAAU,OAAO,KAAKA,MAAI;AACvC,iBAAO;AACX,YAAI,CAAC,KAAK,cAAc;AACpB,gBAAM,EAAE,IAAI,IAAI,KAAK;AACrB,gBAAM,MAAM,KAAK,QAAQ;AACzB,gBAAM,WAAW,OAAO,CAAC,GAAG,IAAI,iBAAiB,GAAG,CAAC;AACrD,gBAAM,eAAe,CAAC,GAAG,KAAK,aAAa;AAC3C,gBAAM,OAAO,CAAC,GAAG,aAAa,IAAI,iBAAiB,GAAG,CAAC,GAAG,GAAG,OAAO;AACpE,eAAK,eAAe,SAAS,MAAM,MAAS;AAAA,QAChD;AACA,eAAO,KAAK,aAAaA,QAAM,KAAK;AAAA,MACxC;AAAA,MACA,aAAaA,QAAMN,OAAM;AACrB,eAAO,CAAC,KAAK,WAAWM,QAAMN,KAAI;AAAA,MACtC;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,iBAAiBM,QAAM;AACnB,eAAO,IAAI,YAAYA,QAAM,KAAK,QAAQ,gBAAgB,IAAI;AAAA,MAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,eAAe,WAAW;AACtB,cAAM,MAAc,iBAAQ,SAAS;AACrC,YAAI,CAAC,KAAK,SAAS,IAAI,GAAG;AACtB,eAAK,SAAS,IAAI,KAAK,IAAI,SAAS,KAAK,KAAK,YAAY,CAAC;AAC/D,eAAO,KAAK,SAAS,IAAI,GAAG;AAAA,MAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,oBAAoB,OAAO;AACvB,YAAI,KAAK,QAAQ;AACb,iBAAO;AACX,eAAO,QAAQ,OAAO,MAAM,IAAI,IAAI,GAAK;AAAA,MAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,QAAQ,WAAW,MAAM,aAAa;AAIlC,cAAMA,SAAe,cAAK,WAAW,IAAI;AACzC,cAAM,WAAmB,iBAAQA,MAAI;AACrC,sBACI,eAAe,OAAO,cAAc,KAAK,SAAS,IAAIA,MAAI,KAAK,KAAK,SAAS,IAAI,QAAQ;AAG7F,YAAI,CAAC,KAAK,UAAU,UAAUA,QAAM,GAAG;AACnC;AAEJ,YAAI,CAAC,eAAe,KAAK,SAAS,SAAS,GAAG;AAC1C,eAAK,IAAI,WAAW,MAAM,IAAI;AAAA,QAClC;AAGA,cAAM,KAAK,KAAK,eAAeA,MAAI;AACnC,cAAM,0BAA0B,GAAG,YAAY;AAE/C,gCAAwB,QAAQ,CAAC,WAAW,KAAK,QAAQA,QAAM,MAAM,CAAC;AAEtE,cAAM,SAAS,KAAK,eAAe,SAAS;AAC5C,cAAM,aAAa,OAAO,IAAI,IAAI;AAClC,eAAO,OAAO,IAAI;AAMlB,YAAI,KAAK,cAAc,IAAI,QAAQ,GAAG;AAClC,eAAK,cAAc,OAAO,QAAQ;AAAA,QACtC;AAEA,YAAI,UAAUA;AACd,YAAI,KAAK,QAAQ;AACb,oBAAkB,kBAAS,KAAK,QAAQ,KAAKA,MAAI;AACrD,YAAI,KAAK,QAAQ,oBAAoB,KAAK,eAAe,IAAI,OAAO,GAAG;AACnE,gBAAM,QAAQ,KAAK,eAAe,IAAI,OAAO,EAAE,WAAW;AAC1D,cAAI,UAAU,OAAG;AACb;AAAA,QACR;AAGA,aAAK,SAAS,OAAOA,MAAI;AACzB,aAAK,SAAS,OAAO,QAAQ;AAC7B,cAAM,YAAY,cAAc,OAAG,aAAa,OAAG;AACnD,YAAI,cAAc,CAAC,KAAK,WAAWA,MAAI;AACnC,eAAK,MAAM,WAAWA,MAAI;AAE9B,aAAK,WAAWA,MAAI;AAAA,MACxB;AAAA;AAAA;AAAA;AAAA,MAIA,WAAWA,QAAM;AACb,aAAK,WAAWA,MAAI;AACpB,cAAM,MAAc,iBAAQA,MAAI;AAChC,aAAK,eAAe,GAAG,EAAE,OAAe,kBAASA,MAAI,CAAC;AAAA,MAC1D;AAAA;AAAA;AAAA;AAAA,MAIA,WAAWA,QAAM;AACb,cAAM,UAAU,KAAK,SAAS,IAAIA,MAAI;AACtC,YAAI,CAAC;AACD;AACJ,gBAAQ,QAAQ,CAAC,WAAW,OAAO,CAAC;AACpC,aAAK,SAAS,OAAOA,MAAI;AAAA,MAC7B;AAAA,MACA,eAAeA,QAAM,QAAQ;AACzB,YAAI,CAAC;AACD;AACJ,YAAI,OAAO,KAAK,SAAS,IAAIA,MAAI;AACjC,YAAI,CAAC,MAAM;AACP,iBAAO,CAAC;AACR,eAAK,SAAS,IAAIA,QAAM,IAAI;AAAA,QAChC;AACA,aAAK,KAAK,MAAM;AAAA,MACpB;AAAA,MACA,UAAU,MAAM,MAAM;AAClB,YAAI,KAAK;AACL;AACJ,cAAM,UAAU,EAAE,MAAM,OAAG,KAAK,YAAY,MAAM,OAAO,MAAM,GAAG,MAAM,OAAO,EAAE;AACjF,YAAI,SAAS,SAAS,MAAM,OAAO;AACnC,aAAK,SAAS,IAAI,MAAM;AACxB,eAAO,KAAK,WAAW,MAAM;AACzB,mBAAS;AAAA,QACb,CAAC;AACD,eAAO,KAAK,SAAS,MAAM;AACvB,cAAI,QAAQ;AACR,iBAAK,SAAS,OAAO,MAAM;AAC3B,qBAAS;AAAA,UACb;AAAA,QACJ,CAAC;AACD,eAAO;AAAA,MACX;AAAA,IACJ;AAeA,IAAO,cAAQ,EAAE,OAAO,UAAU;AAAA;AAAA;;;AC7xBlC,OAAOM,YAAU;AACjB,OAAOC,UAAQ;AACf,OAAOC,SAAQ;AAgDf,eAAsB,mBACpB,SACwB;AACxB,QAAM,EAAE,SAAS,MAAM,OAAO,WAAW,UAAU,YAAY,YAAY,IAAI;AAG/E,QAAM,cACJ,SACC,MAAMA,IAAG,CAAC,GAAG,OAAO,UAAU,GAAG;AAAA,IAChC,KAAK;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ,CAAC,gBAAgB,oBAAoB,WAAW;AAAA,EAC1D,CAAC;AAEH,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO,EAAE,eAAe,CAAC,EAAE;AAAA,EAC7B;AAGA,QAAM,UAAUF,OAAK,QAAQ,SAAS,IAAI;AAC1C,QAAMC,KAAG,SAAS,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAGpD,QAAM,UAAU,kBAAkB,OAAO;AAIzC,QAAM,UAAU,MAAM,OAAO,SAAS;AACtC,QAAM,UAAUD,OAAK,QAAQ,SAAS,OAAO;AAC7C,QAAM,SAAS,MAAM,QAAQ,MAAM;AAAA,IACjC;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA;AAAA,IAEA,GAAI,aACA,EAAE,QAAQ,EAAE,wBAAwB,eAAe,GAAG,cAAc,KAAK,IACzE,CAAC;AAAA,IACL;AAAA;AAAA,IAEA,GAAI,cAAc,EAAE,OAAO,MAAe,IAAI,CAAC;AAAA,EACjD,CAAC;AAID,MAAI,eAAe,OAAO,aAAa;AACrC,UAAM,QAAQ;AAAA,MACZ,OAAO,YAAY,IAAI,OAAO,SAAS;AACrC,cAAMC,KAAG,SAAS,MAAMD,OAAK,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpE,cAAM,MAAM,GAAG,KAAK,IAAI,QAAQ,QAAQ,GAAG,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAClF,cAAMC,KAAG,SAAS,UAAU,KAAK,KAAK,QAAQ;AAC9C,cAAMA,KAAG,SAAS,OAAO,KAAK,KAAK,IAAI;AAAA,MACzC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,eAAe,YAAY;AACtC;AAjHA;AAAA;AAAA;AAGA;AACA;AAAA;AAAA;;;ACgCA,eAAsB,iBAAiB,SAAoD;AACzF,SAAO,mBAAmB,EAAE,GAAG,SAAS,aAAa,KAAK,CAAC;AAC7D;AAtCA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmCO,SAAS,uBAAuB,SAAoD;AACzF,QAAM,EAAE,SAAS,aAAa,KAAK,QAAQ,IAAI;AAE/C,MAAI,QAA8C;AAClD,MAAI,eAAyB,CAAC;AAC9B,MAAI,eAAe;AACnB,MAAI,kBAAkB;AAEtB,iBAAe,MAAqB;AAElC,QAAI,cAAc;AAChB,wBAAkB;AAClB;AAAA,IACF;AACA,mBAAe;AACf,QAAI;AAEF,SAAG;AACD,0BAAkB;AAClB,cAAM,QAAQ;AACd,uBAAe,CAAC;AAChB,YAAI;AACF,gBAAM,QAAQ,KAAK;AAAA,QACrB,SAAS,KAAK;AAEZ,yBAAe,CAAC,GAAG,OAAO,GAAG,YAAY;AACzC,oBAAU,GAAG;AAAA,QACf;AAAA,MACF,SAAS;AAAA,IACX,UAAE;AACA,qBAAe;AAAA,IACjB;AAAA,EACF;AAEA,WAAS,mBAAyB;AAChC,QAAI,MAAO,cAAa,KAAK;AAC7B,YAAQ,WAAW,MAAM;AACvB,cAAQ;AACR,WAAK,IAAI;AAAA,IACX,GAAG,UAAU;AAAA,EACf;AAEA,WAAS,aAAmB;AAG1B,QAAI,cAAc;AAChB,wBAAkB;AAClB;AAAA,IACF;AACA,qBAAiB;AAAA,EACnB;AAEA,SAAO;AAAA,IACL,SAAS,OAAO;AAEd,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,aAAa,SAAS,IAAI,EAAG,cAAa,KAAK,IAAI;AAAA,MAC1D;AACA,iBAAW;AAAA,IACb;AAAA,IACA,WAAW;AACT,iBAAW;AAAA,IACb;AAAA,EACF;AACF;AAnGA;AAAA;AAAA;AAAA;AAAA;;;ACCA,OAAOE,YAAU;AAwCV,SAAS,aAAa,SAA6B;AACxD,QAAM,EAAE,SAAS,KAAK,QAAQ,IAAI;AAElC,iBAAe,cAAc,OAAgC;AAE3D,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,iBAAiB;AAAA,QACrB;AAAA,QACA,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAGA,UAAM,cAAc,EAAE,SAAS,MAAM,QAAQ,CAAC;AAG9C,UAAM,IAAI,aAAa;AAGvB,UAAM,IAAI,YAAY;AAGtB,UAAM,IAAI,aAAa;AAEvB,YAAQ;AAAA,MACN,mBAAmB,MAAM,SAAS,IAAI,KAAK,MAAM,MAAM,wBAAwB,EAAE;AAAA,IACnF;AAAA,EACF;AAGA,QAAM,YAAY,uBAAuB;AAAA,IACvC,SAAS;AAAA,IACT,SAAS,CAAC,QAAQ;AAGhB,cAAQ,MAAM,4BAA4B;AAC1C,YAAM,aAAa;AAGnB,UAAI,MAAM,QAAQ,YAAY,MAAM,KAAK,WAAW,OAAO,SAAS,GAAG;AACrE,mBAAW,KAAK,WAAW,QAAQ;AACjC,gBAAM,MAAM,EAAE,UAAU,OACpB,GAAG,EAAE,SAAS,IAAI,GAAG,EAAE,SAAS,QAAQ,OAAO,IAAI,EAAE,SAAS,IAAI,KAAK,EAAE,OACzE;AACJ,kBAAQ,MAAM,KAAK,GAAG,GAAG,EAAE,QAAQ,eAAe,EAAE;AAAA,QACtD;AAAA,MACF,OAAO;AACL,gBAAQ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACrE;AAAA,IACF;AAAA,EACF,CAAC;AAMD,QAAM,eAAe,CAAC,mBAAmB,iBAAiB;AAC1D,QAAM,iBAAiB,IAAI,IAAI,aAAa,IAAI,CAAC,MAAMA,OAAK,QAAQ,SAAS,CAAC,CAAC,CAAC;AAGhF,QAAM,aAAa,CAAC,OAAO,iBAAiB,GAAG,YAAY;AAC3D,QAAM,eAAe,oBAAI,IAAI,CAAC,GAAG,gBAAgBA,OAAK,QAAQ,SAAS,eAAe,CAAC,CAAC;AACxF,QAAM,UAAU,YAAS,MAAM,YAAY;AAAA,IACzC,KAAK;AAAA,IACL,eAAe;AAAA,IACf,SAAS,CAAC,UAAU,UAAU;AAE5B,YAAM,QAAQ,SAAS,MAAM,OAAO;AACpC,UACE,MAAM,SAAS,cAAc,KAC7B,MAAM,SAAS,MAAM,KACrB,MAAM,SAAS,QAAQ,KACtB,WAAW,MAAM,SAAS,OAAO,GAClC;AACA,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,MAAO,QAAO;AAEnB,UAAI,MAAM,YAAY,EAAG,QAAO;AAEhC,aAAO,CAAC,SAAS,SAAS,KAAK,KAAK,CAAC,SAAS,SAAS,KAAK;AAAA,IAC9D;AAAA,EACF,CAAC;AAKD,UAAQ,GAAG,OAAO,CAAC,SAAS,gBAAgB,IAAI,CAAC;AACjD,UAAQ,GAAG,UAAU,CAAC,SAAS,gBAAgB,IAAI,CAAC;AASpD,WAAS,gBAAgB,MAAoB;AAC3C,UAAM,MAAMA,OAAK,QAAQ,SAAS,IAAI;AACtC,QAAI,aAAa,IAAI,GAAG,GAAG;AACzB,gBAAU,SAAS;AACnB;AAAA,IACF;AACA,QAAI,4BAA4B,KAAK,IAAI,QAAQ,OAAO,GAAG,CAAC,KAAK,IAAI,SAAS,OAAO,GAAG;AACtF;AAAA,IACF;AACA,cAAU,SAAS,CAAC,GAAG,CAAC;AAAA,EAC1B;AACA,UAAQ,GAAG,UAAU,MAAM;AAEzB,cAAU,SAAS;AAAA,EACrB,CAAC;AACD,UAAQ,GAAG,SAAS,CAAC,QAAQ;AAC3B,YAAQ,MAAM,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EACpF,CAAC;AACD,UAAQ,GAAG,SAAS,MAAM;AACxB,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,WAAW,OAAO,KAAK,OAAO,EAAE;AACtC,UAAM,YAAY,OAAO,OAAO,OAAO,EAAE,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AACrF,YAAQ,IAAI,oBAAoB,QAAQ,YAAY,SAAS,kBAAkB;AAAA,EACjF,CAAC;AAED,UAAQ,IAAI,sBAAsB;AACpC;AAtKA;AAAA;AAAA;AAAA,IAAAC;AAEA;AACA;AAEA;AAAA;AAAA;;;ACMO,SAAS,qBAAqB,QAAwC;AAC3E,QAAM,MAAM,oBAAI,IAA2B;AAE3C,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,GAAG,MAAM,MAAM,IAAI,MAAM,OAAO;AAC5C,UAAM,WAAW,IAAI,IAAI,GAAG;AAC5B,QAAI,UAAU;AACZ,eAAS,MAAM,KAAK,MAAM,QAAQ;AAAA,IACpC,OAAO;AACL,UAAI,IAAI,KAAK;AAAA,QACX,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM;AAAA,QACf,OAAO,CAAC,MAAM,QAAQ;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,YAA6B,CAAC;AACpC,aAAW,YAAY,IAAI,OAAO,GAAG;AACnC,QAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,gBAAU,KAAK,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,SAAO;AACT;AArCA;AAAA;AAAA;AAAA;AAAA;;;ACkDA,SAAS,aAAa,QAAwC;AAC5D,MAAI,QAAQ,eAAe,IAAI,MAAM;AACrC,MAAI,MAAO,QAAO;AAElB,UAAQ,EAAE,QAAQ,oBAAI,IAAI,GAAG,qBAAqB,oBAAI,IAAI,GAAG,UAAU,CAAC,EAAE;AAC1E,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,WAAW;AACnB,YAAM,SAAS,KAAK;AAAA,QAClB;AAAA,QACA,UAAU,MAAM,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO;AAAA,MACnD,CAAC;AAAA,IACH,OAAO;AACL,YAAM,OAAO,IAAI,GAAG,MAAM,MAAM,IAAI,MAAM,OAAO,IAAI,KAAK;AAC1D,UAAI,UAAU,MAAM,oBAAoB,IAAI,MAAM,OAAO;AACzD,UAAI,CAAC,SAAS;AACZ,kBAAU,oBAAI,IAAI;AAClB,cAAM,oBAAoB,IAAI,MAAM,SAAS,OAAO;AAAA,MACtD;AACA,cAAQ,IAAI,MAAM,MAAM;AAAA,IAC1B;AAAA,EACF;AAEA,iBAAe,IAAI,QAAQ,KAAK;AAChC,SAAO;AACT;AAGA,SAAS,WAAW,QAAwC;AAC1D,MAAI,QAAQ,aAAa,IAAI,MAAM;AACnC,MAAI,MAAO,QAAO;AAElB,UAAQ,EAAE,QAAQ,oBAAI,IAAI,GAAG,UAAU,CAAC,EAAE;AAC1C,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,WAAW;AACnB,YAAM,SAAS,KAAK,KAAK;AAAA,IAC3B,OAAO;AACL,YAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAAA,IACvC;AAAA,EACF;AAEA,eAAa,IAAI,QAAQ,KAAK;AAC9B,SAAO;AACT;AAcO,SAAS,WAAW,QAAuB,QAAgBC,QAAiC;AACjG,QAAM,QAAQ,aAAa,MAAM;AACjC,QAAM,QAAQ,OAAO,YAAY;AAEjC,QAAM,MAAM,cAAc,OAAO,OAAOA,MAAI;AAC5C,MAAI,IAAK,QAAO;AAEhB,MAAI,UAAU,QAAQ;AACpB,WAAO,cAAc,OAAO,OAAOA,MAAI;AAAA,EACzC;AAEA,SAAO;AACT;AAGA,SAAS,cAAc,OAAwB,QAAgBA,QAAiC;AAE9F,QAAM,YAAY,MAAM,OAAO,IAAI,GAAG,MAAM,IAAIA,MAAI,EAAE;AACtD,MAAI,WAAW;AACb,WAAO,EAAE,OAAO,WAAW,QAAQ,CAAC,EAAE;AAAA,EACxC;AAGA,aAAW,SAAS,MAAM,UAAU;AAClC,UAAM,QAAQ,MAAM;AACpB,QAAI,MAAM,WAAW,QAAQ;AAC3B;AAAA,IACF;AACA,UAAM,SAAS,cAAc,MAAM,UAAUA,QAAM,MAAM,YAAY,MAAM,UAAU;AACrF,QAAI,WAAW,MAAM;AACnB,aAAO,EAAE,OAAO,OAAO;AAAA,IACzB;AAAA,EACF;AAEA,SAAO;AACT;AASO,SAAS,aAAa,UAA2BA,QAAmC;AACzF,QAAM,QAAQ,WAAW,QAAQ;AAEjC,QAAM,YAAY,MAAM,OAAO,IAAIA,MAAI;AACvC,MAAI,WAAW;AACb,WAAO,EAAE,OAAO,WAAW,QAAQ,CAAC,EAAE;AAAA,EACxC;AAEA,aAAW,SAAS,MAAM,UAAU;AAClC,UAAM,SAAS,iBAAiB,MAAM,SAASA,QAAM,MAAM,YAAY,MAAM,UAAU;AACvF,QAAI,WAAW,MAAM;AACnB,aAAO,EAAE,OAAO,OAAO;AAAA,IACzB;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,mBAAmB,QAAuBA,QAAwB;AAChF,QAAM,QAAQ,aAAa,MAAM;AACjC,QAAM,UAAU,oBAAI,IAAY;AAGhC,QAAM,gBAAgB,MAAM,oBAAoB,IAAIA,MAAI;AACxD,MAAI,eAAe;AACjB,eAAW,UAAU,eAAe;AAClC,cAAQ,IAAI,MAAM;AAAA,IACpB;AAAA,EACF;AAGA,aAAW,SAAS,MAAM,UAAU;AAClC,UAAM,SAAS;AAAA,MACb,MAAM;AAAA,MACNA;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,IACd;AACA,QAAI,WAAW,MAAM;AACnB,cAAQ,IAAI,MAAM,MAAM,MAAM;AAAA,IAChC;AAAA,EACF;AAIA,MAAI,QAAQ,IAAI,KAAK,GAAG;AACtB,YAAQ,IAAI,MAAM;AAAA,EACpB;AAEA,SAAO,MAAM,KAAK,OAAO;AAC3B;AAQO,SAAS,iBACd,SACAA,QACA,YACA,YAC+B;AAC/B,SAAO,cAAc,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO,GAAGA,QAAM,YAAY,UAAU;AACvF;AAQA,SAAS,cACP,iBACAA,QACA,YACA,YAC+B;AAC/B,QAAM,eAAeA,OAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAGnD,MAAI,YAAY;AAGd,UAAM,mBAAmB,gBAAgB,SAAS;AAClD,QAAI,aAAa,UAAU,kBAAkB;AAC3C,aAAO;AAAA,IACT;AAEA,UAAMC,UAAiC,CAAC;AAExC,aAAS,IAAI,GAAG,IAAI,kBAAkB,KAAK;AAEzC,YAAM,aAAa,gBAAgB,CAAC;AACpC,YAAM,UAAU,aAAa,CAAC;AAE9B,UAAI,WAAW,WAAW,GAAG,GAAG;AAC9B,cAAM,YAAY,WAAW,MAAM,CAAC;AACpC,QAAAA,QAAO,SAAS,IAAI;AAAA,MACtB,WAAW,eAAe,SAAS;AACjC,eAAO;AAAA,MACT;AAAA,IACF;AAGA,UAAM,gBAAgB,aAAa,MAAM,gBAAgB,EAAE,KAAK,GAAG;AAEnE,UAAM,oBAAoB,gBAAgB,gBAAgB,EAAG,MAAM,CAAC;AACpE,IAAAA,QAAO,iBAAiB,IAAI;AAE5B,QAAI,OAAO,KAAKA,OAAM,EAAE,WAAW,WAAW,QAAQ;AACpD,aAAO;AAAA,IACT;AAEA,WAAOA;AAAA,EACT;AAGA,MAAI,gBAAgB,WAAW,aAAa,QAAQ;AAClD,WAAO;AAAA,EACT;AAEA,QAAM,SAAiC,CAAC;AAExC,WAAS,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;AAE/C,UAAM,aAAa,gBAAgB,CAAC;AACpC,UAAM,UAAU,aAAa,CAAC;AAE9B,QAAI,WAAW,WAAW,GAAG,GAAG;AAE9B,YAAM,YAAY,WAAW,MAAM,CAAC;AACpC,aAAO,SAAS,IAAI;AAAA,IACtB,WAAW,eAAe,SAAS;AAEjC,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,OAAO,KAAK,MAAM,EAAE,WAAW,WAAW,QAAQ;AACpD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AA9SA,IA8CM,gBACA;AA/CN;AAAA;AAAA;AA8CA,IAAM,iBAAiB,oBAAI,QAAwC;AACnE,IAAM,eAAe,oBAAI,QAAwC;AAAA;AAAA;;;ACrC1D,SAAS,cAAc,QAAiC,YAA6B;AAE1F,MAAI,cAAc,UAAU,OAAO,OAAO,UAAU,MAAM,aAAa;AACrE,WAAO,OAAO,UAAU;AAAA,EAC1B;AAGA,QAAM,gBAAgB,OAAO;AAC7B,MAAI,kBAAkB,QAAQ,OAAO,kBAAkB,UAAU;AAC/D,UAAM,QAAS,cAA0C,UAAU;AACnE,QAAI,UAAU,QAAW;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AA1BA;AAAA;AAAA;AAAA;AAAA;;;ACGO,SAAS,oBACd,OACA,QACA,UACkD;AAClD,MAAI,OAAO,UAAU,YAAY;AAC/B,UAAM,IAAI;AAAA,MACR,iBAAiB,QAAQ,iDAAiD,MAAM,+BAClD,OAAO,KAAK;AAAA,IAC5C;AAAA,EACF;AACF;AAdA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,YAAU;AACjB,OAAOC,UAAQ;AA2Cf,SAAS,eAAe,eAAuB,gBAAiC;AAC9E,MAAI;AACF,UAAM,UAAUA,KAAG,SAAS,aAAa;AACzC,UAAM,WAAWA,KAAG,SAAS,cAAc;AAC3C,WAAO,SAAS,WAAW,QAAQ;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA4BA,SAAS,yBAA2C;AAClD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,eAAe,oBAAI,IAAI;AAAA,IACvB,kBAAkB,oBAAI,IAAI;AAAA,IAC1B,sBAAsB,oBAAI,IAAI;AAAA,IAC9B,2BAA2B,oBAAI,IAAI;AAAA,EACrC;AACF;AA2BO,SAAS,qBAA2B;AACzC,QAAM,cAAc,MAAM;AAC1B,QAAM,qBAAqB,MAAM;AACjC,kBAAgB,MAAM;AACxB;AAyBA,eAAsB,eACpB,eACA,SACA,MACkB;AAElB,QAAMC,YAAW,MAAM,qBAAqB,IAAI,aAAa;AAC7D,MAAIA,WAAU;AACZ,UAAMA,UAAS,MAAM,MAAM;AAAA,IAE3B,CAAC;AACD,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,cAAc,IAAI,aAAa,GAAG;AAC1C,WAAO;AAAA,EACT;AAGA,MAAI,CAACD,KAAG,WAAW,aAAa,GAAG;AACjC,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,4BAA4B,eAAe,SAAS,IAAI;AAC5E,MAAI,eAAe,eAAe,eAAe,WAAW,GAAG;AAC7D,UAAM,cAAc,IAAI,aAAa;AACrC,WAAO;AAAA,EACT;AAGA,QAAM,kBAAkB,YAAY;AAElC,UAAM,EAAE,YAAY,IAAI,MAAM,uBAAuB,CAAC,aAAa,GAAG,OAAO;AAC7E,UAAM,QAAQ,CAAC,aAAa;AAC5B,eAAW,OAAO,aAAa;AAC7B,UAAI,MAAM,cAAc,IAAI,GAAG,EAAG;AAClC,YAAM,aAAa,4BAA4B,KAAK,SAAS,IAAI;AACjE,UAAI,cAAc,eAAe,KAAK,UAAU,EAAG;AACnD,YAAM,KAAK,GAAG;AAAA,IAChB;AAEA,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AACD,eAAW,QAAQ,OAAO;AACxB,YAAM,cAAc,IAAI,IAAI;AAAA,IAC9B;AACA,UAAM,cAAc,IAAI,aAAa;AAAA,EACvC,GAAG;AAEH,QAAM,qBAAqB,IAAI,eAAe,cAAc;AAC5D,MAAI;AACF,UAAM;AACN,WAAO;AAAA,EACT,UAAE;AACA,UAAM,qBAAqB,OAAO,aAAa;AAAA,EACjD;AACF;AAaA,eAAsB,0BACpB,iBACA,SACe;AACf,MAAI,CAAC,qBAAqB,KAAK,gBAAgB,WAAW,EAAG;AAC7D,QAAM,OAAO,WAAW;AACxB,MAAI,CAAC,KAAM;AAEX,aAAW,UAAU,iBAAiB;AACpC,UAAM,aAAa,qBAAqB,QAAQ,SAAS,IAAI;AAC7D,QAAI;AACF,YAAM,eAAe,YAAY,SAAS,IAAI;AAAA,IAChD,SAAS,KAAK;AAGZ,cAAQ,MAAM,+CAA+C,UAAU,KAAK,GAAG;AAAA,IACjF;AAAA,EACF;AACF;AAOA,SAAS,4BACP,eACA,SACA,MACe;AACf,QAAM,MAAMD,OAAK,SAAS,SAAS,aAAa,EAAE,QAAQ,OAAO,GAAG;AACpE,MAAI,CAAC,IAAI,WAAW,MAAM,EAAG,QAAO;AACpC,QAAM,gBAAgB,IAAI,MAAM,CAAC;AACjC,QAAM,QAAQ,cAAc,QAAQ,SAAS,KAAK;AAClD,SAAOA,OAAK,QAAQ,SAAS,MAAM,KAAK;AAC1C;AASO,SAAS,wBAA8B;AAC5C,QAAM,iBAAiB,MAAM;AAC7B,QAAM,0BAA0B,MAAM;AACxC;AA2BA,eAAsB,sBACpB,YACA,eACA,QACA,SACA,MACkB;AAElB,QAAME,YAAW,MAAM,0BAA0B,IAAI,UAAU;AAC/D,MAAIA,WAAU;AACZ,UAAMA,UAAS,MAAM,MAAM;AAAA,IAE3B,CAAC;AACD,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,iBAAiB,IAAI,UAAU,GAAG;AAC1C,WAAO;AAAA,EACT;AAGA,QAAM,cAAcF,OAAK,QAAQ,SAAS,aAAa;AACvD,QAAM,gBAAgB,qBAAqB,aAAa,SAAS,IAAI;AACrE,MAAI,CAACC,KAAG,WAAW,aAAa,GAAG;AACjC,WAAO;AAAA,EACT;AAGA,MAAI,eAAe,eAAe,UAAU,GAAG;AAC7C,UAAM,iBAAiB,IAAI,UAAU;AACrC,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,aAAa;AACpE,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AAIA,QAAM,gBAAgBD,OAAK,SAAS,SAAS,aAAa,EAAE,QAAQ,OAAO,GAAG;AAC9E,QAAM,eAAe,WAAW,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU,cAAc,EAAE;AAG9E,QAAM,mBAAmB,YAAY;AAEnC,UAAM,oBAAoB,cAAc,SAAS,IAAI;AACrD,UAAM,iBAAiB,IAAI,UAAU;AAAA,EACvC,GAAG;AAEH,QAAM,0BAA0B,IAAI,YAAY,eAAe;AAC/D,MAAI;AACF,UAAM;AACN,WAAO;AAAA,EACT,UAAE;AACA,UAAM,0BAA0B,OAAO,UAAU;AAAA,EACnD;AACF;AAUA,eAAsB,kBACpB,QACA,SACA,MACe;AACf,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,SAAS,QAAQ;AAC1B,UAAM,aAAa,qBAAqB,MAAM,UAAU,MAAM,OAAO;AACrE,QAAI,QAAQ,IAAI,UAAU,EAAG;AAC7B,YAAQ,IAAI,UAAU;AACtB,QAAI;AACF,YAAMC,KAAG,SAAS,OAAO,UAAU;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAeO,SAAS,qBAAqB,aAAqB,SAAiB,MAAsB;AAC/F,QAAM,SAAS,gBAAgB,IAAI,WAAW;AAC9C,MAAI,OAAQ,QAAO;AAEnB,QAAM,MAAMD,OAAK,SAAS,SAAS,WAAW,EAAE,QAAQ,OAAO,GAAG;AAElE,MAAI,iBAAiB;AACrB,MAAI,eAAe,WAAW,GAAG,IAAI,GAAG,GAAG;AACzC,qBAAiB,eAAe,MAAM,KAAK,SAAS,CAAC;AAAA,EACvD;AAEA,QAAM,SAAS,OAAO,cAAc;AAEpC,QAAM,QAAQ,OAAO,QAAQ,SAAS,KAAK;AAC3C,QAAM,QAAQA,OAAK,QAAQ,SAAS,KAAK;AACzC,MAAI;AACJ,MAAIC,KAAG,WAAW,KAAK,GAAG;AACxB,aAAS;AAAA,EACX,OAAO;AAEL,aAASD,OAAK,QAAQ,SAAS,MAAM;AAAA,EACvC;AACA,kBAAgB,IAAI,aAAa,MAAM;AACvC,SAAO;AACT;AAYO,SAAS,sBAAsB,SAAwB;AAC5D,QAAM,UAAU;AAClB;AAEO,SAAS,uBAAgC;AAC9C,SAAO,MAAM;AACf;AAOO,SAAS,WAAW,MAAoB;AAC7C,QAAM,UAAU;AAClB;AAEO,SAAS,aAAiC;AAC/C,SAAO,MAAM;AACf;AAjcA,IA2FM,OA6SA;AAxYN;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAsFA,IAAM,QAA0B,uBAAuB;AA6SvD,IAAM,kBAAkB,oBAAI,IAAoB;AAAA;AAAA;;;AC1WhD,eAAsB,gBACpB,UACA,QACA,SACsB;AAOtB,MAAI,qBAAqB,KAAK,SAAS;AACrC,UAAM,OAAO,WAAW;AACxB,QAAI,MAAM;AACR,YAAM,aAAa,qBAAqB,UAAU,SAAS,IAAI;AAC/D,UAAI,YAAY;AACd,YAAI;AACF,gBAAM,eAAe,YAAY,SAAS,IAAI;AAAA,QAChD,SAAS,YAAY;AACnB,gBAAM,SAAS,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU;AACnF,gBAAM,IAAI,MAAM,mCAAmC,UAAU,MAAM,MAAM,IAAI;AAAA,YAC3E,OAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,oBAAoB,UAAU,qBAAqB,CAAC;AAAA,EACrE,SAAS,KAAc;AACrB,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI,MAAM,gCAAgC,QAAQ,MAAM,MAAM,IAAI,EAAE,OAAO,IAAI,CAAC;AAAA,EACxF;AAEA,QAAM,UAAU,cAAc,QAAQ,MAAM;AAC5C,sBAAoB,SAAS,QAAQ,QAAQ;AAE7C,SAAO,EAAE,SAAS,OAAO;AAC3B;AAvEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;;;ACgDO,SAAS,eAAe,OAAyB;AACtD,MAAI,MAAM;AAGV,MAAI,MAAM,YAAY,QAAW;AAC/B,WAAO,KAAK,MAAM,OAAO;AAAA;AAAA,EAC3B;AAGA,MAAI,MAAM,UAAU,QAAW;AAC7B,WAAO,UAAU,MAAM,KAAK;AAAA;AAAA,EAC9B;AAGA,MAAI,MAAM,OAAO,QAAW;AAC1B,WAAO,OAAO,MAAM,EAAE;AAAA;AAAA,EACxB;AAGA,MAAI,MAAM,UAAU,QAAW;AAC7B,WAAO,UAAU,MAAM,KAAK;AAAA;AAAA,EAC9B;AAGA,MAAI,MAAM,SAAS,QAAW;AAC5B,QAAI;AACJ,QAAI,OAAO,MAAM,SAAS,UAAU;AAClC,gBAAU,MAAM;AAAA,IAClB,WAAW,MAAM,SAAS,MAAM;AAC9B,gBAAU;AAAA,IACZ,OAAO;AACL,gBAAU,KAAK,UAAU,MAAM,IAAI;AAAA,IACrC;AAEA,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAW,QAAQ,OAAO;AACxB,aAAO,SAAS,IAAI;AAAA;AAAA,IACtB;AAAA,EACF;AAGA,SAAO;AACP,SAAO;AACT;AA2CO,SAAS,kBAA6B;AAC3C,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,aAAiE;AACrE,MAAI,SAAS;AACb,MAAI,UAAU;AAEd,QAAM,SAAS,IAAI,eAA2B;AAAA,IAC5C,MAAM,GAAG;AACP,mBAAa;AAAA,IACf;AAAA,IACA,SAAS;AAEP,gBAAU;AACV,eAAS;AACT,mBAAa;AAAA,IACf;AAAA,EACF,CAAC;AAED,QAAM,WAAW,IAAI,SAAS,QAAQ;AAAA,IACpC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd;AAAA,EACF,CAAC;AAED,QAAM,SAAoB;AAAA,IACxB,KAAK,OAAuB;AAC1B,UAAI,UAAU,CAAC,WAAY;AAC3B,YAAM,OAAO,eAAe,KAAK;AACjC,iBAAW,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACzC;AAAA,IAEA,QAAQ,OAAkC;AACxC,UAAI,UAAU,CAAC,WAAY;AAC3B,YAAM,QAAQ,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK,IAAI;AAClE,iBAAW,QAAQ,KAAK;AAAA,IAC1B;AAAA,IAEA,UAAU,OAAsB;AAC9B,UAAI,UAAU,CAAC,WAAY;AAC3B,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,OAAO,eAAe,EAAE,OAAO,SAAS,MAAM,QAAQ,CAAC;AAC7D,UAAI;AACF,mBAAW,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,MACzC,UAAE;AACA,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAAA,IAEA,QAAc;AACZ,UAAI,OAAQ;AACZ,eAAS;AACT,UAAI,YAAY;AACd,YAAI;AACF,qBAAW,MAAM;AAAA,QACnB,QAAQ;AAAA,QAER;AACA,qBAAa;AAAA,MACf;AAAA,IACF;AAAA,IAEA,IAAI,SAAkB;AACpB,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,UAAmB;AACrB,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,WAAqB;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAxNA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAEa;AAFb;AAAA;AAAA;AAEO,IAAM,aAAN,cAAyB,MAAM;AAAA,MACpC,YACkB,MAChB,SACgB,YAChB;AACA,cAAM,OAAO;AAJG;AAEA;AAGhB,aAAK,OAAO;AAAA,MACd;AAAA,MANkB;AAAA,MAEA;AAAA,IAKpB;AAAA;AAAA;;;ACKA,SAAS,iBAAiB,QAAmC;AAE3D,QAAM,SAAS,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,SAAS,eAAe;AAC3F,SAAO,SAAS,MAAM;AACxB;AApBA,IAsBa,iBAUA,oBAOA,uBAWA,eAoBA;AAtEb;AAAA;AAAA;AAAA;AAsBO,IAAM,kBAAN,cAA8B,WAAW;AAAA,MAC9C,YACE,SACgB,QAChB;AACA,cAAM,oBAAoB,SAAS,iBAAiB,MAAM,CAAC;AAF3C;AAGhB,aAAK,OAAO;AAAA,MACd;AAAA,MAJkB;AAAA,IAKpB;AAEO,IAAM,qBAAN,cAAiC,WAAW;AAAA,MACjD,YAAYG,QAAc;AACxB,cAAM,mBAAmB,oBAAoBA,MAAI,IAAI,GAAG;AACxD,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAEO,IAAM,wBAAN,cAAoC,WAAW;AAAA,MACpD,YACE,QACAA,QACgB,gBAChB;AACA,cAAM,sBAAsB,UAAU,MAAM,oBAAoBA,MAAI,IAAI,GAAG;AAF3D;AAGhB,aAAK,OAAO;AAAA,MACd;AAAA,MAJkB;AAAA,IAKpB;AAEO,IAAM,gBAAN,cAA4B,WAAW;AAAA,MAC5C,YAAY,SAAiB;AAC3B,cAAM,kBAAkB,SAAS,GAAG;AACpC,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAeO,IAAM,uBAAN,cAAmC,WAAW;AAAA,MACnD,YAAY,SAAiB;AAC3B,cAAM,qBAAqB,sCAAsC,OAAO,UAAU,GAAG;AACrF,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA;AAAA;;;ACtDO,SAAS,UAAU,MAAwC;AAChE,SAAO,EAAE,KAAK;AAChB;AAOO,SAAS,YAAY,GAIA;AAC1B,QAAM,QAAiC,EAAE,SAAS,EAAE,QAAQ;AAC5D,MAAI,EAAE,SAAS,OAAW,OAAM,OAAO,EAAE;AACzC,SAAO,EAAE,MAAM;AACjB;AAGO,SAAS,kBACd,QAC4B;AAC5B,SAAQ,QAAsD;AAChE;AAGO,SAAS,YAAY,QAA6C;AACvE,SAAO,kBAAkB,MAAM,GAAG,MAAM;AAC1C;AAGO,SAAS,cAAc,QAA6C;AACzE,SAAO,kBAAkB,MAAM,GAAG,QAAQ;AAC5C;AAOO,SAAS,OAAO,MAAe,SAAS,KAAK,cAAsC;AACxF,SAAO,QAAQ,MAAM,QAAQ,YAAY;AAC3C;AAGA,SAAS,QAAQ,MAAe,QAAgB,cAAsC;AACpF,QAAM,UAAU,IAAI,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAClE,MAAI,cAAc;AAChB,UAAM,QAAQ,IAAI,QAAQ,YAAY;AACtC,UAAM,QAAQ,CAAC,OAAO,QAAQ,QAAQ,IAAI,KAAK,KAAK,CAAC;AAAA,EACvD;AACA,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG,EAAE,QAAQ,QAAQ,CAAC;AAC/D;AAcO,SAAS,aACd,QACA,QACS;AACT,MAAI,kBAAkB,SAAU,QAAO;AACvC,SAAO,YAAY,MAAM,EAAE,MAAM;AACnC;AAUO,SAAS,mBACd,SACA,QACU;AACV,QAAM,SAAS,cAAc,MAAM;AACnC,QAAM,OAAO,OAAO;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,MAAM,QAAQ;AAAA,IACd,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,SAAO,OAAO,MAAM,QAAQ,UAAU,GAAG;AAC3C;AAkBO,SAAS,oBACd,OACA,QACU;AACV,QAAM,SAAS,cAAc,MAAM;AAEnC,MAAI,iBAAiB,iBAAiB;AACpC,UAAMC,QAAO,OAAO;AAAA,MAClB,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,IACjB,CAAC;AAID,UAAM,UACJ,OAAOA,UAAS,YAAYA,UAAS,OAChC,EAAE,GAAIA,MAAiC,IACxC,EAAE,OAAOA,MAAK;AACpB,UAAM,gBAAgB,QAAQ;AAC9B,QAAI,iBAAiB,OAAO,kBAAkB,UAAU;AACtD,cAAQ,QAAQ,EAAE,GAAG,eAAe,QAAQ,MAAM,OAAO;AAAA,IAC3D,WAAW,OAAOA,UAAS,YAAYA,UAAS,MAAM;AAEpD,cAAQ,SAAS,MAAM;AAAA,IACzB,OAAO;AACL,cAAQ,QAAQ,EAAE,QAAQ,MAAM,OAAO;AAAA,IACzC;AACA,WAAO,OAAO,SAAS,MAAM,UAAU;AAAA,EACzC;AAEA,MAAI,iBAAiB,uBAAuB;AAC1C,UAAMA,QAAO,OAAO;AAAA,MAClB,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,IACjB,CAAC;AACD,WAAO,OAAOA,OAAM,MAAM,YAAY,EAAE,OAAO,MAAM,eAAe,KAAK,IAAI,EAAE,CAAC;AAAA,EAClF;AAEA,MAAI,iBAAiB,sBAAsB;AACzC,UAAMA,QAAO,OAAO;AAAA,MAClB,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,IACjB,CAAC;AACD,WAAO,OAAOA,OAAM,MAAM,UAAU;AAAA,EACtC;AAEA,MAAI,iBAAiB,YAAY;AAC/B,UAAMA,QAAO,OAAO;AAAA,MAClB,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,IACjB,CAAC;AACD,WAAO,OAAOA,OAAM,MAAM,UAAU;AAAA,EACtC;AAGA,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,QAAM,OAAO,OAAO,EAAE,QAAQ,KAAK,MAAM,kBAAkB,QAAQ,CAAC;AACpE,SAAO,OAAO,MAAM,GAAG;AACzB;AAnMA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;;;ACKA,SAAS,aAAa,cAA2C;AAC/D,QAAM,UAAU,oBAAI,IAAoB;AACxC,MAAI,CAAC,aAAc,QAAO;AAC1B,aAAW,QAAQ,aAAa,MAAM,GAAG,GAAG;AAC1C,UAAM,CAAC,MAAM,GAAG,IAAI,IAAI,KAAK,MAAM,GAAG;AACtC,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,SAAS;AACX,cAAQ,IAAI,SAAS,KAAK,KAAK,GAAG,EAAE,KAAK,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,gBAAgB,MAAc,OAAe,SAAiC;AACrF,MAAI,SAAS,GAAG,IAAI,IAAI,KAAK;AAC7B,MAAI,SAAS,OAAQ,WAAU,YAAY,QAAQ,MAAM;AACzD,MAAI,SAAS,KAAM,WAAU,UAAU,QAAQ,IAAI;AACnD,MAAI,SAAS,WAAW,OAAW,WAAU,aAAa,QAAQ,MAAM;AACxE,MAAI,SAAS,QAAS,WAAU,aAAa,QAAQ,QAAQ,YAAY,CAAC;AAC1E,MAAI,SAAS,SAAU,WAAU;AACjC,MAAI,SAAS,OAAQ,WAAU;AAC/B,MAAI,SAAS,SAAU,WAAU,cAAc,QAAQ,QAAQ;AAC/D,SAAO;AACT;AAYO,SAAS,cACd,SACA,QACA,SAAkC,CAAC,GACnC,KAAa,IACb,YACc;AACd,SAAO,qBAAqB,SAAS,IAAI,IAAI,QAAQ,GAAG,GAAG,QAAQ,QAAQ,IAAI,UAAU;AAC3F;AASO,SAAS,qBACd,SACA,KACA,QACA,SAAkC,CAAC,GACnC,KAAa,IACb,YACc;AACd,QAAM,OAAqB,EAAE,SAAS,CAAC,GAAG,YAAY,CAAC,EAAE;AACzD,QAAM,gBAAgB,aAAa,QAAQ,QAAQ,IAAI,QAAQ,KAAK,EAAE;AACtE,QAAM,aAAqC,CAAC;AAC5C,aAAW,CAAC,KAAK,GAAG,KAAK,eAAe;AACtC,eAAW,GAAG,IAAI;AAAA,EACpB;AAEA,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA,OAAO,IAAI;AAAA,IACX,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,MAAM,IAAI;AAAA,IACV;AAAA,IACA,IAAI,QAAQ,QAAQ,IAAI,YAAY,KAAK;AAAA,IACzC,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IAEA,UAAU,QAAgB;AACxB,WAAK,SAAS;AAAA,IAChB;AAAA,IAEA,UAAU,KAAa,OAAe;AACpC,WAAK,QAAQ,GAAG,IAAI;AAAA,IACtB;AAAA,IAEA,QAAQ,OAAe;AACrB,WAAK,QAAQ,MAAM,IAAI;AAAA,IACzB;AAAA,IAEA,SAASC,MAAa,SAAS,KAAe;AAC5C,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB;AAAA,QACA,SAAS,EAAE,UAAUA,KAAI;AAAA,MAC3B,CAAC;AAAA,IACH;AAAA,IAEA,KAAK,MAAe,QAA2B;AAC7C,aAAO,OAAO,MAAM,UAAU,GAAG;AAAA,IACnC;AAAA,IAEA,KAAK,MAAc,QAA2B;AAC5C,YAAM,UAAkC,EAAE,gBAAgB,2BAA2B;AACrF,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ,UAAU;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,UAAU,MAAkC;AAC1C,aAAO,cAAc,IAAI,IAAI;AAAA,IAC/B;AAAA,IAEA,UAAU,MAAc,OAAe,SAA+B;AACpE,WAAK,WAAW,KAAK,gBAAgB,MAAM,OAAO,OAAO,CAAC;AAAA,IAC5D;AAAA,IAEA,aAAa,MAAoB;AAC/B,WAAK,WAAW,KAAK,gBAAgB,MAAM,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;AAAA,IAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,MAAiB;AACf,YAAM,SAAS,gBAAgB;AAC/B,YAAM,aAAa;AAInB,iBAAW,gBAAgB,OAAO;AAClC,iBAAW,cAAc;AACzB,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWA,GAAG,MAAyB;AAC1B,YAAM,OAAO,aAAa,MAAM,MAAM;AAGtC,aAAO,OAAO,MAAM,GAAG;AAAA,IACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcA,KAAK,SAAgC;AACnC,aAAO,mBAAmB,SAAS,MAAM;AAAA,IAC3C;AAAA,EACF;AAGA,MAAI,YAAY;AACd,QAAI,aAAa;AAAA,EACnB;AAGA,QAAM,SAAS,QAAQ;AACvB,MAAI,OAAO,WAAW,YAAY;AAChC,WAAO,GAAG;AAAA,EACZ;AAEA,SAAO;AACT;AArMA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;;;ACSO,SAAS,cAAc,QAAkD;AAC9E,QAAM,SAAkC,CAAC;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,UAAM,WAAW,OAAO,GAAG;AAC3B,QAAI,aAAa,QAAW;AAC1B,aAAO,GAAG,IAAI;AAAA,IAChB,WAAW,MAAM,QAAQ,QAAQ,GAAG;AAClC,eAAS,KAAK,KAAK;AAAA,IACrB,OAAO;AACL,aAAO,GAAG,IAAI,CAAC,UAAU,KAAK;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAxBA;AAAA;AAAA;AAAA;AAAA;;;ACEO,SAAS,cAAc,MAA+B;AAC3D,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,WAAO,EAAE,SAAS,MAAM,KAAK;AAAA,EAC/B,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,OAAO,oBAAoB;AAAA,EACtD;AACF;AATA;AAAA;AAAA;AAAA;AAAA;;;AC2BA,eAAsB,eAAe,SAA4C;AAC/E,QAAM,WAAW,MAAM,QAAQ,SAAS;AACxC,QAAM,SAA4C,CAAC;AACnD,QAAM,QAAwB,CAAC;AAE/B,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,GAAG;AAC7C,QAAI,iBAAiB,MAAM;AACzB,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU,MAAM;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM,MAAM,YAAY;AAAA,MACvC,CAAC;AAAA,IACH,OAAO;AAEL,UAAI,OAAO,QAAQ;AACjB,cAAM,WAAW,OAAO,GAAG;AAC3B,YAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,mBAAS,KAAK,KAAK;AAAA,QACrB,OAAO;AAEL,iBAAO,GAAG,IAAI,CAAC,UAAW,KAAK;AAAA,QACjC;AAAA,MACF,OAAO;AACL,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,MAAM;AACzB;AA1DA;AAAA;AAAA;AAAA;AAAA;;;ACoCA,eAAsB,oBACpB,QACA,SACA,KACkB;AAClB,QAAM,YAAY,sBAAsB,MAAM;AAG9C,MAAI,cAAc,QAAQ;AACxB,UAAM,cAAc,QAAQ,QAAQ,IAAI,cAAc,KAAK;AAG3D,QAAI,YAAY,SAAS,qBAAqB,GAAG;AAC/C,aAAO,eAAe,OAAO;AAAA,IAC/B;AAGA,QAAI,YAAY,SAAS,mCAAmC,GAAG;AAC7D,YAAMC,QAAO,MAAM,QAAQ,KAAK;AAChC,UAAI,YAAYA,KAAI,EAAG,QAAO;AAC9B,YAAM,SAAS,IAAI,gBAAgBA,KAAI;AACvC,YAAM,MAA8B,CAAC;AACrC,iBAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,YAAI,GAAG,IAAI;AAAA,MACb;AACA,aAAO;AAAA,IACT;AAGA,UAAM,OAAO,MAAM,QAAQ,KAAK;AAGhC,QAAI,YAAY,IAAI,GAAG;AACrB,aAAO;AAAA,IACT;AAGA,UAAM,SAAS,cAAc,IAAI;AACjC,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,gBAAgB,yDAAiB;AAAA,QACzC;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,UAAU;AAAA,UACV,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO,OAAO;AAAA,EAChB;AAGA,SAAO,cAAc,IAAI,YAAY;AACvC;AAGA,SAAS,YAAY,MAAuB;AAC1C,SAAO,KAAK,WAAW,KAAK,CAAC,KAAK,KAAK,IAAI;AAC7C;AAeA,eAAsB,0BAA0B,SAAoC;AAClF,QAAM,OAAO,MAAM,QAAQ,KAAK;AAChC,MAAI,YAAY,IAAI,EAAG,QAAO;AAC9B,QAAM,SAAS,cAAc,IAAI;AACjC,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,gBAAgB,yDAAiB;AAAA,MACzC;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,UAAU;AAAA,QACV,UAAU;AAAA,QACV,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,OAAO;AAChB;AA9HA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;;;ACJO,SAAS,cAAc,OAAkD;AAC9E,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,OAAO,eAAe,KAAK;AACzC,SAAO,UAAU,QAAQ,UAAU,OAAO;AAC5C;AAbA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,SAAS,iBAAiB,UAAoB,SAAuC;AAC1F,QAAM,WAAW,QAAQ,IAAI,QAAQ;AACrC,MAAI,UAAU;AACZ,WAAO,OAAO,UAAU,OAAO;AAC/B;AAAA,EACF;AACA,UAAQ,IAAI,UAAU,EAAE,GAAG,QAAQ,CAAC;AACtC;AAKO,SAAS,0BAA0B,UAAwD;AAChG,QAAM,UAAU,QAAQ,IAAI,QAAQ;AACpC,MAAI,QAAS,SAAQ,OAAO,QAAQ;AACpC,SAAO;AACT;AAGO,SAAS,kBAAkB,MAA6B;AAC7D,SAAO,KAAK,WAAW,UAAa,KAAK,WAAW,WAAW;AACjE;AA3CA,IAeM;AAfN;AAAA;AAAA;AAeA,IAAM,UAAU,oBAAI,QAA0C;AAAA;AAAA;;;ACI9D,eAAsB,WAAW,OAAgB,MAAwC;AAEvF,MAAI,iBAAiB,SAAS;AAC5B,WAAO,WAAW,MAAM,OAAO,IAAI;AAAA,EACrC;AAGA,QAAM,YAAY,CAACC,aAA2B;AAC5C,QAAI,CAAC,KAAM;AACX,eAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAK,OAAO,GAAG;AACrD,MAAAA,SAAQ,IAAI,KAAK,GAAG;AAAA,IACtB;AACA,eAAW,UAAU,KAAK,cAAc,CAAC,GAAG;AAC1C,MAAAA,SAAQ,OAAO,cAAc,MAAM;AAAA,IACrC;AAAA,EACF;AAGA,MAAI,iBAAiB,UAAU;AAC7B,QACE,SACC,KAAK,WAAW,UACf,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,KACnC,KAAK,WAAW,SAAS,IAC3B;AAGA,UAAI,kBAAkB,IAAI,GAAG;AAC3B,yBAAiB,OAAO,KAAK,OAAO;AACpC,eAAO;AAAA,MACT;AACA,YAAMA,WAAU,IAAI,QAAQ,MAAM,OAAO;AACzC,gBAAUA,QAAO;AACjB,aAAO,IAAI,SAAS,MAAM,MAAM;AAAA,QAC9B,QAAQ,KAAK,UAAU,MAAM;AAAA,QAC7B,SAAAA;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAGA,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAMA,WAAU,IAAI,QAAQ;AAC5B,cAAUA,QAAO;AACjB,WAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,SAAAA,SAAQ,CAAC;AAAA,EAC/C;AAGA,MAAI,OAAO,mBAAmB,eAAe,iBAAiB,gBAAgB;AAC5E,UAAMA,WAAU,IAAI,QAAQ,EAAE,gBAAgB,2BAA2B,CAAC;AAC1E,cAAUA,QAAO;AACjB,WAAO,IAAI,SAAS,OAAO;AAAA,MACzB,QAAQ,MAAM,UAAU;AAAA,MACxB,SAAAA;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,WAAW,eAAe,OAAO,SAAS,KAAK,GAAG;AAC3D,UAAMA,WAAU,IAAI,QAAQ,EAAE,gBAAgB,2BAA2B,CAAC;AAC1E,cAAUA,QAAO;AACjB,WAAO,IAAI,SAAS,OAAmB;AAAA,MACrC,QAAQ,MAAM,UAAU;AAAA,MACxB,SAAAA;AAAA,IACF,CAAC;AAAA,EACH;AACA,MAAI,iBAAiB,YAAY;AAC/B,UAAMA,WAAU,IAAI,QAAQ,EAAE,gBAAgB,2BAA2B,CAAC;AAC1E,cAAUA,QAAO;AACjB,WAAO,IAAI,SAAS,OAAmB;AAAA,MACrC,QAAQ,MAAM,UAAU;AAAA,MACxB,SAAAA;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,cAAc,KAAK,KAAK,MAAM,QAAQ,KAAK,GAAG;AAChD,UAAMC,QAAO,KAAK,UAAU,KAAK;AACjC,UAAMD,WAAU,IAAI,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAClE,cAAUA,QAAO;AACjB,WAAO,IAAI,SAASC,OAAM;AAAA,MACxB,QAAQ,MAAM,UAAU;AAAA,MACxB,SAAAD;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAMA,WAAU,IAAI,QAAQ,EAAE,gBAAgB,aAAa,CAAC;AAC5D,cAAUA,QAAO;AACjB,WAAO,IAAI,SAAS,OAAO;AAAA,MACzB,QAAQ,MAAM,UAAU;AAAA,MACxB,SAAAA;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC3D,UAAMA,WAAU,IAAI,QAAQ,EAAE,gBAAgB,aAAa,CAAC;AAC5D,cAAUA,QAAO;AACjB,WAAO,IAAI,SAAS,OAAO,KAAK,GAAG;AAAA,MACjC,QAAQ,MAAM,UAAU;AAAA,MACxB,SAAAA;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,OAAO,KAAK,UAAU,KAAK;AACjC,QAAM,UAAU,IAAI,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAClE,YAAU,OAAO;AACjB,SAAO,IAAI,SAAS,MAAM;AAAA,IACxB,QAAQ,MAAM,UAAU;AAAA,IACxB;AAAA,EACF,CAAC;AACH;AAvIA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;;;AC6BO,SAAS,qBAAmC;AACjD,MAAI,WAAW,oBAAI,IAA0B;AAC7C,SAAO;AAAA,IACL,QAAQ,OAAO;AACb,YAAM,OAAO,oBAAI,IAA0B;AAC3C,iBAAW,QAAQ,OAAO;AACxB,aAAK,IAAI,KAAK,MAAM,IAAI;AAAA,MAC1B;AACA,iBAAW;AAAA,IACb;AAAA,IACA,IAAI,MAAM;AACR,aAAO,SAAS,IAAI,IAAI;AAAA,IAC1B;AAAA,IACA,OAAO;AACL,aAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AAAA,IACrC;AAAA,IACA,QAAQ;AACN,iBAAW,oBAAI,IAAI;AAAA,IACrB;AAAA,EACF;AACF;AAmCO,SAAS,oBAAoB,MAAmC;AACrE,MAAI,WAAW,oBAAI,IAA2B;AAE9C,QAAM,WAAW,CAAC,SAAwC,SAAS,IAAI,IAAI;AAE3E,SAAO;AAAA,IACL,QAAQ,QAAQ;AACd,YAAM,OAAO,oBAAI,IAA2B;AAC5C,iBAAW,SAAS,QAAQ;AAC1B,aAAK,IAAI,MAAM,MAAM,KAAK;AAAA,MAC5B;AACA,iBAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA,cAAc,MAAM;AAClB,aAAO,SAAS,IAAI,IAAI;AAAA,IAC1B;AAAA,IACA,aAAa;AACX,YAAM,SAAS,oBAAI,IAAuB;AAC1C,iBAAW,SAAS,SAAS,OAAO,EAAG,QAAO,IAAI,MAAM,MAAM,KAAK;AACnE,aAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AAAA,IACnC;AAAA,IACA,OAAO,MAAM;AACX,YAAM,QAAQ,SAAS,IAAI;AAC3B,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,SAAS,MAAM,IAAI;AAAA,QACzB,WAAW,MAAM;AAAA,QACjB,aAAa,MAAM;AAAA,QACnB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,kBAAkB,MAAM;AACtB,YAAM,QAAQ,SAAS,IAAI;AAC3B,UAAI,CAAC,MAAO,QAAO,CAAC;AAEpB,YAAM,SAAS,oBAAI,IAA0B;AAC7C,UAAI,MAAM,OAAO;AACf,mBAAW,YAAY,MAAM,OAAO;AAClC,gBAAM,WAAW,KAAK,IAAI,QAAQ;AAClC,cAAI,SAAU,QAAO,IAAI,SAAS,MAAM,QAAQ;AAAA,QAElD;AAAA,MACF;AACA,aAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AAAA,IACnC;AAAA,IACA,iBAAiB,MAAM;AACrB,YAAM,QAAQ,SAAS,IAAI;AAC3B,UAAI,CAAC,SAAS,CAAC,MAAM,OAAQ,QAAO,CAAC;AAErC,YAAM,SAAsB,CAAC;AAC7B,iBAAW,WAAW,MAAM,QAAQ;AAClC,cAAM,MAAM,SAAS,OAAO;AAC5B,YAAI,IAAK,QAAO,KAAK,GAAG;AAAA,MAE1B;AACA,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AACN,iBAAW,oBAAI,IAAI;AAAA,IACrB;AAAA,EACF;AACF;AAeO,SAAS,sBAAqC;AACnD,MAAI,WAAW,oBAAI,IAAuB;AAC1C,SAAO;AAAA,IACL,QAAQ,QAAQ;AACd,YAAM,OAAO,oBAAI,IAAuB;AACxC,iBAAW,SAAS,QAAQ;AAC1B,aAAK,IAAI,MAAM,MAAM,KAAK;AAAA,MAC5B;AACA,iBAAW;AAAA,IACb;AAAA,IACA,OAAO,OAAO;AACZ,eAAS,IAAI,MAAM,MAAM,KAAK;AAAA,IAChC;AAAA,IACA,OAAO,MAAM;AACX,eAAS,OAAO,IAAI;AAAA,IACtB;AAAA,IACA,IAAI,MAAM;AACR,aAAO,SAAS,IAAI,IAAI;AAAA,IAC1B;AAAA,IACA,OAAO;AACL,aAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AAAA,IACrC;AAAA,IACA,QAAQ;AACN,iBAAW,oBAAI,IAAI;AAAA,IACrB;AAAA,EACF;AACF;AAeO,SAAS,yBAA2C;AACzD,MAAI,iBAA4C;AAChD,SAAO;AAAA,IACL,SAAS,SAAS;AAChB,uBAAiB;AAAA,IACnB;AAAA,IACA,IAAI,KAAK;AACP,UAAI,mBAAmB,KAAM,QAAO;AACpC,aAAO,eAAe,GAAG;AAAA,IAC3B;AAAA,IACA,QAAQ;AACN,uBAAiB;AAAA,IACnB;AAAA,EACF;AACF;AAaO,SAAS,sBAAqC;AACnD,QAAM,OAAO,mBAAmB;AAChC,QAAM,QAAQ,oBAAoB,IAAI;AACtC,QAAM,QAAQ,oBAAoB;AAClC,QAAM,cAAc,uBAAuB;AAC3C,SAAO,EAAE,MAAM,OAAO,OAAO,YAAY;AAC3C;AA9OA,IAsPa;AAtPb;AAAA;AAAA;AAsPO,IAAM,oBAAmC,oBAAoB;AAAA;AAAA;;;ACnL7D,SAAS,aAA0B;AACxC,SAAO,kBAAkB,MAAM,WAAW;AAC5C;AArEA;AAAA;AAAA;AACA;AAAA;AAAA;;;AC4CO,SAAS,eAAe,KAA4B;AACzD,SAAO,kBAAkB,YAAY,IAAI,GAAG;AAC9C;AA/CA;AAAA;AAAA;AAiBA;AAAA;AAAA;;;ACCA,SAAS,yBAAyB,MAAqB,KAAmB,MAAyB;AACjG,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,cAAc,IAAI,KAAK;AAAA,IAChC,KAAK;AACH,aAAO,IAAI;AAAA,IACb,KAAK;AACH,aAAO,IAAI;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,IAAI;AAAA,IACb,KAAK;AACH,aAAO,IAAI;AAAA,IACb,KAAK;AACH,aAAO,IAAI;AAAA,IACb,KAAK;AACH,aAAO;AAAA;AAAA;AAAA,IAGT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,UAAI,QAAQ,OAAO,SAAS,YAAY,WAAW,MAAM;AACvD,eAAQ,KAAyB;AAAA,MACnC;AACA,aAAO,CAAC;AAAA,IACV,KAAK;AACH,UAAI,QAAQ,OAAO,SAAS,YAAY,YAAY,MAAM;AACxD,eAAQ,KAAyB;AAAA,MACnC;AACA,aAAO,CAAC;AAAA;AAAA;AAAA,IAGV,KAAK;AACH,aAAO,IAAI,aAAa,IAAI,WAAW,MAAM,WAAW,IAAI,WAAW;AAAA;AAAA,IAEzE,KAAK;AACH,aAAO,IAAI,aAAa,IAAI,WAAW,YAAY,IAAI,GAAG,IAAI,eAAe,GAAG;AAAA,IAClF;AACE,aAAO;AAAA,EACX;AACF;AAQA,eAAsB,kBACpB,SACA,KACA,MACA,WACkB;AAClB,QAAM,aAAa,iBAAiB,OAAO;AAE3C,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,MAAM,QAAQ;AAAA,EACvB;AAEA,QAAM,OAAO,MAAM,QAAQ;AAAA,IACzB,WAAW,IAAI,OAAO,cAAc;AAElC,UAAI,UAAU,SAAS,WAAW;AAChC,eAAO,yBAAyB,UAAU,MAAM,KAAK,IAAI;AAAA,MAC3D;AAEA,UAAI,aAAa,UAAU,QAAQ,WAAW;AAE5C,eAAO,MAAM,UAAU,UAAU,IAAI,EAAG,GAAG;AAAA,MAC7C;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO,MAAM,QAAQ,GAAG,IAAI;AAC9B;AAhGA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;;;ACqBA,SAAS,WAAW,QAAiB,KAA4B;AAC/D,SAAO,aAAa,QAAQ,IAAI,MAAM;AACxC;AAQO,SAAS,UAAU,UAAoB,MAA8B;AAC1E,QAAM,UACJ,KAAK,WAAW,UAAa,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,KAAK,KAAK,WAAW,SAAS;AAChG,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,kBAAkB,IAAI,GAAG;AAC3B,qBAAiB,UAAU,KAAK,OAAO;AACvC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,IAAI,QAAQ,SAAS,OAAO;AAC5C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,OAAO,GAAG;AACvD,YAAQ,IAAI,KAAK,KAAK;AAAA,EACxB;AACA,aAAW,UAAU,KAAK,YAAY;AACpC,YAAQ,OAAO,cAAc,MAAM;AAAA,EACrC;AACA,SAAO,IAAI,SAAS,SAAS,MAAM;AAAA,IACjC,QAAQ,KAAK,UAAU,SAAS;AAAA,IAChC;AAAA,EACF,CAAC;AACH;AAmBA,eAAsB,QACpB,aACA,KACA,cACmB;AACnB,QAAM,OAAQ,IAA8C;AAC5D,MAAI,QAAQ;AAEZ,iBAAe,SAAS,GAA8B;AACpD,QAAI,KAAK,OAAO;AACd,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,YAAQ;AAGR,QAAI,KAAK,YAAY,QAAQ;AAC3B,aAAO,MAAM,aAAa;AAAA,IAC5B;AAGA,UAAM,KAAK,YAAY,CAAC;AAExB,QAAI;AACJ,UAAM,OAAO,YAA+B;AAC1C,sBAAgB,MAAM,SAAS,IAAI,CAAC;AACpC,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,MAAM,GAAG,KAAK,IAAI;AAGjC,QAAI,kBAAkB,UAAU;AAC9B,aAAO,UAAU,QAAQ,IAAI;AAAA,IAC/B;AAEA,QAAI,kBAAkB,QAAW;AAC/B,aAAO;AAAA,IACT;AAEA,UAAM,IAAI,MAAM,yEAAiC;AAAA,EACnD;AAEA,SAAO,MAAM,SAAS,CAAC;AACzB;AAeA,eAAsB,cACpB,SACA,KACA,MACA,aACA,WACmB;AACnB,QAAM,OAAQ,IAA8C;AAS5D,QAAM,sBAAsB,MAAuB;AACjD,UAAM,YAAa,IAAmD;AACtE,QAAI,CAAC,UAAW,QAAO;AAEvB,QAAI,CAAC,UAAU,UAAU,CAAC,UAAU,SAAS;AAC3C,gBAAU,MAAM;AAAA,IAClB;AACA,WAAO,UAAU,UAAU,UAAU,IAAI;AAAA,EAC3C;AAGA,QAAM,sBAAsB,MAAY;AACtC,UAAM,YAAa,IAAmD;AACtE,QAAI,aAAa,CAAC,UAAU,UAAU,CAAC,UAAU,SAAS;AACxD,gBAAU,MAAM;AAAA,IAClB;AAAA,EACF;AAGA,MAAI,CAAC,eAAe,YAAY,WAAW,GAAG;AAC5C,QAAI;AACF,YAAM,SAAS,MAAM,kBAAkB,SAAS,KAAK,MAAM,SAAS;AACpE,YAAM,cAAc,oBAAoB;AACxC,UAAI,YAAa,QAAO;AACxB,aAAO,WAAW,WAAW,QAAQ,GAAG,GAAG,IAAI;AAAA,IACjD,SAAS,KAAK;AAEZ,0BAAoB;AACpB,YAAM;AAAA,IACR;AAAA,EACF;AAGA,QAAM,eAAe,YAA+B;AAClD,QAAI;AACF,YAAM,SAAS,MAAM,kBAAkB,SAAS,KAAK,MAAM,SAAS;AACpE,YAAM,cAAc,oBAAoB;AACxC,UAAI,YAAa,QAAO;AACxB,aAAO,WAAW,WAAW,QAAQ,GAAG,GAAG,IAAI;AAAA,IACjD,SAAS,KAAK;AAEZ,0BAAoB;AACpB,YAAM;AAAA,IACR;AAAA,EACF;AAEA,SAAO,MAAM,QAAQ,aAAa,KAAK,YAAY;AACrD;AAvMA;AAAA;AAAA;AAKA;AACA;AACA;AACA;AAAA;AAAA;;;ACPA,SAAS,YAAAE,iBAAgB;AAczB,eAAsB,iBAAiB,UAAoB,KAAoC;AAE7F,MAAI,aAAa,SAAS;AAG1B,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,SAAS;AAC3C,QAAI,IAAI,YAAY,MAAM,cAAc;AAEtC,UAAI,aAAa,KAAK,KAAK;AAAA,IAC7B,OAAO;AACL,UAAI,UAAU,KAAK,KAAK;AAAA,IAC1B;AAAA,EACF;AAGA,QAAM,WAAW,0BAA0B,QAAQ;AACnD,MAAI,UAAU;AACZ,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,UAAI,UAAU,KAAK,KAAK;AAAA,IAC1B;AAAA,EACF;AAGA,MAAI,SAAS,MAAM;AAEjB,UAAM,aAAaA,UAAS,QAAQ,SAAS,IAAa;AAC1D,UAAM,IAAI,QAAc,CAACC,UAAS,WAAW;AAE3C,iBAAW,GAAG,SAAS,MAAM;AAG7B,YAAM,QAAQ,MAAM;AAClB,mBAAW,QAAQ;AACnB,QAAAA,SAAQ;AAAA,MACV;AACA,UAAI,GAAG,SAAS,KAAK;AACrB,UAAI,GAAG,SAAS,MAAM;AAEpB,YAAI,CAAC,IAAI,cAAe,OAAM;AAAA,MAChC,CAAC;AAED,UAAI,GAAG,UAAUA,QAAO;AACxB,iBAAW,KAAK,GAAG;AAAA,IACrB,CAAC;AACD;AAAA,EACF;AAEA,MAAI,IAAI;AACV;AA/DA;AAAA;AAAA;AAEA;AAAA;AAAA;;;AC0CO,SAAS,wBAA8B;AAC5C,cAAY,MAAM;AACpB;AAQA,eAAe,iBAAiB,YAA2C;AACzE,MAAI,MAAM,YAAY,IAAI,UAAU;AACpC,MAAI,CAAC,KAAK;AAGR,UAAO,MAAM,oBAAoB,YAAY,qBAAqB,CAAC;AACnE,gBAAY,IAAI,YAAY,GAAG;AAAA,EACjC;AACA,SAAO;AACT;AA0BA,eAAsB,cACpB,YACA,QACA,WACA,OAC2B;AAC3B,QAAM,aAAa,cAAc,QAAQ,SAAS;AAClD,QAAM,YAAY,GAAG,UAAU;AAE/B,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,iBAAiB,UAAU;AAAA,EACzC,SAAS,KAAK;AACZ,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI,cAAc,gDAAkB,UAAU,KAAK,MAAM,EAAE;AAAA,EACnE;AAEA,QAAM,SAAS,IAAI,SAAS;AAG5B,MAAI,WAAW,UAAa,WAAW,MAAM;AAC3C,WAAO,EAAE,OAAO,MAAM,QAAQ,CAAC,GAAG,MAAM,MAAM;AAAA,EAChD;AAGA,MACE,OAAO,WAAW,YAClB,OAAQ,OAAmC,cAAc,YACzD;AACA,UAAM,IAAI,cAAc,qDAA4B,UAAU,IAAI,UAAU,EAAE;AAAA,EAChF;AAKA,QAAM,YAAY;AAClB,QAAM,SAAS,UAAU,UAAU,KAAK;AAExC,MAAI,OAAO,SAAS;AAElB,WAAO,EAAE,OAAO,MAAM,QAAQ,CAAC,GAAG,MAAM,OAAO,KAAK;AAAA,EACtD;AAGA,QAAM,SAAS,aAAa,OAAO,KAAK;AACxC,SAAO,EAAE,OAAO,OAAO,QAAQ,MAAM,MAAM;AAC7C;AA0CA,SAAS,aAAa,OAAsD;AAC1E,SAAO,MAAM,OAAO,IAAI,CAAC,UAAU;AACjC,UAAM,OAAO,WAAW,KAAK;AAC7B,UAAMC,SAAO,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,KAAK;AACjD,WAAO;AAAA,MACL,MAAAA;AAAA,MACA;AAAA,MACA,UAAU,MAAM,YAAY,uBAAuB,MAAM,OAAO;AAAA,MAChE,UAAU,MAAM,YAAY,uBAAuB,MAAM,OAAO;AAAA,MAChE,SAAS,MAAM;AAAA,IACjB;AAAA,EACF,CAAC;AACH;AAeA,SAAS,WAAW,OAA0C;AAC5D,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AAGH,UAAI,MAAM,aAAa,eAAe,sBAAsB,KAAK,MAAM,OAAO,GAAG;AAC/E,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKA,SAAS,uBAAuB,SAAyB;AAEvD,QAAM,QAAQ,QAAQ,MAAM,mBAAmB;AAC/C,SAAO,QAAQ,MAAM,CAAC,EAAG,YAAY,IAAI;AAC3C;AAKA,SAAS,uBAAuB,SAAyB;AACvD,QAAM,QAAQ,QAAQ,MAAM,mBAAmB;AAC/C,SAAO,QAAQ,MAAM,CAAC,EAAG,YAAY,IAAI;AAC3C;AAxPA,IAuCM;AAvCN;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAmCA,IAAM,cAAc,oBAAI,IAA0B;AAAA;AAAA;;;ACrB3C,SAAS,YAAY,KAAsB,eAAe,OAAe;AAE9E,MAAI,cAAc;AAChB,UAAM,MAAM,IAAI,QAAQ,iBAAiB;AACzC,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,YAAM,QAAQ,IAAI,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK;AACtC,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AAGA,QAAM,SAAS,IAAI,QAAQ;AAC3B,MAAI,QAAQ;AAEV,QAAI,OAAO,WAAW,SAAS,GAAG;AAChC,aAAO,OAAO,MAAM,CAAC;AAAA,IACvB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAvCA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,SAAS,KAAK,UAAuB,CAAC,GAAoB;AAC/D,QAAM;AAAA,IACJ,SAAS;AAAA,IACT,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,EACF,IAAI;AAEJ,SAAO,OAAO,KAAK,SAAS;AAC1B,UAAM,YAAY,IAAI,QAAQ,IAAI,QAAQ;AAC1C,QAAI,CAAC,WAAW;AACd,YAAM,KAAK;AACX;AAAA,IACF;AAGA,QAAI,cAA6B;AACjC,QAAI,WAAW,MAAM;AACnB,oBAAc;AAAA,IAChB,WAAW,OAAO,WAAW,UAAU;AACrC,oBAAc,cAAc,SAAS,SAAS;AAAA,IAChD,WAAW,MAAM,QAAQ,MAAM,GAAG;AAChC,oBAAc,OAAO,SAAS,SAAS,IAAI,YAAY;AAAA,IACzD;AAKA,QAAI,WAAW,QAAQ,MAAM,QAAQ,MAAM,GAAG;AAC5C,YAAM,eAAe,IAAI,QAAQ,IAAI,MAAM;AAC3C,UAAI,cAAc;AAChB,YAAI,CAAC,aAAa,YAAY,EAAE,SAAS,QAAQ,GAAG;AAClD,cAAI,UAAU,QAAQ,GAAG,YAAY,UAAU;AAAA,QACjD;AAAA,MACF,OAAO;AACL,YAAI,UAAU,QAAQ,QAAQ;AAAA,MAChC;AAAA,IACF;AAEA,QAAI,CAAC,aAAa;AAChB,YAAM,KAAK;AACX;AAAA,IACF;AAGA,QAAI,UAAU,+BAA+B,WAAW;AAExD,QAAI,UAAU,gCAAgC,QAAQ,KAAK,IAAI,CAAC;AAEhE,QAAI,gBAAgB;AAClB,UAAI,UAAU,gCAAgC,eAAe,KAAK,IAAI,CAAC;AAAA,IACzE,OAAO;AAEL,YAAM,iBAAiB,IAAI,QAAQ,IAAI,gCAAgC;AACvE,UAAI,gBAAgB;AAClB,YAAI,UAAU,gCAAgC,cAAc;AAAA,MAC9D;AAAA,IACF;AAEA,QAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,UAAI,UAAU,iCAAiC,cAAc,KAAK,IAAI,CAAC;AAAA,IACzE;AAEA,QAAI,aAAa;AACf,UAAI,UAAU,oCAAoC,MAAM;AAAA,IAC1D;AAEA,QAAI,WAAW,QAAW;AACxB,UAAI,UAAU,0BAA0B,OAAO,MAAM,CAAC;AAAA,IACxD;AAGA,QAAI,IAAI,WAAW,WAAW;AAC5B,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C;AAEA,UAAM,KAAK;AAAA,EACb;AACF;AAtGA,IAWM;AAXN;AAAA;AAAA;AAWA,IAAM,kBAAkB,CAAC,OAAO,QAAQ,OAAO,SAAS,UAAU,QAAQ,SAAS;AAAA;AAAA;;;ACyB5E,SAAS,OAAO,UAAyB,CAAC,GAAoB;AACnE,QAAM,OAAO,EAAE,GAAG,UAAU,GAAG,QAAQ;AAEvC,SAAO,OAAO,KAAK,SAAS;AAC1B,QAAI,KAAK,0BAA0B,OAAO;AACxC,UAAI,UAAU,2BAA2B,KAAK,qBAAqB;AAAA,IACrE;AACA,QAAI,KAAK,kBAAkB,OAAO;AAChC,UAAI,UAAU,mBAAmB,KAAK,aAAa;AAAA,IACrD;AACA,QAAI,KAAK,qBAAqB;AAC5B,UAAI,UAAU,0BAA0B,SAAS;AAAA,IACnD;AACA,QAAI,KAAK,mBAAmB,OAAO;AACjC,UAAI,UAAU,mBAAmB,KAAK,cAAc;AAAA,IACtD;AACA,QAAI,KAAK,4BAA4B,OAAO;AAC1C,UAAI,UAAU,6BAA6B,KAAK,uBAAuB;AAAA,IACzE;AACA,QAAI,KAAK,qBAAqB;AAC5B,UAAI,UAAU,0BAA0B,KAAK;AAAA,IAC/C;AACA,QAAI,KAAK,kBAAkB;AACzB,UAAI,UAAU,sBAAsB,QAAQ;AAAA,IAC9C;AACA,QAAI,KAAK,kCAAkC,OAAO;AAChD,UAAI,UAAU,qCAAqC,KAAK,6BAA6B;AAAA,IACvF;AACA,QAAI,KAAK,4BAA4B,OAAO;AAC1C,UAAI,UAAU,8BAA8B,KAAK,uBAAuB;AAAA,IAC1E;AACA,QAAI,KAAK,8BAA8B,OAAO;AAC5C,UAAI,UAAU,gCAAgC,KAAK,yBAAyB;AAAA,IAC9E;AACA,QAAI,KAAK,8BAA8B,OAAO;AAC5C,UAAI,UAAU,gCAAgC,KAAK,yBAAyB;AAAA,IAC9E;AACA,QAAI,KAAK,oBAAoB;AAC3B,UAAI,UAAU,wBAAwB,IAAI;AAAA,IAC5C;AACA,QAAI,KAAK,YAAY;AACnB,UAAI,UAAU,gBAAgB,OAAO;AAAA,IACvC;AAEA,WAAO,MAAM,KAAK;AAAA,EACpB;AACF;AAlFA,IAkBM;AAlBN;AAAA;AAAA;AAkBA,IAAM,WAEF;AAAA,MACF,uBAAuB;AAAA,MACvB,eAAe;AAAA,MACf,qBAAqB;AAAA,MACrB,gBAAgB;AAAA,MAChB,yBAAyB;AAAA,MACzB,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,+BAA+B;AAAA,MAC/B,yBAAyB;AAAA,MACzB,2BAA2B;AAAA,MAC3B,2BAA2B;AAAA,MAC3B,oBAAoB;AAAA,MACpB,YAAY;AAAA,IACd;AAAA;AAAA;;;AClCA,OAAO,UAAU;AACjB,SAAS,iBAAiB;AA2B1B,SAAS,eAAe,aAA8B;AACpD,QAAM,KAAK,YAAY,MAAM,GAAG,EAAE,CAAC,EAAG,KAAK,EAAE,YAAY;AACzD,MAAI,OAAO,GAAI,QAAO;AACtB,SAAO,oBAAoB,KAAK,CAAC,UAAU,MAAM,EAAE,CAAC;AACtD;AAYO,SAAS,oBAAoB,QAAoC;AACtE,SAAO,OACJ,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,IAAI,CAAC,SAAS;AACb,UAAM,CAAC,UAAU,GAAG,MAAM,IAAI,KAAK,MAAM,GAAG;AAC5C,QAAI,UAAU;AACd,eAAW,SAAS,QAAQ;AAC1B,YAAM,UAAU,MAAM,KAAK;AAC3B,UAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,cAAM,IAAI,OAAO,QAAQ,MAAM,CAAC,CAAC;AACjC,YAAI,OAAO,SAAS,CAAC,EAAG,WAAU;AAAA,MACpC;AAAA,IACF;AACA,WAAO,EAAE,WAAW,YAAY,IAAI,KAAK,EAAE,YAAY,GAAG,QAAQ;AAAA,EACpE,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE,UAAU,CAAC;AACrD;AAQO,SAAS,eAAe,UAAgE;AAC7F,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,EAAE,UAAU,QAAQ,KAAK,UAAU;AAE5C,WAAO,IAAI,UAAU,KAAK,IAAI,OAAO,IAAI,QAAQ,KAAK,GAAG,OAAO,CAAC;AAAA,EACnE;AAEA,aAAW,CAAC,UAAU,OAAO,KAAK,QAAQ;AACxC,QAAI,YAAY,EAAG,QAAO,OAAO,QAAQ;AAAA,EAC3C;AAEA,aAAW,aAAa,CAAC,MAAM,QAAQ,SAAS,GAAY;AAC1D,SAAK,OAAO,IAAI,SAAS,KAAK,KAAK,EAAG,QAAO;AAAA,EAC/C;AACA,OAAK,OAAO,IAAI,GAAG,KAAK,KAAK,EAAG,QAAO;AACvC,SAAO;AACT;AAEA,eAAe,aACb,UACA,MACqB;AACrB,QAAM,MAAM,OAAO,KAAK,MAAM,OAAO;AACrC,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,YAAY,GAAG;AAAA,IACxB,KAAK;AACH,aAAO,UAAU,GAAG;AAAA,IACtB,KAAK;AACH,aAAO,aAAa,GAAG;AAAA,EAC3B;AACF;AAGA,SAAS,UAAU,MAAoB,OAAqB;AAC1D,QAAM,WAAW,KAAK,QAAQ,MAAM,KAAK,KAAK,QAAQ,MAAM;AAC5D,MAAI,CAAC,UAAU;AACb,SAAK,QAAQ,MAAM,IAAI;AACvB;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,GAAG;AACzD,UAAM,MAAM,KAAK,QAAQ,MAAM,MAAM,SAAY,SAAS;AAC1D,SAAK,QAAQ,GAAG,IAAI,GAAG,QAAQ,KAAK,KAAK;AAAA,EAC3C;AACF;AAUO,SAAS,YAAY,UAA8B,CAAC,GAAoB;AAC7E,QAAM,YAAY,QAAQ,aAAa;AAEvC,SAAO,OAAO,KAAK,SAAS;AAC1B,UAAM,OAAQ,IAA8C;AAC5D,UAAM,WAAW,MAAM,KAAK;AAC5B,QAAI,CAAC,SAAU,QAAO;AAGtB,cAAU,MAAM,iBAAiB;AAGjC,UAAM,iBAAiB,IAAI,QAAQ,QAAQ,IAAI,iBAAiB,KAAK;AACrE,UAAM,WAAW,eAAe,oBAAoB,cAAc,CAAC;AACnE,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,QACE,CAAC,YACD,CAAC,eAAe,WAAW,KAC3B,SAAS,QAAQ,IAAI,kBAAkB,MACtC,SAAS,QAAQ,IAAI,eAAe,KAAK,IAAI,SAAS,cAAc,KACrE,SAAS,WAAW,OACpB,SAAS,WAAW,OACpB,SAAS,SAAS,MAClB;AACA,aAAO;AAAA,IACT;AAIA,UAAM,kBAAkB,0BAA0B,QAAQ;AAE1D,UAAM,WAAW,MAAM,SAAS,KAAK;AACrC,QAAI,SAAS,SAAS,WAAW;AAE/B,YAAMC,WAAU,IAAI,QAAQ,SAAS,OAAO;AAC5C,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,mBAAmB,CAAC,CAAC,GAAG;AAChE,QAAAA,SAAQ,IAAI,KAAK,KAAK;AAAA,MACxB;AACA,aAAO,IAAI,SAAS,UAAU;AAAA,QAC5B,QAAQ,SAAS;AAAA,QACjB,YAAY,SAAS;AAAA,QACrB,SAAAA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,MAAM,aAAa,UAAU,QAAQ;AACxD,UAAM,UAAU,IAAI,QAAQ,SAAS,OAAO;AAC5C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,mBAAmB,CAAC,CAAC,GAAG;AAChE,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AACA,YAAQ,IAAI,oBAAoB,QAAQ;AAExC,YAAQ,OAAO,gBAAgB;AAC/B,WAAO,IAAI,SAAS,YAAmC;AAAA,MACrD,QAAQ,SAAS;AAAA,MACjB,YAAY,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAxLA,IAMM,WACA,cACA,aAUA,mBAGA;AArBN;AAAA;AAAA;AAIA;AAEA,IAAM,YAAY,UAAU,KAAK,IAAI;AACrC,IAAM,eAAe,UAAU,KAAK,OAAO;AAC3C,IAAM,cAAc,UAAU,KAAK,cAAc;AAUjD,IAAM,oBAAoB;AAG1B,IAAM,sBAA+D;AAAA,MACnE,CAAC,OAAO,OAAO;AAAA,MACf,CAAC,OAAO,OAAO;AAAA,MACf,CAAC,OAAO,OAAO;AAAA,MACf,CAAC,OAAO,GAAG,WAAW,OAAO,KAAK,OAAO;AAAA,IAC3C;AAAA;AAAA;;;AC1BA,SAAS,kBAAkB;AAiB3B,SAAS,YAAY,MAAc,MAAuB;AACxD,QAAM,OAAO,WAAW,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,QAAQ;AAC5D,SAAO,OAAO,MAAM,IAAI,MAAM,IAAI,IAAI;AACxC;AAGA,SAAS,mBAAmB,aAAqBC,OAAc,MAAwB;AACrF,QAAMC,aAAY,CAAC,QAAgB,IAAI,KAAK,EAAE,QAAQ,SAAS,EAAE;AACjE,MAAI,MAAM;AACR,UAAM,SAASA,WAAUD,KAAI;AAC7B,WAAO,YAAY,MAAM,GAAG,EAAE,KAAK,CAAC,QAAQ;AAC1C,YAAM,UAAU,IAAI,KAAK;AACzB,aAAO,YAAY,OAAOC,WAAU,OAAO,MAAM;AAAA,IACnD,CAAC;AAAA,EACH;AAEA,SAAO,YAAY,MAAM,GAAG,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,MAAMD,SAAQ,IAAI,KAAK,MAAM,GAAG;AACvF;AAWO,SAAS,KAAK,UAAuB,CAAC,GAAoB;AAC/D,QAAM,OAAO,EAAE,GAAGE,WAAU,GAAG,QAAQ;AAEvC,SAAO,OAAO,KAAK,SAAS;AAC1B,UAAM,OAAQ,IAA8C;AAC5D,UAAM,WAAW,MAAM,KAAK;AAC5B,QAAI,CAAC,SAAU,QAAO;AAGtB,UAAM,SAAS,IAAI,QAAQ,OAAO,YAAY;AAC9C,QAAI,WAAW,SAAS,WAAW,OAAQ,QAAO;AAClD,QAAI,SAAS,SAAS,OAAO,SAAS,UAAU,IAAK,QAAO;AAG5D,QAAI,KAAK,QAAQ,MAAM,MAAM,UAAa,SAAS,QAAQ,IAAI,MAAM,GAAG;AACtE,aAAO;AAAA,IACT;AAEA,QACE,SAAS,SAAS,SACjB,SAAS,QAAQ,IAAI,cAAc,KAAK,IAAI,SAAS,mBAAmB,GACzE;AACA,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,MAAM,SAAS,KAAK;AACrC,UAAM,YAAY,YAAY,UAAU,KAAK,IAAI;AAGjD,UAAM,cAAc,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC3D,QAAI,eAAe,mBAAmB,aAAa,WAAW,KAAK,IAAI,GAAG;AACxE,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,YAAY,SAAS;AAAA,QACrB,SAAS,EAAE,MAAM,UAAU;AAAA,MAC7B,CAAC;AAAA,IACH;AAGA,SAAK,QAAQ,MAAM,IAAI;AACvB,UAAM,UAAU,IAAI,QAAQ,SAAS,OAAO;AAE5C,UAAM,kBAAkB,0BAA0B,QAAQ;AAC1D,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,mBAAmB,CAAC,CAAC,GAAG;AAChE,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AACA,WAAO,IAAI,SAAS,UAAU;AAAA,MAC5B,QAAQ,SAAS;AAAA,MACjB,YAAY,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAjGA,IAcMA;AAdN;AAAA;AAAA;AAGA;AAWA,IAAMA,YAAkC,EAAE,MAAM,KAAK;AAAA;AAAA;;;ACe9C,SAAS,OAAO,UAAyB,CAAC,GAAoB;AACnE,SAAO,OAAO,KAAK,SAAS;AAC1B,UAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,UAAM,QAAQ,KAAK,IAAI;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,KAAK;AAC5B,YAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,YAAM,QAAQ;AAAA,QACZ,QAAQ,IAAI;AAAA,QACZ,MAAM,IAAI;AAAA,QACV,QAAQ,SAAS;AAAA,QACjB,YAAY;AAAA,MACd;AACA,UAAI,OAAO,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,SAAS,MAAM,IAAI,QAAQ,IAAI;AACvE,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,SAAU,KAAiC,cAAc;AAC/D,YAAM,QAAQ;AAAA,QACZ,QAAQ,IAAI;AAAA,QACZ,MAAM,IAAI;AAAA,QACV;AAAA,QACA,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AACA,UAAI,OAAO,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,MAAM,IAAI,QAAQ,QAAQ,OAAO,EAAE;AAC3E,YAAM;AAAA,IACR;AAAA,EACF;AACF;AA3DA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAUA;AAAA;AAAA;;;ACFO,SAAS,qBAAqB,KAA+B;AAClE,QAAM,UAAU,IAAI,QAAQ;AAC5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AACtD,QAAI,UAAU,OAAW;AACzB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,KAAK,MAAO,SAAQ,OAAO,KAAK,CAAC;AAAA,IAC9C,OAAO;AACL,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AACT;AAcO,SAAS,mBACd,KACA,QACU;AACV,MAAI;AACF,WAAO,oBAAoB,KAAK,MAAM;AAAA,EACxC,QAAQ;AAEN,WAAO,IAAI;AAAA,MACT,KAAK,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,SAAS,wBAAwB,EAAE,CAAC;AAAA,MACtF;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACF;AAjDA;AAAA;AAAA;AACA;AAAA;AAAA;;;ACwEO,SAAS,aAAa,WAIhB;AACX,SAAO;AAAA,IACL,KAAK,MAAsC;AACzC,YAAM,UACJ,OAAO,SAAS,YAAY,OAAO,SAAS,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI;AAChF,gBAAU,KAAK,OAAO;AAAA,IACxB;AAAA,IACA,MAAM,MAAe,QAAgC;AACnD,gBAAU,MAAM,MAAM,MAAM;AAAA,IAC9B;AAAA,IACA,IAAI,aAAqB;AACvB,aAAO,UAAU;AAAA,IACnB;AAAA,EACF;AACF;AA3FA;AAAA;AAAA;AAAA;AAAA;;;ACcA,OAAOC,UAAQ;AACf,SAAS,iBAAiB,iBAAiB;AAE3C,OAAOC,YAAU;AA+BjB,SAAS,YAAY,KAA8B;AACjD,QAAM,MAAM,IAAI,OAAO;AACvB,QAAM,MAAM,IAAI,QAAQ,GAAG;AAC3B,SAAO,OAAO,IAAI,IAAI,MAAM,GAAG,GAAG,IAAI;AACxC;AAWA,eAAe,cACb,UACA,KACA,SACiC;AAEjC,MAAI,qBAAqB,KAAK,SAAS;AACrC,UAAM,OAAO,WAAW;AACxB,QAAI,MAAM;AACR,YAAM,aAAa,qBAAqB,UAAU,SAAS,IAAI;AAC/D,UAAI,cAAcD,KAAG,WAAW,UAAU,GAAG;AAC3C,cAAM,eAAe,YAAY,SAAS,IAAI;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAU,MAAM,oBAAoB,UAAU,qBAAqB,CAAC;AAK1E,QAAM,UAAU,OAAO,IAAI;AAC3B,MAAI,OAAO,YAAY,YAAY;AACjC,UAAM,IAAI,MAAM,0BAA0B,QAAQ,EAAE;AAAA,EACtD;AACA,SAAQ,QAAsB,GAAG;AACnC;AAQA,SAAS,WAAW,WAAsB,UAAwC;AAChF,MAAI,CAAC,SAAU;AACf,QAAM,KAAK,aAAa,SAAS;AAEjC,MAAI,SAAS,QAAQ;AAEnB,QAAI,UAAU,eAAe,UAAU,MAAM;AAC3C,eAAS,OAAQ,EAAE;AAAA,IACrB,OAAO;AACL,gBAAU,KAAK,QAAQ,MAAM,SAAS,OAAQ,EAAE,CAAC;AAAA,IACnD;AAAA,EACF;AACA,MAAI,SAAS,WAAW;AACtB,cAAU,GAAG,WAAW,CAAC,MAAe,aAAsB;AAI5D,YAAM,MAAM,WAAW,IAAI;AAC3B,eAAS,UAAW,IAAI,WAAW,MAAM,IAAI,SAAS,MAAM,CAAC;AAAA,IAC/D,CAAC;AAAA,EACH;AACA,MAAI,SAAS,SAAS;AACpB,cAAU,GAAG,SAAS,CAAC,MAAc,WAAmB;AACtD,eAAS,QAAS,IAAI,MAAM,OAAO,SAAS,MAAM,CAAC;AAAA,IACrD,CAAC;AAAA,EACH;AACA,MAAI,SAAS,SAAS;AACpB,cAAU,GAAG,SAAS,CAAC,QAAe;AACpC,eAAS,QAAS,IAAI,GAAG;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AAGA,SAAS,WAAW,MAAuB;AACzC,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO;AAClC,MAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,OAAO,OAAO,IAAI;AAClD,SAAO,OAAO,KAAK,IAAmB;AACxC;AAOA,eAAe,qBAAqB,QAAgB,UAAmC;AACrF,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,QAAM,aAAa,YAAY,SAAS,MAAM,IAAI,SAAS,cAAc,EAAE;AAAA;AAC3E,QAAM,cAAwB,CAAC;AAC/B,MAAI,mBAAmB;AAEvB,QAAM,kBAAkB,0BAA0B,QAAQ;AAC1D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,mBAAmB,CAAC,CAAC,GAAG;AAChE,QAAI,IAAI,YAAY,MAAM,iBAAkB,oBAAmB;AAC/D,gBAAY,KAAK,GAAG,GAAG,KAAK,KAAK,EAAE;AAAA,EACrC;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,SAAS;AAC3C,QAAI,IAAI,YAAY,MAAM,kBAAkB;AAC1C,yBAAmB;AAAA,IACrB;AACA,gBAAY,KAAK,GAAG,GAAG,KAAK,KAAK,EAAE;AAAA,EACrC;AACA,MAAI,CAAC,kBAAkB;AACrB,gBAAY,KAAK,mBAAmB,OAAO,WAAW,IAAI,CAAC,EAAE;AAAA,EAC/D;AACA,SAAO,MAAM,aAAa,YAAY,KAAK,MAAM,IAAI,aAAa,IAAI;AACtE,SAAO,QAAQ;AACjB;AAqCO,SAAS,gBAAgB,SAA2C;AACzE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,EACF,IAAI;AAEJ,QAAM,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AAElD,SAAO,GAAG,WAAW,OAAO,KAAsB,QAAgB,SAAiB;AACjF,QAAI;AACF,YAAM,qBAAqB,KAAK,QAAQ,IAAI;AAAA,IAC9C,SAAS,KAAK;AAIZ,cAAQ,MAAM,gDAA4B,GAAG;AAC7C,UAAI,CAAC,OAAO,WAAW;AACrB,YAAI;AACF,cAAI,CAAC,OAAO,eAAe;AACzB,mBAAO,MAAM,4CAA4C;AAAA,UAC3D;AAAA,QACF,QAAQ;AAAA,QAER;AACA,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF,CAAC;AAED,iBAAe,qBACb,KACA,QACA,MACe;AACf,UAAM,kBAAkB,UAAU;AAClC,UAAM,WAAW,YAAY,GAAG;AAChC,UAAM,QAA6B,aAAa,iBAAiB,QAAQ;AAEzE,QAAI,CAAC,OAAO;AAEV,aAAO,MAAM,gCAAgC;AAC7C,aAAO,QAAQ;AACf;AAAA,IACF;AAEA,UAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,UAAM,UAAU,qBAAqB,GAAG;AACxC,UAAM,OAAO,IAAI,QAAQ,QAAQ;AACjC,UAAM,MAAM,UAAU,IAAI,GAAG,IAAI,OAAO,GAAG;AAG3C,UAAM,UAAU,IAAI,QAAQ,KAAK,EAAE,QAAQ,OAAO,QAAQ,CAAC;AAC3D,UAAM,MAAM,cAAc,SAAS,QAAQ,QAAQ,YAAY,KAAK,YAAY,GAAG,UAAU;AAC7F,UAAM,OAAQ,IAA8C;AAG5D,QAAI,WAAW;AAGf,UAAM,eAAe,YAA+B;AAClD,UAAI;AACJ,UAAI;AACF,cAAM,mBAAmBC,OAAK,QAAQ,SAAS,MAAM,QAAQ;AAG7D,mBAAW,MAAM,cAAc,kBAAkB,KAA6B,OAAO;AAAA,MACvF,SAAS,KAAK;AACZ,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,gBAAQ,MAAM,+CAA2B,MAAM,QAAQ,KAAK,MAAM,EAAE;AACpE,eAAO,IAAI,SAAS,yBAAyB,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC9D;AAGA,YAAM,IAAI,QAAc,CAACC,UAAS,WAAW;AAC3C,YAAI,cAAc,KAAK,QAAQ,MAAM,CAAC,cAAyB;AAC7D,cAAI;AACF,uBAAW,WAAW,QAAQ;AAC9B,gBAAI,KAAK,cAAc,WAAW,GAAG;AACrC,uBAAW;AACX,YAAAA,SAAQ;AAAA,UACV,SAAS,KAAK;AACZ,mBAAO,GAAG;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAGD,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C;AAGA,QAAI;AACJ,QAAI;AAEF,UAAI,MAAM,gBAAgB,UAAa,MAAM,iBAAiB;AAE5D,cAAM,0BAA0B,MAAM,iBAAiB,OAAO;AAC9D,cAAM,SAAS,MAAM,sBAAsB,MAAM,eAAe;AAChE,YAAI,QAAQ;AACV,gBAAM,cAAc,OAAO;AAAA,QAC7B,OAAO;AACL,gBAAM,cAAc,CAAC;AAAA,QACvB;AAAA,MACF;AACA,YAAM,iBAAiB,MAAM,eAAe,CAAC;AAC7C,YAAM,iBACJ,qBAAqB,kBAAkB,SAAS,IAC5C,CAAC,GAAG,mBAAmB,GAAG,cAAc,IACxC;AACN,UAAI,eAAe,SAAS,GAAG;AAC7B,mBAAW,MAAM,QAAQ,gBAAgB,KAAK,YAAY;AAAA,MAC5D,OAAO;AACL,mBAAW,MAAM,aAAa;AAAA,MAChC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,UAAU;AAEZ,gBAAQ,MAAM,gEAAwB,GAAG;AACzC;AAAA,MACF;AAGA,iBAAW,mBAAmB,GAAG;AAAA,IACnC;AAGA,QAAI,UAAU;AACZ;AAAA,IACF;AAGA,UAAM,qBAAqB,QAAQ,UAAU,UAAU,IAAI,CAAC;AAAA,EAC9D;AAEA,SAAO;AACT;AArVA;AAAA;AAAA;AAmBA;AACA;AACA;AAIA;AACA;AACA;AACA;AAOA;AAEA;AACA;AAAA;AAAA;;;ACtCA;AAAA,EACE,gBAAgB;AAAA,OAIX;AACP,SAAS,sBAAsB,+BAA+B;AAC9D,SAAS,oBAAoB;AAC7B,SAAS,YAAAC,iBAAgB;AACzB,OAAOC,YAAU;AA8CjB,SAAS,aACP,KACA,YAAoB,oBACpB,eAKA;AAEA,QAAM,iBAAiB,IAAI,QAAQ,mBAAmB;AACtD,QAAM,WAAW,MAAM,QAAQ,cAAc,IACxC,eAAe,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,KAAK,SAC5C,gBAAgB,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,KAAK;AAC9C,QAAM,OAAO,IAAI,QAAQ,QAAQ;AAEjC,QAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,GAAG,QAAQ,MAAM,IAAI,EAAE;AAE3D,QAAM,UAAU,qBAAqB,GAAG;AAExC,QAAM,SAAS,IAAI,UAAU;AAG7B,MAAI,WAAW,SAAS,WAAW,QAAQ;AACzC,WAAO;AAAA,MACL,SAAS,IAAI,QAAQ,IAAI,SAAS,GAAG,EAAE,QAAQ,SAAS,QAAQ,cAAc,CAAC;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AAIA,QAAM,gBAAgB,IAAI,QAAQ,gBAAgB;AAClD,MAAI,kBAAkB,QAAW;AAC/B,UAAM,WAAW,OAAO,MAAM,QAAQ,aAAa,IAAI,cAAc,CAAC,IAAI,aAAa;AACvF,QAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,WAAW;AACrD,YAAM,IAAI,qBAAqB,SAAS;AAAA,IAC1C;AAAA,EACF;AAIA,QAAM,SAASD,UAAS,MAAM,GAAG;AACjC,QAAM,gBAAgB,gBAAgB,QAAQ,SAAS;AACvD,SAAO;AAAA,IACL,SAAS,IAAI,QAAQ,IAAI,SAAS,GAAG;AAAA,MACnC;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,CAAgB;AAAA,IAChB;AAAA,EACF;AACF;AAaA,SAAS,gBACP,QACA,SAC4B;AAC5B,MAAI,YAAY;AAChB,MAAI;AACJ,MAAI,UAAU;AAEd,QAAM,gBAAgB,MAAY;AAChC,QAAI,QAAQ;AACV,UAAI;AACF,eAAO,YAAY;AAAA,MACrB,QAAQ;AAAA,MAER;AACA,eAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,aAAa,CACjB,YACA,QACS;AACT,QAAI,QAAS;AACb,cAAU;AACV,eAAW,MAAM,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AACpE,kBAAc;AAAA,EAChB;AAEA,SAAO,IAAI,eAA2B;AAAA,IACpC,MAAM,KAAK,YAAY;AACrB,UAAI,CAAC,OAAQ,UAAS,OAAO,UAAU;AACvC,UAAI;AACF,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,MAAM;AACR,qBAAW,MAAM;AACjB,wBAAc;AACd;AAAA,QACF;AACA,qBAAa,MAAM;AACnB,YAAI,YAAY,SAAS;AAEvB,qBAAW,YAAY,IAAI,qBAAqB,OAAO,CAAC;AACxD;AAAA,QACF;AACA,mBAAW,QAAQ,KAAK;AAAA,MAC1B,SAAS,KAAK;AAEZ,mBAAW,YAAY,GAAG;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,OAAO,QAAQ;AACb,UAAI,QAAQ;AACV,YAAI;AACF,iBAAO,OAAO,MAAM;AAAA,QACtB,QAAQ;AAAA,QAER;AACA,sBAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AA4DO,SAAS,aAAa,SAG3B;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,MAAM;AAAA,IACN;AAAA,IACA,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,eAAe;AAAA,EACjB,IAAI;AAGJ,QAAM,YAAuB,EAAE,SAAS,QAAQ,WAAW,YAAY,CAAC,EAAE;AAG1E,QAAM,oBAAuC,CAAC;AAG9C,MAAI,mBAAmB;AACrB,UAAM,WAAW,OAAO,sBAAsB,WAAW,oBAAoB,CAAC;AAC9E,sBAAkB,KAAK,YAAY,QAAQ,CAAC;AAAA,EAC9C;AAGA,QAAM,iBACJ,eAAe,QACX,OACA,eAAe,QAAQ,eAAe,SACpC,KAAK,IACL,KAAK,UAAU;AACvB,MAAI,eAAgB,mBAAkB,KAAK,cAAc;AAGzD,MAAI,cAAc;AAChB,UAAM,WAAW,OAAO,iBAAiB,WAAW,eAAe,CAAC;AACpE,sBAAkB,KAAK,OAAO,QAAQ,CAAC;AAAA,EACzC;AAGA,QAAM,uBACJ,iBAAiB,QACb,OACA,iBAAiB,QAAQ,iBAAiB,SACxC,OAAiB,IACjB,OAAiB,YAAY;AACrC,MAAI,qBAAsB,mBAAkB,KAAK,oBAAoB;AAIrE,MAAI,YAAY;AACd,UAAM,WAAW,OAAO,eAAe,WAAW,aAAa,CAAC;AAChE,sBAAkB,KAAK,KAAK,QAAQ,CAAC;AAAA,EACvC;AAIA,QAAM,mBAAsC,CAAC,GAAG,iBAAiB;AACjE,MAAI,qBAAqB,kBAAkB,SAAS,GAAG;AACrD,qBAAiB,KAAK,GAAG,iBAAiB;AAAA,EAC5C;AAEA,QAAM,UAAU,MAAc;AAC5B,QAAI,aAAa;AACf,YAAM,SAAS,OAAO,gBAAgB,WAAW,cAAc,CAAC;AAChE,aAAO,wBAAwB;AAAA,QAC7B,KAAK,OAAO,MAAM,aAAa,OAAO,GAAG,IAAI;AAAA,QAC7C,MAAM,OAAO,OAAO,aAAa,OAAO,IAAI,IAAI;AAAA,QAChD,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AACA,WAAO,iBAAiB;AAAA,EAC1B,GAAG;AAEH,SAAO,GAAG,WAAW,CAAC,KAAsB,QAAwB;AAElE,UAAM,gBAAgB,UAAU;AAEhC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,MAAM,MAAM;AACZ,UAAI,aAAa;AACjB,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AAKD,kBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,EAAE,QAAQ,UAAU;AAC7B;AAWA,SAAS,eACP,KACA,QACA,WACA,cACA,YACA,eAQA;AACA,QAAM,EAAE,SAAS,IAAI,IAAI,aAAa,KAAK,WAAW,aAAa;AACnE,QAAM,SAAS,QAAQ,OAAO,YAAY;AAC1C,QAAM,UAAU,IAAI;AACpB,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD;AAAA,IACA,YAAY,KAAK,YAAY;AAAA,IAC7B;AAAA,EACF;AACA,QAAM,OAAQ,IAA8C;AAC5D,SAAO,EAAE,SAAS,KAAK,KAAK,MAAM,QAAQ,QAAQ;AACpD;AAOA,SAAS,oBAAoB,QAAuB,QAAgB,SAA6B;AAC/F,QAAM,QAAQ,WAAW,QAAQ,QAAQ,OAAO;AAChD,MAAI,MAAO,QAAO;AAGlB,QAAM,iBAAiB,mBAAmB,QAAQ,OAAO;AACzD,MAAI,eAAe,SAAS,GAAG;AAC7B,UAAM,IAAI,sBAAsB,QAAQ,SAAS,cAAc;AAAA,EACjE;AAGA,QAAM,IAAI,mBAAmB,OAAO;AACtC;AAiBA,SAAS,cACP,OACA,SACA,MAC6C;AAC7C,MAAI,SAAS,eAAe,IAAI,KAAK;AACrC,MAAI,CAAC,QAAQ;AACX,aAAS;AAAA,MACP,aAAaC,OAAK,QAAQ,SAAS,MAAM,QAAQ;AAAA,MACjD,YAAY,qBAAqB,MAAM,UAAU,MAAM,OAAO;AAAA,IAChE;AACA,mBAAe,IAAI,OAAO,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,MAUD;AAC1B,QAAM,EAAE,QAAQ,QAAQ,SAAS,KAAK,KAAK,SAAS,SAAS,MAAM,gBAAgB,IAAI;AACvF,SAAO,YAAY;AAEjB,UAAM,QAAQ,oBAAoB,QAAQ,QAAQ,OAAO;AACzD,QAAI,SAAS,MAAM;AACnB,UAAM,EAAE,MAAM,IAAI;AAClB,UAAM,EAAE,aAAa,WAAW,IAAI,cAAc,OAAO,SAAS,IAAI;AAGtE,UAAM,cAAc,MAAM,gBAAgB,aAAa,MAAM,QAAQ,OAAO;AAG5E,UAAM,QAAQ,MAAM,oBAAoB,MAAM,QAAQ,SAAS,GAAG;AAGlE,UAAM,YAAY,sBAAsB,MAAM,MAAM;AAIpD,QAAI,qBAAqB,GAAG;AAC1B,YAAM,UAAU,WAAW;AAC3B,UAAI,SAAS;AACX,cAAM,sBAAsB,YAAY,MAAM,UAAU,QAAQ,SAAS,IAAI;AAAA,MAC/E;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,cAAc,YAAY,MAAM,QAAQ,WAAW,KAAK;AAC7E,QAAI,CAAC,OAAO,OAAO;AACjB,YAAM,IAAI,gBAAgB,wCAAU,OAAO,MAAM;AAAA,IACnD;AAOA,UAAM,OACJ,cAAc,WAAW,QAAQ,MAAM,MAAM,IACzC,MAAM,0BAA0B,OAAO,IACvC,QAAQ,MAAM,MAAM,IAClB,OAAO,OACP;AAIR,QAAI,MAAM,gBAAgB,UAAa,MAAM,cAAc,UAAa,MAAM,iBAAiB;AAE7F,YAAM,0BAA0B,MAAM,iBAAiB,OAAO;AAC9D,YAAM,SAAS,MAAM,sBAAsB,MAAM,eAAe;AAChE,UAAI,QAAQ;AACV,cAAM,cAAc,OAAO;AAC3B,cAAM,YAAY,OAAO;AAAA,MAC3B,OAAO;AAEL,cAAM,cAAc,CAAC;AACrB,cAAM,YAAY,CAAC;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,kBAAkB,kBACpB,EAAE,GAAG,iBAAiB,GAAG,MAAM,UAAU,IACzC,MAAM;AAGV,WAAO,MAAM,cAAc,YAAY,SAAS,KAAK,MAAM,MAAM,aAAa,eAAe;AAAA,EAC/F;AACF;AAKA,eAAe,oBAAoB,UAAoB,KAAoC;AACzF,QAAM,iBAAiB,UAAU,GAAG;AACtC;AAeA,eAAe,kBACb,KACA,MACA,KACA,SACA,KACe;AACf,QAAM,iBAAiB,UAAU,mBAAmB,KAAK,KAAK,MAAM,GAAG,IAAI,GAAG,GAAG;AAGjF,MAAI,WAAW,KAAK;AAClB,QAAI;AACF,YAAM,QAAQ,KAAK,GAAG;AAAA,IACxB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,eAAe,cACb,QACA,SACA,MACA,KACA,KACA,kBACA,SACA,QACA,iBACA,WACA,cACA,YACe;AAEf,MAAI,OAAqB,EAAE,SAAS,CAAC,GAAG,YAAY,CAAC,EAAE;AACvD,MAAI;AAIJ,QAAM,kBAAkB,IAAI,gBAAgB;AAC5C,MAAI,GAAG,SAAS,MAAM;AACpB,QAAI,CAAC,IAAI,cAAe,iBAAgB,MAAM;AAAA,EAChD,CAAC;AACD,MAAI;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,IAClB;AACA,UAAM,SAAS;AACf,WAAO,SAAS;AAChB,UAAM,EAAE,SAAS,KAAK,QAAQ,QAAQ,IAAI;AAG1C,UAAM,gBAAgB,oBAAoB;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAID,UAAM,WACJ,iBAAiB,SAAS,IACtB,MAAM,QAAQ,kBAAkB,KAAK,aAAa,IAClD,MAAM,cAAc;AAE1B,UAAM,oBAAoB,UAAU,GAAG;AAAA,EACzC,SAAS,KAAc;AAGrB,QAAI,IAAI,aAAa,IAAI,cAAe;AACxC,UAAM,kBAAkB,KAAK,MAAM,KAAK,SAAS,GAAG;AAAA,EACtD;AACF;AAloBA,IAqDM,oBAgYA;AArbN;AAAA;AAAA;AAWA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAMA;AASA,IAAM,qBAAqB,KAAK,OAAO;AAgYvC,IAAM,iBAAiB,oBAAI,QAGzB;AAAA;AAAA;;;AC5UK,SAAS,oBACd,QACA,iBACA,iBACM;AAEN,MAAI,gBAAgB,SAAS,GAAG;AAC9B,UAAM,YAAY,OAAO,UAAU,SAAS;AAC5C,UAAM,WAAW,UAAU,CAAC;AAC5B,QAAI,UAAU;AACZ,aAAO,mBAAmB,SAAS;AACnC,UAAI,UAA0B;AAC9B,iBAAW,QAAQ,iBAAiB;AAClC,kBAAU,KAAK,OAAO;AAAA,MACxB;AACA,aAAO,GAAG,WAAW,OAAO;AAAA,IAC9B;AAAA,EACF;AAGA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,UAAM,YAAY,OAAO,UAAU,SAAS;AAC5C,UAAM,WAAW,UAAU,CAAC;AAC5B,WAAO,mBAAmB,SAAS;AACnC,QAAI,UAAsC;AAC1C,eAAW,QAAQ,iBAAiB;AAClC,gBAAU,KAAK,OAAO;AAAA,IACxB;AACA,QAAI,SAAS;AACX,aAAO,GAAG,WAAW,OAAO;AAAA,IAC9B;AAAA,EACF;AACF;AA5IA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,YAAU;AACjB,OAAOC,UAAQ;AACf,SAAS,iBAAAC,sBAAqB;AAyD9B,eAAsB,YACpB,cACA,KACA,SACA,MAC2B;AAC3B,QAAM,kBAAuE,CAAC;AAC9E,QAAM,kBAAmF,CAAC;AAC1F,QAAM,WAAyD,CAAC;AAEhE,MAAI,CAAC,gBAAgB,aAAa,WAAW,GAAG;AAC9C,WAAO,EAAE,iBAAiB,iBAAiB,SAAS;AAAA,EACtD;AAGA,QAAM,UAAU;AAAA,IACd,GAAG;AAAA,IACH,aAAa,CAAC,OAAqD;AACjE,sBAAgB,KAAK,EAAE;AAAA,IACzB;AAAA,IACA,oBAAoB,CAAC,OAAiE;AACpF,sBAAgB,KAAK,EAAE;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,SAAS,oBAAI,IAAY;AAE/B,aAAW,QAAQ,cAAc;AAE/B,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,OAAC,EAAE,WAAW,SAAS,OAAO,IAAI,mBAAmB,IAAI;AAAA,IAC3D,SAAS,KAAK;AACZ,eAAS,KAAK;AAAA,QACZ,WAAW,KAAK,UAAU,IAAI;AAAA,QAC9B,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACzD,CAAC;AACD;AAAA,IACF;AAGA,QAAI,WAAW,MAAO;AAGtB,QAAI,OAAO,IAAI,SAAS,GAAG;AACzB,cAAQ,KAAK,4BAA4B,SAAS,YAAY;AAC9D;AAAA,IACF;AACA,WAAO,IAAI,SAAS;AAEpB,QAAI;AACF,YAAM,MAAM,MAAM,mBAAmB,WAAW,WAAW,QAAQ,IAAI,GAAG,IAAI;AAC9E,YAAM,SAAsB,IAAI,WAAY;AAE5C,UAAI,OAAO,OAAO,UAAU,YAAY;AACtC,iBAAS,KAAK,EAAE,WAAW,QAAQ,+BAA+B,CAAC;AACnE;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,EAAE,GAAG,SAAS,QAAQ,CAAC;AAC1C,cAAQ,IAAI,oBAAoB,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC5D,SAAS,KAAK;AACZ,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACzD,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,SAAS,SAAS,GAAG;AACvB,YAAQ;AAAA,MACN,WAAW,SAAS,MAAM;AAAA,IACxB,SAAS,IAAI,CAAC,MAAM,OAAO,EAAE,SAAS,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,IAAI;AAAA,IACpE;AAAA,EACF;AAEA,SAAO,EAAE,iBAAiB,iBAAiB,SAAS;AACtD;AAiBO,SAAS,yBAAyB,WAAmB,SAAgC;AAC1F,QAAM,OAAOF,OAAK,WAAW,SAAS,IAAI,YAAYA,OAAK,QAAQ,SAAS,SAAS;AACrF,OAAK,QAAQ,KAAK,IAAI,KAAK,QAAQ,KAAK,IAAI,MAAMC,KAAG,WAAW,IAAI,EAAG,QAAO;AAC9E,aAAW,OAAO,CAAC,OAAO,KAAK,GAAG;AAChC,QAAIA,KAAG,WAAW,OAAO,GAAG,EAAG,QAAO,OAAO;AAAA,EAC/C;AACA,aAAW,YAAY,CAAC,aAAa,WAAW,GAAG;AACjD,QAAIA,KAAG,WAAW,OAAO,QAAQ,EAAG,QAAO,OAAO;AAAA,EACpD;AACA,SAAO;AACT;AAUA,SAAS,kBAAkB,YAAoB,SAAiB,SAAyB;AACvF,MAAI,MAAMD,OAAK,SAAS,SAAS,UAAU,EAAE,QAAQ,OAAO,GAAG;AAC/D,MAAI,IAAI,WAAW,MAAM,EAAG,OAAM,IAAI,MAAM,CAAC;AAC7C,SAAOA,OAAK,QAAQ,SAAS,IAAI,QAAQ,SAAS,KAAK,CAAC;AAC1D;AAMA,SAAS,cAAc,YAAoB,aAA8B;AACvE,MAAI;AACF,WAAOC,KAAG,SAAS,UAAU,EAAE,UAAUA,KAAG,SAAS,WAAW,EAAE;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAWA,eAAe,mBACb,WACA,SACA,MACuB;AACvB,QAAM,aAAa,UAAU,WAAW,IAAI,KAAK,UAAU,WAAW,KAAK;AAC3E,MAAI,CAAC,cAAc,CAACD,OAAK,WAAW,SAAS,GAAG;AAC9C,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,UAAU,OAAOA,OAAK,QAAQ,SAAS,IAAI,IAAI;AACrD,QAAM,aAAa,yBAAyB,WAAW,OAAO;AAC9D,QAAM,cACJ,cAAc,UAAU,kBAAkB,YAAY,SAAS,OAAO,IAAI;AAG5E,MACE,gBAAgB,QAChBC,KAAG,WAAW,WAAW,MACxB,eAAe,QAAQ,CAAC,cAAc,YAAY,WAAW,IAC9D;AACA,WAAO,OAAOC,eAAc,WAAW,EAAE;AAAA,EAC3C;AAGA,MAAI,cAAc,QAAQ,KAAK,UAAU,GAAG;AAC1C,QAAI,CAAC,qBAAqB,KAAK,CAAC,SAAS;AACvC,YAAM,IAAI;AAAA,QACR,iBAAiB,SAAS;AAAA,MAE5B;AAAA,IACF;AACA,UAAM,cAAcF,OAAK,SAAS,SAAS,UAAU,EAAE,QAAQ,OAAO,GAAG;AACzE,QAAI,YAAY,WAAW,MAAM,GAAG;AAElC,YAAM,eAAe,YAAY,SAAS,IAAK;AAAA,IACjD,OAAO;AAEL,YAAM,sBAAsB,CAAC,UAAU,GAAG,SAAS,IAAK;AAAA,IAC1D;AACA,QAAI,eAAeC,KAAG,WAAW,WAAW,GAAG;AAC7C,aAAO,OAAOC,eAAc,WAAW,EAAE;AAAA,IAC3C;AAAA,EAEF;AAGA,MAAI,cAAc,QAAQ,KAAK,UAAU,GAAG;AAC1C,WAAO,OAAOA,eAAc,UAAU,EAAE;AAAA,EAC1C;AAGA,QAAM,aAAa;AAAA,IACjB,GAAG,SAAS;AAAA,IACZ,GAAG,SAAS;AAAA,IACZ,GAAG,SAAS;AAAA,IACZ,GAAG,SAAS;AAAA,EACd,EAAE,IAAI,CAAC,MAAOF,OAAK,WAAW,CAAC,IAAI,IAAIA,OAAK,KAAK,SAAS,CAAC,CAAE;AAC7D,QAAM,IAAI;AAAA,IACR,6BAA6B,SAAS,oBAAoB,OAAO;AAAA,IACxC,WAAW,KAAK,MAAM,CAAC;AAAA;AAAA,EAElD;AACF;AAKA,SAAS,mBAAmB,MAI1B;AACA,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,EAAE,WAAW,KAAK;AAAA,EAC3B;AAEA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,UAAM,CAAC,WAAW,OAAO,IAAI;AAC7B,WAAO,EAAE,WAAW,QAAQ;AAAA,EAC9B;AAEA,MAAI,aAAa,MAAM;AACrB,WAAO,EAAE,WAAW,KAAK,SAAS,SAAS,KAAK,SAAS,QAAQ,KAAK,OAAO;AAAA,EAC/E;AAEA,MAAI,UAAU,MAAM;AAClB,WAAO,EAAE,WAAW,KAAK,MAAM,SAAS,KAAK,SAAS,QAAQ,KAAK,OAAO;AAAA,EAC5E;AAEA,QAAM,IAAI,MAAM,+BAA+B,KAAK,UAAU,IAAI,CAAC,EAAE;AACvE;AAtSA,IA8IM,SAEA;AAhJN;AAAA;AAAA;AAKA;AACA;AAwIA,IAAM,UAAU;AAEhB,IAAM,UAAU;AAAA;AAAA;;;AC/IhB,OAAOG,UAAQ;AACf,OAAOC,YAAU;AACjB,SAAS,aAAa,YAAAC,iBAAgB;AA0DtC,eAAsB,oBACpB,SACA,MACA,aAA4B,mBACH;AACzB,QAAM,YAAYD,OAAK,QAAQ,SAAS,MAAME,WAAU;AACxD,MAAI,CAACH,KAAG,WAAW,SAAS,GAAG;AAC7B,WAAO,CAAC;AAAA,EACV;AACA,QAAM,aAAc,MAAM,oBAAoB,SAAS;AAGvD,QAAM,WAAW,aAAa,WAAW,SAAS,CAAC,CAAC;AACpD,aAAW,KAAK,QAAQ,QAAQ;AAChC,SAAO;AACT;AAYA,eAAsB,qBACpB,SACA,MACA,aAA4B,mBACF;AAC1B,QAAM,aAAaC,OAAK,QAAQ,SAAS,MAAMG,YAAW;AAC1D,MAAI,CAACJ,KAAG,WAAW,UAAU,GAAG;AAC9B,WAAO,CAAC;AAAA,EACV;AACA,QAAM,aAAc,MAAM,oBAAoB,UAAU;AAGxD,QAAM,WAAW,cAAc,WAAW,UAAU,CAAC,CAAC;AACtD,aAAW,MAAM,QAAQ,QAAQ;AACjC,SAAO;AACT;AAsBA,SAAS,gBAAgC;AACvC,SAAQ,WAAmD,gBAAgB,KAAK;AAClF;AAGA,SAAS,cAAc,KAA2B;AAChD,MAAI,QAAQ,MAAM;AAChB,WAAQ,WAAmD,gBAAgB;AAAA,EAC7E,OAAO;AACL,IAAC,WAAmD,gBAAgB,IAAI;AAAA,EAC1E;AACF;AAYA,SAAS,kCAAwC;AAC/C,QAAM,IAAI;AACV,MAAI,EAAE,sBAAsB,EAAG;AAC/B,IAAE,sBAAsB,IAAI;AAE5B,QAAM,WAAW,CAAC,WAAyB;AACzC,YAAQ,IAAI;AAAA,aAAgB,MAAM,oBAAoB;AACtD,UAAM,YAAY;AAChB,YAAM,MAAM,cAAc;AAC1B,UAAI,IAAK,OAAM,IAAI,MAAM;AACzB,cAAQ,KAAK,CAAC;AAAA,IAChB,GAAG;AAAA,EACL;AACA,UAAQ,GAAG,WAAW,MAAM,SAAS,SAAS,CAAC;AAC/C,UAAQ,GAAG,UAAU,MAAM,SAAS,QAAQ,CAAC;AAC/C;AAyDA,SAAS,iBAAiB,KAAsB;AAC9C,SAAO,kBAAkB,IAAI,GAAG;AAClC;AAgFA,eAAsB,cAAc,SAGjC;AACD,QAAM,UAAU,SAAS,WAAW,QAAQ,IAAI;AAChD,QAAM,OAAO,SAAS,QAAQ,QAAQ,IAAI,cAAc;AAGxD,QAAM,aAAaC,OAAK,QAAQ,SAAS,MAAM,WAAW;AAC1D,MAAI,CAACD,KAAG,WAAW,UAAU,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR,WAAW,IAAI,IAAI,WAAW;AAAA,IAChC;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,WAAW,SAAS,IAAI;AAG7C,QAAM,aAAc,MAAM,oBAAoB,UAAU;AACxD,QAAM,WAAW,MAAM,cAAc,UAAU;AAC/C,MAAI,SAAS,WAAW,SAAS,MAAM;AACvC,MAAI,WAAW,SAAS;AAGxB,QAAM,YAAY,qBAAqB,MAAM;AAC7C,MAAI,UAAU,SAAS,GAAG;AACxB,eAAW,YAAY,WAAW;AAChC,cAAQ,KAAK,+BAAW,SAAS,MAAM,IAAI,SAAS,OAAO,EAAE;AAC7D,iBAAW,QAAQ,SAAS,OAAO;AACjC,gBAAQ,KAAK,OAAO,IAAI,EAAE;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAIA,QAAM,aAAa,oBAAoB;AAGvC,QAAM,QAAQ,MAAM,oBAAoB,SAAS,MAAM,UAAU;AAGjE,QAAM,SAAS,MAAM,qBAAqB,SAAS,MAAM,UAAU;AAGnE,QAAM,eAAwC,SAC1C,OAAO,YAAY,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAC/E,CAAC;AAGL,QAAM,EAAE,QAAQ,UAAU,IAAI,aAAa;AAAA,IACzC,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,MAAM,QAAQ,QAAQ;AAAA,IACtB,SAAS,QAAQ,WAAW;AAAA,IAC5B,QAAS,UAA6C;AAAA,IACtD;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,WAAW,QAAQ;AAAA,IACnB,QAAQ,QAAQ;AAAA,IAChB,aAAa,QAAQ;AAAA,IACrB,MAAM,QAAQ;AAAA,IACd,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,OAAO,QAAQ;AAAA,IACf,cAAc,QAAQ;AAAA,IACtB;AAAA,EACF,CAAC;AAGD,QAAM,EAAE,iBAAiB,gBAAgB,IAAI,MAAM;AAAA,IACjD,QAAQ;AAAA,IACR;AAAA,MACE;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,WAAW,MAAM;AAAA,MACjB;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,sBAAoB,QAAQ,iBAAiB,eAAe;AAG5D,MAAI,SAAS;AAEb,QAAM,MAAe;AAAA,IACnB,QAAQ;AAAA,IACR;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IAEA,MAAM,OAAO,YAAsC;AAEjD,YAAM,UAAU,QAAQ,IAAI,OAAO,OAAO,QAAQ,IAAI,IAAI,IAAI;AAC9D,YAAM,aAAa,cAAc,SAAS,QAAQ,WAAW;AAK7D,UAAI,QAAQ,WAAW,QAAQ;AAC7B,YAAI;AACF,gBAAM,OAAO,UAAU,OAAO,EAAE,SAAS,QAAQ,QAAQ,QAAQ,WAAW,CAAC;AAC7E,kBAAQ,IAAI,wBAAwB;AAAA,QACtC,SAAS,KAAK;AACZ,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,kBAAQ,MAAM,+BAA+B,OAAO,EAAE;AACtD,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,aAAO,IAAI,QAAgB,CAACK,UAAS,WAAW;AAG9C,cAAM,gBAAgB,CAAC,QAAqB;AAC1C,cAAK,IAA8B,SAAS,cAAc;AACxD;AAAA,cACE,IAAI;AAAA,gBACF,QAAQ,UAAU;AAAA,cAEpB;AAAA,YACF;AACA;AAAA,UACF;AACA,iBAAO,GAAG;AAAA,QACZ;AACA,eAAO,KAAK,SAAS,aAAa;AAClC,eAAO,OAAO,YAAY,YAAY;AACpC,iBAAO,IAAI,SAAS,aAAa;AACjC,gBAAM,UAAU,OAAO,QAAQ;AAC/B,gBAAM,IAAI,OAAO,YAAY,YAAY,YAAY,OAAO,QAAQ,OAAO;AAE3E,kBAAQ,IAAI,sBAAsB;AAClC,kBAAQ,IAAI,6BAA6B,CAAC,EAAE;AAC5C,kBAAQ,IAAI,kBAAkB;AAC9B,qBAAW,SAAS,QAAQ;AAC1B,oBAAQ,IAAI,KAAK,MAAM,OAAO,OAAO,CAAC,CAAC,GAAG,MAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAAA,UAC9E;AACA,cAAI,SAAS,SAAS,GAAG;AACvB,oBAAQ,IAAI,qBAAqB;AACjC,uBAAW,SAAS,UAAU;AAC5B,sBAAQ,IAAI,YAAY,MAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAAA,YAC5D;AAAA,UACF;AACA,cAAI,MAAM,SAAS,GAAG;AACpB,oBAAQ,IAAI,YAAY,MAAM,MAAM,WAAW;AAC/C,uBAAW,QAAQ,OAAO;AACxB,sBAAQ,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ,EAAE;AAAA,YAChD;AAAA,UACF;AACA,cAAI,OAAO,SAAS,GAAG;AACrB,oBAAQ,IAAI,YAAY,OAAO,MAAM,YAAY;AACjD,uBAAW,SAAS,QAAQ;AAC1B,oBAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,sBAAQ,IAAI,KAAK,MAAM,IAAI,KAAK,OAAO,MAAM,MAAM,QAAQ,EAAE;AAAA,YAC/D;AAAA,UACF;AAGA,0CAAgC;AAGhC,cAAI,QAAQ,WAAW,SAAS;AAC9B,kBAAM,OAAO,UAAU,QAAQ,EAAE,SAAS,QAAQ,QAAQ,QAAQ,WAAW,CAAC;AAC9E,oBAAQ,IAAI,yBAAyB;AAAA,UACvC;AAEA,cAAI,SAAS;AACb,UAAAA,SAAQ,MAAM;AAAA,QAChB,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,OAAO,YAAqD;AAChE,YAAM;AAAA,QACJ,SAAS;AAAA,QACT,MAAM,UAAU;AAAA,QAChB,SAAS,aAAa,CAAC;AAAA,QACvB;AAAA,QACA;AAAA,MACF,IAAI,cAAc,CAAC;AAEnB,YAAM,WAAW,QACb,MACA,IAAI,gBAAgB,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,IACpF;AAEJ,aAAO,IAAI,QAAwB,CAACA,UAAS,WAAW;AAGtD,cAAM,SAAmB,CAAC;AAC1B,cAAM,UAAU,IAAI,YAAY;AAOhC,gBAAQ,aAAa;AACrB,gBAAQ,WAAW,CAAC;AACpB,gBAAQ,YAAY,SAAU,MAAc,OAAe;AACzD,eAAK,SAAS,KAAK,YAAY,CAAC,IAAI;AAAA,QACtC;AACA,gBAAQ,eAAe,SAAU,MAAc,OAAe;AAC5D,gBAAM,MAAM,KAAK,YAAY;AAC7B,gBAAM,WAAW,KAAK,SAAS,GAAG;AAClC,eAAK,SAAS,GAAG,IAAI,WAAW,GAAG,QAAQ,KAAK,KAAK,KAAK;AAAA,QAC5D;AACA,gBAAQ,YAAY,SAAU,QAAgB,SAAkC;AAC9E,eAAK,aAAa;AAClB,cAAI,SAAS;AACX,mBAAO,OAAO,KAAK,UAAU,OAAO;AAAA,UACtC;AAAA,QACF;AAEA,gBAAQ,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACxD,gBAAQ,GAAG,SAAS,MAAM;AAC1B,gBAAQ,GAAG,UAAU,MAAM;AACzB,gBAAMC,QAAO,OAAO,OAAO,MAAM;AACjC,cAAI;AACJ,cAAI;AACF,qBAAS,KAAK,MAAMA,MAAK,SAAS,CAAC;AAAA,UACrC,QAAQ;AACN,qBAASA,MAAK,SAAS;AAAA,UACzB;AACA,UAAAD,SAAQ;AAAA,YACN,QAAQ,QAAQ;AAAA,YAChB,SAAS,IAAI,QAAQ,QAAQ,QAAkC;AAAA,YAC/D,MAAM;AAAA,UACR,CAAC;AAAA,QACH,CAAC;AAED,cAAM,YAAY,OAAO,UAAU,SAAS;AAC5C,cAAM,UAAU,UAAU,UAAU,SAAS,CAAC;AAC9C,YAAI,OAAO,YAAY,YAAY;AACjC,iBAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C;AAAA,QACF;AAEA,cAAM,UAKFH,UAAS,KAAK,SAAS,SAAY,CAAC,OAAO,KAAK,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAC/E,gBAAQ,SAAS;AACjB,gBAAQ,MAAM,GAAG,OAAO,GAAG,QAAQ;AACnC,gBAAQ,UAAU;AAAA,UAChB,GAAG;AAAA,UACH,MAAM;AAAA,UACN,gBAAgB,SAAS,SAAY,qBAAqB;AAAA,QAC5D;AACA,gBAAQ,SAAS,EAAE,eAAe,YAAY;AAE9C;AAAA,UACE;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,QAAuB;AAC3B,UAAI,OAAQ;AACZ,eAAS;AAET,YAAM,IAAI;AAOV,QAAE,uBAAuB;AAEzB,UAAI,QAAQ,WAAW,SAAS;AAC9B,cAAM,OAAO,UAAU,QAAQ,EAAE,SAAS,QAAQ,QAAQ,QAAQ,WAAW,CAAC;AAAA,MAChF;AAIA,iBAAW,KAAK,MAAM;AACtB,iBAAW,MAAM,MAAM;AACvB,iBAAW,MAAM,MAAM;AACvB,iBAAW,YAAY,MAAM;AAG7B,UAAI,CAAC,OAAO,WAAW;AACrB,YAAI,SAAS;AAEb,YAAI,cAAc,MAAM,IAAK,eAAc,IAAI;AAC/C;AAAA,MACF;AAEA,YAAM,UAAU,IAAI,QAAc,CAACG,aAAY;AAC7C,eAAO,MAAM,CAAC,QAAQ;AACpB,cAAI,IAAK,SAAQ,MAAM,yBAAyB,GAAG;AACnD,UAAAA,SAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AAID,YAAM,iBAAiB,OAAO,QAAQ,IAAI,6BAA6B,GAAM;AAC7E,UACE,OAAO,EAAE,wBAAwB,cACjC,OAAO,SAAS,cAAc,KAC9B,kBAAkB,GAClB;AACA,cAAM,aAAa,IAAI,QAAc,CAACA,aAAY;AAChD,gBAAM,QAAQ,WAAW,MAAM;AAC7B,cAAE,sBAAsB;AACxB,YAAAA,SAAQ;AAAA,UACV,GAAG,cAAc;AAEjB,gBAAM,QAAQ;AAAA,QAChB,CAAC;AACD,cAAM,QAAQ,KAAK,CAAC,SAAS,UAAU,CAAC;AAExC,cAAM,OAAO,IAAI,QAAc,CAACA,aAAY,WAAWA,UAAS,GAAG,EAAE,QAAQ,CAAC;AAC9E,cAAM,QAAQ,KAAK,CAAC,SAAS,IAAI,CAAC;AAAA,MACpC,OAAO;AACL,cAAM;AAAA,MACR;AAEA,UAAI,SAAS;AAEb,UAAI,cAAc,MAAM,IAAK,eAAc,IAAI;AAAA,IACjD;AAAA,EACF;AAIA,gBAAc,GAAG;AAGjB,QAAM,MAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,WAA0B,aAAoC;AACzE,eAAS;AACT,iBAAW;AACX,UAAI,SAAS;AACb,UAAI,WAAW;AACf,gBAAU,UAAU;AACpB,gBAAU,YAAY;AAAA,IACxB;AAAA,EACF;AAEA,SAAO,EAAE,KAAK,IAAI;AACpB;AArpBA,IAwCM,cAEA,cAEA,aAEAF,aAEAC,cAqEA,kBAsBA,wBAgEA;AA3MN;AAAA;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AAiBA,IAAM,eAAe;AAErB,IAAM,eAAe;AAErB,IAAM,cAAc;AAEpB,IAAMD,cAAa;AAEnB,IAAMC,eAAc;AAqEpB,IAAM,mBAAmB,uBAAO,IAAI,oBAAoB;AAsBxD,IAAM,yBAAyB,uBAAO,IAAI,gCAAgC;AAgE1E,IAAM,oBAAoB,oBAAI,IAAI;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA;AAAA;;;ACxKD,eAAsB,aAAa,SAA6C;AAC9E,QAAM,EAAE,KAAK,IAAI,IAAI,MAAM,cAAc,OAAO;AAEhD,QAAM,SAAS;AAEf,SAAO,eAAe,YAA2B;AAE/C,qBAAiB,KAAK,IAAI,CAAC;AAE3B,8BAA0B;AAC1B,2BAAuB;AACvB,0BAAsB;AAEtB,uBAAmB;AAGnB,UAAM,YAAY,MAAM,WAAW,IAAI,SAAS,IAAI,UAAU,IAAI,IAAI;AACtE,UAAM,SAAS,WAAW,UAAU,MAAM;AAE1C,QAAI,qBAAqB,GAAG;AAG1B,YAAM,kBAAkB,QAAQ,IAAI,SAAS,IAAI,IAAI;AACrD,4BAAsB;AAMtB,YAAM,YAA2B;AAC/B,YAAI;AACF,gBAAM,EAAE,qBAAAG,qBAAoB,IAAI,MAAM;AACtC,gBAAMA,qBAAoB,QAAQ,IAAI,SAAS,IAAI,IAAI;AAAA,QACzD,SAAS,KAAK;AAEZ,kBAAQ,MAAM,kDAAkD,GAAG;AAAA,QACrE;AAAA,MACF,GAAG;AAAA,IACL,OAAO;AAEL,YAAM,EAAE,qBAAAA,qBAAoB,IAAI,MAAM;AACtC,YAAMA,qBAAoB,QAAQ,IAAI,SAAS,IAAI,IAAI;AAAA,IACzD;AAGA,QAAI,aAAa,QAAQ,UAAU,QAAQ;AAAA,EAC7C;AAEA,SAAO,cAAc,YAA2B;AAE9C,qBAAiB,KAAK,IAAI,CAAC;AAE3B,2BAAuB;AAEvB,UAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,aAAa;AAGxD,UAAM,sBAAsB,OAAO,IAAI,SAAS,IAAI,MAAM;AAAA,MACxD,YAAY,qBAAqB;AAAA,IACnC,CAAC;AAED,UAAM,oBAAoB,IAAI,SAAS,IAAI,MAAM,IAAI,UAAU;AAAA,EACjE;AAEA,SAAO,eAAe,YAA2B;AAE/C,qBAAiB,KAAK,IAAI,CAAC;AAE3B,2BAAuB;AAEvB,UAAM,SAAS,MAAM,WAAW,IAAI,SAAS,sBAAsB;AAGnE,UAAM,uBAAuB,QAAQ,IAAI,SAAS,IAAI,IAAI;AAE1D,UAAM,qBAAqB,IAAI,SAAS,IAAI,MAAM,IAAI,UAAU;AAAA,EAClE;AAEA,SAAO;AACT;AAjIA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;;;ACdA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAOC,YAAU;AA4DjB,eAAsB,WAAW,SAA4C;AAC3E,QAAM,UAAU,QAAQ,IAAI;AAI5B,MAAI,CAAC,QAAQ,IAAI,SAAU,SAAQ,IAAI,WAAW;AAClD,UAAQ,OAAO;AAGf,QAAM,UAAU;AAChB,UAAQ,IAAI,aAAa;AACzB,wBAAsB,IAAI;AAC1B,aAAW,OAAO;AAClB,UAAQ,IAAI,wCAAwC;AAGpD,UAAQ,IAAI,uBAAuB;AACnC,QAAM,cAAc,EAAE,SAAS,MAAM,QAAQ,CAAC;AAC9C,QAAM,UAAU,MAAM,WAAW,SAAS,OAAO;AAGjD,UAAQ,IAAI,6DAA6D;AACzE,QAAM,uBAAuB,SAAS,gBAAgB,OAAO;AAI7D,UAAQ,IAAI,+BAA+B;AAC3C,QAAM,4BAA4B,SAAS,OAAO;AAKlD,UAAQ,IAAI,gCAAgC;AAC5C,QAAM,6BAA6B,SAAS,OAAO;AAGnD,UAAQ,IAAI,uBAAuB;AACnC,QAAM,MAAM,MAAM,aAAa,EAAE,SAAS,MAAM,SAAS,KAAK,CAAC;AAC/D,QAAM,IAAI,OAAO;AAGjB,eAAa,EAAE,SAAS,KAAK,QAAQ,CAAC;AACxC;AAQA,eAAsB,uBACpB,SACA,UACA,MACe;AAEf,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,SAAS,UAAU,IAAI;AACrE,QAAM,SAAS,WAAW,MAAM;AAGhC,QAAM,aAAaA,OAAK,QAAQ,SAAS,MAAMC,YAAW;AAC1D,QAAM,aAAa,gBAAgB,QAAQ,UAAU,SAAS,IAAI;AAClE,QAAM,kBAAkB,YAAY,UAAU;AAChD;AASA,eAAsB,4BAA4B,SAAiB,MAA6B;AAC9F,QAAM,QAAQ,MAAM,UAAU,SAAS,aAAa;AACpD,QAAM,sBAAsB,OAAO,SAAS,MAAM,EAAE,YAAY,KAAK,CAAC;AACxE;AASA,eAAsB,6BAA6B,SAAiB,MAA6B;AAC/F,QAAM,SAAS,MAAM,WAAW,SAAS,sBAAsB;AAC/D,QAAM,uBAAuB,QAAQ,SAAS,IAAI;AACpD;AAnJA,IAmBM,UAEAA;AArBN;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAM,WAAW;AAEjB,IAAMA,eAAc;AAAA;AAAA;;;ACqBpB,eAAsB,mBAAmB,SAAsD;AAC7F,SAAO,mBAAmB,EAAE,GAAG,SAAS,YAAY,KAAK,CAAC;AAC5D;AA5CA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAiBA,OAAOC,YAAU;AACjB,OAAOC,UAAQ;AA4Cf,eAAsB,aAAa,SAAuC;AACxE,QAAM,UAAU,SAAS,WAAW,QAAQ,IAAI;AAChD,QAAM,SAAS,SAAS,QAAQC;AAKhC,QAAM,SAASF,OAAK,QAAQ,SAAS,MAAM;AAC3C,QAAM,WAAW,WAAWA,OAAK,QAAQ,OAAO,CAAC;AACjD,MAAI,YAAY,WAAW,MAAM,GAAG,QAAQ,GAAG;AAC7C,IAAAC,KAAG,OAAO,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD,OAAO;AACL,YAAQ;AAAA,MACN,uBAAuB,MAAM;AAAA,IAC/B;AAAA,EACF;AAIA,QAAM,cAAc,EAAE,SAAS,MAAM,OAAO,CAAC;AAC7C,QAAM,UAAU,MAAM,WAAW,SAAS,MAAM;AAIhD,QAAM,UAAUD,OAAK,QAAQ,SAAS,cAAc;AACpD,MAAIC,KAAG,WAAW,OAAO,GAAG;AAC1B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMA,KAAG,aAAa,SAAS,OAAO,CAAC;AACxD,UAAI,IAAI,SAAS,UAAU;AACzB,gBAAQ;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,UAAQ,IAAI,qBAAqB;AACjC,UAAQ,IAAI,WAAW,OAAO,EAAE;AAChC,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,IAAI,aAAa,MAAM,EAAE;AAGjC,UAAQ,IAAI,iDAAiD;AAC7D,QAAM,SAAS,MAAM,mBAAmB;AAAA,IACtC;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AAAA,EACZ,CAAC;AACD,UAAQ,IAAI,cAAc,OAAO,cAAc,MAAM,UAAU;AAC/D,MAAI,OAAO,cAAc,WAAW,GAAG;AACrC,YAAQ,KAAK,6CAA6C;AAC1D;AAAA,EACF;AAQA,QAAM,qBAAqB,0BAA0B,SAAS,SAAS,OAAO;AAC9E,MAAI,mBAAmB,SAAS,GAAG;AACjC,YAAQ,IAAI,sCAAsC;AAClD,UAAM,UAAU,mBAAmB,OAAO,CAAC,MAAM;AAC/C,YAAM,MAAMD,OAAK,SAAS,SAAS,CAAC,EAAE,QAAQ,OAAO,GAAG;AACxD,aAAO,CAAC,IAAI,WAAW,MAAM;AAAA,IAC/B,CAAC;AACD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,sBAAsB,SAAS,SAAS,MAAM;AAAA,IACtD;AACA,YAAQ,IAAI,cAAc,mBAAmB,MAAM,kBAAkB;AAAA,EACvE;AAGA,UAAQ,IAAI,4BAA4B;AACxC,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,SAAS,gBAAgB,MAAM;AAC7E,QAAM,SAAS,WAAW,MAAM;AAChC,UAAQ,IAAI,WAAW,OAAO,MAAM,YAAY,SAAS,MAAM,YAAY;AAG3E,QAAM,YAAY,qBAAqB,MAAM;AAC7C,MAAI,UAAU,SAAS,GAAG;AACxB,YAAQ,KAAK,oDAAY;AACzB,eAAW,YAAY,WAAW;AAChC,cAAQ,KAAK,KAAK,SAAS,MAAM,IAAI,SAAS,OAAO,EAAE;AACvD,iBAAW,QAAQ,SAAS,OAAO;AACjC,gBAAQ,KAAK,SAAS,IAAI,EAAE;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,IAAI,8BAA8B;AAC1C,QAAM,oBAAoB,QAAQ,SAAS,MAAM;AACjD,UAAQ,IAAI,gCAAgCA,OAAK,QAAQ,SAAS,MAAM,CAAC,EAAE;AAG3E,UAAQ,IAAI,uCAAuC;AACnD,QAAM,aAAaA,OAAK,QAAQ,SAAS,QAAQ,iBAAiB;AAClE,QAAM,aAAa,gBAAgB,QAAQ,UAAU,SAAS,MAAM;AACpE,QAAM,kBAAkB,YAAY,UAAU;AAC9C,UAAQ,IAAI,gBAAgB,UAAU,EAAE;AAKxC,UAAQ,IAAI,gDAAgD;AAC5D,QAAM,QAAQ,MAAM,UAAU,SAAS,aAAa;AACpD,QAAM,WAAW,MAAM,sBAAsB,OAAO,SAAS,MAAM;AACnE,UAAQ,IAAI,WAAW,SAAS,MAAM,UAAU;AAChD,UAAQ,IAAI,oBAAoBA,OAAK,QAAQ,SAAS,QAAQ,gBAAgB,CAAC,EAAE;AAKjF,UAAQ,IAAI,sCAAsC;AAClD,QAAM,SAAS,MAAM,WAAW,SAAS,sBAAsB;AAC/D,QAAM,YAAY,MAAM,uBAAuB,QAAQ,SAAS,MAAM;AACtE,UAAQ,IAAI,WAAW,UAAU,MAAM,WAAW;AAClD,UAAQ,IAAI,qBAAqBA,OAAK,QAAQ,SAAS,QAAQ,iBAAiB,CAAC,EAAE;AAMnF,UAAQ,IAAI,kCAAkC;AAC9C,QAAM,WAAWA,OAAK,QAAQ,SAAS,QAAQ,SAAS;AAIxD,QAAM,oBACJ,SAAS,QAAQ,QAAQ,SAASE,gBAAe,WAAW,KAAK,UAAU,MAAM,CAAC,OAAO;AAC3F,QAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAOY,iBAAiB;AAAA;AAAA;AAGjD,QAAMD,KAAG,SAAS,UAAU,UAAU,aAAa,OAAO;AAC1D,UAAQ,IAAI,gBAAgB,QAAQ,EAAE;AAEtC,UAAQ,IAAI,yBAAyB;AACvC;AASA,SAAS,0BACP,cACA,SACU;AACV,MAAI,CAAC,gBAAgB,aAAa,WAAW,EAAG,QAAO,CAAC;AACxD,QAAM,UAAoB,CAAC;AAC3B,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ,cAAc;AAC/B,QAAI;AACJ,QAAI,OAAO,SAAS,SAAU,aAAY;AAAA,aACjC,MAAM,QAAQ,IAAI,EAAG,aAAY,KAAK,CAAC;AAAA,aACvC,UAAU,KAAM,aAAY,KAAK;AAC1C,QAAI,CAAC,UAAW;AAChB,QACE,CAAC,UAAU,WAAW,IAAI,KAC1B,CAAC,UAAU,WAAW,KAAK,KAC3B,CAACD,OAAK,WAAW,SAAS,GAC1B;AACA;AAAA,IACF;AACA,UAAM,SAAS,yBAAyB,WAAW,OAAO;AAC1D,QAAI,UAAU,CAAC,KAAK,IAAI,MAAM,GAAG;AAC/B,WAAK,IAAI,MAAM;AACf,cAAQ,KAAK,MAAM;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAtPA,IAsBME;AAtBN;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAGA,IAAMA,gBAAe;AAAA;AAAA;;;ACtBrB,SAAS,oBAAoB;AAE7B,SAAS,MAAM,KAAK;AACnB,SAAO,OAAO,OAAO,CAAC,IAAI,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG;AAC1D;AAEA,SAAS,MAAM,KAAK,KAAK,KAAK,MAAM;AACnC,MAAI,GAAG,MAAI,IAAI,GAAG,GAAG,MACpB,CAAC,CAAC,CAAC,KAAK,OAAO,QAAQ,GAAG,IAAK,OAAO,QAAQ,QAAQ,OAAO,KAAK,OAAO,GAAG,IAC1E,OAAO,QAAQ,YAAY,MAC3B,CAAC,CAAC,CAAC,KAAK,QAAQ,QAAQ,GAAG,IAAK,QAAQ,UAAU,QAAQ,QAAQ,WAAW,IAAI,EAAE,MAAM,IAAI,CAAC,KAAI,IAAI,MAAM,KAAK,IAAI,GAAG,GAAE,CAAC,CAAC,QAC3H,IAAI,CAAC,KAAI,IAAI,MAAM,KAAK,IAAI;AAEhC,MAAI,GAAG,IAAI,OAAO,OAAO,MAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,OAAO,GAAG,IAAI,CAAC,KAAK,GAAG;AACjF;AAEA,SAAS,KAAM,MAAM,MAAM;AAC1B,SAAO,QAAQ,CAAC;AAChB,SAAO,QAAQ,CAAC;AAEhB,MAAI,GAAG,KAAK,KAAK,MAAM,KAAK,MAAI,EAAE,GAAE,CAAC,EAAE;AACvC,MAAI,IAAE,GAAG,IAAE,GAAG,MAAI,GAAG,MAAI,KAAK;AAE9B,QAAM,QAAQ,KAAK,UAAU;AAC7B,QAAM,SAAS,KAAK,YAAY;AAChC,QAAM,WAAW,KAAK,YAAY;AAElC,OAAK,QAAQ,KAAK,SAAS,CAAC;AAC5B,OAAK,SAAS,MAAM,KAAK,MAAM;AAC/B,OAAK,UAAU,MAAM,KAAK,OAAO;AAEjC,MAAI,OAAO;AACV,SAAK,KAAK,KAAK,OAAO;AACrB,YAAM,KAAK,MAAM,CAAC,IAAI,MAAM,KAAK,MAAM,CAAC,CAAC;AACzC,WAAK,IAAE,GAAG,IAAI,IAAI,QAAQ,KAAK;AAC9B,SAAC,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,OAAO,GAAG,CAAC;AAAA,MACjD;AAAA,IACD;AAAA,EACD;AAEA,OAAK,IAAE,KAAK,QAAQ,QAAQ,MAAM,KAAI;AACrC,UAAM,KAAK,MAAM,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC;AACtC,SAAK,IAAE,IAAI,QAAQ,MAAM,IAAI,MAAK,QAAQ,KAAK,IAAI,CAAC,CAAC;AAAA,EACtD;AAEA,OAAK,IAAE,KAAK,OAAO,QAAQ,MAAM,KAAI;AACpC,UAAM,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC,KAAK,CAAC;AACrC,SAAK,IAAE,IAAI,QAAQ,MAAM,IAAI,MAAK,OAAO,KAAK,IAAI,CAAC,CAAC;AAAA,EACrD;AAEA,MAAI,UAAU;AACb,SAAK,KAAK,KAAK,SAAS;AACvB,aAAO,OAAO,KAAK,QAAQ,CAAC;AAC5B,YAAM,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC;AACxC,UAAI,KAAK,IAAI,MAAM,QAAQ;AAC1B,aAAK,IAAI,EAAE,KAAK,CAAC;AACjB,aAAK,IAAE,GAAG,IAAI,IAAI,QAAQ,KAAK;AAC9B,eAAK,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC;AAAA,QACvB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,QAAM,OAAO,SAAS,OAAO,KAAK,KAAK,KAAK,IAAI,CAAC;AAEjD,OAAK,IAAE,GAAG,IAAI,KAAK,KAAK;AACvB,UAAM,KAAK,CAAC;AAEZ,QAAI,QAAQ,MAAM;AACjB,UAAI,IAAI,IAAI,EAAE,OAAO,KAAK,MAAM,EAAE,CAAC,CAAC;AACpC;AAAA,IACD;AAEA,SAAK,IAAE,GAAG,IAAI,IAAI,QAAQ,KAAK;AAC9B,UAAI,IAAI,WAAW,CAAC,MAAM,GAAI;AAAA,IAC/B;AAEA,QAAI,MAAM,GAAG;AACZ,UAAI,EAAE,KAAK,GAAG;AAAA,IACf,WAAW,IAAI,UAAU,GAAG,IAAI,CAAC,MAAM,OAAO;AAC7C,aAAO,IAAI,UAAU,IAAI,CAAC;AAC1B,UAAI,UAAU,CAAC,CAAC,KAAK,QAAQ,IAAI,GAAG;AACnC,eAAO,KAAK,QAAQ,GAAG;AAAA,MACxB;AACA,UAAI,IAAI,IAAI;AAAA,IACb,OAAO;AACN,WAAK,MAAI,IAAE,GAAG,MAAM,IAAI,QAAQ,OAAO;AACtC,YAAI,IAAI,WAAW,GAAG,MAAM,GAAI;AAAA,MACjC;AAEA,aAAO,IAAI,UAAU,GAAG,GAAG;AAC3B,YAAM,IAAI,UAAU,EAAE,GAAG,MAAM,IAAE,MAAM,QAAQ,KAAG,KAAK,IAAE,CAAC,GAAG,WAAW,CAAC,MAAM,MAAM,KAAK,EAAE,CAAC;AAC7F,YAAO,MAAM,IAAI,CAAC,IAAI,IAAI;AAE1B,WAAK,MAAI,GAAG,MAAM,IAAI,QAAQ,OAAO;AACpC,eAAO,IAAI,GAAG;AACd,YAAI,UAAU,CAAC,CAAC,KAAK,QAAQ,IAAI,EAAG,QAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,IAAI;AAC5E,cAAM,KAAK,MAAO,MAAM,IAAI,IAAI,UAAW,KAAK,IAAI;AAAA,MACrD;AAAA,IACD;AAAA,EACD;AAEA,MAAI,UAAU;AACb,SAAK,KAAK,KAAK,SAAS;AACvB,UAAI,IAAI,CAAC,MAAM,QAAQ;AACtB,YAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;AAAA,MACxB;AAAA,IACD;AAAA,EACD;AAEA,MAAI,OAAO;AACV,SAAK,KAAK,KAAK;AACd,YAAM,KAAK,MAAM,CAAC,KAAK,CAAC;AACxB,aAAO,IAAI,SAAS,GAAG;AACtB,YAAI,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC;AAAA,MACzB;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAEA,IAAM,iBAAiB,CAAC,MAAM,EAAE,QAAQ,UAAU,EAAE,EAAE,KAAK;AAC3D,IAAM,kBAAkB,CAAC,MAAM;AAC7B,QAAM,2BAA2B;AACjC,QAAM,2BAA2B;AACjC,QAAM,MAAM,CAAC;AACb,QAAM,QAAQ,CAAC,UAAU;AACvB,QAAI,WAAW;AACf,QAAI,QAAQ,MAAM,CAAC;AACnB,QAAI,MAAM,WAAW,KAAK,GAAG;AAC3B,cAAQ,MAAM,MAAM,CAAC;AACrB,iBAAW;AAAA,IACb;AACA,WAAO;AAAA,MACL,UAAU,MAAM,CAAC,EAAE,WAAW,GAAG;AAAA,MACjC;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI;AACJ,SAAO,cAAc,yBAAyB,KAAK,CAAC,GAAG;AACrD,QAAI,KAAK,MAAM,WAAW,CAAC;AAAA,EAC7B;AACA,MAAI;AACJ,SAAO,cAAc,yBAAyB,KAAK,CAAC,GAAG;AACrD,QAAI,KAAK,MAAM,WAAW,CAAC;AAAA,EAC7B;AACA,SAAO;AACT;AACA,IAAM,gBAAgB,CAAC,YAAY;AACjC,QAAM,SAAS,EAAC,OAAO,CAAC,GAAG,SAAS,CAAC,EAAC;AACtC,aAAW,CAAC,OAAO,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC/C,QAAI,OAAO,MAAM,SAAS,GAAG;AAC3B,aAAO,MAAM,OAAO,MAAM,CAAC,CAAC,IAAI,OAAO,MAAM,MAAM,CAAC;AAAA,IACtD;AACA,QAAI,OAAO,WAAW;AACpB,UAAI,OAAO,SAAS;AAClB,cAAM,sBAAsB,QAAQ,KAAK,CAAC,GAAG,MAAM;AACjD,iBAAO,MAAM,SAAS,EAAE,MAAM,KAAK,CAAC,SAAS,OAAO,MAAM,SAAS,IAAI,CAAC,KAAK,OAAO,EAAE,aAAa;AAAA,QACrG,CAAC;AACD,YAAI,CAAC,qBAAqB;AACxB,iBAAO,QAAQ,KAAK,OAAO,MAAM,CAAC,CAAC;AAAA,QACrC;AAAA,MACF,OAAO;AACL,eAAO,QAAQ,KAAK,OAAO,MAAM,CAAC,CAAC;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AACA,IAAM,cAAc,CAAC,QAAQ;AAC3B,SAAO,IAAI,KAAK,CAAC,GAAG,MAAM;AACxB,WAAO,EAAE,SAAS,EAAE,SAAS,KAAK;AAAA,EACpC,CAAC,EAAE,CAAC;AACN;AACA,IAAM,WAAW,CAAC,KAAK,WAAW;AAChC,SAAO,IAAI,UAAU,SAAS,MAAM,GAAG,GAAG,GAAG,IAAI,OAAO,SAAS,IAAI,MAAM,CAAC;AAC9E;AACA,IAAM,YAAY,CAAC,UAAU;AAC3B,SAAO,MAAM,QAAQ,oBAAoB,CAAC,GAAG,IAAI,OAAO;AACtD,WAAO,KAAK,GAAG,YAAY;AAAA,EAC7B,CAAC;AACH;AACA,IAAM,aAAa,CAAC,KAAK,MAAM,QAAQ;AACrC,MAAI,IAAI;AACR,MAAI,SAAS,KAAK;AAClB,MAAI,IAAI;AACR,MAAI;AACJ,SAAO,IAAI,QAAQ,EAAE,GAAG;AACtB,QAAI,EAAE,KAAK,CAAC,CAAC;AACb,QAAI,EAAE,KAAK,CAAC,CAAC,IAAI,MAAM,SAAS,IAAI,MAAM,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC;AAAA,EACxH;AACF;AACA,IAAM,YAAY,CAAC,KAAK,eAAe;AACrC,aAAW,OAAO,OAAO,KAAK,UAAU,GAAG;AACzC,UAAM,YAAY,WAAW,GAAG;AAChC,QAAI,UAAU,iBAAiB;AAC7B,UAAI,GAAG,IAAI,MAAM,UAAU,OAAO,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC;AACnD,UAAI,OAAO,UAAU,sBAAsB,YAAY;AACrD,YAAI,GAAG,IAAI,IAAI,GAAG,EAAE,IAAI,UAAU,iBAAiB;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AACF;AACA,IAAM,cAAc,CAAC,UAAU;AAC7B,QAAM,IAAI,cAAc,KAAK,KAAK;AAClC,SAAO,IAAI,EAAE,CAAC,IAAI;AACpB;AACA,IAAM,sBAAsB,CAAC,SAAS;AACpC,SAAO,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,GAAG,MAAM;AACnC,WAAO,MAAM,IAAI,UAAU,CAAC,IAAI;AAAA,EAClC,CAAC,EAAE,KAAK,GAAG;AACb;AACA,IAAM,WAAN,cAAuB,MAAM;AAAA,EAC3B,YAAY,SAAS;AACnB,UAAM,OAAO;AACb,SAAK,OAAO,KAAK,YAAY;AAC7B,QAAI,OAAO,MAAM,sBAAsB,YAAY;AACjD,YAAM,kBAAkB,MAAM,KAAK,WAAW;AAAA,IAChD,OAAO;AACL,WAAK,QAAQ,IAAI,MAAM,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AACF;AAEA,IAAM,SAAN,MAAa;AAAA,EACX,YAAY,SAAS,aAAa,QAAQ;AACxC,SAAK,UAAU;AACf,SAAK,cAAc;AACnB,SAAK,SAAS,OAAO,OAAO,CAAC,GAAG,MAAM;AACtC,cAAU,QAAQ,QAAQ,SAAS,EAAE;AACrC,SAAK,UAAU;AACf,SAAK,QAAQ,eAAe,OAAO,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM;AACzD,UAAI,OAAO,EAAE,KAAK,EAAE,QAAQ,WAAW,EAAE;AACzC,UAAI,KAAK,WAAW,KAAK,GAAG;AAC1B,aAAK,UAAU;AACf,eAAO,KAAK,QAAQ,QAAQ,EAAE;AAAA,MAChC;AACA,aAAO,oBAAoB,IAAI;AAAA,IACjC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,SAAS,IAAI,EAAE;AAC9C,SAAK,OAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAC5C,QAAI,KAAK,WAAW,KAAK,OAAO,WAAW,MAAM;AAC/C,WAAK,OAAO,UAAU;AAAA,IACxB;AACA,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,WAAK,WAAW;AAAA,IAClB,WAAW,QAAQ,SAAS,GAAG,GAAG;AAChC,WAAK,WAAW;AAAA,IAClB,OAAO;AACL,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AACF;AAEA,IAAM,cAAc,QAAQ;AAC5B,IAAM,eAAe,GAAG,QAAQ,QAAQ,IAAI,QAAQ,IAAI,SAAS,QAAQ,OAAO;AAEhF,IAAM,UAAN,MAAc;AAAA,EACZ,YAAY,SAAS,aAAa,SAAS,CAAC,GAAG,KAAK;AAClD,SAAK,UAAU;AACf,SAAK,cAAc;AACnB,SAAK,SAAS;AACd,SAAK,MAAM;AACX,SAAK,UAAU,CAAC;AAChB,SAAK,aAAa,CAAC;AACnB,SAAK,OAAO,eAAe,OAAO;AAClC,SAAK,OAAO,gBAAgB,OAAO;AACnC,SAAK,WAAW,CAAC;AAAA,EACnB;AAAA,EACA,MAAM,MAAM;AACV,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EACA,sBAAsB;AACpB,SAAK,OAAO,sBAAsB;AAClC,WAAO;AAAA,EACT;AAAA,EACA,2BAA2B;AACzB,SAAK,OAAO,2BAA2B;AACvC,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,SAAS,cAAc,iBAAiB;AAC9C,SAAK,gBAAgB;AACrB,SAAK,OAAO,aAAa,wBAAwB;AACjD,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,SAAS;AACf,SAAK,SAAS,KAAK,OAAO;AAC1B,WAAO;AAAA,EACT;AAAA,EACA,OAAO,SAAS,aAAa,QAAQ;AACnC,UAAM,SAAS,IAAI,OAAO,SAAS,aAAa,MAAM;AACtD,SAAK,QAAQ,KAAK,MAAM;AACxB,WAAO;AAAA,EACT;AAAA,EACA,MAAM,MAAM;AACV,SAAK,WAAW,KAAK,IAAI;AACzB,WAAO;AAAA,EACT;AAAA,EACA,OAAO,UAAU;AACf,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA,EACA,UAAU,MAAM;AACd,WAAO,KAAK,SAAS,QAAQ,KAAK,WAAW,SAAS,IAAI;AAAA,EAC5D;AAAA,EACA,IAAI,mBAAmB;AACrB,WAAO,KAAK,SAAS,MAAM,KAAK,WAAW,SAAS,GAAG;AAAA,EACzD;AAAA,EACA,IAAI,kBAAkB;AACpB,WAAO,gBAAgB;AAAA,EACzB;AAAA,EACA,UAAU,MAAM;AACd,WAAO,KAAK,MAAM,GAAG,EAAE,CAAC;AACxB,WAAO,KAAK,QAAQ,KAAK,CAAC,WAAW;AACnC,aAAO,OAAO,MAAM,SAAS,IAAI;AAAA,IACnC,CAAC;AAAA,EACH;AAAA,EACA,aAAa;AACX,UAAM,EAAC,MAAM,SAAQ,IAAI,KAAK;AAC9B,UAAM;AAAA,MACJ;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF,IAAI,KAAK,IAAI;AACb,QAAI,WAAW;AAAA,MACb;AAAA,QACE,MAAM,GAAG,IAAI,GAAG,gBAAgB,IAAI,aAAa,KAAK,EAAE;AAAA,MAC1D;AAAA,IACF;AACA,aAAS,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,MAAM,OAAO,IAAI,IAAI,KAAK,aAAa,KAAK,OAAO;AAAA,IACrD,CAAC;AACD,UAAM,gBAAgB,KAAK,mBAAmB,KAAK,qBAAqB,SAAS,SAAS;AAC1F,QAAI,cAAc;AAChB,YAAM,qBAAqB,YAAY,SAAS,IAAI,CAAC,YAAY,QAAQ,OAAO,CAAC;AACjF,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,MAAM,SAAS,IAAI,CAAC,YAAY;AAC9B,iBAAO,KAAK,SAAS,QAAQ,SAAS,mBAAmB,MAAM,CAAC,KAAK,QAAQ,WAAW;AAAA,QAC1F,CAAC,EAAE,KAAK,IAAI;AAAA,MACd,CAAC;AACD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,MAAM,SAAS,IAAI,CAAC,YAAY,OAAO,IAAI,GAAG,QAAQ,SAAS,KAAK,KAAK,IAAI,QAAQ,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI;AAAA,MACjH,CAAC;AAAA,IACH;AACA,QAAI,UAAU,KAAK,kBAAkB,gBAAgB,CAAC,GAAG,KAAK,SAAS,GAAG,iBAAiB,CAAC,CAAC;AAC7F,QAAI,CAAC,KAAK,mBAAmB,CAAC,KAAK,kBAAkB;AACnD,gBAAU,QAAQ,OAAO,CAAC,WAAW,OAAO,SAAS,SAAS;AAAA,IAChE;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,oBAAoB,YAAY,QAAQ,IAAI,CAAC,WAAW,OAAO,OAAO,CAAC;AAC7E,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,MAAM,QAAQ,IAAI,CAAC,WAAW;AAC5B,iBAAO,KAAK,SAAS,OAAO,SAAS,kBAAkB,MAAM,CAAC,KAAK,OAAO,WAAW,IAAI,OAAO,OAAO,YAAY,SAAS,KAAK,aAAa,OAAO,OAAO,OAAO,GAAG;AAAA,QACxK,CAAC,EAAE,KAAK,IAAI;AAAA,MACd,CAAC;AAAA,IACH;AACA,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,MAAM,KAAK,SAAS,IAAI,CAAC,YAAY;AACnC,cAAI,OAAO,YAAY,YAAY;AACjC,mBAAO,QAAQ,IAAI;AAAA,UACrB;AACA,iBAAO;AAAA,QACT,CAAC,EAAE,KAAK,IAAI;AAAA,MACd,CAAC;AAAA,IACH;AACA,QAAI,cAAc;AAChB,iBAAW,aAAa,QAAQ,KAAK;AAAA,IACvC;AACA,YAAQ,IAAI,SAAS,IAAI,CAAC,YAAY;AACpC,aAAO,QAAQ,QAAQ,GAAG,QAAQ,KAAK;AAAA,EAC3C,QAAQ,IAAI,KAAK,QAAQ;AAAA,IACvB,CAAC,EAAE,KAAK,MAAM,CAAC;AAAA,EACjB;AAAA,EACA,gBAAgB;AACd,UAAM,EAAC,KAAI,IAAI,KAAK;AACpB,UAAM,EAAC,cAAa,IAAI,KAAK,IAAI;AACjC,QAAI,eAAe;AACjB,cAAQ,IAAI,GAAG,IAAI,IAAI,aAAa,IAAI,YAAY,EAAE;AAAA,IACxD;AAAA,EACF;AAAA,EACA,oBAAoB;AAClB,UAAM,mBAAmB,KAAK,KAAK,OAAO,CAAC,QAAQ,IAAI,QAAQ,EAAE;AACjE,QAAI,KAAK,IAAI,KAAK,SAAS,kBAAkB;AAC3C,YAAM,IAAI,SAAS,uCAAuC,KAAK,OAAO,IAAI;AAAA,IAC5E;AAAA,EACF;AAAA,EACA,sBAAsB;AACpB,UAAM,EAAC,SAAS,cAAa,IAAI,KAAK;AACtC,QAAI,CAAC,KAAK,OAAO,qBAAqB;AACpC,iBAAW,QAAQ,OAAO,KAAK,OAAO,GAAG;AACvC,YAAI,SAAS,QAAQ,CAAC,KAAK,UAAU,IAAI,KAAK,CAAC,cAAc,UAAU,IAAI,GAAG;AAC5E,gBAAM,IAAI,SAAS,oBAAoB,KAAK,SAAS,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,EAAE,IAAI;AAAA,QACvF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,mBAAmB;AACjB,UAAM,EAAC,SAAS,eAAe,cAAa,IAAI,KAAK;AACrD,UAAM,UAAU,CAAC,GAAG,cAAc,SAAS,GAAG,KAAK,OAAO;AAC1D,eAAW,UAAU,SAAS;AAC5B,YAAM,QAAQ,cAAc,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AACrD,UAAI,OAAO,UAAU;AACnB,cAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,OAAO,IAAI,CAAC;AACjF,YAAI,UAAU,QAAQ,UAAU,SAAS,CAAC,YAAY;AACpD,gBAAM,IAAI,SAAS,YAAY,OAAO,OAAO,qBAAqB;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AACA,IAAM,gBAAN,cAA4B,QAAQ;AAAA,EAClC,YAAY,KAAK;AACf,UAAM,cAAc,IAAI,CAAC,GAAG,GAAG;AAAA,EACjC;AACF;AAEA,IAAI,WAAW,OAAO;AACtB,IAAM,MAAN,cAAkB,aAAa;AAAA,EAC7B,YAAY,OAAO,IAAI;AACrB,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,WAAW,CAAC;AACjB,SAAK,UAAU,CAAC;AAChB,SAAK,OAAO,CAAC;AACb,SAAK,UAAU,CAAC;AAChB,SAAK,gBAAgB,IAAI,cAAc,IAAI;AAC3C,SAAK,cAAc,MAAM,qBAAqB;AAAA,EAChD;AAAA,EACA,MAAM,MAAM;AACV,SAAK,cAAc,MAAM,IAAI;AAC7B,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,SAAS,aAAa,QAAQ;AACpC,UAAM,UAAU,IAAI,QAAQ,SAAS,eAAe,IAAI,QAAQ,IAAI;AACpE,YAAQ,gBAAgB,KAAK;AAC7B,SAAK,SAAS,KAAK,OAAO;AAC1B,WAAO;AAAA,EACT;AAAA,EACA,OAAO,SAAS,aAAa,QAAQ;AACnC,SAAK,cAAc,OAAO,SAAS,aAAa,MAAM;AACtD,WAAO;AAAA,EACT;AAAA,EACA,KAAK,UAAU;AACb,SAAK,cAAc,OAAO,cAAc,sBAAsB;AAC9D,SAAK,cAAc,eAAe;AAClC,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,SAAS,cAAc,iBAAiB;AAC9C,SAAK,cAAc,QAAQ,SAAS,WAAW;AAC/C,SAAK,oBAAoB;AACzB,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,SAAS;AACf,SAAK,cAAc,QAAQ,OAAO;AAClC,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,QAAI,KAAK,gBAAgB;AACvB,WAAK,eAAe,WAAW;AAAA,IACjC,OAAO;AACL,WAAK,cAAc,WAAW;AAAA,IAChC;AAAA,EACF;AAAA,EACA,gBAAgB;AACd,SAAK,cAAc,cAAc;AAAA,EACnC;AAAA,EACA,cAAc,EAAC,MAAM,QAAO,GAAG,gBAAgB,oBAAoB;AACjE,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,QAAI,gBAAgB;AAClB,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAI,oBAAoB;AACtB,WAAK,qBAAqB;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA,EACA,sBAAsB;AACpB,SAAK,iBAAiB;AACtB,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EACA,MAAM,OAAO,aAAa;AAAA,IACxB,MAAM;AAAA,EACR,IAAI,CAAC,GAAG;AACN,SAAK,UAAU;AACf,QAAI,CAAC,KAAK,MAAM;AACd,WAAK,OAAO,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,CAAC,IAAI;AAAA,IAC/C;AACA,QAAI,cAAc;AAClB,eAAW,WAAW,KAAK,UAAU;AACnC,YAAM,SAAS,KAAK,IAAI,KAAK,MAAM,CAAC,GAAG,OAAO;AAC9C,YAAM,cAAc,OAAO,KAAK,CAAC;AACjC,UAAI,QAAQ,UAAU,WAAW,GAAG;AAClC,sBAAc;AACd,cAAM,aAAa,SAAS,SAAS,CAAC,GAAG,MAAM,GAAG;AAAA,UAChD,MAAM,OAAO,KAAK,MAAM,CAAC;AAAA,QAC3B,CAAC;AACD,aAAK,cAAc,YAAY,SAAS,WAAW;AACnD,aAAK,KAAK,WAAW,WAAW,IAAI,OAAO;AAAA,MAC7C;AAAA,IACF;AACA,QAAI,aAAa;AACf,iBAAW,WAAW,KAAK,UAAU;AACnC,YAAI,QAAQ,SAAS,IAAI;AACvB,wBAAc;AACd,gBAAM,SAAS,KAAK,IAAI,KAAK,MAAM,CAAC,GAAG,OAAO;AAC9C,eAAK,cAAc,QAAQ,OAAO;AAClC,eAAK,KAAK,aAAa,OAAO;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AACA,QAAI,aAAa;AACf,YAAM,SAAS,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC;AACrC,WAAK,cAAc,MAAM;AAAA,IAC3B;AACA,QAAI,KAAK,QAAQ,QAAQ,KAAK,gBAAgB;AAC5C,WAAK,WAAW;AAChB,YAAM;AACN,WAAK,oBAAoB;AAAA,IAC3B;AACA,QAAI,KAAK,QAAQ,WAAW,KAAK,qBAAqB,KAAK,sBAAsB,MAAM;AACrF,WAAK,cAAc;AACnB,YAAM;AACN,WAAK,oBAAoB;AAAA,IAC3B;AACA,UAAM,aAAa,EAAC,MAAM,KAAK,MAAM,SAAS,KAAK,QAAO;AAC1D,QAAI,KAAK;AACP,WAAK,kBAAkB;AAAA,IACzB;AACA,QAAI,CAAC,KAAK,kBAAkB,KAAK,KAAK,CAAC,GAAG;AACxC,WAAK,KAAK,WAAW;AAAA,IACvB;AACA,WAAO;AAAA,EACT;AAAA,EACA,IAAI,MAAM,SAAS;AACjB,UAAM,aAAa;AAAA,MACjB,GAAG,KAAK,cAAc;AAAA,MACtB,GAAG,UAAU,QAAQ,UAAU,CAAC;AAAA,IAClC;AACA,UAAM,aAAa,cAAc,UAAU;AAC3C,QAAI,wBAAwB,CAAC;AAC7B,UAAM,oBAAoB,KAAK,QAAQ,IAAI;AAC3C,QAAI,oBAAoB,IAAI;AAC1B,8BAAwB,KAAK,MAAM,oBAAoB,CAAC;AACxD,aAAO,KAAK,MAAM,GAAG,iBAAiB;AAAA,IACxC;AACA,QAAI,SAAS,KAAK,MAAM,UAAU;AAClC,aAAS,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,KAAK,SAAS;AACjD,aAAO,SAAS,SAAS,CAAC,GAAG,GAAG,GAAG;AAAA,QACjC,CAAC,oBAAoB,IAAI,CAAC,GAAG,OAAO,IAAI;AAAA,MAC1C,CAAC;AAAA,IACH,GAAG,EAAC,GAAG,CAAC,EAAC,CAAC;AACV,UAAM,OAAO,OAAO;AACpB,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,IACR;AACA,UAAM,gBAAgB,WAAW,QAAQ,OAAO,2BAA2B,QAAQ,OAAO,2BAA2B,KAAK,cAAc,OAAO;AAC/I,QAAI,aAAa,uBAAO,OAAO,IAAI;AACnC,eAAW,aAAa,YAAY;AAClC,UAAI,CAAC,iBAAiB,UAAU,OAAO,YAAY,QAAQ;AACzD,mBAAW,QAAQ,UAAU,OAAO;AAClC,kBAAQ,IAAI,IAAI,UAAU,OAAO;AAAA,QACnC;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,UAAU,OAAO,IAAI,GAAG;AACxC,YAAI,WAAW,UAAU,IAAI,MAAM,QAAQ;AACzC,qBAAW,UAAU,IAAI,IAAI,uBAAO,OAAO,IAAI;AAC/C,qBAAW,UAAU,IAAI,EAAE,iBAAiB,IAAI;AAChD,qBAAW,UAAU,IAAI,EAAE,mBAAmB,IAAI,UAAU,OAAO,KAAK,CAAC;AAAA,QAC3E;AAAA,MACF;AAAA,IACF;AACA,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,UAAI,QAAQ,KAAK;AACf,cAAM,OAAO,IAAI,MAAM,GAAG;AAC1B,mBAAW,SAAS,MAAM,OAAO,GAAG,CAAC;AACrC,kBAAU,SAAS,UAAU;AAAA,MAC/B;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,oBAAoB;AAClB,UAAM,EAAC,MAAM,SAAS,gBAAgB,QAAO,IAAI;AACjD,QAAI,CAAC,WAAW,CAAC,QAAQ;AACvB;AACF,YAAQ,oBAAoB;AAC5B,YAAQ,iBAAiB;AACzB,YAAQ,kBAAkB;AAC1B,UAAM,aAAa,CAAC;AACpB,YAAQ,KAAK,QAAQ,CAAC,KAAK,UAAU;AACnC,UAAI,IAAI,UAAU;AAChB,mBAAW,KAAK,KAAK,MAAM,KAAK,CAAC;AAAA,MACnC,OAAO;AACL,mBAAW,KAAK,KAAK,KAAK,CAAC;AAAA,MAC7B;AAAA,IACF,CAAC;AACD,eAAW,KAAK,OAAO;AACvB,WAAO,QAAQ,cAAc,MAAM,MAAM,UAAU;AAAA,EACrD;AACF;AAEA,IAAM,MAAM,CAAC,OAAO,OAAO,IAAI,IAAI,IAAI;;;AClmBvC,SAAS,qBAAqB;AAS9B,SAAS,iBAAyB;AAChC,QAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,aAAW,OAAO,CAAC,mBAAmB,oBAAoB,GAAG;AAC3D,QAAI;AACF,YAAM,EAAE,QAAQ,IAAIA,SAAQ,GAAG;AAC/B,UAAI,QAAS,QAAO;AAAA,IACtB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AASO,SAAS,YAAY;AAC1B,QAAM,UAAU,eAAe;AAE/B,QAAM,MAAM,IAAI,OAAO;AAEvB,MACG,QAAQ,EAAE,EACV,MAAM,KAAK,EACX,OAAO,mBAAmB,uDAAe,EACzC,OAAO,OAAO,YAAY;AACzB,UAAM,EAAE,YAAAC,YAAW,IAAI,MAAM;AAC7B,UAAM,WAAW,MAAMA,YAAW,OAAO,CAAC;AAAA,EAC5C,CAAC;AAEH,MACG,QAAQ,SAAS,sBAAsB,EACvC,OAAO,gBAAgB,6DAAgB,EACvC,OAAO,OAAO,YAAY;AACzB,UAAM,EAAE,cAAAC,cAAa,IAAI,MAAM;AAC/B,UAAM,WAAW,MAAMA,cAAa,OAAO,CAAC;AAAA,EAC9C,CAAC;AAEH,MAAI,KAAK;AACT,MAAI,QAAQ,OAAO;AACnB,SAAO;AACT;AAMA,eAAe,WAAW,KAAyC;AACjE,MAAI;AACF,UAAM,IAAI;AAAA,EACZ,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,MAAM;AAAA,SAAY,OAAO,EAAE;AACnC,QAAI,QAAQ,IAAI,eAAe,eAAe,SAAS,IAAI,OAAO;AAChE,cAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AACA,YAAQ,WAAW;AAAA,EACrB;AACF;AAGA,UAAU,EAAE,MAAM;","names":["path","fs","path","fs","SPEC_RE","path","fs","path","fs","stat","path","fs","fs","path","path","ts","url","path","fs","fg","path","fs","ts","ts","ts","fs","path","ts","ts","ts","ts","path","unionInner","path","path","fg","path","fs","ts","path","path","fs","fs","path","path","basename","stat","lstat","path","rawEmitter","listener","basename","dirname","newStats","closer","resolve","realpath","stats","stat","readdir","EventEmitter","sysPath","relative","normalizePath","path","DOUBLE_SLASH_RE","testString","init_esm","stats","now","path","fs","fg","path","init_esm","path","params","path","fs","inFlight","path","body","url","text","headers","body","Readable","resolve","path","headers","etag","normalize","DEFAULTS","fs","path","resolve","Readable","path","path","fs","pathToFileURL","fs","path","Readable","TOOLS_FILE","AGENTS_FILE","resolve","body","generateSchemaFiles","path","ROUTES_FILE","path","fs","DEFAULT_DIST","require","devCommand","buildCommand"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/resolveAlias.ts","../../src/utils/prodPaths.ts","../../src/utils/readTsconfig.ts","../../src/cli/aliasPlugin.ts","../../src/cli/collectImports.ts","../../src/cli/compileConfig.ts","../../src/utils/atomicWrite.ts","../../src/cli/generateRoutes.ts","../../src/router/constants.ts","../../src/utils/normalizePath.ts","../../src/router/parseRouteFile.ts","../../src/utils/importWithCacheBust.ts","../../src/middleware/loadMiddlewares.ts","../../src/router/scanRoutes.ts","../../src/router/sortRoutes.ts","../../src/tools/scanTools.ts","../../src/ast/jsDocMetadata.ts","../../src/ast/extractToolMetadata.ts","../../src/ast/createProgram.ts","../../src/ast/resolveTypeNode.ts","../../src/ast/extractHandlerTypes.ts","../../src/runtime/inputType.ts","../../src/validator/schemaName.ts","../../src/injection/resolveInjection.ts","../../src/injection/analyzeInjection.ts","../../src/cli/collectRouteSchemaSources.ts","../../src/ast/generateZodSchema.ts","../../src/cli/generateSchemaFiles.ts","../../src/cli/generateToolArtifacts.ts","../../src/agents/scanAgents.ts","../../src/ast/extractAgentMetadata.ts","../../src/cli/generateAgentArtifacts.ts","../../src/config/loadConfig.ts","../../src/cli/loadEnv.ts","../../../../node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js","../../../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/handler.js","../../../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js","../../src/cli/compileSourceFiles.ts","../../src/cli/compileDevRoutes.ts","../../src/cli/rebuildScheduler.ts","../../src/cli/watcher.ts","../../src/router/detectRouteConflicts.ts","../../src/router/matchRoute.ts","../../src/loader/resolveExports.ts","../../src/loader/validateRouteModule.ts","../../src/cli/compileOnDemand.ts","../../src/loader/loadRouteModule.ts","../../src/runtime/sse.ts","../../src/errors/FaapiError.ts","../../src/errors/httpErrors.ts","../../src/response/responseFormatter.ts","../../src/runtime/createContext.ts","../../src/utils/queryToObject.ts","../../src/utils/parseJsonBody.ts","../../src/utils/parseMultipart.ts","../../src/runtime/resolveInput.ts","../../src/utils/isPlainObject.ts","../../src/response/pendingMeta.ts","../../src/response/toResponse.ts","../../src/injection/registries.ts","../../src/injection/agentRegistry.ts","../../src/injection/agentHandle.ts","../../src/injection/injectParams.ts","../../src/runtime/invokeHandler.ts","../../src/response/sendNodeResponse.ts","../../src/validator/validateInput.ts","../../src/utils/getClientIp.ts","../../src/middleware/cors.ts","../../src/middleware/helmet.ts","../../src/middleware/compression.ts","../../src/middleware/etag.ts","../../src/middleware/logger.ts","../../src/errors/formatErrorResponse.ts","../../src/server/serverUtils.ts","../../src/runtime/wsHandler.ts","../../src/server/handleWsUpgrade.ts","../../src/server/createServer.ts","../../src/server/startServer.ts","../../src/cli/loadPlugins.ts","../../src/cli/createAppCore.ts","../../src/cli/createDevApp.ts","../../src/cli/devCommand.ts","../../src/cli/compileBuildRoutes.ts","../../src/cli/buildCommand.ts","../../../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs","../../src/cli/index.ts"],"sourcesContent":["import type { TsconfigPathsConfig } from './readTsconfig';\n\n/**\n * 按 tsconfig paths 配置把 import specifier 解析为候选绝对路径\n *\n * 纯函数,不检查文件存在性。调用方需自行尝试 .ts/.js/index 等后缀。\n * 支持精确匹配(无 *)和单个 * 通配匹配。\n */\nexport function resolveAlias(specifier: string, config: TsconfigPathsConfig): string[] {\n const candidates: string[] = [];\n\n for (const [pattern, targets] of Object.entries(config.paths)) {\n const wildcardIndex = pattern.indexOf('*');\n\n if (wildcardIndex === -1) {\n // 精确匹配\n if (specifier === pattern) {\n candidates.push(...targets);\n }\n continue;\n }\n\n // 通配匹配:按 * 分割为 prefix / suffix\n const prefix = pattern.slice(0, wildcardIndex);\n const suffix = pattern.slice(wildcardIndex + 1);\n\n if (\n specifier.startsWith(prefix) &&\n specifier.endsWith(suffix) &&\n specifier.length >= prefix.length + suffix.length\n ) {\n const captured = specifier.slice(prefix.length, specifier.length - suffix.length);\n for (const target of targets) {\n candidates.push(target.replace('*', captured));\n }\n }\n }\n\n return candidates;\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\n\n/** 路由源码目录(写死为 src) */\nexport const APP_DIR = 'src';\n\n/** 路由文件扫描 patterns(devCommand / buildCommand / createAppCore 共用) */\nexport const ROUTE_PATTERNS = ['src/api/**/*.ts'];\n\n/**\n * 把源码 filePath(src/api/hello/handler.ts)转为产物路径(<dist>/api/hello/handler.js)\n *\n * 产物结构打平 src/ 前缀:去掉 `src/`,加 dist 前缀,.ts → .js。\n * 已带 `<dist>/` 前缀的输入保持不变(幂等)。\n */\nexport function toProdFilePath(filePath: string, dist: string): string {\n let rel = filePath.replace(/\\\\/g, '/');\n // 去掉 src/ 前缀(打平产物结构)\n if (rel.startsWith('src/')) {\n rel = rel.slice(4);\n }\n const jsPath = rel.replace(/\\.ts$/, '.js');\n return jsPath.startsWith(`${dist}/`) ? jsPath : `${dist}/${jsPath}`;\n}\n\n/**\n * 源文件后缀 → 产物后缀(.ts/.tsx/.jsx → .js,其余原样)\n */\nexport function toProdExtension(filePath: string): string {\n if (filePath.endsWith('.ts')) return filePath.slice(0, -3) + '.js';\n if (filePath.endsWith('.tsx')) return filePath.slice(0, -4) + '.js';\n if (filePath.endsWith('.jsx')) return filePath.slice(0, -4) + '.js';\n return filePath;\n}\n\n/**\n * 规范化路径为 realpath(处理 macOS /tmp → /private/tmp 等符号链接场景)\n *\n * 目录不存在时回退到原路径(不抛错),保证调用方逻辑稳定。\n */\nexport function toRealPath(p: string): string {\n try {\n return fs.realpathSync(p);\n } catch {\n return p;\n }\n}\n\n/**\n * 判断 filePath 是否位于 dir 目录下(不依赖路径前缀字符串比较,兼容符号链接规范化差异)\n *\n * 基于 `path.relative`:相对路径不以 `..` 开头且非绝对路径时视为位于 dir 下。\n * 调用前应先用 `toRealPath` 规范化两侧路径,确保前缀一致。\n */\nexport function isInsideDir(filePath: string, dir: string): boolean {\n const rel = path.relative(dir, filePath);\n return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}\n\n/**\n * 源文件相对路径剥离 `src/` 前缀(打平产物结构)\n *\n * schema/tool zod.js 输出路径计算的公共步骤:\n * `src/api/hello/handler.ts` → `api/hello/handler.ts`(取目录后 join dist + zod.js)\n */\nexport function stripSrcPrefix(sourceFile: string): string {\n const rel = sourceFile.replace(/\\\\/g, '/');\n return rel.startsWith('src/') ? rel.slice(4) : rel;\n}\n","import ts from 'typescript';\nimport path from 'node:path';\nimport fs from 'node:fs';\n\n/**\n * tsconfig paths 别名配置(已规范化为绝对路径)\n *\n * - baseUrl:绝对路径(无 baseUrl 时为 tsconfig 所在目录)\n * - paths:pattern -> 绝对路径数组(保留 * 通配符,供 resolveAlias 替换)\n */\nexport interface TsconfigPathsConfig {\n baseUrl: string;\n paths: Record<string, string[]>;\n}\n\n/**\n * 读取项目 tsconfig.json,提取 baseUrl + paths,规范化为绝对路径配置\n *\n * 用 TypeScript Compiler API 解析(处理注释、extends 合并)。\n * 仅读取 rootDir 下的 tsconfig.json,不向上递归查找。\n * 无 paths 时返回 null。\n *\n * 按 tsconfig mtime 缓存:每次编译(watcher 每轮重建、每次首请求按需编译)都会\n * 调本函数,Compiler API 的 readConfigFile + extends 链合并在长 extends 链项目上\n * 开销可观。tsconfig 变化时 mtime 变化 → 缓存自动失效,无需手动清理。\n */\ninterface TsconfigCacheEntry {\n mtimeMs: number;\n config: TsconfigPathsConfig | null;\n}\nconst tsconfigCache = new Map<string, TsconfigCacheEntry>();\n\nexport function readTsconfig(rootDir: string): TsconfigPathsConfig | null {\n const tsconfigPath = path.resolve(rootDir, 'tsconfig.json');\n if (!fs.existsSync(tsconfigPath)) return null;\n\n // mtime 缓存:tsconfig 未变化时直接复用解析结果\n let mtimeMs: number;\n try {\n mtimeMs = fs.statSync(tsconfigPath).mtimeMs;\n } catch {\n return null;\n }\n const cached = tsconfigCache.get(tsconfigPath);\n if (cached && cached.mtimeMs === mtimeMs) {\n return cached.config;\n }\n\n const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n if (configFile.error || !configFile.config) return null;\n\n // parseJsonConfigFileContent 处理 extends 合并、解析 baseUrl 为绝对路径\n const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, rootDir);\n\n // baseUrl:parseJsonConfigFileContent 已解析为绝对路径;未设置时回退到 rootDir\n const baseUrl = parsed.options.baseUrl ?? rootDir;\n\n const rawPaths = parsed.options.paths;\n const config: TsconfigPathsConfig | null = rawPaths\n ? (() => {\n // paths 目标:原始字符串相对 baseUrl,解析为绝对路径(保留 *)\n const paths: Record<string, string[]> = {};\n for (const [pattern, targets] of Object.entries(rawPaths)) {\n paths[pattern] = targets.map((t) => path.resolve(baseUrl, t));\n }\n return { baseUrl, paths };\n })()\n : null;\n\n tsconfigCache.set(tsconfigPath, { mtimeMs, config });\n return config;\n}\n","import type { Plugin } from 'esbuild';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { resolveAlias } from '../utils/resolveAlias';\nimport { APP_DIR, isInsideDir, toProdExtension, toRealPath } from '../utils/prodPaths';\nimport { readTsconfig, type TsconfigPathsConfig } from '../utils/readTsconfig';\n\n// 兼容 re-export:toProdExtension 此前由本模块导出\nexport { toProdExtension };\n\n/**\n * 把候选源文件路径转为产物 import 路径(相对 importer,POSIX 风格,带 .js 后缀)\n */\nfunction toProdImportPath(sourceFile: string, importer: string): string {\n const importerDir = path.dirname(importer);\n let rel = path.relative(importerDir, sourceFile);\n rel = rel.split(path.sep).join('/');\n if (!rel.startsWith('.')) rel = './' + rel;\n return toProdExtension(rel);\n}\n\n/**\n * 把 src 下的源文件路径转为剥离 src/ 前缀的产物路径(相对 dist 根,POSIX 风格,带 .js 后缀)\n *\n * 用于 src 外 importer(config 文件、本地插件等,位于 rootDir)引用 src 内模块的场景:\n * - 源文件 `<rootDir>/src/lib/errors.ts` → 产物 `dist/lib/errors.js` → 相对 dist 根 `./lib/errors.js`\n *\n * 与 `toProdImportPath` 的区别:后者相对 importer 目录(适用于 importer 也在 src 内的场景,\n * outbase 打平后相对结构不变);本函数相对 src 根(适用于 importer 在 src 外的场景,\n * 需要剥离 src/ 前缀以匹配 compileDevRoutes 的打平产物结构)。\n *\n * 内部用 `toRealPath` 规范化 appDirAbs,兼容 macOS 符号链接(esbuild onLoad 传入的\n * args.path 已是 realpath,未规范化的 appDirAbs 会导致前缀比较失败)。\n */\nfunction toStrippedProdImportPath(sourceFile: string, rootDir: string): string {\n const appDirAbs = toRealPath(path.resolve(rootDir, APP_DIR));\n const sourceReal = toRealPath(sourceFile);\n let rel = path.relative(appDirAbs, sourceReal);\n rel = rel.split(path.sep).join('/');\n if (!rel.startsWith('.')) rel = './' + rel;\n return toProdExtension(rel);\n}\n\n/**\n * 把「相对 dist 根」的产物路径改写为「相对 importer 产物位置」的 import 路径\n *\n * src 外 importer 的产物位置 = importer 源文件相对 rootDir 的路径(.js 后缀),\n * 因为 src 外编译(compileConfig 步骤 1a / compileProjectModules)以 rootDir 为\n * outbase,产物在 `<dist>/` 下保留相对结构。\n *\n * - config `faapi.config.ts` → 产物 `faapi.config.js`(dist 根)→ `./lib/errors.js` 保持不变\n * - 插件 `plugins/db-skills.ts` → 产物 `plugins/db-skills.js`(dist 子目录)\n * → 引用 `./lib/errors.js` 需改写为 `../lib/errors.js`(相对 dist 根的路径在\n * 子目录模块里会解析到 `plugins/lib/errors.js`,错位)\n */\nfunction toProdImportFromImporter(importer: string, rootDir: string, relFromDist: string): string {\n // 两侧 realpath 归一化,兼容 macOS 符号链接(esbuild onLoad 传入的 importer 已是\n // realpath,rootDir 可能仍是 /var 形式——不归一化则 relative 产生绕行路径)\n const importerRel = path\n .relative(toRealPath(path.resolve(rootDir)), toRealPath(importer))\n .split(path.sep)\n .join('/');\n const importerProdDir = path.posix.dirname(toProdExtension(importerRel));\n if (importerProdDir === '.') return relFromDist;\n const target = relFromDist.replace(/^\\.\\//, '');\n let rel = path.posix.relative(importerProdDir, target);\n if (!rel.startsWith('.')) rel = './' + rel;\n return rel;\n}\n\n/**\n * aliasPlugin 扩展选项\n *\n * - `rootDir`:启用 src/ 前缀剥离。当 importer 不在 src 下(如 config 文件)\n * 且引用的源文件在 src 下时,重写为剥离前缀的产物路径(相对 dist 根),以匹配\n * `compileDevRoutes`/`compileBuildRoutes` 的打平产物结构。\n */\nexport interface AliasPluginOptions {\n /** 项目根目录(启用剥离时必填) */\n rootDir?: string;\n}\n\n/**\n * 产物后缀集合(已带这些后缀的 specifier 视为产物路径,不重写)\n */\nconst PROD_EXTS = ['.js', '.mjs', '.cjs'];\n\n/**\n * 源文件后缀集合(重写时尝试这些后缀定位实际文件)\n */\nconst SOURCE_EXTS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];\n\n/**\n * index 文件后缀集合(specifier 指向目录时尝试)\n */\nconst INDEX_EXTS = [\n '/index.ts',\n '/index.tsx',\n '/index.js',\n '/index.jsx',\n '/index.mjs',\n '/index.cjs',\n];\n\n/**\n * 解析相对 specifier 到实际源文件绝对路径\n *\n * - 已带产物后缀(.js/.mjs/.cjs)且文件存在 → 返回该路径(不重写)\n * - 已带源后缀(.ts/.tsx/.jsx)且文件存在 → 返回该路径(重写为 .js)\n * - 无后缀 → 尝试各源后缀 + index 文件\n *\n * @param importer 导入文件绝对路径\n * @param specifier 相对 specifier(./xxx 或 ../xxx)\n * @returns 解析到的源文件绝对路径,或 null(未解析到)\n */\nexport function resolveRelativeSpecifier(importer: string, specifier: string): string | null {\n const importerDir = path.dirname(importer);\n const base = path.resolve(importerDir, specifier);\n\n // 已带产物后缀:文件存在则返回(视为产物路径,不重写)\n if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {\n return fs.existsSync(base) ? base : null;\n }\n\n // 已带源后缀:文件存在则返回(将重写为产物后缀)\n if (/\\.(ts|tsx|jsx)$/.test(specifier)) {\n return fs.existsSync(base) ? base : null;\n }\n\n // 无后缀:尝试各源后缀\n for (const ext of SOURCE_EXTS) {\n const file = base + ext;\n if (fs.existsSync(file)) return file;\n }\n\n // 无后缀:尝试 index 文件\n for (const indexExt of INDEX_EXTS) {\n const file = base + indexExt;\n if (fs.existsSync(file)) return file;\n }\n\n return null;\n}\n\n/**\n * esbuild specifier 重写插件(dev/build/config 编译共用)\n *\n * 在 `onLoad` 阶段把源码 import/export 中的 specifier 重写为产物相对路径(带 `.js` 后缀):\n *\n * 1. **相对 specifier**(`./xxx`、`../xxx`):解析到实际源文件,重写为产物相对路径。\n * - 无后缀 → 解析 + 加 `.js`\n * - `.ts`/`.tsx`/`.jsx` → 改为 `.js`\n * - `.js`/`.mjs`/`.cjs` → 不处理(视为产物路径)\n * - 解析失败(文件不存在)→ 原样保留\n *\n * 2. **别名 specifier**(tsconfig.paths,如 `@/xxx`):调 `resolveAlias` 解析候选路径,\n * 命中则重写为产物相对路径。\n *\n * `bundle: false` 模式下 esbuild 不递归解析依赖(`onResolve` 不触发),specifier 会原样\n * 保留到产物 `.js`,运行时 Node.js ESM loader 无法解析(无后缀推断)。本插件通过\n * `onLoad` 介入,确保产物中所有相对 import 都带 `.js` 后缀。\n *\n * **src/ 前缀剥离**(可选,通过 `options.rootDir` 启用):\n * 当 importer 不在 src 下(如 `faapi.config.ts` 位于 rootDir)且引用的源文件在 src 下时,\n * 重写为剥离 src/ 前缀的产物路径(相对 dist 根),以匹配 `compileDevRoutes`/`compileBuildRoutes`\n * 的打平产物结构。典型场景:config 引用项目模块(如 `./src/lib/errors` → `./lib/errors.js`),\n * 使 config 与 routes 共享同一份模块产物,`instanceof` 跨边界生效。\n *\n * 覆盖的 import 形式:\n * - `import { x } from './base'` / `import { x } from '@/base'`\n * - `export { x } from './base'`(含 `from`,被同一正则匹配)\n * - `import('./base')`(动态)\n *\n * 绝对路径(`/`)、`file:` URL、`node:` 协议不处理,交 esbuild 默认。\n */\nexport function createAliasPlugin(\n config: TsconfigPathsConfig,\n options?: AliasPluginOptions,\n): Plugin {\n // 匹配 from '...' / from \"...\" 和 import('...') / import(\"...\")\n const SPEC_RE = /(\\bfrom\\s*|import\\s*\\(\\s*)(['\"])([^'\"]+)\\2/g;\n // 预计算 src 绝对路径(realpath 规范化,用于判断 importer 和 resolved 是否在 src 内)\n // 规范化是为了兼容 macOS 符号链接:esbuild onLoad 传入的 args.path 是 realpath,\n // 未规范化的 appDirAbs 会导致 isInsideDir 比较失败(/var/folders vs /private/var/folders)\n const appDirAbs = options?.rootDir ? toRealPath(path.resolve(options.rootDir, APP_DIR)) : null;\n return {\n name: 'faapi-alias',\n setup(build) {\n build.onLoad({ filter: /\\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {\n let source: string;\n try {\n source = fs.readFileSync(args.path, 'utf8');\n } catch {\n return undefined;\n }\n const importer = args.path;\n // importer 是否在 src 外(启用剥离时才计算,用 isInsideDir 兼容符号链接)\n const importerOutsideAppDir = appDirAbs ? !isInsideDir(importer, appDirAbs) : false;\n let modified = false;\n const newSource = source.replace(SPEC_RE, (full, prefix, quote, specifier) => {\n // 绝对路径 / file: URL / node: 协议不处理\n if (\n specifier.startsWith('/') ||\n specifier.startsWith('file:') ||\n specifier.startsWith('node:')\n ) {\n return full;\n }\n\n // 相对 specifier(./ 或 ../):解析到实际文件并重写\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n const resolved = resolveRelativeSpecifier(importer, specifier);\n if (resolved) {\n // 已带产物后缀的 specifier 不重写(resolveRelativeSpecifier 返回原路径)\n if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {\n return full;\n }\n // src 前缀剥离:importer 在 src 外、resolved 在 src 内\n // → 重写为剥离前缀的产物路径(相对 importer 产物位置)\n if (appDirAbs && importerOutsideAppDir && isInsideDir(resolved, appDirAbs)) {\n modified = true;\n return `${prefix}${quote}${toProdImportFromImporter(\n importer,\n options!.rootDir!,\n toStrippedProdImportPath(resolved, options!.rootDir!),\n )}${quote}`;\n }\n modified = true;\n return `${prefix}${quote}${toProdImportPath(resolved, importer)}${quote}`;\n }\n return full;\n }\n\n // 别名 specifier:调 resolveAlias 解析\n const candidates = resolveAlias(specifier, config);\n for (const candidate of candidates) {\n for (const ext of SOURCE_EXTS) {\n const file = candidate + ext;\n if (fs.existsSync(file)) {\n modified = true;\n // 别名解析到的文件如果在 src 内且 importer 在 src 外,同样剥离前缀\n if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {\n return `${prefix}${quote}${toProdImportFromImporter(\n importer,\n options!.rootDir!,\n toStrippedProdImportPath(file, options!.rootDir!),\n )}${quote}`;\n }\n return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;\n }\n }\n for (const indexExt of INDEX_EXTS) {\n const file = candidate + indexExt;\n if (fs.existsSync(file)) {\n modified = true;\n if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {\n return `${prefix}${quote}${toProdImportFromImporter(\n importer,\n options!.rootDir!,\n toStrippedProdImportPath(file, options!.rootDir!),\n )}${quote}`;\n }\n return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;\n }\n }\n }\n return full;\n });\n if (!modified) return undefined;\n return { contents: newSource, loader: 'default' };\n });\n },\n };\n}\n\n/**\n * 读取 tsconfig paths 并构造 specifier 重写插件\n *\n * 始终返回含本插件的数组:相对路径重写不依赖 tsconfig(`bundle: false` 下 Node ESM 必需),\n * 别名重写依赖 tsconfig.paths(无 paths 时别名不重写)。\n *\n * @param rootDir 项目根目录。传入时启用 src/ 前缀剥离:importer 在 src 外\n * 且引用 src 内文件时,重写为剥离前缀的产物路径。主要用于 compileConfig\n * 编译 config 文件(位于 rootDir)引用项目模块(位于 src)的场景。\n */\nexport function buildAliasPlugins(rootDir: string): Plugin[] {\n const tsconfig = readTsconfig(rootDir);\n return [createAliasPlugin(tsconfig ?? { baseUrl: '.', paths: {} }, { rootDir })];\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { isInsideDir, toRealPath } from '../utils/prodPaths';\nimport { resolveRelativeSpecifier } from './aliasPlugin';\nimport { resolveAlias } from '../utils/resolveAlias';\nimport { readTsconfig } from '../utils/readTsconfig';\n\n/**\n * 匹配 from '...' / from \"...\" / import('...') / import(\"...\") 中的 specifier\n */\nconst SPEC_RE = /(\\bfrom\\s*|import\\s*\\(\\s*)(['\"])([^'\"]+)\\2/g;\n\n/**\n * 从源文件内容中提取所有 import specifier\n *\n * 覆盖相对 specifier(./xxx、../xxx)与 tsconfig paths 别名 specifier(如 @/xxx)。\n * 动态 import(import('...'))同样覆盖。\n */\nfunction extractImportSpecifiers(source: string): string[] {\n const specifiers: string[] = [];\n let match: RegExpExecArray | null;\n SPEC_RE.lastIndex = 0;\n while ((match = SPEC_RE.exec(source)) !== null) {\n specifiers.push(match[3]!);\n }\n return specifiers;\n}\n\n/**\n * 从目录解析 specifier 为源文件绝对路径(探测 .ts/.tsx 等源后缀与 index 文件)\n *\n * 复用 resolveRelativeSpecifier 的探测逻辑:它按 importer 文件所在目录解析,\n * 传入目录内的合成文件名即可从指定目录解析。\n */\nfunction resolveSpecifierFromDir(dir: string, specifier: string): string | null {\n return resolveRelativeSpecifier(path.join(dir, '__faapi_probe__.ts'), specifier);\n}\n\n/**\n * 递归收集入口文件的所有项目内 import 依赖(含传递依赖),按是否位于 appDir 内分组\n *\n * 供两类调用方使用:\n * - `compileConfig`:编译 config 源 + 其引用的项目模块,使 config 产物的 import\n * 在运行时能解析到实际文件(与 routes 编译的同一产物共享,instanceof 跨边界生效)\n * - `compileOnDemand.ensureCompiled`:dev 按需编译 handler 时补齐依赖闭包,\n * 否则 handler 引用的共享模块(如 `../../lib/db`)产物不存在,首次请求即\n * ERR_MODULE_NOT_FOUND\n *\n * 解析规则:\n * - 相对 specifier:`resolveRelativeSpecifier` 探测源后缀与 index 文件\n * - 别名 specifier(tsconfig paths,如 `@/lib/db`):`resolveAlias` 解析候选目标后\n * 同样探测源后缀(无 tsconfig/paths 时跳过)\n * - 已带产物后缀(.js/.mjs/.cjs)的 specifier 不递归(视为已编译产物,源码不在此处)\n * - 项目外依赖(node_modules、rootDir 外文件)不收集——按 external 语义运行时解析\n *\n * @param entryFiles 起始文件(绝对路径)\n * @param rootDir 项目根目录\n * @returns 收集到的文件,分为 src 内(打平前缀编译)和 src 外(保留结构编译)两组\n * (绝对路径,去重,不含入口文件本身)\n */\nexport async function collectRelativeImports(\n entryFiles: string[],\n rootDir: string,\n): Promise<{ insideFiles: string[]; outsideFiles: string[] }> {\n // 用 realpath 规范化 src 绝对路径,兼容 macOS 符号链接(esbuild 传入的路径已是 realpath)\n const appDirAbs = toRealPath(path.resolve(rootDir, 'src'));\n const tsconfig = readTsconfig(rootDir);\n const visited = new Set<string>();\n const insideFiles = new Set<string>();\n const outsideFiles = new Set<string>();\n\n async function collect(filePath: string): Promise<void> {\n if (visited.has(filePath)) return;\n visited.add(filePath);\n\n let source: string;\n try {\n source = await fs.promises.readFile(filePath, 'utf8');\n } catch {\n return;\n }\n\n for (const specifier of extractImportSpecifiers(source)) {\n // 已带产物后缀:运行时 import 的就是产物(或第三方 .js),源码不递归\n if (/\\.(js|mjs|cjs)$/.test(specifier)) continue;\n\n let resolved: string | null;\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n resolved = resolveRelativeSpecifier(filePath, specifier);\n } else if (tsconfig) {\n // 别名 specifier:解析候选目标(相对 rootDir)后探测源文件\n resolved = null;\n for (const candidate of resolveAlias(specifier, tsconfig)) {\n const probed = resolveSpecifierFromDir(rootDir, candidate);\n if (probed) {\n resolved = probed;\n break;\n }\n }\n } else {\n resolved = null;\n }\n\n if (!resolved) continue;\n // 项目外文件(node_modules / rootDir 外)不收集。\n // 归类判断用 realpath 归一化两侧(tmpdir 符号链接 /var → /private/var 会误判),\n // 但收集结果保留原始解析路径——esbuild 的 outbase 基于调用方传入的 rootDir 形式,\n // entry 路径必须与 outbase 字符串前缀一致,否则产物落入 _.._ 垃圾目录\n if (!isInsideDir(toRealPath(resolved), toRealPath(path.resolve(rootDir)))) continue;\n\n if (isInsideDir(toRealPath(resolved), appDirAbs)) {\n insideFiles.add(resolved);\n } else {\n outsideFiles.add(resolved);\n }\n await collect(resolved);\n }\n }\n\n for (const entry of entryFiles) {\n await collect(entry);\n }\n\n return {\n insideFiles: Array.from(insideFiles),\n outsideFiles: Array.from(outsideFiles),\n };\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport type { Plugin } from 'esbuild';\nimport { buildAliasPlugins } from './aliasPlugin';\nimport { collectRelativeImports } from './collectImports';\nimport { toProdExtension, toRealPath } from '../utils/prodPaths';\n\n/**\n * 基础配置文件查找顺序(与 loadConfig 保持一致)\n */\nconst BASE_CONFIG_FILES = ['faapi.config.ts', 'faapi.config.js'];\n\n/**\n * 查找基础配置文件(返回源文件名,含后缀)\n *\n * 返回相对 rootDir 的路径(如 'faapi.config.ts'),供 esbuild 作为 entryPoint。\n */\nfunction findBaseConfig(rootDir: string): string | null {\n for (const f of BASE_CONFIG_FILES) {\n if (fs.existsSync(path.join(rootDir, f))) {\n return f;\n }\n }\n return null;\n}\n\n/**\n * 配置文件名 → 产物 import 名(如 'faapi.config.ts' → 'faapi.config.js')\n */\nfunction toProdImport(filename: string): string {\n return toProdExtension(filename);\n}\n\nexport interface CompileConfigOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(如 dist) */\n dist: string;\n}\n\nexport interface CompileConfigResult {\n /** 是否生成了配置产物(无基础配置文件时不生成) */\n generated: boolean;\n /** 输出文件绝对路径(generated=false 时为空字符串) */\n outputFile: string;\n /** 是否命中 mtime 短路缓存跳过了编译(watcher 重建场景,输入无变化时省 3 次 esbuild build) */\n skipped: boolean;\n}\n\n/**\n * compileConfig 的 mtime 短路缓存\n *\n * watcher 每次重建都会调 compileConfig;若无短路,即使配置源毫无变化,\n * 也要付出 3 次 esbuild build + collectRelativeImports 递归读整个依赖图的开销。\n *\n * key 为 `rootDir::dist`,value 记录上次编译的全部输入文件 mtime。\n * 再次调用时输入文件集合 mtime 全部一致且产物仍存在 → 跳过编译;\n * 任一变化/缺失 → 全量重编译并更新缓存。编译抛错时不写缓存(下次可重试)。\n */\ninterface CompileConfigCacheEntry {\n /** 上次编译的输入文件(绝对路径 → mtimeMs) */\n inputs: Map<string, number>;\n /** 主产物绝对路径(faapi-config.js) */\n outputFile: string;\n}\n\nconst compileConfigCache = new Map<string, CompileConfigCacheEntry>();\n\n/** 检查缓存是否仍然新鲜:产物存在 + 全部输入文件存在且 mtime 一致 */\nasync function isCacheFresh(entry: CompileConfigCacheEntry): Promise<boolean> {\n try {\n await fs.promises.stat(entry.outputFile);\n } catch {\n return false;\n }\n for (const [filePath, mtimeMs] of entry.inputs) {\n try {\n const stat = await fs.promises.stat(filePath);\n if (stat.mtimeMs !== mtimeMs) return false;\n } catch {\n return false;\n }\n }\n return true;\n}\n\n/**\n * 匹配 from '...' / from \"...\" / import('...') / import(\"...\") 中的 specifier\n * 与递归收集逻辑见 collectImports.ts(compileConfig 与 compileOnDemand 共用)\n */\n\n/**\n * 创建 external 相对路径插件(步骤 2 用)\n *\n * 在 `bundle: true` 模式下,把所有相对路径 import(./xxx 或 ../xxx)标记为 external,\n * 阻止 esbuild 把已编译的 config 产物 inline 进 faapi-config.js。\n *\n * 这使 faapi-config.js 保留 `import base from './faapi.config.js'` 语句,运行时\n * Node.js ESM loader 加载 faapi.config.js,后者再 import 项目模块产物——\n * 与 routes 编译的同一文件共享,instanceof 生效。\n */\nfunction createExternalRelativePlugin(): Plugin {\n return {\n name: 'faapi-external-relative',\n setup(build) {\n // 相对路径(./ 或 ../开头)标记为 external\n build.onResolve({ filter: /^\\.{1,2}\\// }, (args) => ({\n path: args.path,\n external: true,\n }));\n },\n };\n}\n\n/**\n * 编译 src 外入口文件及其项目内依赖闭包(compileConfig / 本地 TS 插件编译共用)\n *\n * - 入口 + src 外依赖:outbase=rootDir,产物在 `<dist>/` 下保留相对结构\n * (如 `plugins/db-skills.ts` → `dist/plugins/db-skills.js`)\n * - src 内依赖:outbase=rootDir/src(打平前缀,与 compileDevRoutes 一致),\n * 使入口产物 import 的 src 内模块与 routes/config 共享同一份产物(instanceof 生效)\n * - aliasPlugin 重写 specifier:相对路径加 .js 后缀;src 外 importer 引用 src 内\n * 模块时剥离前缀(import 路径相对 importer 产物位置计算,见 aliasPlugin 的\n * toProdImportFromImporter)\n *\n * @param entryPoints src 外入口文件(绝对路径);src 内入口请走 compileDevRoutes /\n * compileBuildRoutes / ensureCompiled(打平产物 + 同一运行时对象)\n * @returns 收集到的 src 内/外依赖文件(供调用方记录 mtime 缓存)\n */\nexport async function compileProjectModules(\n entryPoints: string[],\n rootDir: string,\n dist: string,\n): Promise<{ insideFiles: string[]; outsideFiles: string[] }> {\n const { insideFiles, outsideFiles } = await collectRelativeImports(entryPoints, rootDir);\n\n const esbuild = await import('esbuild');\n const aliasPlugins = buildAliasPlugins(rootDir);\n const absDist = path.resolve(rootDir, dist);\n\n // src 外文件(outbase=rootDir):入口 + src 外依赖,产物保留相对结构\n await esbuild.build({\n entryPoints: [...entryPoints, ...outsideFiles],\n outdir: absDist,\n outbase: rootDir,\n bundle: false,\n platform: 'node',\n format: 'esm',\n sourcemap: true,\n packages: 'external',\n plugins: aliasPlugins,\n logLevel: 'silent',\n });\n\n // src 内依赖(outbase=rootDir/src,打平前缀,与 compileDevRoutes 一致)\n if (insideFiles.length > 0) {\n const appOutbase = path.resolve(rootDir, 'src');\n await esbuild.build({\n entryPoints: insideFiles,\n outdir: absDist,\n outbase: appOutbase,\n bundle: false,\n platform: 'node',\n format: 'esm',\n sourcemap: true,\n packages: 'external',\n plugins: aliasPlugins,\n logLevel: 'silent',\n });\n }\n\n return { insideFiles, outsideFiles };\n}\n\n/**\n * build 时编译配置文件,生成 `dist/faapi-config.js`\n *\n * 采用两步编译,使 config 引用的项目模块与 routes 共享同一份运行时对象(instanceof 跨边界生效):\n *\n * **步骤 1:逐文件编译 config 源文件(`bundle: false`)**\n * - 编译 `faapi.config.ts` → `dist/faapi.config.js`\n * - 递归收集 config 引用的项目模块,按 src 内/外分别编译:\n * - src 内:outbase=rootDir/src(打平前缀,与 compileDevRoutes 一致)→ `dist/lib/errors.js`\n * - src 外:outbase=rootDir → `dist/base.js`\n * - aliasPlugin 重写 specifier:相对路径加 .js 后缀;config 引用 src 内模块时剥离前缀\n *\n * **步骤 2:编译合并入口(`bundle: true` + external 相对路径)**\n * - 生成虚拟入口源码(import 已编译的 config 产物 + export base)\n * - 相对路径 import 标记为 external(不 inline config 产物)\n * - 第三方依赖(`packages: 'external'`)也保持 external\n * - 产物 `dist/faapi-config.js` 保留 `import base from './faapi.config.js'`\n *\n * 产物由 `loadConfig` 在运行时统一 import。环境变量通过 `.env` 文件加载(见 `loadEnv`),\n * 配置文件中通过 `process.env.XXX` 读取,运行时取值。不传 `define`,保留 `process.env` 表达式。\n */\nexport async function compileConfig(options: CompileConfigOptions): Promise<CompileConfigResult> {\n const { rootDir, dist } = options;\n\n const baseConfigName = findBaseConfig(rootDir);\n if (!baseConfigName) {\n // 无基础配置文件:不生成产物,loadConfig 会返回 null\n return { generated: false, outputFile: '', skipped: false };\n }\n\n const absDist = path.resolve(rootDir, dist);\n await fs.promises.mkdir(absDist, { recursive: true });\n\n const outputFile = path.resolve(absDist, 'faapi-config.js');\n const cacheKey = `${toRealPath(rootDir)}::${dist}`;\n\n // mtime 短路:上次编译的全部输入文件无变化且产物仍存在 → 跳过编译\n const cached = compileConfigCache.get(cacheKey);\n if (cached && (await isCacheFresh(cached))) {\n return { generated: true, outputFile: cached.outputFile, skipped: true };\n }\n compileConfigCache.delete(cacheKey);\n\n // 收集 config 入口文件(绝对路径)\n const configEntryPoints: string[] = [path.resolve(rootDir, baseConfigName)];\n\n // 步骤 1:逐文件编译 config 源 + 项目模块(共享实现见 compileProjectModules)\n const { insideFiles: appDirFiles, outsideFiles: nonAppDirFiles } = await compileProjectModules(\n configEntryPoints,\n rootDir,\n dist,\n );\n\n // 步骤 2:编译入口(bundle:true + external 相对路径)\n // 入口源码 import 已编译的 config 产物(带 .js 后缀)+ export base\n const baseImport = `import base from './${toProdImport(baseConfigName)}';`;\n const exportDefault = 'export default base;';\n\n const entryCode = [baseImport, exportDefault].join('\\n');\n\n const esbuild = await import('esbuild');\n await esbuild.build({\n stdin: { contents: entryCode, resolveDir: absDist, loader: 'ts' },\n outfile: outputFile,\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node20',\n sourcemap: true,\n packages: 'external',\n plugins: [createExternalRelativePlugin()],\n logLevel: 'silent',\n });\n\n // 编译成功:记录全部输入文件的 mtime(config 源 + src 内/外依赖模块 + tsconfig——\n // alias 重写依赖 tsconfig paths,tsconfig 变化必须触发重编译),\n // 供下次调用的 mtime 短路判断。编译抛错时不走此处(下次可重试)。\n const inputs = new Map<string, number>();\n const tsconfigPath = path.resolve(rootDir, 'tsconfig.json');\n const inputCandidates = [\n ...configEntryPoints,\n ...appDirFiles,\n ...nonAppDirFiles,\n ...(fs.existsSync(tsconfigPath) ? [tsconfigPath] : []),\n ];\n for (const filePath of inputCandidates) {\n try {\n const stat = await fs.promises.stat(filePath);\n inputs.set(filePath, stat.mtimeMs);\n } catch {\n // 收集阶段存在的文件理论上不会消失;消失则不缓存该文件,下次触发重编译\n }\n }\n compileConfigCache.set(cacheKey, { inputs, outputFile });\n\n return { generated: true, outputFile, skipped: false };\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\n\n/**\n * 原子写文件:先写临时文件再 rename 替换\n *\n * rename 在同一文件系统上是原子的(POSIX),并发读者要么看到旧文件要么看到\n * 新文件,不会读到截断的半成品。用于运行时会 import 的产物(zod.js、路由清单、\n * tool/agent 清单)——dev watch 重建与在途请求并发时,非原子写会让请求 import\n * 到半成品文件,报 SyntaxError/ENOENT 且错误信息不指向根因。\n *\n * 与 compileSourceFiles 的 esbuild 原子写(write:false + tmp + rename)同源语义。\n *\n * @param outputPath 目标绝对路径\n * @param content 文件内容\n */\nexport async function atomicWriteFile(outputPath: string, content: string): Promise<void> {\n await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });\n const tmp = `${outputPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;\n await fs.promises.writeFile(tmp, content, 'utf-8');\n await fs.promises.rename(tmp, outputPath);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type {\n RouteManifest,\n WsRouteManifest,\n RouteRecord,\n WsRouteRecord,\n} from '../router/routeTypes';\nimport type { HttpMethod } from '../router/constants';\nimport { toProdFilePath } from '../utils/prodPaths';\nimport { atomicWriteFile } from '../utils/atomicWrite';\n\n/**\n * 序列化路由记录(可写入 JS 模块,无函数引用)\n *\n * build 时生成,start 时读取。filePath/middlewarePaths 已转为 prd 形式\n * (打平 src/ 前缀 + dist 前缀 + .js)。\n */\nexport interface SerializedRouteRecord {\n method: HttpMethod;\n urlPath: string;\n filePath: string;\n paramNames: string[];\n isDynamic: boolean;\n isCatchAll?: boolean;\n /** 从根到路由目录的中间件文件绝对路径列表(已排序,根在前) */\n middlewarePaths: string[];\n}\n\nexport interface SerializedWsRouteRecord {\n urlPath: string;\n filePath: string;\n paramNames: string[];\n isDynamic: boolean;\n isCatchAll?: boolean;\n middlewarePaths: string[];\n}\n\nexport interface SerializedRouteManifest {\n routes: SerializedRouteRecord[];\n wsRoutes: SerializedWsRouteRecord[];\n}\n\n/**\n * 序列化路由清单为可写入 JS 模块的结构\n *\n * - filePath 转为产物路径(打平 src/ 前缀 + dist 前缀 + .js)\n * - middlewares/injectors 不序列化(函数无法序列化),改为 middlewarePaths(中间件文件绝对路径列表)\n * - middlewarePaths 已排序(根在前,路由目录在后),start 时按序加载即可还原洋葱模型\n *\n * @param rootDir 项目根目录\n * @param dist 产物目录(dist 或 .faapi),用于转换 filePath 和查找中间件\n */\nexport function serializeRoutes(\n routes: RouteManifest,\n wsRoutes: WsRouteManifest,\n rootDir: string,\n dist: string = 'dist',\n): SerializedRouteManifest {\n const serialize = <T extends RouteRecord | WsRouteRecord>(\n route: T,\n ): T extends RouteRecord ? SerializedRouteRecord : SerializedWsRouteRecord => {\n const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir, dist);\n const serialized = {\n urlPath: route.urlPath,\n filePath: toProdFilePath(route.filePath, dist),\n paramNames: route.paramNames,\n isDynamic: route.isDynamic,\n isCatchAll: route.isCatchAll,\n middlewarePaths,\n };\n if ('method' in route) {\n (serialized as SerializedRouteRecord).method = (route as RouteRecord).method;\n }\n return serialized as T extends RouteRecord ? SerializedRouteRecord : SerializedWsRouteRecord;\n };\n\n return {\n routes: routes.map(serialize) as SerializedRouteRecord[],\n wsRoutes: wsRoutes.map(serialize) as SerializedWsRouteRecord[],\n };\n}\n\n/**\n * 从路由文件路径,向上查找所有中间件文件绝对路径(根在前,路由目录在后)\n *\n * 与 scanRoutes.findMergedMiddlewares 的收集逻辑一致,但只返回路径不加载。\n * 检查源码形式(.ts)中间件文件是否存在,返回产物形式(.js)绝对路径。\n *\n * @param routeFilePath 源码相对路径(如 src/api/hello/handler.ts)\n * @param rootDir 项目根目录\n * @param dist 产物目录(dist 或 .faapi)\n */\nfunction extractMiddlewarePaths(routeFilePath: string, rootDir: string, dist: string): string[] {\n const routeDir = path.dirname(routeFilePath);\n const resolvedRoot = path.resolve(rootDir);\n\n const paths: string[] = [];\n let currentDir = path.resolve(rootDir, routeDir);\n while (true) {\n // 检查源码 middlewares.ts 是否存在,回退 middlewares.js(prd 残留场景)\n const mwTsPath = path.join(currentDir, 'middlewares.ts');\n const mwJsPath = path.join(currentDir, 'middlewares.js');\n const absTsPath = path.resolve(rootDir, mwTsPath);\n const absJsPath = path.resolve(rootDir, mwJsPath);\n const absMwPath = fs.existsSync(absTsPath)\n ? absTsPath\n : fs.existsSync(absJsPath)\n ? absJsPath\n : null;\n if (absMwPath) {\n // 转为产物形式绝对路径(打平 src/ 前缀)\n const relMwPath = path.relative(rootDir, absMwPath);\n const prodAbsPath = path.resolve(rootDir, toProdFilePath(relMwPath, dist));\n paths.push(prodAbsPath);\n }\n if (currentDir === resolvedRoot) break;\n // 不超出 src 目录(避免向上查到非源码目录)\n // 到达 src 的父目录(rootDir)时停止\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n\n // paths 是从路由目录向上收集的,需反转为根在前\n paths.reverse();\n return paths;\n}\n\n/**\n * 把序列化的路由清单写入 JS 模块\n *\n * 生成 ESM 模块,start 时通过 import() 加载。\n */\nexport async function writeRoutesModule(\n manifest: SerializedRouteManifest,\n outputPath: string,\n): Promise<void> {\n // 用 JSON.stringify 嵌入,保证字符串转义安全\n const content = `// 自动生成,请勿手动编辑(faapi build 产物)\nexport const routes = ${JSON.stringify(manifest.routes, null, 2)};\nexport const wsRoutes = ${JSON.stringify(manifest.wsRoutes, null, 2)};\n`;\n\n await atomicWriteFile(outputPath, content);\n}\n\n/**\n * 从序列化清单水合路由(还原 RouteRecord,不预加载中间件)\n *\n * 中间件改为按需加载:只把 `middlewarePaths` 存到 `route.middlewarePaths`,\n * 请求阶段由 `createServer` / `handleWsUpgrade` 调 `loadMergedMiddlewares` 按需加载。\n * 这样 dev/prod 启动时都不全量加载中间件模块(Vite 风格),首次请求时才加载。\n */\nexport async function hydrateRoutes(\n manifest: SerializedRouteManifest,\n): Promise<{ routes: RouteManifest; wsRoutes: WsRouteManifest }> {\n const hydrateRoute = (serialized: SerializedRouteRecord): RouteRecord => ({\n method: serialized.method,\n urlPath: serialized.urlPath,\n filePath: serialized.filePath,\n paramNames: serialized.paramNames,\n isDynamic: serialized.isDynamic,\n isCatchAll: serialized.isCatchAll,\n middlewarePaths: serialized.middlewarePaths,\n });\n\n const hydrateWsRoute = (serialized: SerializedWsRouteRecord): WsRouteRecord => ({\n urlPath: serialized.urlPath,\n filePath: serialized.filePath,\n paramNames: serialized.paramNames,\n isDynamic: serialized.isDynamic,\n isCatchAll: serialized.isCatchAll,\n middlewarePaths: serialized.middlewarePaths,\n });\n\n const routes = manifest.routes.map(hydrateRoute);\n const wsRoutes = manifest.wsRoutes.map(hydrateWsRoute);\n return { routes, wsRoutes };\n}\n","export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const;\n\nexport type HttpMethod = (typeof HTTP_METHODS)[number];\n\nconst HTTP_METHOD_SET: ReadonlySet<string> = new Set(HTTP_METHODS);\n\nexport function isHttpMethod(value: string): value is HttpMethod {\n return HTTP_METHOD_SET.has(value);\n}\n","/**\n * 标准化路径:\n * - 将反斜杠替换为正斜杠\n * - 去除重复的斜杠\n * - 去除尾部斜杠\n * - 确保以 / 开头(如果非空)\n */\nexport function normalizePath(path: string): string {\n if (!path) return '';\n\n // 将反斜杠替换为正斜杠\n let result = path.replace(/\\\\/g, '/');\n\n // 去除重复的斜杠\n result = result.replace(/\\/+/g, '/');\n\n // 去除尾部斜杠\n result = result.replace(/\\/+$/, '');\n\n // 确保以 / 开头(如果非空)\n if (result && !result.startsWith('/')) {\n result = '/' + result;\n }\n\n return result;\n}\n","import { normalizePath } from '../utils/normalizePath';\n\n/**\n * 将 [name] 格式的目录名转换为 :name 格式\n * '[id]' -> ':id'\n * '[...slug]' -> ':...slug'\n * 'login' -> 'login'\n */\nexport function dynamicSegmentToParam(segment: string): string {\n const match = segment.match(/^\\[(.+)\\]$/);\n if (match) {\n return ':' + match[1];\n }\n return segment;\n}\n\n/**\n * 从 URL 路径中提取动态参数名\n * '/user/:id' -> ['id']\n * '/auth/login' -> []\n * '/user/:id/posts/:postId' -> ['id', 'postId']\n * '/shop/:...slug' -> ['slug']\n */\nexport function extractParamNames(urlPath: string): string[] {\n const params: string[] = [];\n const segments = urlPath.split('/');\n for (const segment of segments) {\n if (segment.startsWith(':...')) {\n // catch-all 参数::...slug -> slug\n params.push(segment.slice(4));\n } else if (segment.startsWith(':')) {\n params.push(segment.slice(1));\n }\n }\n return params;\n}\n\n/**\n * 判断路径段是否为 catch-all 路由\n * '[...slug]' -> true\n * '[id]' -> false\n */\nexport function isCatchAllSegment(segment: string): boolean {\n return /^\\[\\.\\.\\..+\\]$/.test(segment);\n}\n\n/**\n * 判断路径段是否为路由分组\n * '(marketing)' -> true\n * 'login' -> false\n */\nexport function isRouteGroup(segment: string): boolean {\n return /^\\(.+\\)$/.test(segment);\n}\n\n/**\n * 将路由文件路径转换为 URL 路径\n * 输入: 相对于项目根目录的文件路径,如 'src/api/auth/login/handler.ts'\n * 输出: URL 路径,如 '/api/auth/login'\n *\n * 规则:\n * - 去掉 src/ 前缀(路由源码目录写死为 src)\n * - 去掉文件名(handler.ts 等)\n * - 将 [id] 转换为 :id\n * - 忽略路由分组 (groupName) — 不影响 URL\n * - 标准化路径\n *\n * 说明:路由源码目录写死为 src/api/,src/ 前缀剥离后 api/ 前缀保留在 URL 中,\n * 因此 URL 始终带 /api 前缀。\n */\nexport function filePathToUrlPath(filePath: string): string {\n // 去掉 src/ 前缀(路由源码目录写死为 src)\n const withoutPrefix = filePath.startsWith('src/') ? filePath.slice(4) : filePath;\n\n // 去掉文件名(最后一个路径段)\n const lastSlashIndex = withoutPrefix.lastIndexOf('/');\n const dirPath = lastSlashIndex === -1 ? '' : withoutPrefix.slice(0, lastSlashIndex);\n\n if (!dirPath) {\n return '';\n }\n\n // 将每个路径段中的 [name] 转换为 :name,忽略路由分组 (name)\n const segments = dirPath\n .split('/')\n .filter((s) => !isRouteGroup(s)) // 过滤掉路由分组\n .map(dynamicSegmentToParam);\n\n // 拼接并标准化路径\n return normalizePath(segments.join('/'));\n}\n","import { pathToFileURL } from 'node:url';\n\n/**\n * 当前模块加载时间戳(watch 模式下用于绕过 ESM 缓存)\n *\n * 由 createDevApp.reloadRoutes 调用 setLoadTimestamp 设置。\n * ES 模块单例保证所有 import 此模块的地方共享同一个值,无需 globalThis。\n */\nlet loadTs: number | undefined;\n\nexport function setLoadTimestamp(ts: number): void {\n loadTs = ts;\n}\n\n/**\n * vitest 的 importActual 函数类型\n *\n * vi.importActual(path) 走 Vite SSR pipeline:\n * - 识别 vitest.config.ts 的 resolve.alias 与 tsconfig paths 别名\n * - 让 vi.mock 在加载的模块内生效\n */\ntype ImportActualFn = (path: string) => Promise<unknown>;\n\n/**\n * 检测 vitest 的 vi.importActual(globals: true 时注入 globalThis.vi)\n *\n * 业务方 vitest.config.ts 需 test.globals: true,或在测试文件内\n * 显式 `import { vi } from 'vitest'` 后挂到 globalThis.vi。\n *\n * 不在 vitest 环境下返回 undefined,调用方回退到 Node 原生 import()。\n */\nfunction getVitestImportActual(): ImportActualFn | undefined {\n const vi = (globalThis as { vi?: { importActual?: ImportActualFn } }).vi;\n if (typeof vi?.importActual !== 'function') return undefined;\n return vi.importActual.bind(vi);\n}\n\n/**\n * 动态 import 文件\n *\n * 两种加载路径:\n *\n * 1. **vitest 环境**(`globalThis.vi.importActual` 可用):走 Vite SSR pipeline,\n * 识别 tsconfig paths 别名(如 `@/lib/db`)+ 让 `vi.mock` 生效。\n * 业务方在 vitest 下用 `createTestServer` 时自动启用。\n *\n * 2. **非 vitest 环境**:Node 原生 `import()`。watch 模式下拼接 `?t=<timestamp>`\n * query 绕过 ESM 缓存;非 watch 模式等价普通 `import()`。\n *\n * @param filePath 文件绝对路径\n * @param bustViteCache dev 按需模式下走 Node 原生 import(绕过 Vite SSR pipeline)。\n * handler.js / zod.js 是已编译产物(别名已在编译时重写为相对路径),不需要 Vite alias 解析,\n * 直接走 Node 原生 import + 时间戳 query 避免 Vite SSR 缓存干扰。\n * @returns 模块导出对象\n */\nexport async function importWithCacheBust(\n filePath: string,\n bustViteCache = false,\n): Promise<Record<string, unknown>> {\n // vitest 环境优先走 Vite pipeline(识别 tsconfig paths + vi.mock)\n const importActual = getVitestImportActual();\n if (importActual) {\n if (bustViteCache) {\n // dev 按需模式:handler.js / zod.js 是已编译产物(别名已在编译时重写为相对路径),\n // 不需要 Vite alias 解析,直接走 Node 原生 import + 时间戳 query 避免 Vite SSR 缓存干扰\n let url = pathToFileURL(filePath).href;\n url += `?t=${Date.now()}`;\n return (await import(url)) as Record<string, unknown>;\n }\n return (await importActual(filePath)) as Record<string, unknown>;\n }\n\n // 否则走 Node 原生 ESM import\n let url = pathToFileURL(filePath).href;\n if (loadTs !== undefined) {\n url += `?t=${loadTs}`;\n }\n return (await import(url)) as Record<string, unknown>;\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\nimport type { InjectorMap } from './injectorTypes';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\n\n/**\n * 中间件 + 注入器加载结果\n */\nexport interface LoadedMiddlewareBundle {\n middlewares: FaapiMiddleware[];\n injectors: InjectorMap;\n}\n\n/**\n * 中间件缓存,key 为 middlewares.ts 的绝对路径\n */\nconst middlewareCache = new Map<string, LoadedMiddlewareBundle>();\n\n/**\n * in-flight 加载去重:同一文件并发首载共享同一 Promise(冷启动并发首请求\n * 不重复 import + 合并)。invalidateMiddlewareCache 时一并清空\n */\nconst inFlight = new Map<string, Promise<LoadedMiddlewareBundle>>();\n\n/**\n * 失效所有中间件缓存(watch 模式下文件变化时调用)\n */\nexport function invalidateMiddlewareCache(): void {\n middlewareCache.clear();\n inFlight.clear();\n}\n\n/**\n * 从缓存中读取中间件 bundle(未命中返回 undefined)\n */\nexport function getCachedMiddlewares(absPath: string): LoadedMiddlewareBundle | undefined {\n return middlewareCache.get(absPath);\n}\n\n/**\n * 写入中间件缓存\n */\nexport function setCachedMiddlewares(absPath: string, bundle: LoadedMiddlewareBundle): void {\n middlewareCache.set(absPath, bundle);\n}\n\n/**\n * 从绝对路径加载 middlewares.ts 并校验\n *\n * 文件可导出:\n * - `default`:中间件数组(洋葱模型,每项为 async 函数)\n * - `injectors`:注入器映射表(按参数名匹配 handler 参数)\n *\n * 两者都是可选的,但至少要有一个。\n *\n * 加载失败时的语义:\n * - 文件 import 抛错(语法错误 / 路径不存在 / 运行时抛错)→ console.error 输出原始错误,\n * 返回空 bundle,确保服务仍可启动而非崩溃。鉴权等关键中间件失效后业务可由 onError 感知。\n * 开发期 watcher 会因 reloadRoutes 触发重新加载,实现自愈。\n */\nexport async function loadMiddlewaresFile(filePath: string): Promise<LoadedMiddlewareBundle> {\n let module: Record<string, unknown>;\n try {\n module = (await importWithCacheBust(filePath)) as Record<string, unknown>;\n } catch (err) {\n // 关键可见性:必须打印原始错误,否则业务方完全无感知(鉴权中间件失效等同于裸奔)\n console.error(\n `[faapi] Failed to load middlewares from ${filePath}:`,\n err instanceof Error ? (err.stack ?? err.message) : err,\n );\n return { middlewares: [], injectors: {} };\n }\n\n // 加载中间件数组\n const middlewares = (module.default ?? module.middlewares ?? []) as unknown[];\n if (!Array.isArray(middlewares)) {\n console.warn(`[faapi] middlewares.ts 应导出数组,已忽略: ${filePath}`);\n return { middlewares: [], injectors: {} };\n }\n\n const validMiddlewares = middlewares.filter((m: unknown) => {\n if (typeof m !== 'function') {\n console.warn(`[faapi] 无效的中间件项(应为函数),已忽略: ${typeof m}`);\n return false;\n }\n return true;\n }) as FaapiMiddleware[];\n\n // 加载注入器映射表(可选命名导出)\n const injectors = (module.injectors ?? {}) as InjectorMap;\n if (typeof injectors !== 'object' || injectors === null) {\n console.warn(`[faapi] injectors 应导出对象,已忽略: ${filePath}`);\n return { middlewares: validMiddlewares, injectors: {} };\n }\n\n // 校验注入器:每个值必须是函数\n const validInjectors: InjectorMap = {};\n for (const [name, injector] of Object.entries(injectors)) {\n if (typeof injector !== 'function') {\n console.warn(`[faapi] 注入器 ${name} 应为函数,已忽略`);\n continue;\n }\n validInjectors[name] = injector;\n }\n\n return { middlewares: validMiddlewares, injectors: validInjectors };\n}\n\n/**\n * 按路径列表加载并合并中间件(根在前,路由目录在后)\n *\n * 合并语义:\n * - 子级中间件追加在父级之后(洋葱模型:后注册的中间件在内层)\n * - 子级注入器覆盖父级同名注入器\n *\n * 单文件加载带缓存(getCachedMiddlewares/setCachedMiddlewares),重复调用仅首次真正加载。\n *\n * @param middlewarePaths 中间件文件绝对路径列表(根在前,路由目录在后)\n * @returns 合并后的中间件+注入器;无中间件时返回 undefined\n */\nexport async function loadMergedMiddlewares(\n middlewarePaths: string[],\n): Promise<LoadedMiddlewareBundle | undefined> {\n if (middlewarePaths.length === 0) return undefined;\n\n const mergedMiddlewares: FaapiMiddleware[] = [];\n const mergedInjectors: InjectorMap = {};\n\n for (const absMwPath of middlewarePaths) {\n let bundle: LoadedMiddlewareBundle | undefined = getCachedMiddlewares(absMwPath);\n if (bundle === undefined) {\n // in-flight 去重:并发首载共享同一 Promise(对照 compileOnDemand 的 mutex 模式)\n let loading = inFlight.get(absMwPath);\n if (!loading) {\n loading = loadMiddlewaresFile(absMwPath).then((result) => {\n setCachedMiddlewares(absMwPath, result);\n return result;\n });\n inFlight.set(absMwPath, loading);\n }\n bundle = await loading;\n }\n mergedMiddlewares.push(...bundle.middlewares);\n for (const [name, injector] of Object.entries(bundle.injectors)) {\n mergedInjectors[name] = injector;\n }\n }\n\n if (mergedMiddlewares.length === 0 && Object.keys(mergedInjectors).length === 0) {\n return undefined;\n }\n\n return { middlewares: mergedMiddlewares, injectors: mergedInjectors };\n}\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { RouteManifest, WsRouteManifest } from './routeTypes';\nimport { HTTP_METHODS } from './constants';\nimport { filePathToUrlPath, extractParamNames, isCatchAllSegment } from './parseRouteFile';\nimport { loadMergedMiddlewares } from '../middleware/loadMiddlewares';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { toProdFilePath } from '../utils/prodPaths';\n\n/**\n * 匹配源码中导出的 HTTP 方法或 WS 函数\n *\n * 支持:\n * - `export function GET() {}`\n * - `export async function POST() {}`\n * - `export const GET = () => {}` / `export const GET = async () => {}`\n * - `export function WS() {}`\n *\n * 不通过运行时 import 提取方法名,避免启动时全量加载 handler 模块(Vite 风格:\n * 路由发现与 handler 加载解耦,handler.js 按需编译/导入)。\n */\nconst HTTP_OR_WS_EXPORT_RE = new RegExp(\n String.raw`export\\s+(?:async\\s+)?(?:function\\s+|const\\s+)(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|WS)\\b`,\n 'g',\n);\n\n/**\n * 从源码内容中提取所有 HTTP 方法 + WS 导出名(去重)\n */\nfunction extractExportsFromSource(source: string): Set<string> {\n const names = new Set<string>();\n let match: RegExpExecArray | null;\n HTTP_OR_WS_EXPORT_RE.lastIndex = 0;\n while ((match = HTTP_OR_WS_EXPORT_RE.exec(source)) !== null) {\n names.add(match[1]!);\n }\n return names;\n}\n\n/**\n * 从路由文件所在目录向上逐级查找 middlewares.ts(源码),\n * 返回从根到路由目录的中间件文件**绝对路径列表**(不加载模块)。\n *\n * 设计意图:scanRoutes 不再启动时全量 import middlewares.js,仅收集路径;\n * 实际的中间件加载延后到 hydrateRoutes / 请求阶段(与 prod 的 hydrateRoutes 一致)。\n * 这样 dev 启动时 zero import,启动速度接近 Vite。\n *\n * 路径选择规则(与 generateRoutes.extractMiddlewarePaths 一致):\n * - dist 传入:返回**产物** middlewares.js 绝对路径(打平 src/ 前缀,存在性检查源码 .ts)\n * - dist 不传:返回**源码** middlewares.ts/.js 绝对路径(兼容无 dist 的旧调用方,如 testServer)\n *\n * @param routeFilePath 源码相对路径(如 src/api/hello/handler.ts)\n * @param rootDir 项目根目录\n * @param dist 产物目录(dist 或 .faapi),不传则返回源码路径\n * @returns 中间件文件绝对路径列表(根在前,路由目录在后);空数组表示无中间件\n */\nfunction collectMiddlewarePaths(routeFilePath: string, rootDir: string, dist?: string): string[] {\n const routeDir = path.dirname(routeFilePath);\n const resolvedRoot = path.resolve(rootDir);\n\n const paths: string[] = [];\n let currentDir = path.resolve(rootDir, routeDir);\n while (true) {\n if (dist) {\n // 产物模式:检查源码 middlewares.ts 是否存在,返回产物 middlewares.js 绝对路径\n const mwTsPath = path.join(currentDir, 'middlewares.ts');\n const mwJsPath = path.join(currentDir, 'middlewares.js');\n const absTsPath = path.resolve(rootDir, mwTsPath);\n const absJsPath = path.resolve(rootDir, mwJsPath);\n const absMwPath = fs.existsSync(absTsPath)\n ? absTsPath\n : fs.existsSync(absJsPath)\n ? absJsPath\n : null;\n if (absMwPath) {\n // 转为产物形式绝对路径(打平 src/ 前缀,加 dist 前缀)\n const relMwPath = path.relative(rootDir, absMwPath);\n const prodAbsPath = path.resolve(rootDir, toProdFilePath(relMwPath, dist));\n paths.push(prodAbsPath);\n }\n } else {\n // 源码模式(testServer / 单元测试用):直接返回源码 middlewares.ts/.js 绝对路径\n for (const ext of ['.ts', '.js']) {\n const mwPath = path.join(currentDir, `middlewares${ext}`);\n const absMwPath = path.resolve(rootDir, mwPath);\n if (fs.existsSync(absMwPath)) {\n paths.push(absMwPath);\n break;\n }\n }\n }\n if (currentDir === resolvedRoot) break;\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n\n // paths 是从路由目录向上收集的,需反转为根在前\n paths.reverse();\n return paths;\n}\n\n/**\n * 按路径列表加载并合并中间件(根在前,路由目录在后)\n *\n * 已移至 `middleware/loadMiddlewares.ts` 的 `loadMergedMiddlewares`,\n * scanRoutes 不再预加载中间件(dist 模式只收集路径,请求阶段按需加载)。\n */\n\n/**\n * 扫描 api 目录,同时生成 HTTP 路由清单和 WebSocket 路由清单\n *\n * **Vite 风格**:启动时只读源码 + 正则提取方法名,不 import handler.js / middlewares.js。\n * 中间件改为\"收集路径\",加载延后到 hydrateRoutes / 请求阶段。\n *\n * 路由文件格式:handler.ts,导出 HTTP 方法名作为 handler,也可导出 `WS` 函数声明 WebSocket 路由\n * ```ts\n * // api/user/handler.ts\n * export function GET() { return { list: [] } }\n * export function POST(body: any) { return { created: true } }\n *\n * // WebSocket 路由:导出 WS 函数\n * export function WS(ctx: WsContext) {\n * return { onMessage(ws, msg) { ws.send(`echo: ${msg}`) } };\n * }\n * ```\n * 一个 handler.ts 可同时导出 HTTP 方法(GET/POST...)和 WS 函数,\n * 分别生成 HTTP RouteRecord 和 WsRouteRecord。\n *\n * **中间件加载策略**:\n * - dev/prod(dist 传入):scanRoutes 仅收集 middlewarePaths,不加载模块;\n * 由 hydrateRoutes(prod)或 dev 启动流程加载\n * - 无 dist 模式(testServer / 单元测试):scanRoutes 直接加载中间件并塞入 route.middlewares\n * (保持向后兼容,避免破坏现有测试)\n *\n * @param rootDir 项目根目录\n * @param patterns glob patterns(源码 .ts 路径,如 src/api 下所有 .ts)\n * @param dist 产物目录(dist 或 .faapi)。传入时 scanRoutes 不 import 任何模块;\n * 不传时 import 源码 .ts(旧模式,需要 tsx,仅 testServer/单测使用)。\n */\nexport async function scanRoutes(\n rootDir: string,\n patterns: string[],\n dist?: string,\n): Promise<{ routes: RouteManifest; wsRoutes: WsRouteManifest }> {\n const files = await fg(patterns, {\n cwd: rootDir,\n onlyFiles: true,\n absolute: false,\n });\n\n const routes: RouteManifest = [];\n const wsRoutes: WsRouteManifest = [];\n\n for (const file of files) {\n const normalizedFile = file.replace(/\\\\/g, '/');\n const fileName = normalizedFile.split('/').pop()!;\n\n // 处理 handler.{ts,js} — API 路由 + WebSocket 路由\n // dev/build 模式扫源码 .ts(dist 传入),start 模式扫产物 .js(由 hydrateRoutes 处理)\n if (fileName === 'handler.ts' || fileName === 'handler.js') {\n const absPath = path.resolve(rootDir, normalizedFile);\n const urlPath = filePathToUrlPath(normalizedFile);\n const paramNames = extractParamNames(urlPath);\n const isDynamic = paramNames.length > 0;\n const isCatchAll = normalizedFile.split('/').some(isCatchAllSegment);\n\n // 中间件:dist 模式只收集路径(按需加载),无 dist 模式直接加载\n let middlewarePaths: string[] | undefined;\n let middlewareBundle: { middlewares: FaapiMiddleware[]; injectors: InjectorMap } | undefined;\n if (dist) {\n // dev/prod 模式:仅收集路径,请求阶段按需加载(Vite 风格)\n middlewarePaths = collectMiddlewarePaths(normalizedFile, rootDir, dist);\n } else {\n // 无 dist 模式(testServer/单测):直接加载源码中间件\n const mwPaths = collectMiddlewarePaths(normalizedFile, rootDir);\n middlewareBundle = await loadMergedMiddlewares(mwPaths);\n }\n\n // 提取导出名(HTTP 方法 + WS):读源码 + 正则匹配,不 import 模块\n const source = await fs.promises.readFile(absPath, 'utf8').catch(() => '');\n const exportNames = extractExportsFromSource(source);\n const methods = HTTP_METHODS.filter((m) => exportNames.has(m));\n\n for (const method of methods) {\n routes.push({\n method,\n urlPath,\n filePath: normalizedFile,\n paramNames,\n isDynamic,\n isCatchAll: isCatchAll || undefined,\n middlewarePaths,\n middlewares: middlewareBundle?.middlewares,\n injectors: middlewareBundle?.injectors,\n });\n }\n\n // WS 导出\n if (exportNames.has('WS')) {\n wsRoutes.push({\n urlPath,\n filePath: normalizedFile,\n paramNames,\n isDynamic,\n isCatchAll: isCatchAll || undefined,\n middlewarePaths,\n middlewares: middlewareBundle?.middlewares,\n injectors: middlewareBundle?.injectors,\n });\n }\n continue;\n }\n }\n\n return { routes, wsRoutes };\n}\n","import type { RouteManifest } from './routeTypes';\n\n/**\n * 路由排序规则:\n * 1. 静态路由优先于动态路由\n * 2. 动态路由优先于 catch-all 路由\n * 3. 路径段数少的优先\n * 4. 字母序排列\n */\nexport function sortRoutes(routes: RouteManifest): RouteManifest {\n return [...routes].sort((a, b) => {\n // 1. 静态路由优先于动态路由\n if (a.isDynamic !== b.isDynamic) {\n return a.isDynamic ? 1 : -1;\n }\n\n // 2. catch-all 路由优先级最低(排在所有动态路由之后)\n if (a.isCatchAll !== b.isCatchAll) {\n return a.isCatchAll ? 1 : -1;\n }\n\n // 3. 路径段数少的优先\n const aSegments = a.urlPath.split('/').filter(Boolean).length;\n const bSegments = b.urlPath.split('/').filter(Boolean).length;\n if (aSegments !== bSegments) {\n return aSegments - bSegments;\n }\n\n // 4. 字母序排列\n return a.urlPath.localeCompare(b.urlPath);\n });\n}\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { ToolManifestList } from './toolTypes';\n\n/**\n * tool 扫描 patterns(框架约定,非用户可配置项)\n *\n * 与路由的 src/api 扫描对称——tool 文件约定放在 `src/tools` 下任意层级子目录的 handler.ts。\n * 所有 tool 都是共享的,agent 通过 config 块的 `tools` 字段显式声明引用哪些。\n */\nexport const TOOL_PATTERNS = ['src/tools/**/*.ts'];\n\n/**\n * 匹配源码中导出的函数名(任意合法标识符,区别于路由的固定 HTTP 方法名)\n *\n * 支持:\n * - `export function getWeather(input) {}`\n * - `export async function getWeather(input) {}`\n * - `export const getWeather = (input) => {}`\n * - `export const getWeather = async (input) => {}`\n *\n * 不匹配:\n * - `export interface WeatherInput {}`(interface 关键字)\n * - `export type X = ...`(type 关键字)\n * - `export { getWeather }`(命名导出引用,非声明)\n * - `export default function ...`(default 后非 function/const)\n *\n * 不通过运行时 import 提取函数名,避免启动时全量加载 tool 模块(Vite 风格:\n * tool 发现与 tool.js 加载解耦,tool.js 按需编译/导入)。\n */\nconst TOOL_EXPORT_RE = new RegExp(\n String.raw`export\\s+(?:async\\s+)?(?:function\\s+|const\\s+)([A-Za-z_$][\\w$]*)\\s*(?:\\(|=)`,\n 'g',\n);\n\n/**\n * 保留导出名(即使匹配正则也不识别为 tool)\n *\n * - `default` — 默认导出保留\n * - `config` — agent 配置块(scanAgents 使用)\n * - `run` — agent 自定义运行函数(scanAgents 使用)\n */\nconst RESERVED_EXPORTS = new Set(['default', 'config', 'run']);\n\n/**\n * 从源码内容中提取所有 tool 函数导出名(去重 + 排除保留名)\n */\nfunction extractToolExportsFromSource(source: string): Set<string> {\n const names = new Set<string>();\n let match: RegExpExecArray | null;\n TOOL_EXPORT_RE.lastIndex = 0;\n while ((match = TOOL_EXPORT_RE.exec(source)) !== null) {\n const name = match[1]!;\n if (!RESERVED_EXPORTS.has(name)) {\n names.add(name);\n }\n }\n return names;\n}\n\n/**\n * 从相对路径(已剥离前缀)提取命名空间\n *\n * weather/handler.ts → 'weather'\n * handler.ts → ''\n * a/b/handler.ts → 'a.b'\n */\nfunction extractNamespaceFromRelPath(relPath: string): string {\n const lastSlash = relPath.lastIndexOf('/');\n const dirPath = lastSlash === -1 ? '' : relPath.slice(0, lastSlash);\n if (!dirPath) return '';\n return dirPath.split('/').join('.');\n}\n\n/**\n * 从文件路径生成 tool 命名空间(子目录,用 . 连接,不含函数名)\n *\n * 命名空间生成规则:\n * 1. 剥离 `tools/` 前缀(找第一个 `tools/`)\n * 2. 剥离文件名(handler.ts)\n * 3. 剩余路径段用 `.` 连接\n *\n * 例子:\n * - src/tools/weather/handler.ts → 'weather'\n * - src/tools/handler.ts → ''\n * - src/tools/a/b/handler.ts → 'a.b'\n */\nfunction filePathToToolNamespace(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n\n // 剥离 tools/ 前缀(找第一个 tools/)\n const toolsMatch = normalized.match(/(?:^|\\/)tools\\/(.+)$/);\n if (toolsMatch) {\n return extractNamespaceFromRelPath(toolsMatch[1]!);\n }\n\n // 不在 tools/ 下,返回空(不应发生)\n return '';\n}\n\n/**\n * 生成 tool 名:命名空间.函数名 或 纯函数名\n */\nfunction buildToolName(namespace: string, functionName: string): string {\n return namespace ? `${namespace}.${functionName}` : functionName;\n}\n\n/**\n * 扫描 tools 目录,生成 tool 清单\n *\n * Vite 风格:启动时只读源码 + 正则提取函数导出名,不 import tool.js。\n * tool.js 加载延后到 loadToolModule 请求阶段(详见 compileOnDemand)。\n *\n * tool 文件格式:handler.ts,导出任意函数即声明一个 tool(一个文件可多个)。\n * 排除保留导出名:default / config / run(agent 系统保留)。\n *\n * 所有 tool 都是共享的——放在 `src/tools` 下任意层级子目录的 handler.ts。\n * agent 通过 config 块的 `tools` 字段显式声明引用哪些 tool。\n *\n * tool 命名规则:子目录.函数名(如 weather.getWeather),无子目录时纯函数名。\n *\n * 重名检测:同名 tool 抛错。\n *\n * @param rootDir 项目根目录\n * @param patterns glob patterns(源码 .ts 路径,匹配 src/tools 下 tool 文件)\n */\nexport async function scanTools(rootDir: string, patterns: string[]): Promise<ToolManifestList> {\n const files = await fg(patterns, {\n cwd: rootDir,\n onlyFiles: true,\n absolute: false,\n });\n\n const tools: ToolManifestList = [];\n // 重名检测:toolName → filePath\n const seen = new Map<string, string>();\n\n for (const file of files) {\n const normalizedFile = file.replace(/\\\\/g, '/');\n const fileName = normalizedFile.split('/').pop()!;\n\n // 只处理 handler.ts(与路由 handler.ts 对称),其他 .ts 文件跳过\n if (fileName !== 'handler.ts' && fileName !== 'handler.js') {\n continue;\n }\n\n const absPath = path.resolve(rootDir, normalizedFile);\n const source = await fs.promises.readFile(absPath, 'utf8').catch(() => '');\n const exportNames = extractToolExportsFromSource(source);\n\n const namespace = filePathToToolNamespace(normalizedFile);\n\n for (const fnName of exportNames) {\n const toolName = buildToolName(namespace, fnName);\n\n // 重名检测:同名 tool 报错\n const prevFile = seen.get(toolName);\n if (prevFile) {\n throw new Error(\n `Tool conflict: \"${toolName}\" declared in both ${prevFile} and ${normalizedFile}`,\n );\n }\n\n seen.set(toolName, normalizedFile);\n\n tools.push({\n name: toolName,\n functionName: fnName,\n filePath: normalizedFile,\n });\n }\n }\n\n return tools;\n}\n","import ts from 'typescript';\n\n/**\n * 判断节点是否有 `export` 修饰符\n */\nexport function hasExportModifier(node: ts.Node): boolean {\n if (!ts.canHaveModifiers(node)) return false;\n const modifiers = ts.getModifiers(node);\n return !!modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);\n}\n\n/**\n * 从节点提取第一个 JSDoc 注释块\n *\n * 1. 标准 API(`ts.getJSDocCommentsAndTags`,jsDocCache 已缓存时命中)\n * 2. 回退:直接访问解析器存入的 `node.jsDoc` 数组(jsDocCache 未同步的情况)\n */\nexport function getJSDocFromNode(node: ts.Node): ts.JSDoc | undefined {\n const apiDocs = ts\n .getJSDocCommentsAndTags(node)\n .filter((entry): entry is ts.JSDoc => ts.isJSDoc(entry));\n if (apiDocs.length > 0) return apiDocs[0];\n\n const directDocs = (node as unknown as { jsDoc?: ts.JSDoc[] }).jsDoc;\n if (directDocs && directDocs.length > 0) return directDocs[0];\n\n return undefined;\n}\n\n/**\n * 提取 JSDoc 描述(注释块自由文本,`@tag` 之前的首段)\n *\n * 多行描述保留换行(TypeScript 已自动剥离每行前缀 ` * `)。\n * 无 JSDoc / JSDoc 无自由文本 / 描述仅空白 / comment 为富文本结构 → `undefined`。\n */\nexport function extractDescription(jsDoc: ts.JSDoc | undefined): string | undefined {\n if (!jsDoc) return undefined;\n if (typeof jsDoc.comment !== 'string') return undefined;\n const trimmed = jsDoc.comment.trim();\n return trimmed || undefined;\n}\n\n/**\n * 提取 `@<tagName>` 标签的覆盖名\n *\n * - `@tool weather.current` / `@agent researcher` → 标签值文本\n * - 花括号包裹(`@tool {name}`)去除花括号\n * - 无指定标签 / 标签无 comment 文本 → `undefined`(调用方回退到路径推导值,不报错)\n */\nexport function extractJSDocTagValue(\n jsDoc: ts.JSDoc | undefined,\n tagName: string,\n): string | undefined {\n if (!jsDoc || !jsDoc.tags) return undefined;\n for (const tag of jsDoc.tags) {\n if (tag.tagName.text !== tagName) continue;\n // tag.comment 类型为 string | false | undefined;false 表示该 tag 无 comment 文本\n if (typeof tag.comment !== 'string') return undefined;\n const text = tag.comment.trim();\n if (!text) return undefined;\n const cleaned = text.replace(/^\\{|\\}$/g, '').trim();\n return cleaned || undefined;\n }\n return undefined;\n}\n","import ts from 'typescript';\nimport {\n extractDescription,\n extractJSDocTagValue,\n getJSDocFromNode,\n hasExportModifier,\n} from './jsDocMetadata';\n\n/**\n * Tool 的 LLM 可见核心字段\n *\n * 描述\"tool 是什么\"——LLM 真正需要消费的字段(发往 LLM 的 tool 定义只含\n * `name` / `description` / input schema),**不含**代码本体加载细节\n * (`filePath` / `functionName` / `inputTypeName`)。\n *\n * 与 [AgentCore](./extractAgentMetadata.md) 对称——LLM-facing 字段与代码加载\n * 细节分离,便于未来扩展(如 DB-driven tool 只实现 `ToolCore` 即可)。\n *\n * `toolRegistry` 查询入口 / `@faapi/agent` 子包的 `buildToolDefinitions`\n * 都消费 `ToolCore` 字段组装 LLM tool 列表。\n */\nexport interface ToolCore {\n /** tool 名(`@tool` JSDoc 覆盖值 或 路径推导值) */\n name: string;\n /** JSDoc 描述(tool 描述,对 LLM 可见),无 JSDoc 或 JSDoc 无自由文本时为 `undefined` */\n description?: string;\n}\n\n/**\n * Tool 完整元数据(文件型 tool)\n *\n * 继承 [ToolCore](./extractToolMetadata.md) 的 LLM 字段,额外扩展**代码本体加载细节**:\n * - `filePath` — `loadToolModule` 加载 `handler.js` 产物定位函数用\n * - `functionName` — 源码导出函数名(不受 `@tool` 覆盖影响,AST 定位 + 运行时 resolveExport 用)\n * - `inputTypeName` — 第一个参数的 TypeReference 名(供 [extractTypeInfo](./extractHandlerTypes.md)\n * 生成 zod schema;运行时 `resolveToolSchema` 据此定位 `zod.js`)\n *\n * 由 [extractToolMetadata](./extractToolMetadata.md) 产出,合并路径推导字段\n * (来自 [scanTools](../tools/scanTools.md) 的 `ToolManifest`)与 AST 提取字段\n * (JSDoc 描述、`@tool` 覆盖名、第一个参数 interface 名)。\n *\n * 字段来源:\n * - `name` — `@tool` JSDoc 覆盖值,或 `pathMeta.name`(路径推导)\n * - `description` — JSDoc 注释块自由文本(对 LLM 可见)\n * - `filePath` / `functionName` — 由 `pathMeta` 透传\n * - `inputTypeName` — 第一个参数的 TypeReference 名(供 [extractTypeInfo](./extractHandlerTypes.md) 生成 zod schema)\n */\nexport interface ToolMetadata extends ToolCore {\n /** 第一个参数的 interface/type 名(用于生成 zod schema),\n * 无参数/参数无类型标注/参数为内联类型字面量时为 `undefined` */\n inputTypeName?: string;\n /** 源码相对路径(从 `pathMeta` 透传) */\n filePath: string;\n /** 源码中的导出函数名(从 `pathMeta` 透传,AST 定位用,不受 `@tool` 覆盖影响) */\n functionName: string;\n}\n\n/**\n * 路径推导的 tool 元数据(由 [scanTools](../tools/scanTools.ts) 计算)\n *\n * 透传到 [ToolMetadata](./extractToolMetadata.ts) 输出,与 AST 提取字段合并。\n */\nexport interface ToolPathMeta {\n /** 路径推导的 tool 名(如 `weather.getWeather`) */\n name: string;\n /** 源码相对路径(如 `src/tools/weather/handler.ts`) */\n filePath: string;\n}\n\n/** 函数节点联合类型(FunctionDeclaration / ArrowFunction / FunctionExpression) */\ntype FunctionLike = ts.FunctionDeclaration | ts.ArrowFunction | ts.FunctionExpression;\n\n/**\n * 从 tool handler.ts 提取单个 tool 的元数据\n *\n * 提取内容:\n * 1. **JSDoc 描述** — 注释块自由文本(`@tag` 之前的首段),对 LLM 可见\n * 2. **`@tool` 覆盖名** — JSDoc 中 `@tool` 标签后的文本(如 `customName`),覆盖路径推导的 `name`\n * 3. **第一个参数 interface 名** — 供 [extractTypeInfo](./extractHandlerTypes.ts) 生成 zod schema\n *\n * 不提取(由 `pathMeta` 透传): `filePath` / `functionName`\n *\n * 函数查找支持四种导出形式,与 [scanTools](../tools/scanTools.ts) 的 `TOOL_EXPORT_RE` 正则同构:\n * - `export function name() {}`\n * - `export async function name() {}`\n * - `export const name = () => {}`\n * - `export const name = async () => {}`(以及 `= function () {}`)\n *\n * JSDoc 查找对箭头函数/函数表达式自动回溯到外层 `VariableStatement`\n * (JSDoc 通常写在 `export const` 上方,而非箭头函数本身)。\n *\n * @param program TypeScript Program\n * @param filePath 源文件**绝对路径**(AST 用,需与 `program.getSourceFile` 一致)\n * @param functionName 源码导出函数名(在文件中查找的目标)\n * @param pathMeta 路径推导的元数据(scanTools 已计算)\n * @returns `ToolMetadata` 或 `null`(函数未找到或源文件不在 Program 中)\n */\nexport function extractToolMetadata(\n program: ts.Program,\n filePath: string,\n functionName: string,\n pathMeta: ToolPathMeta,\n): ToolMetadata | null {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const found = findExportedFunction(sourceFile, functionName);\n if (!found) return null;\n\n const { fn, jsDocOwner } = found;\n const jsDoc = getJSDocFromNode(jsDocOwner);\n const description = extractDescription(jsDoc);\n const toolNameOverride = extractJSDocTagValue(jsDoc, 'tool');\n const inputTypeName = getFirstParamTypeName(fn, sourceFile);\n\n return {\n name: toolNameOverride ?? pathMeta.name,\n description,\n inputTypeName,\n filePath: pathMeta.filePath,\n functionName,\n };\n}\n\n/** 函数查找结果:函数节点 + JSDoc 持有节点 */\ninterface FoundFunction {\n /** 函数节点(FunctionDeclaration / ArrowFunction / FunctionExpression) */\n fn: FunctionLike;\n /** JSDoc 持有节点(FunctionDeclaration 时为函数本身;箭头/函数表达式时为外层 VariableStatement) */\n jsDocOwner: ts.Node;\n}\n\n/**\n * 在源文件中查找指定名称的导出函数\n *\n * 遍历顶层节点,匹配四种导出形式。返回函数节点和 JSDoc 持有节点:\n * - `export function name` → 两者都是 FunctionDeclaration 本身\n * - `export const name = () => {}` → fn 是 ArrowFunction,jsDocOwner 是外层 VariableStatement\n * (JSDoc 通常写在 `export const` 上方,而非箭头函数本身)\n *\n * 不依赖 `node.parent` 链——`ts.createProgram` 配置 `noEmit: true` 时不会设置父指针,\n * 故改在遍历时直接绑定 JSDoc 持有节点。\n *\n * 非 export 的同名函数不被识别。\n */\nfunction findExportedFunction(\n sourceFile: ts.SourceFile,\n functionName: string,\n): FoundFunction | null {\n let result: FoundFunction | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n // export function name() {} / export async function name() {}\n if (\n ts.isFunctionDeclaration(node) &&\n hasExportModifier(node) &&\n node.name?.text === functionName\n ) {\n result = { fn: node, jsDocOwner: node };\n return;\n }\n\n // export const name = () => {} / export const name = function () {}\n if (ts.isVariableStatement(node) && hasExportModifier(node)) {\n for (const decl of node.declarationList.declarations) {\n if (result) break;\n const nameText = ts.isIdentifier(decl.name)\n ? decl.name.text\n : decl.name.getText(sourceFile);\n if (nameText !== functionName || !decl.initializer) continue;\n if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {\n // JSDoc 通常在 VariableStatement 上(写在 `export const` 上方)\n result = { fn: decl.initializer, jsDocOwner: node };\n }\n }\n }\n });\n\n return result;\n}\n\n/**\n * 提取第一个参数的 interface/type 名(TypeReference)\n *\n * - 参数有 TypeReference 类型标注(如 `input: WeatherInput`)→ `'WeatherInput'`\n * - 参数无类型标注 / 内联类型字面量(如 `input: { city: string }`)→ `undefined`\n * - 函数无参数 → `undefined`\n * - 多参数时取第一个参数的类型名\n *\n * 返回 `undefined` 而非抛错——tool 允许无参数输入或无 schema 校验,\n * 是否生成 `zod.js` 由下游 `generateToolArtifacts` 根据 `inputTypeName` 是否为 `undefined` 决定。\n */\nfunction getFirstParamTypeName(fn: FunctionLike, sourceFile: ts.SourceFile): string | undefined {\n const firstParam = fn.parameters[0];\n if (!firstParam) return undefined;\n if (!firstParam.type) return undefined;\n if (!ts.isTypeReferenceNode(firstParam.type)) return undefined;\n return firstParam.type.typeName.getText(sourceFile);\n}\n","import ts from 'typescript';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Program 缓存,key 为入口文件路径\n *\n * watch 模式下通过 invalidateProgramCache() 全量清理。\n * 同一文件多次创建 Program 时复用缓存,避免重复解析。\n */\nconst programCache = new Map<string, ts.Program>();\n\n/**\n * tsconfig 解析结果缓存,key 为 tsconfig.json 绝对路径\n *\n * 同一 tsconfig 下多个文件共享解析结果,避免每个文件都读盘 + parse。\n * invalidateProgramCache() 同时清空此缓存。\n */\ninterface TsConfigResult {\n /** parseJsonConfigFileContent 解析得到的 module 选项 */\n module?: ts.ModuleKind;\n /** parseJsonConfigFileContent 解析得到的 moduleResolution 选项 */\n moduleResolution?: ts.ModuleResolutionKind;\n /** tsconfig include 模式匹配的所有 .ts 文件绝对路径(作为 program rootNames) */\n fileNames: string[];\n}\nconst tsConfigCache = new Map<string, TsConfigResult>();\n\n/**\n * 清理所有 Program 缓存 + tsconfig 解析缓存(watch 模式下文件变化时调用)\n *\n * 全量清理而非增量清理,理由:\n * - 简单可靠,无状态一致性问题\n * - 跨文件类型引用需要所有文件的 Program 同步更新\n * - tsconfig 可能变化,需重新读取\n * - dev 模式文件量有限,全量重建在百毫秒级\n */\nexport function invalidateProgramCache(): void {\n programCache.clear();\n tsConfigCache.clear();\n}\n\n/**\n * 从指定文件路径向上查找最近的 tsconfig.json\n *\n * 从 filePath 所在目录开始逐级向上,返回第一个找到的 tsconfig.json 绝对路径。\n * 找不到返回 null(如 os.tmpdir() 测试场景)。\n */\nfunction findTsConfig(filePath: string): string | null {\n let dir = path.dirname(filePath);\n const root = path.parse(dir).root;\n while (true) {\n const candidate = path.join(dir, 'tsconfig.json');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n if (dir === root) return null;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * 解析 tsconfig.json,提取 module / moduleResolution / fileNames\n *\n * 使用 ts.readConfigFile 读取 + ts.parseJsonConfigFileContent 解析。\n * 保留 tsconfig 原始的 include/exclude/files 配置,让 ts 自动扫描所有相关 .ts 文件\n * 作为 program 的 rootNames——这样跨文件 import 的源文件会被加载到 program 中,\n * 使 resolveTypeReference 的兜底遍历(找同名声明)能成功。\n *\n * 只提取 module / moduleResolution 两个 compilerOptions 覆盖默认值,其余字段\n * (target / strict / jsx / paths 等)用框架默认值,避免业务项目配置干扰 AST 提取。\n *\n * 解析失败(语法错误 / 缺字段)返回空 fileNames + 空选项,回退到默认 NodeNext,\n * 保证 build 不被 tsconfig 损坏阻塞。\n */\nfunction parseTsConfig(tsconfigPath: string): TsConfigResult {\n const cached = tsConfigCache.get(tsconfigPath);\n if (cached) return cached;\n\n const result: TsConfigResult = { fileNames: [] };\n\n try {\n const configFile = ts.readConfigFile(tsconfigPath, (p) => fs.readFileSync(p, 'utf-8'));\n if (configFile.error) {\n tsConfigCache.set(tsconfigPath, result);\n return result;\n }\n\n const config = configFile.config ?? {};\n const basePath = path.dirname(tsconfigPath);\n\n // 保留 tsconfig 原始 include/exclude/files,让 ts 自动扫描 .ts 文件\n // 这些文件作为 program 的 rootNames,让跨文件 import 的源文件被加载进来\n const parsed = ts.parseJsonConfigFileContent(\n config,\n ts.sys,\n basePath,\n /* existingOptions */ undefined,\n tsconfigPath,\n );\n\n result.fileNames = parsed.fileNames;\n if (parsed.options.module !== undefined) {\n result.module = parsed.options.module;\n }\n if (parsed.options.moduleResolution !== undefined) {\n result.moduleResolution = parsed.options.moduleResolution;\n }\n } catch {\n // 任何异常都回退默认,保证 build 不被 tsconfig 阻塞\n }\n\n tsConfigCache.set(tsconfigPath, result);\n return result;\n}\n\n/**\n * 为指定文件创建 TypeScript Program(带缓存)\n *\n * 从 filePath 向上查找最近的 tsconfig.json:\n * - 找到:用 ts.parseJsonConfigFileContent 解析,取 module / moduleResolution\n * 覆盖默认值,同时取 parsed.fileNames 作为 program 的 rootNames(让跨文件\n * import 的源文件被加载进 program)\n * - 找不到(如 os.tmpdir() 测试场景):回退到默认 NodeNext,rootNames 仅包含 filePath\n *\n * 加载全部相关文件保证 `resolveTypeReference` 的兜底遍历(找同名声明)能成功,\n * 修复业务项目用 `moduleResolution: Bundler` + 无扩展名相对导入时跨文件\n * `import type` 解析失败的问题。\n *\n * 同一次生成中需要分析多个文件时用 {@link createPrograms}(按 tsconfig 分组共享\n * 同一个 Program),避免每个文件单独创建 Program 重复解析全项目源码。\n *\n * @param filePath 要分析的 .ts 文件绝对路径\n */\nexport function createProgram(filePath: string): ts.Program {\n const cached = programCache.get(filePath);\n if (cached) {\n return cached;\n }\n\n const program = buildProgram([filePath], findTsConfig(filePath));\n\n programCache.set(filePath, program);\n return program;\n}\n\n/**\n * 为多个文件创建 Program(按 tsconfig 分组共享,带缓存)\n *\n * 同一次批量提取(build 时生成 routes/tools/agents 产物)中,所有向上查找到\n * 同一个 tsconfig.json 的文件共用**同一个 Program**——rootNames 为\n * tsconfig fileNames ∪ 全部入口文件,跨文件类型解析语义与单文件 `createProgram`\n * 完全一致,但 N 个文件只创建 1 个 Program(原来每个文件都全量解析一遍项目源码,\n * 是 build 时间的最大单项开销)。\n *\n * 缓存 key 为 `shared::<tsconfigPath>`(同一 tsconfig 一份 Program):同一批次重复\n * 调用命中缓存;不同批次通过 `program.getSourceFile` 校验缓存已覆盖本次全部入口文件,\n * 未覆盖(入口在 tsconfig include 之外)时重建。若把文件列表纳入 key,dev 按需模式下\n * 每个路由文件会各自持有一份全项目 Program(内存 O(路由数 × 项目大小))。\n * `invalidateProgramCache()` 同时清理共享缓存与单文件缓存。\n *\n * 向上查找不到 tsconfig.json 的文件(如 os.tmpdir() 测试场景)逐个回退到\n * {@link createProgram} 单文件行为,不参与共享。\n *\n * @param filePaths 要分析的 .ts 文件绝对路径列表(内部去重)\n * @returns filePath → Program 映射(包含全部请求文件)\n */\nexport function createPrograms(filePaths: string[]): Map<string, ts.Program> {\n const unique = [...new Set(filePaths)];\n const result = new Map<string, ts.Program>();\n\n // 按向上查找到的 tsconfig.json 分组(同组共享一个 Program)\n const groups = new Map<string, { tsconfigPath: string; files: string[] }>();\n const noTsconfigFiles: string[] = [];\n for (const filePath of unique) {\n const tsconfigPath = findTsConfig(filePath);\n if (!tsconfigPath) {\n noTsconfigFiles.push(filePath);\n continue;\n }\n const group = groups.get(tsconfigPath);\n if (group) {\n group.files.push(filePath);\n } else {\n groups.set(tsconfigPath, { tsconfigPath, files: [filePath] });\n }\n }\n\n for (const { tsconfigPath, files } of groups.values()) {\n // 缓存 key 只含 tsconfigPath:buildProgram 的 rootNames = 入口 ∪ tsconfig 全部\n // fileNames,同一 tsconfig 下不同入口批次的 Program 内容几乎一致——若把文件列表\n // 纳入 key,dev 按需模式下每个路由文件会各自持有一份\"全项目 Program\"\n // (内存 O(路由数 × 项目大小),无淘汰)。改为按 tsconfig 共享一份,通过\n // getSourceFile 校验缓存里的 Program 覆盖了本次全部入口文件(入口在 tsconfig\n // include 之外的场景重建,保证不缺文件)\n const cacheKey = `shared::${tsconfigPath}`;\n let program = programCache.get(cacheKey);\n const coversAll =\n program !== undefined && files.every((f) => program!.getSourceFile(f) !== undefined);\n if (!program || !coversAll) {\n program = buildProgram(files, tsconfigPath);\n programCache.set(cacheKey, program);\n }\n for (const filePath of files) {\n result.set(filePath, program);\n }\n }\n\n // 无 tsconfig 的文件回退单文件行为(保持与 createProgram 一致)\n for (const filePath of noTsconfigFiles) {\n result.set(filePath, createProgram(filePath));\n }\n\n return result;\n}\n\n/**\n * 构建 Program:合并框架默认 options 与 tsconfig 的 module / moduleResolution,\n * rootNames = 入口文件 ∪ tsconfig fileNames\n *\n * 由 {@link createProgram}(单文件)和 {@link createPrograms}(批量共享)共用,\n * 保证两条路径的 Program 构建语义一致。\n */\nfunction buildProgram(entryFiles: string[], tsconfigPath: string | null): ts.Program {\n // 默认选项:与项目历史行为一致(NodeNext),保证向后兼容\n const options: ts.CompilerOptions = {\n strict: true,\n target: ts.ScriptTarget.ES2022,\n module: ts.ModuleKind.NodeNext,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n skipLibCheck: true,\n noEmit: true,\n };\n\n // rootNames 默认只包含入口文件;读 tsconfig 后扩展为全部相关文件\n const rootNames = [...entryFiles];\n\n if (tsconfigPath) {\n const tsOptions = parseTsConfig(tsconfigPath);\n if (tsOptions.module !== undefined) {\n options.module = tsOptions.module;\n }\n if (tsOptions.moduleResolution !== undefined) {\n options.moduleResolution = tsOptions.moduleResolution;\n }\n if (tsOptions.fileNames.length > 0) {\n // 确保入口文件在 rootNames 中(即使 tsconfig include 未覆盖到)\n for (const fileName of tsOptions.fileNames) {\n if (!rootNames.includes(fileName)) {\n rootNames.push(fileName);\n }\n }\n }\n }\n\n return ts.createProgram(rootNames, options);\n}\n","import ts from 'typescript';\n\n/**\n * 模块级 Program 上下文,供 `resolveImportAlias` 兜底遍历跨文件声明使用\n *\n * TypeScript 的 `TypeChecker` 类型声明了 `getProgram(): Program` 方法,但\n * 运行时实例未暴露该方法(实测 `typeof checker.getProgram === 'undefined'`)。\n * 因此采用模块级变量在调用入口设置当前 program,resolveImportAlias 兜底时读取。\n *\n * 调用入口(`extractTypeInfo` / `extractAllTypes`)在分析前调\n * `setProgramContext(program)`,分析结束后调 `setProgramContext(null)` 清空。\n *\n * 串行场景安全:faapi 的 AST 提取在单线程顺序执行,无并发。\n */\nlet currentProgram: ts.Program | null = null;\n\n/**\n * 设置当前 Program 上下文(供 resolveImportAlias 兜底遍历跨文件声明使用)\n *\n * @param program 当前 program;分析结束后传 null 清空\n */\nexport function setProgramContext(program: ts.Program | null): void {\n currentProgram = program;\n}\n\n/**\n * Schema 提取错误\n *\n * 遇到无法解析或不支持运行时校验的类型时抛出,\n * 避免静默降级为 any 导致用户不知情。\n */\n/** 错误在源文件中的位置(file:line:column,便于在几百行类型文件中定位) */\nexport interface SchemaErrorLocation {\n file: string;\n line: number;\n column: number;\n}\n\nexport class SchemaExtractionError extends Error {\n constructor(\n public readonly typeText: string,\n public readonly reason: string,\n options?: ErrorOptions,\n public readonly location?: SchemaErrorLocation,\n ) {\n super(\n `无法解析类型 \"${typeText}\": ${reason}` +\n (location ? ` (${location.file}:${location.line}:${location.column})` : ''),\n options,\n );\n this.name = 'SchemaExtractionError';\n }\n\n /**\n * 从 AST 节点构造错误(自动携带 file:line:column)\n *\n * 所有抛错点应优先使用此工厂——错误无行号时,几百行的类型文件只能靠\n * 类型名肉眼定位;解析 lib.d.ts 类型别名时还会出现错误文本与文件上下文错位\n */\n static at(node: ts.Node, typeText: string, reason: string): SchemaExtractionError {\n const sourceFile = node.getSourceFile();\n if (!sourceFile) {\n return new SchemaExtractionError(typeText, reason);\n }\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n return new SchemaExtractionError(typeText, reason, undefined, {\n file: sourceFile.fileName,\n line: line + 1,\n column: character + 1,\n });\n }\n}\n\n/**\n * 运行时类型描述\n *\n * 用于校验器在运行时判断值的结构是否符合声明类型。\n * 相比单纯的字符串,能描述数组元素类型、嵌套对象、联合类型等。\n */\nexport type RuntimeType =\n | { kind: 'string' }\n | { kind: 'number' }\n | { kind: 'boolean' }\n | { kind: 'bigint' }\n | { kind: 'null' }\n | { kind: 'undefined' }\n | { kind: 'any' } // 不校验\n | { kind: 'unknown' } // 不校验\n | { kind: 'literal'; value: string | number | boolean } // 字面量\n | { kind: 'array'; element: RuntimeType }\n | { kind: 'tuple'; elements: TupleElement[] }\n | { kind: 'object'; properties: PropertyType[]; catchall?: RuntimeType }\n | { kind: 'union'; members: RuntimeType[] }\n | { kind: 'date' }\n | { kind: 'record'; key: RuntimeType; value: RuntimeType }\n | { kind: 'map'; key: RuntimeType; value: RuntimeType } // Map<K,V>,JSON 序列化为 entries 数组\n | { kind: 'set'; element: RuntimeType } // Set<T>,JSON 序列化为数组\n | { kind: 'ref'; name: string }; // 命名类型引用(支持循环引用)\n\n/**\n * 元组元素类型\n *\n * - optional: 该位置元素可省略(如 `[string, number?]`)\n * - rest: 该位置是剩余元素(如 `[string, ...number[]]`),必须是数组类型,展开后逐个校验\n */\nexport interface TupleElement {\n type: RuntimeType;\n optional: boolean;\n rest: boolean;\n}\n\nexport interface PropertyType {\n name: string;\n type: RuntimeType;\n optional: boolean;\n /**\n * 字段级 JSDoc 约束标签(@max/@min/@maxLength 等)\n *\n * 来自字段 JSDoc 注释,由 generateZodSchema 转为 zod 链式调用。\n * 约束与字段类型不匹配时在提取阶段抛 SchemaExtractionError。\n */\n constraints?: TypeConstraint[];\n}\n\n/**\n * JSDoc 约束标签的运行时描述\n *\n * 由字段 JSDoc 注释提取,对应 zod schema 的链式约束方法。\n * 仅在 PropertyType.constraints 中出现,不挂在嵌套类型(array 元素、tuple 元素等)上。\n */\nexport type TypeConstraint =\n // 数值约束(仅 number 字段)\n | { kind: 'max'; value: number }\n | { kind: 'min'; value: number }\n | { kind: 'int' }\n | { kind: 'positive' }\n | { kind: 'negative' }\n | { kind: 'nonnegative' }\n | { kind: 'nonpositive' }\n // 长度约束(string 或 array 字段)\n | { kind: 'maxLength'; value: number }\n | { kind: 'minLength'; value: number }\n | { kind: 'length'; value: number }\n // 字符串格式约束(仅 string 字段)\n | { kind: 'regex'; pattern: string; flags?: string }\n | { kind: 'email' }\n | { kind: 'url' }\n | { kind: 'uuid' };\n\n/**\n * 将 TypeScript 类型节点解析为运行时类型描述\n *\n * 支持的类型:\n * - 基础类型:string / number / boolean / null / undefined / any / unknown / void\n * - bigint:不支持(HTTP/JSON 不能传输),AST 提取阶段抛 SchemaExtractionError\n * - 字面量类型:'foo' / 42 / true\n * - 数组类型:T[] / Array<T> / ReadonlyArray<T> / readonly T[]\n * - 元组类型:[string, number] / [string, number?] / [string, ...number[]] / readonly [T, U](按位置校验)\n * - 对象类型:{ name: string; age?: number }(含 readonly 字段修饰符,忽略 readonly)\n * - 联合类型:string | null\n * - 交叉类型:A & B(按对象合并处理)\n * - 引用类型:Date / 其他 interface(递归解析)\n * - 工具类型:Record<K, V> / Partial<T> / Readonly<T>(best effort)\n * - Pick<T, K> / Omit<T, K>:K 支持字面量联合、类型别名、keyof T\n *\n * readonly 是编译期约束,运行时不产生校验语义,所有 readonly 修饰符统一忽略。\n *\n * @param typeNode TypeScript 类型节点\n * @param checker 类型 checker(用于解析引用类型)\n * @param visited 防止递归循环\n * @param bindings 泛型形参绑定(形参名 → 已解析的实参类型),解析泛型声明的\n * 类型体时传入;形参名在 resolveTypeReference 入口优先命中,\n * 遮蔽同名的真实类型声明\n */\nexport function resolveTypeNode(\n typeNode: ts.TypeNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n bindings: Map<string, RuntimeType> = new Map(),\n): RuntimeType {\n const kind = typeNode.kind;\n\n // 基础类型\n switch (kind) {\n case ts.SyntaxKind.StringKeyword:\n return { kind: 'string' };\n case ts.SyntaxKind.NumberKeyword:\n return { kind: 'number' };\n case ts.SyntaxKind.BooleanKeyword:\n return { kind: 'boolean' };\n case ts.SyntaxKind.BigIntKeyword:\n // HTTP/JSON 不能传输 BigInt,在 AST 提取阶段就报错,避免运行时校验必然失败\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'bigint 无法通过 HTTP/JSON 传输,请改用 string 或 number',\n );\n case ts.SyntaxKind.SymbolKeyword:\n // HTTP/JSON 不能传输 Symbol\n throw new SchemaExtractionError(typeNode.getText(), 'symbol 无法通过 HTTP/JSON 传输');\n case ts.SyntaxKind.NullKeyword:\n return { kind: 'null' };\n case ts.SyntaxKind.UndefinedKeyword:\n return { kind: 'undefined' };\n case ts.SyntaxKind.UnknownKeyword:\n // unknown 是唯一允许的\"不校验\"显式声明\n return { kind: 'any' };\n case ts.SyntaxKind.AnyKeyword:\n throw new SchemaExtractionError(typeNode.getText(), 'any 不支持,请使用 unknown 表示不校验');\n case ts.SyntaxKind.VoidKeyword:\n throw new SchemaExtractionError(typeNode.getText(), 'void 不支持运行时校验');\n case ts.SyntaxKind.NeverKeyword:\n throw new SchemaExtractionError(typeNode.getText(), 'never 不支持运行时校验');\n case ts.SyntaxKind.ObjectKeyword:\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'object 不支持,请使用具体对象类型或 unknown',\n );\n }\n\n // 字面量类型\n if (ts.isLiteralTypeNode(typeNode)) {\n const literal = typeNode.literal;\n if (ts.isStringLiteral(literal)) {\n return { kind: 'literal', value: literal.text };\n }\n if (ts.isNumericLiteral(literal)) {\n return { kind: 'literal', value: Number(literal.text) };\n }\n if (literal.kind === ts.SyntaxKind.TrueKeyword) {\n return { kind: 'literal', value: true };\n }\n if (literal.kind === ts.SyntaxKind.FalseKeyword) {\n return { kind: 'literal', value: false };\n }\n if (literal.kind === ts.SyntaxKind.NullKeyword) {\n return { kind: 'null' };\n }\n throw new SchemaExtractionError(typeNode.getText(), '不支持的字面量类型');\n }\n\n // 数组类型:T[]\n if (ts.isArrayTypeNode(typeNode)) {\n return {\n kind: 'array',\n element: resolveTypeNode(typeNode.elementType, checker, visited, bindings),\n };\n }\n\n // 元组类型:[string, number] / [string, number?] / [string, ...number[]]\n if (ts.isTupleTypeNode(typeNode)) {\n const elements: TupleElement[] = typeNode.elements.map((e) => {\n // 剩余元素:...T\n if (ts.isRestTypeNode(e)) {\n const inner = resolveTypeNode(e.type, checker, visited, bindings);\n // ...T[] → 元素类型是 T 的数组元素\n if (inner.kind === 'array') {\n return { type: inner.element, optional: false, rest: true };\n }\n // ...T 但 T 不是数组(罕见,TS 通常要求 rest 是数组)\n return { type: inner, optional: false, rest: true };\n }\n // 命名元组成员:[name: string, age?: number]\n if (ts.isNamedTupleMember(e)) {\n return {\n type: resolveTypeNode(e.type, checker, visited, bindings),\n optional: !!e.questionToken,\n rest: false,\n };\n }\n // 可选元素:number?(无名称,有 ?)\n if (ts.isOptionalTypeNode(e)) {\n return {\n type: resolveTypeNode(e.type, checker, visited, bindings),\n optional: true,\n rest: false,\n };\n }\n // 普通元素\n return {\n type: resolveTypeNode(e, checker, visited, bindings),\n optional: false,\n rest: false,\n };\n });\n return { kind: 'tuple', elements };\n }\n\n // 联合类型:A | B\n if (ts.isUnionTypeNode(typeNode)) {\n const members = typeNode.types.map((t) => resolveTypeNode(t, checker, visited, bindings));\n return { kind: 'union', members };\n }\n\n // 交叉类型:A & B → 合并对象属性(全部成员均为 object 时)\n if (ts.isIntersectionTypeNode(typeNode)) {\n const propMap = new Map<string, PropertyType>();\n for (const t of typeNode.types) {\n const resolved = resolveTypeNode(t, checker, visited, bindings);\n if (resolved.kind !== 'object') {\n // branded 类型(string & {...})等混合交叉在运行时无对应校验物——\n // 静默丢弃非 object 成员会放宽校验,按约定显式抛错(见 AGENTS.md §6.3)\n throw new SchemaExtractionError(\n typeNode.getText(),\n `交叉类型包含非 object 成员(${resolved.kind}),运行时无法校验——branded 类型建议改用具体类型或 unknown`,\n );\n }\n for (const prop of resolved.properties) {\n const existing = propMap.get(prop.name);\n if (!existing) {\n propMap.set(prop.name, prop);\n continue;\n }\n // 同名字段:类型或可选性不一致即 TS 语义下的 never——运行时无法校验,\n // 显式抛错;一致则去重(优先保留带约束的声明,更严格)\n if (\n JSON.stringify(existing.type) !== JSON.stringify(prop.type) ||\n existing.optional !== prop.optional\n ) {\n throw SchemaExtractionError.at(\n typeNode,\n typeNode.getText(),\n `交叉类型成员的同名字段 \"${prop.name}\" 类型冲突(TS 中为 never),运行时无法校验`,\n );\n }\n if (!existing.constraints?.length && prop.constraints?.length) {\n propMap.set(prop.name, prop);\n }\n }\n }\n return { kind: 'object', properties: [...propMap.values()] };\n }\n\n // 内联对象类型:{ name: string; age?: number }\n if (ts.isTypeLiteralNode(typeNode)) {\n return resolveTypeLiteral(typeNode, checker, visited, bindings);\n }\n\n // keyof T — 用 checker 解析为字面量联合\n if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.KeyOfKeyword) {\n return resolveKeyOf(typeNode, checker);\n }\n\n // readonly T(如 readonly string[] / readonly [T, U])— 编译期约束,运行时不校验\n // 递归解析内部类型即可,等同于去掉 readonly 修饰符\n if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.ReadonlyKeyword) {\n return resolveTypeNode(typeNode.type, checker, visited, bindings);\n }\n\n // 引用类型:Date / 自定义 interface / Array<T> / Record<K,V> / Partial<T> 等\n if (ts.isTypeReferenceNode(typeNode)) {\n return resolveTypeReference(typeNode, checker, visited, bindings);\n }\n\n // interface extends 的 heritage 子句(ExpressionWithTypeArguments)\n // 构造 TypeRefLike 视图(expression → typeName),复用 resolveTypeReference\n // 解析(覆盖 Date / Array<T> / 自定义 interface 等全部分支)\n if (ts.isExpressionWithTypeArguments(typeNode)) {\n return resolveTypeReference(\n {\n getText: () => typeNode.getText(),\n typeName: typeNode.expression as ts.EntityName,\n typeArguments: typeNode.typeArguments,\n },\n checker,\n visited,\n bindings,\n );\n }\n\n // 其他无法识别的语法节点\n throw SchemaExtractionError.at(typeNode, typeNode.getText(), '不支持的类型语法');\n}\n\n/**\n * 解析内联对象类型字面量\n */\nfunction resolveTypeLiteral(\n typeNode: ts.TypeLiteralNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n bindings: Map<string, RuntimeType> = new Map(),\n): RuntimeType {\n const properties: PropertyType[] = [];\n // 索引签名([k: string]: T)与属性共存合法:属性保留,索引签名转为 catchall\n let catchall: RuntimeType | undefined;\n\n for (const member of typeNode.members) {\n // 方法签名/存取器:运行时 JSON 数据无方法语义,静默丢弃会弱化校验——显式抛错\n if (\n ts.isMethodSignature(member) ||\n ts.isGetAccessorDeclaration(member) ||\n ts.isSetAccessorDeclaration(member)\n ) {\n throw SchemaExtractionError.at(\n member,\n member.getText(),\n '对象类型含方法签名或存取器,运行时 JSON 数据无法校验方法——请改用具体属性类型',\n );\n }\n // 属性签名:name: string\n if (ts.isPropertySignature(member) && member.name) {\n const name = member.name.getText();\n const optional = !!member.questionToken;\n const type = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n const constraints = extractConstraintsFromJsDoc(member, name);\n validateConstraints(constraints, type, name);\n properties.push(\n constraints.length > 0 ? { name, type, optional, constraints } : { name, type, optional },\n );\n }\n // 索引签名:[key: string]: T → catchall(与属性共存时不丢弃属性)\n if (ts.isIndexSignatureDeclaration(member)) {\n catchall = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n }\n }\n\n return catchall !== undefined\n ? { kind: 'object', properties, catchall }\n : { kind: 'object', properties };\n}\n\n/**\n * 从 RuntimeType 提取字面量字符串集合\n * 用于 Pick<T, K> / Omit<T, K> 的 K 参数解析\n *\n * @returns 字面量值数组;无法解析时返回 null\n */\nfunction extractLiteralKeys(type: RuntimeType): string[] | null {\n if (type.kind === 'literal' && typeof type.value === 'string') {\n return [type.value];\n }\n if (type.kind === 'union') {\n const keys: string[] = [];\n for (const member of type.members) {\n if (member.kind === 'literal' && typeof member.value === 'string') {\n keys.push(member.value);\n } else {\n return null;\n }\n }\n return keys;\n }\n return null;\n}\n\n/**\n * 用 checker 解析 K 的最终类型,提取字面量字符串集合\n *\n * 用于 Pick<T, K> / Omit<T, K> 的 K 参数解析,覆盖 AST 无法直接拿到的场景:\n * - K 为类型别名(type Keys = 'id' | 'name')\n * - K 为 keyof T\n *\n * @returns 字面量值数组;无法解析时返回 null\n */\nfunction extractKeysFromChecker(typeNode: ts.TypeNode, checker?: ts.TypeChecker): string[] | null {\n if (!checker) return null;\n const type = checker.getTypeFromTypeNode(typeNode);\n\n if (type.isUnion()) {\n const keys: string[] = [];\n for (const member of type.types) {\n if (member.isStringLiteral()) {\n keys.push(member.value);\n } else if (member.isNumberLiteral()) {\n keys.push(String(member.value));\n } else {\n return null;\n }\n }\n return keys;\n }\n\n if (type.isStringLiteral()) {\n return [type.value];\n }\n if (type.isNumberLiteral()) {\n return [String(type.value)];\n }\n\n return null;\n}\n\n/**\n * 解析 keyof T — 用 checker 计算最终类型,返回字面量联合\n *\n * 例如 keyof { id: number; name: string } → 'id' | 'name'\n */\nfunction resolveKeyOf(typeNode: ts.TypeOperatorNode, checker?: ts.TypeChecker): RuntimeType {\n if (!checker) {\n throw new SchemaExtractionError(typeNode.getText(), 'keyof T 需要 checker 才能解析');\n }\n const type = checker.getTypeFromTypeNode(typeNode);\n\n if (type.isUnion()) {\n const members: RuntimeType[] = [];\n for (const member of type.types) {\n if (member.isStringLiteral()) {\n members.push({ kind: 'literal', value: member.value });\n } else if (member.isNumberLiteral()) {\n members.push({ kind: 'literal', value: member.value });\n } else {\n throw new SchemaExtractionError(typeNode.getText(), 'keyof T 的结果包含非字面量类型');\n }\n }\n return { kind: 'union', members };\n }\n\n if (type.isStringLiteral()) {\n return { kind: 'literal', value: type.value };\n }\n if (type.isNumberLiteral()) {\n return { kind: 'literal', value: type.value };\n }\n\n throw new SchemaExtractionError(typeNode.getText(), 'keyof T 的结果无法解析为字面量联合');\n}\n\n/**\n * resolveTypeReference 所需的最小节点结构\n *\n * TypeReferenceNode 与 interface extends 的 heritage 节点(ExpressionWithTypeArguments)\n * 均满足该结构——后者用 expression 充当 typeName,typeArguments 同名。\n */\ninterface TypeRefLike {\n getText(): string;\n typeName: ts.EntityName | ts.LeftHandSideExpression;\n typeArguments?: readonly ts.TypeNode[];\n}\n\n/**\n * 解析类型引用(Date / 自定义 interface / Array<T> / Record<K,V> 等)\n *\n * 泛型形参优先:typeName 命中 bindings 时直接返回绑定的实参类型\n * (泛型声明体内部引用形参的场景,形参遮蔽同名真实类型)。\n */\nfunction resolveTypeReference(\n typeNode: TypeRefLike,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n bindings: Map<string, RuntimeType> = new Map(),\n): RuntimeType {\n const typeName = typeNode.typeName.getText();\n\n // 泛型形参命中:直接返回已绑定的实参类型\n const bound = bindings.get(typeName);\n if (bound) {\n return bound;\n }\n\n // Date 类型\n if (typeName === 'Date') {\n return { kind: 'date' };\n }\n\n // Array<T> / ReadonlyArray<T> — 等同处理,readonly 是编译期约束\n if (\n (typeName === 'Array' || typeName === 'ReadonlyArray') &&\n typeNode.typeArguments?.length === 1\n ) {\n const [arg] = typeNode.typeArguments;\n return {\n kind: 'array',\n element: resolveTypeNode(arg!, checker, visited, bindings),\n };\n }\n\n // Record<K, V>\n if (typeName === 'Record' && typeNode.typeArguments?.length === 2) {\n const [keyArg, valueArg] = typeNode.typeArguments;\n return {\n kind: 'record',\n key: resolveTypeNode(keyArg!, checker, visited, bindings),\n value: resolveTypeNode(valueArg!, checker, visited, bindings),\n };\n }\n\n // Partial<T> / Required<T> / Readonly<T> — 解析内部类型\n if (\n (typeName === 'Partial' || typeName === 'Required' || typeName === 'Readonly') &&\n typeNode.typeArguments?.length === 1\n ) {\n const inner = resolveTypeNode(typeNode.typeArguments[0]!, checker, visited, bindings);\n if (inner.kind === 'object' && typeName === 'Partial') {\n // Partial 所有字段变可选\n return {\n kind: 'object',\n properties: inner.properties.map((p) => ({ ...p, optional: true })),\n };\n }\n if (inner.kind === 'object' && typeName === 'Required') {\n // Required 恢复所有字段为必填——静默保留 optional 会让校验弱于 TS 类型\n return {\n kind: 'object',\n properties: inner.properties.map((p) => ({ ...p, optional: false })),\n };\n }\n // Readonly:编译期约束,运行时无校验语义,原样返回(等同去掉 readonly 修饰符)\n return inner;\n }\n\n // Pick<T, K> / Omit<T, K> — 解析 T 的字段,按 K 筛选/排除\n if ((typeName === 'Pick' || typeName === 'Omit') && typeNode.typeArguments?.length === 2) {\n const innerType = resolveTypeNode(typeNode.typeArguments[0]!, checker, visited, bindings);\n if (innerType.kind !== 'object') {\n throw new SchemaExtractionError(\n typeNode.getText(),\n `${typeName} 的 T 必须是对象类型,实际为 ${innerType.kind}`,\n );\n }\n\n // K 解析顺序:AST 字面量联合 → checker 解析(覆盖类型别名 / keyof T)\n const keyTypeNode = typeNode.typeArguments[1]!;\n let keys = extractLiteralKeys(resolveTypeNode(keyTypeNode, checker, visited, bindings));\n if (keys === null) {\n keys = extractKeysFromChecker(keyTypeNode, checker);\n }\n if (keys === null) {\n throw new SchemaExtractionError(typeNode.getText(), `${typeName} 的 K 无法解析为字面量集合`);\n }\n\n const keySet = new Set(keys);\n const properties =\n typeName === 'Pick'\n ? innerType.properties.filter((p) => keySet.has(p.name))\n : innerType.properties.filter((p) => !keySet.has(p.name));\n\n return { kind: 'object', properties };\n }\n\n // Map<K, V> — JSON 序列化为 entries 数组,运行时用 z.preprocess(coerceMap, z.map(...)) 还原\n if (typeName === 'Map') {\n if (!typeNode.typeArguments || typeNode.typeArguments.length !== 2) {\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'Map 必须带 2 个类型参数,如 Map<K, V>,裸 Map 不支持',\n );\n }\n const [mapKey, mapValue] = typeNode.typeArguments;\n return {\n kind: 'map',\n key: resolveTypeNode(mapKey!, checker, visited, bindings),\n value: resolveTypeNode(mapValue!, checker, visited, bindings),\n };\n }\n\n // Set<T> — JSON 序列化为数组,运行时用 z.preprocess(coerceSet, z.set(...)) 还原\n if (typeName === 'Set') {\n if (!typeNode.typeArguments || typeNode.typeArguments.length !== 1) {\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'Set 必须带 1 个类型参数,如 Set<T>,裸 Set 不支持',\n );\n }\n const [setArg] = typeNode.typeArguments;\n return {\n kind: 'set',\n element: resolveTypeNode(setArg!, checker, visited, bindings),\n };\n }\n\n // WeakMap / WeakSet — 运行时无法枚举校验,仍然抛错(Map/Set 不复用其语义)\n if (typeName === 'WeakMap' || typeName === 'WeakSet') {\n throw new SchemaExtractionError(\n typeNode.getText(),\n `${typeName} 运行时无法枚举校验,请改用 Map / Set 或对象`,\n );\n }\n\n // Promise<T> — 运行时无法校验异步值\n if (typeName === 'Promise') {\n throw new SchemaExtractionError(\n typeNode.getText(),\n 'Promise 运行时无法校验,请勿在 query/body 类型中使用',\n );\n }\n\n // Function — HTTP/JSON 不能传输函数\n if (typeName === 'Function') {\n throw new SchemaExtractionError(typeNode.getText(), 'Function 无法通过 HTTP/JSON 传输');\n }\n\n // 防止递归循环:遇到已访问的类型返回 ref,支持循环引用\n if (visited.has(typeName)) {\n return { kind: 'ref', name: typeName };\n }\n visited.add(typeName);\n\n // 使用 checker 解析引用类型(interface / type 别名)\n if (checker) {\n const symbol =\n ts.isIdentifier(typeNode.typeName) || ts.isQualifiedName(typeNode.typeName)\n ? checker.getSymbolAtLocation(typeNode.typeName)\n : undefined;\n\n if (symbol) {\n const declaration = symbol.declarations?.[0];\n if (declaration) {\n // interface 声明\n if (ts.isInterfaceDeclaration(declaration)) {\n return resolveInterfaceDeclaration(\n declaration,\n checker,\n visited,\n bindings,\n typeNode.typeArguments,\n );\n }\n // type 别名声明\n if (ts.isTypeAliasDeclaration(declaration)) {\n const declBindings = bindTypeParameters(\n declaration.typeParameters,\n typeNode.typeArguments,\n bindings,\n checker,\n visited,\n typeNode,\n );\n return resolveTypeNode(declaration.type, checker, visited, declBindings);\n }\n // enum 声明 → 字面量联合\n if (ts.isEnumDeclaration(declaration)) {\n return resolveEnumDeclaration(declaration);\n }\n // import 别名(跨文件 import type):跟到真实声明\n // 业务项目用 moduleResolution: Bundler + 无扩展名导入时,\n // checker 拿到的 symbol 是 ImportSpecifier(Alias),不是真实声明\n if (ts.isImportSpecifier(declaration) || ts.isImportClause(declaration)) {\n const resolved = resolveImportAlias(\n typeNode,\n symbol,\n checker,\n visited,\n bindings,\n typeNode.typeArguments,\n );\n if (resolved) return resolved;\n }\n }\n }\n }\n\n throw new SchemaExtractionError(typeNode.getText(), `无法解析的引用类型 \"${typeName}\"`);\n}\n\n/**\n * 绑定泛型形参与实参,返回声明体解析用的 bindings\n *\n * - 实参表达式在调用方作用域解析(用 outerBindings,可引用外层形参)\n * - 默认类型在声明方作用域解析(用正在构造的 bindings,可引用前面形参,如 `<T, U = T[]>`)\n * - 实参与默认类型均缺失时抛 SchemaExtractionError(TS 本身也编译不过,防御性报错)\n * - 声明无 typeParameters 时原样返回 outerBindings\n */\nfunction bindTypeParameters(\n typeParameters: readonly ts.TypeParameterDeclaration[] | undefined,\n typeArguments: readonly ts.TypeNode[] | undefined,\n outerBindings: Map<string, RuntimeType>,\n checker: ts.TypeChecker | undefined,\n visited: Set<string>,\n errorNode: { getText(): string },\n): Map<string, RuntimeType> {\n if (!typeParameters || typeParameters.length === 0) return outerBindings;\n\n const bindings = new Map(outerBindings);\n for (let i = 0; i < typeParameters.length; i++) {\n const param = typeParameters[i];\n if (!param) continue;\n const arg = typeArguments?.[i];\n if (arg) {\n bindings.set(param.name.text, resolveTypeNode(arg, checker, visited, outerBindings));\n } else if (param.default) {\n bindings.set(param.name.text, resolveTypeNode(param.default, checker, visited, bindings));\n } else {\n throw new SchemaExtractionError(\n errorNode.getText(),\n `泛型参数 \"${param.name.text}\" 缺少类型实参(且无默认类型)`,\n );\n }\n }\n return bindings;\n}\n\n/**\n * 解析 import 别名符号,跟到真实的 interface / type alias / enum 声明\n *\n * 业务项目用 `moduleResolution: Bundler` + 无扩展名相对导入(如\n * `import type { StyleGuide } from '../../db/schema'`)时,checker 拿到的 symbol\n * 是 `ImportSpecifier`(Alias symbol),不是真实声明节点。\n *\n * 解析顺序:\n * 1. `checker.getAliasedSymbol(symbol)` — 优先路径,直接跟到真实 symbol\n * 2. 兜底:遍历 program 的所有 sourceFiles 找名为 `typeName` 的\n * `InterfaceDeclaration` / `TypeAliasDeclaration` / `EnumDeclaration`\n *\n * 兜底路径用于 `getAliasedSymbol` 在 `noEmit` 模式下未完全绑定 alias 的场景\n * (实测:即使业务项目 tsconfig 用 Bundler + program 加载了全部相关文件,\n * `getAliasedSymbol` 也可能返回 unknown / 无 declarations)。\n *\n * @returns RuntimeType 解析结果;无法解析返回 null(由调用方抛 SchemaExtractionError)\n */\nfunction resolveImportAlias(\n typeNode: TypeRefLike,\n symbol: ts.Symbol,\n checker: ts.TypeChecker,\n visited: Set<string>,\n bindings: Map<string, RuntimeType> = new Map(),\n typeArguments?: readonly ts.TypeNode[],\n): RuntimeType | null {\n const typeName = typeNode.typeName.getText();\n\n // 路径1:getAliasedSymbol 直接跟到真实 symbol\n try {\n const aliased = checker.getAliasedSymbol(symbol);\n if (aliased && aliased.declarations && aliased.declarations.length > 0) {\n const decl = aliased.declarations[0]!;\n if (ts.isInterfaceDeclaration(decl)) {\n return resolveInterfaceDeclaration(decl, checker, visited, bindings, typeArguments);\n }\n if (ts.isTypeAliasDeclaration(decl)) {\n const declBindings = bindTypeParameters(\n decl.typeParameters,\n typeArguments,\n bindings,\n checker,\n visited,\n typeNode,\n );\n return resolveTypeNode(decl.type, checker, visited, declBindings);\n }\n if (ts.isEnumDeclaration(decl)) {\n return resolveEnumDeclaration(decl);\n }\n }\n } catch {\n // getAliasedSymbol 抛错时走兜底\n }\n\n // 路径2:兜底遍历 program 的 sourceFiles 找同名声明\n // 用模块级变量 currentProgram(createProgram 时通过 setProgramContext 设置)\n // TypeChecker 类型上声明了 getProgram() 但运行时未暴露,只能通过外部传入\n const program = currentProgram;\n if (!program) return null;\n\n const allSFs = program.getSourceFiles();\n for (const sourceFile of allSFs) {\n // 跳过 lib.d.ts / node_modules / TypeScript 内置文件\n if (\n sourceFile.fileName.includes('/node_modules/') ||\n sourceFile.fileName.includes('typescript/lib/')\n ) {\n continue;\n }\n // 顶层声明查找(用独立函数返回,避免 TS 控制流分析把闭包赋值的 let 变量收窄为 never)\n const found = findTopLevelDecl(sourceFile, typeName);\n if (found) {\n if (found.kind === 'interface') {\n return resolveInterfaceDeclaration(found.node, checker, visited, bindings, typeArguments);\n }\n if (found.kind === 'typeAlias') {\n const declBindings = bindTypeParameters(\n found.node.typeParameters,\n typeArguments,\n bindings,\n checker,\n visited,\n typeNode,\n );\n return resolveTypeNode(found.node.type, checker, visited, declBindings);\n }\n if (found.kind === 'enum') {\n return resolveEnumDeclaration(found.node);\n }\n }\n }\n\n return null;\n}\n\n/**\n * 在 sourceFile 顶层查找名为 typeName 的 interface / type alias / enum 声明\n *\n * 独立为函数是因为 TS 控制流分析对\"闭包中赋值的 let 变量\"保守处理:\n * 即使 forEachChild 同步调用回调,分析器仍认为赋值可能未发生,\n * 导致外层读取时变量被收窄为初始值类型(null)。\n * 用函数返回值(带显式返回类型)绕过此限制。\n */\nexport type TopLevelDecl =\n | { kind: 'interface'; node: ts.InterfaceDeclaration }\n | { kind: 'typeAlias'; node: ts.TypeAliasDeclaration }\n | { kind: 'enum'; node: ts.EnumDeclaration };\n\nexport function findTopLevelDecl(sourceFile: ts.SourceFile, typeName: string): TopLevelDecl | null {\n let found: TopLevelDecl | null = null;\n ts.forEachChild(sourceFile, (node) => {\n if (found) return;\n if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {\n found = { kind: 'interface', node };\n } else if (ts.isTypeAliasDeclaration(node) && node.name.text === typeName) {\n found = { kind: 'typeAlias', node };\n } else if (ts.isEnumDeclaration(node) && node.name.text === typeName) {\n found = { kind: 'enum', node };\n }\n });\n return found;\n}\n\n/**\n * 解析 enum 声明为字面量联合类型\n *\n * - 字符串枚举:`enum Role { Admin = 'admin' }` → `'admin' | ...`\n * - 数值枚举:`enum Code { OK = 200 }` → `200 | ...`\n * - 隐式数值枚举:`enum Dir { Up, Down }` → `0 | 1 | ...`\n *\n * HTTP 视角:枚举值在 JSON 中是普通 string/number,按字面量联合校验即可。\n */\nexport function resolveEnumDeclaration(node: ts.EnumDeclaration): RuntimeType {\n const members: RuntimeType[] = [];\n let nextNumericValue = 0;\n\n for (const member of node.members) {\n if (member.initializer) {\n if (ts.isStringLiteral(member.initializer)) {\n members.push({ kind: 'literal', value: member.initializer.text });\n } else if (ts.isNumericLiteral(member.initializer)) {\n const num = Number(member.initializer.text);\n members.push({ kind: 'literal', value: num });\n nextNumericValue = num + 1;\n } else {\n throw new SchemaExtractionError(\n node.name.text,\n `enum 成员 \"${member.name.getText()}\" 的初始化值类型不支持,仅支持 string/number 字面量`,\n );\n }\n } else {\n // 隐式数值枚举:无初始化值,使用递增整数\n members.push({ kind: 'literal', value: nextNumericValue });\n nextNumericValue++;\n }\n }\n\n return { kind: 'union', members };\n}\n\n/**\n * 解析 interface 声明(含继承与泛型形参绑定)\n *\n * @param node interface 声明节点\n * @param checker 类型 checker\n * @param visited 防止递归循环\n * @param outerBindings 外层泛型形参绑定(调用方作用域)\n * @param typeArguments 本次引用携带的类型实参(如 `Base<string>` 的 [string]),\n * 与 node.typeParameters 按位置配对构造声明体 bindings\n */\nexport function resolveInterfaceDeclaration(\n node: ts.InterfaceDeclaration,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n outerBindings: Map<string, RuntimeType> = new Map(),\n typeArguments?: readonly ts.TypeNode[],\n): RuntimeType {\n const properties: PropertyType[] = [];\n const propMap = new Map<string, PropertyType>();\n // 索引签名([k: string]: T)与属性共存合法:属性保留,索引签名转为 catchall\n let catchall: RuntimeType | undefined;\n\n // 绑定本声明的泛型形参(声明体与 heritage 均在该作用域下解析)\n const bindings = bindTypeParameters(\n node.typeParameters,\n typeArguments,\n outerBindings,\n checker,\n visited,\n node,\n );\n\n // 处理继承的父接口\n for (const heritageClause of node.heritageClauses ?? []) {\n if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword) {\n for (const expr of heritageClause.types) {\n const parentType = resolveTypeNode(expr, checker, visited, bindings);\n if (parentType.kind === 'object') {\n for (const prop of parentType.properties) {\n propMap.set(prop.name, prop);\n }\n }\n }\n }\n }\n\n // 处理自身成员(覆盖继承的同名字段)\n for (const member of node.members) {\n // 方法签名/存取器:运行时 JSON 数据无方法语义,静默丢弃会弱化校验——显式抛错\n if (\n ts.isMethodSignature(member) ||\n ts.isGetAccessorDeclaration(member) ||\n ts.isSetAccessorDeclaration(member)\n ) {\n throw SchemaExtractionError.at(\n member,\n member.getText(),\n '接口含方法签名或存取器,运行时 JSON 数据无法校验方法——请改用具体属性类型',\n );\n }\n if (ts.isPropertySignature(member) && member.name) {\n const name = member.name.getText();\n const optional = !!member.questionToken;\n const type = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n const constraints = extractConstraintsFromJsDoc(member, name);\n validateConstraints(constraints, type, name);\n propMap.set(\n name,\n constraints.length > 0 ? { name, type, optional, constraints } : { name, type, optional },\n );\n }\n // 索引签名 → catchall(与属性/继承属性共存时不丢弃)\n if (ts.isIndexSignatureDeclaration(member)) {\n catchall = member.type\n ? resolveTypeNode(member.type, checker, visited, bindings)\n : { kind: 'any' as const };\n }\n }\n\n for (const prop of propMap.values()) {\n properties.push(prop);\n }\n\n return catchall !== undefined\n ? { kind: 'object', properties, catchall }\n : { kind: 'object', properties };\n}\n\n/**\n * 数值约束标签集合(仅 number 字段)\n */\nconst NUMBER_CONSTRAINT_KINDS = new Set<TypeConstraint['kind']>([\n 'max',\n 'min',\n 'int',\n 'positive',\n 'negative',\n 'nonnegative',\n 'nonpositive',\n]);\n\n/**\n * 长度约束标签集合(string 或 array 字段)\n */\nconst LENGTH_CONSTRAINT_KINDS = new Set<TypeConstraint['kind']>([\n 'maxLength',\n 'minLength',\n 'length',\n]);\n\n/**\n * 字符串格式约束标签集合(仅 string 字段)\n */\nconst STRING_FORMAT_CONSTRAINT_KINDS = new Set<TypeConstraint['kind']>([\n 'regex',\n 'email',\n 'url',\n 'uuid',\n]);\n\n/**\n * 从字段的 JSDoc 注释中提取约束标签\n *\n * 解析 `/** ... *\\/` 块注释中的 `@max`/`@min`/`@maxLength` 等标签。\n * 行内注释 `// @max 100` 不识别。\n *\n * @param node 字段节点(PropertySignature)\n * @param fieldName 字段名,用于错误信息\n * @returns 约束数组(无约束时返回空数组)\n */\nfunction extractConstraintsFromJsDoc(\n node: ts.PropertySignature,\n fieldName: string,\n): TypeConstraint[] {\n // ts.getJSDocCommentsAndTags 返回 JSDoc/JsDocTag 数组\n const jsDocs = ts\n .getJSDocCommentsAndTags(node)\n .filter((entry): entry is ts.JSDoc => ts.isJSDoc(entry));\n\n if (jsDocs.length === 0) return [];\n\n const constraints: TypeConstraint[] = [];\n\n for (const jsDoc of jsDocs) {\n if (!jsDoc.tags) continue;\n for (const tag of jsDoc.tags) {\n const constraint = parseJsDocTag(tag, fieldName);\n if (constraint) constraints.push(constraint);\n }\n }\n\n return constraints;\n}\n\n/**\n * 提取 JSDoc 标签后的文本注释\n *\n * `tag.comment` 类型为 `string | NodeArray<JSDocComment> | undefined`,\n * 此处仅取纯文本形式(约束标签后跟的均为字面量值,不会包含 JSDocLink 等节点)。\n *\n * @returns 标签后的文本;无文本或非纯字符串时返回 undefined\n */\nfunction getTagCommentText(tag: ts.JSDocTag): string | undefined {\n const comment = tag.comment;\n if (typeof comment === 'string') return comment;\n return undefined;\n}\n\n/**\n * 解析单个 JSDoc 标签为约束\n *\n * @param tag JSDocTag 节点\n * @param fieldName 字段名,用于错误信息\n * @returns 约束对象;非约束标签返回 null\n */\nfunction parseJsDocTag(tag: ts.JSDocTag, fieldName: string): TypeConstraint | null {\n const tagName = tag.tagName.text;\n\n switch (tagName) {\n // 数值约束(带值)\n case 'max':\n case 'min': {\n const value = parseNumberValue(tag, fieldName, tagName);\n return { kind: tagName, value };\n }\n\n // 长度约束(带值)\n case 'maxLength':\n case 'minLength':\n case 'length': {\n const value = parseNumberValue(tag, fieldName, tagName);\n return { kind: tagName, value };\n }\n\n // 正则约束(带 /pattern/flags 值)\n case 'regex':\n case 'pattern': {\n const text = getTagCommentText(tag);\n if (!text) {\n throw new SchemaExtractionError(fieldName, `@${tagName} 标签需要 /pattern/flags 形式的值`);\n }\n const regex = parseRegexLiteral(text.trim(), fieldName);\n return { kind: 'regex', pattern: regex.pattern, flags: regex.flags };\n }\n\n // 数值约束(无值)\n case 'int':\n case 'positive':\n case 'negative':\n case 'nonnegative':\n case 'nonpositive':\n return { kind: tagName };\n\n // 字符串格式约束(无值)\n case 'email':\n case 'url':\n case 'uuid':\n return { kind: tagName };\n\n default:\n return null;\n }\n}\n\n/**\n * 解析标签后的数字值\n *\n * @param tag JSDocTag 节点\n * @param fieldName 字段名,用于错误信息\n * @param tagName 标签名,用于错误信息\n */\nfunction parseNumberValue(tag: ts.JSDocTag, fieldName: string, tagName: string): number {\n const text = getTagCommentText(tag);\n if (!text) {\n throw new SchemaExtractionError(fieldName, `@${tagName} 标签需要一个数字值`);\n }\n const trimmed = text.trim();\n const num = Number(trimmed);\n if (!Number.isFinite(num)) {\n throw new SchemaExtractionError(fieldName, `@${tagName} 标签的值 \"${trimmed}\" 不是有效数字`);\n }\n return num;\n}\n\n/**\n * 解析 /pattern/flags 形式的正则字面量\n *\n * @param text 标签后的文本,如 \"/^[a-z]+$/i\"\n * @param fieldName 字段名,用于错误信息\n */\nfunction parseRegexLiteral(text: string, fieldName: string): { pattern: string; flags?: string } {\n // 必须以 / 开头,匹配结尾的 / 和可选 flags\n const match = /^\\/(.+)\\/([gimsuy]*)$/.exec(text);\n if (!match) {\n throw new SchemaExtractionError(fieldName, `正则值 \"${text}\" 不是 /pattern/flags 形式`);\n }\n const [, pattern, flags] = match;\n if (!pattern) {\n throw new SchemaExtractionError(fieldName, `正则值 \"${text}\" 的 pattern 部分为空`);\n }\n return flags ? { pattern, flags } : { pattern };\n}\n\n/**\n * 校验约束与字段类型是否匹配\n *\n * 不匹配时抛 SchemaExtractionError(复用现有错误类型)。\n *\n * @param constraints 约束数组\n * @param type 字段类型\n * @param fieldName 字段名,用于错误信息\n */\nfunction validateConstraints(\n constraints: TypeConstraint[],\n type: RuntimeType,\n fieldName: string,\n): void {\n if (constraints.length === 0) return;\n\n for (const constraint of constraints) {\n const kind = constraint.kind;\n\n if (NUMBER_CONSTRAINT_KINDS.has(kind)) {\n if (type.kind !== 'number') {\n throw new SchemaExtractionError(\n fieldName,\n `@${kind} 约束仅适用于 number 字段,实际为 ${type.kind}`,\n );\n }\n continue;\n }\n\n if (LENGTH_CONSTRAINT_KINDS.has(kind)) {\n if (type.kind !== 'string' && type.kind !== 'array') {\n throw new SchemaExtractionError(\n fieldName,\n `@${kind} 约束仅适用于 string 或 array 字段,实际为 ${type.kind}`,\n );\n }\n continue;\n }\n\n if (STRING_FORMAT_CONSTRAINT_KINDS.has(kind)) {\n if (type.kind !== 'string') {\n throw new SchemaExtractionError(\n fieldName,\n `@${kind} 约束仅适用于 string 字段,实际为 ${type.kind}`,\n );\n }\n continue;\n }\n }\n}\n","import ts from 'typescript';\nimport {\n resolveTypeNode,\n resolveInterfaceDeclaration,\n resolveEnumDeclaration,\n findTopLevelDecl,\n setProgramContext,\n type PropertyType,\n type RuntimeType,\n SchemaExtractionError,\n} from './resolveTypeNode';\n\nexport interface HandlerTypeInfo {\n name: string; // 类型名,如 'GETQuery'\n properties: PropertyType[];\n /** 完整的运行时类型描述(用于嵌套校验) */\n runtimeType: RuntimeType;\n}\n\n/**\n * 从源文件中提取指定名称的类型信息\n *\n * 支持的类型声明:\n * - interface 声明(含继承)\n * - type 别名(type Query = { ... })\n * - enum 声明(跨文件回退路径支持)\n *\n * 自身文件找不到目标声明时,回退到 program 的其他源文件查找同名顶层声明\n * (跳过 node_modules / TypeScript lib,首个匹配生效,与 resolveImportAlias\n * 兜底路径语义一致)。该回退服务于入口类型中第二次出现的跨文件引用:首次引用\n * 已由 checker 内联,二次引用被标记为 ref,代码生成阶段经\n * createLazyTypeResolver → extractTypeInfo 解析,若无回退会静默生成\n * z.unknown(),违背「不降级放行」约定。\n *\n * 遇到不支持的类型时抛 `SchemaExtractionError`,错误信息包含文件路径和类型名。\n *\n * @param program TypeScript Program\n * @param filePath 源文件路径\n * @param typeName 类型名,如 'GETQuery'\n */\nexport function extractTypeInfo(\n program: ts.Program,\n filePath: string,\n typeName: string,\n): HandlerTypeInfo | null {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const checker = program.getTypeChecker();\n\n // 设置 program 上下文,供 resolveImportAlias 兜底遍历跨文件声明使用\n // (TypeChecker 运行时未暴露 getProgram(),通过模块级变量传入)\n setProgramContext(program);\n try {\n // 1. 查找 interface 声明\n let result: HandlerTypeInfo | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {\n const visited = new Set<string>();\n visited.add(typeName); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, typeName, () =>\n resolveInterfaceDeclaration(node, checker, visited),\n );\n result = {\n name: typeName,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n };\n return;\n }\n\n // 2. 查找 type 别名\n if (ts.isTypeAliasDeclaration(node) && node.name.text === typeName) {\n const visited = new Set<string>();\n visited.add(typeName); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, typeName, () =>\n resolveTypeNode(node.type, checker, visited),\n );\n result = {\n name: typeName,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n };\n return;\n }\n });\n\n if (result) return result;\n\n // 3. 跨文件回退:自身文件无该声明时,在 program 的其他源文件中查找同名顶层声明\n for (const sf of program.getSourceFiles()) {\n if (sf === sourceFile) continue;\n // 与 resolveImportAlias 兜底路径使用相同的过滤规则\n if (sf.fileName.includes('/node_modules/') || sf.fileName.includes('typescript/lib/')) {\n continue;\n }\n const found = findTopLevelDecl(sf, typeName);\n if (!found) continue;\n\n const visited = new Set<string>();\n visited.add(typeName);\n const runtimeType = withFileContext(filePath, typeName, () => {\n if (found.kind === 'interface') {\n return resolveInterfaceDeclaration(found.node, checker, visited);\n }\n if (found.kind === 'typeAlias') {\n return resolveTypeNode(found.node.type, checker, visited);\n }\n return resolveEnumDeclaration(found.node);\n });\n return {\n name: typeName,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n };\n }\n\n return null;\n } finally {\n setProgramContext(null);\n }\n}\n\n/**\n * 从源文件中提取所有 interface 和 type 别名的类型信息\n *\n * 用于:\n * - 生成 schema 模块时遍历所有命名类型\n * - 作为 typeResolver 提供给 generateZodSchema,解析循环引用中的 ref\n *\n * @returns Map<类型名, HandlerTypeInfo>\n */\nexport function extractAllTypes(\n program: ts.Program,\n filePath: string,\n): Map<string, HandlerTypeInfo> {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return new Map();\n\n const checker = program.getTypeChecker();\n\n // 设置 program 上下文,供 resolveImportAlias 兜底遍历跨文件声明使用\n setProgramContext(program);\n try {\n const result = new Map<string, HandlerTypeInfo>();\n\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isInterfaceDeclaration(node)) {\n const visited = new Set<string>();\n visited.add(node.name.text); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, node.name.text, () =>\n resolveInterfaceDeclaration(node, checker, visited),\n );\n result.set(node.name.text, {\n name: node.name.text,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n });\n return;\n }\n\n if (ts.isTypeAliasDeclaration(node)) {\n const visited = new Set<string>();\n visited.add(node.name.text); // 标记当前类型,使自引用直接返回 ref\n const runtimeType = withFileContext(filePath, node.name.text, () =>\n resolveTypeNode(node.type, checker, visited),\n );\n result.set(node.name.text, {\n name: node.name.text,\n properties: runtimeType.kind === 'object' ? runtimeType.properties : [],\n runtimeType,\n });\n return;\n }\n });\n\n return result;\n } finally {\n setProgramContext(null);\n }\n}\n\n/**\n * 惰性类型解析器\n *\n * 按名称解析类型并缓存。与 extractAllTypes 的差异:extractAllTypes 提前解析\n * 文件中全部顶层类型——与路由无关的类型也会被解析,其中任何类型含不支持语法\n * 都会拖垮整个 build/reload;惰性解析器只在类型真正被需要时(入口类型或\n * ref 引用)才解析,无关类型零开销。\n */\nexport interface LazyTypeResolver {\n /**\n * 按名称解析类型\n *\n * @returns 类型信息;文件中无该名称的声明时返回 null\n * @throws SchemaExtractionError 类型声明含不支持语法时(与 extractTypeInfo 一致,不降级)\n */\n resolve(name: string): HandlerTypeInfo | null;\n}\n\n/**\n * 创建文件的惰性类型解析器\n *\n * 缓存保证同一类型只解析一次(幂等返回同一实例)。\n *\n * @param program TypeScript Program(解析期间会临时设置 program 上下文,供\n * resolveImportAlias 兜底遍历跨文件声明使用)\n * @param filePath 源文件绝对路径\n */\nexport function createLazyTypeResolver(program: ts.Program, filePath: string): LazyTypeResolver {\n const cache = new Map<string, HandlerTypeInfo | null>();\n return {\n resolve(name: string): HandlerTypeInfo | null {\n if (cache.has(name)) return cache.get(name)!;\n const info = extractTypeInfo(program, filePath, name);\n cache.set(name, info);\n return info;\n },\n };\n}\n\n/**\n * 包装类型解析,捕获 SchemaExtractionError 并补充文件路径和类型名上下文\n *\n * 只包装一次(最外层),避免嵌套调用时重复包装。\n */\nfunction withFileContext<T>(filePath: string, typeName: string, fn: () => T): T {\n try {\n return fn();\n } catch (err) {\n if (err instanceof SchemaExtractionError) {\n // 补充文件路径和类型名,只包装一次;保留行号位置(错误在类型文件中的精确定位)\n const fileName = filePath.split('/').pop() ?? filePath;\n const enriched = new SchemaExtractionError(\n err.typeText,\n `${err.reason}(文件: ${fileName}, 类型: ${typeName})`,\n { cause: err },\n err.location,\n );\n throw enriched;\n }\n throw err;\n }\n}\n","/**\n * 根据 HTTP 方法判断主输入类型\n *\n * - GET / DELETE / HEAD:query(URL 查询参数)\n * - POST / PUT / PATCH:body(请求体)\n *\n * 注意:所有方法都可能同时有 query 和 body,\n * 这里返回的是\"主输入\"(用于校验和注入)。\n * DELETE 请求也支持 body(见 resolveInput.ts)。\n */\nexport function getInputTypeForMethod(method: string): 'query' | 'body' {\n const upper = method.toUpperCase();\n if (upper === 'GET' || upper === 'DELETE' || upper === 'HEAD') {\n return 'query';\n }\n return 'body';\n}\n\n/**\n * 判断方法是否有请求体\n *\n * POST / PUT / PATCH / DELETE 可能有 body\n * GET / HEAD 不应该有 body\n */\nexport function hasBody(method: string): boolean {\n const upper = method.toUpperCase();\n return upper === 'POST' || upper === 'PUT' || upper === 'PATCH' || upper === 'DELETE';\n}\n","/**\n * 根据 HTTP 方法和输入类型生成 schema 类型名\n * GET + query -> GETQuery\n * POST + body -> POSTBody\n * GET + params -> GETParams\n */\nexport function getSchemaName(method: string, inputType: 'query' | 'body' | 'params'): string {\n return `${method.toUpperCase()}${inputType.charAt(0).toUpperCase() + inputType.slice(1)}`;\n}\n","import ts from 'typescript';\n\n/**\n * 参数注入类型\n *\n * Phase 2.3 扩展 `agent` / `agents`:\n * - `agent` —— AgentHandle(由 `@faapi/agent` 插件工厂注入;调用时需显式传\n * `options.agent` 指定 agent 名,无默认 agent)\n * - `agents` —— 所有已注册 agent 的元数据列表(`agentRegistry.listAgents()`)\n */\nexport type InjectionType =\n | 'query'\n | 'body'\n | 'form'\n | 'headers'\n | 'params'\n | 'context'\n | 'cookies'\n | 'ip'\n | 'ua'\n | 'files'\n | 'fields'\n | 'agent'\n | 'agents'\n | 'unknown';\n\n/**\n * 注入项信息\n */\nexport interface InjectionItem {\n name: string; // 参数名\n type: InjectionType; // 注入类型\n hasType: boolean; // 是否有类型标注(运行时类型已擦除,始终为 false)\n}\n\n/**\n * 参数名到注入类型的映射(单一来源,构建期和运行时共用)\n *\n * `form` 与 `body` 互斥:handler 声明其一即可。`form` 适用于\n * `application/x-www-form-urlencoded` 请求体(值均为 string,schema coerce=true);\n * `body` 适用于 JSON 请求体(coerce=false)。两者共享 `resolveInput` 的解析结果。\n *\n * Phase 2.3 添加 `agent` / `agents`:\n * - `agent` → 注入默认 agent 元数据(Phase 2.4 实现,暂返回 `undefined`)\n * - `agents` → 注入所有已注册 agent 元数据列表(`agentRegistry.listAgents()`)\n *\n * Phase 3.x 的 `@faapi/agent` 插件通过注入器机制增强为 `AgentHandle`\n * (含可调用 `run` 函数),覆盖内置的元数据注入。\n */\nexport const PARAM_TYPE_MAP: Record<string, InjectionType> = {\n query: 'query',\n body: 'body',\n form: 'form',\n headers: 'headers',\n params: 'params',\n context: 'context',\n ctx: 'context', // 别名\n cookies: 'cookies',\n ip: 'ip',\n ua: 'ua',\n files: 'files',\n fields: 'fields',\n agent: 'agent', // Phase 2.3\n agents: 'agents', // Phase 2.3\n};\n\n/**\n * 分析结果按函数引用缓存。\n *\n * handler 函数在路由模块加载后引用稳定,参数列表不会变化;用 WeakMap 弱键,\n * dev 热替换后旧模块的函数不再被引用即可随 GC 回收,不需要手动失效。\n */\ntype AnyFn = (...args: unknown[]) => unknown;\nconst injectionCache = new WeakMap<AnyFn, InjectionItem[]>();\n\n/**\n * 分析函数参数,决定需要注入什么内容\n *\n * 使用 TypeScript AST 解析 fn.toString() 的结果,\n * 正确处理解构参数、默认值、rest 参数等情况。\n *\n * 注意:运行时类型信息已被擦除,hasType 始终为 false。\n *\n * 返回的数组是缓存的共享实例,调用方不得就地修改。\n */\nexport function resolveInjection(fn: (...args: unknown[]) => unknown): InjectionItem[] {\n const cached = injectionCache.get(fn);\n if (cached) {\n return cached;\n }\n\n const fnStr = fn.toString();\n const params = extractParamsWithAst(fnStr);\n\n const items = params.map((param) => {\n const type = PARAM_TYPE_MAP[param.name] || 'unknown';\n return {\n name: param.name,\n type,\n hasType: false, // 运行时类型已擦除\n };\n });\n\n injectionCache.set(fn, items);\n return items;\n}\n\n/**\n * 使用 TypeScript AST 从函数字符串中提取参数名\n *\n * 相比正则解析,AST 能正确处理:\n * - 解构参数 `function GET({ page, size })` → page, size\n * - 默认值 `function GET(query = {})` → query\n * - rest 参数 `function GET(...args)` → args\n * - 含逗号的泛型(类型擦除后不存在该问题)\n */\nfunction extractParamsWithAst(fnStr: string): Array<{ name: string }> {\n // 包装成模块,让 TS 能解析\n const sourceFile = ts.createSourceFile(\n '__faapi_injection__.ts',\n fnStr,\n ts.ScriptTarget.Latest,\n true,\n );\n\n const paramNames: string[] = [];\n\n function visit(node: ts.Node): void {\n // 函数声明:function GET(query) {}\n if (ts.isFunctionDeclaration(node) && node.parameters.length > 0) {\n for (const param of node.parameters) {\n extractParamName(param, paramNames);\n }\n return;\n }\n\n // 箭头函数 / 函数表达式:(query) => {} 或 function(query) {}\n if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && node.parameters.length > 0) {\n for (const param of node.parameters) {\n extractParamName(param, paramNames);\n }\n return;\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n\n return paramNames.map((name) => ({ name }));\n}\n\n/**\n * 从单个参数节点提取参数名\n *\n * 支持的参数形式:\n * - 标识符:query → query\n * - 解构对象:{ page, size } → page, size(每个属性都作为一个注入项)\n * - 解构数组:[a, b] → a, b\n * - rest 参数:...args → args\n * - 默认值:query = {} → query\n */\nfunction extractParamName(param: ts.ParameterDeclaration, names: string[]): void {\n const name = param.name;\n\n // 标识符:query\n if (ts.isIdentifier(name)) {\n names.push(name.text);\n return;\n }\n\n // 解构对象:{ page, size }\n if (ts.isObjectBindingPattern(name)) {\n for (const element of name.elements) {\n if (ts.isBindingElement(element)) {\n const elemName = element.name;\n if (ts.isIdentifier(elemName)) {\n names.push(elemName.text);\n }\n }\n }\n return;\n }\n\n // 解构数组:[a, b]\n if (ts.isArrayBindingPattern(name)) {\n for (const element of name.elements) {\n if (element && ts.isBindingElement(element)) {\n const elemName = element.name;\n if (ts.isIdentifier(elemName)) {\n names.push(elemName.text);\n }\n }\n }\n return;\n }\n}\n","import ts from 'typescript';\nimport type { InjectionType } from './resolveInjection';\nimport { PARAM_TYPE_MAP } from './resolveInjection';\n\n/**\n * 参数元数据\n */\nexport interface ParamMeta {\n name: string; // 参数名\n type: InjectionType; // 注入类型\n typeName?: string; // 引用类型名(如 'Query')\n schema?: PropertyInfo[]; // 类型结构(内联类型时)\n}\n\n/**\n * 属性信息\n */\nexport interface PropertyInfo {\n name: string;\n type: string;\n optional: boolean;\n}\n\n/**\n * 注入元数据\n */\nexport interface InjectionMeta {\n params: ParamMeta[];\n}\n\n/**\n * AST 分析 handler 函数,提取参数注入元数据\n *\n * @param code handler 源码字符串\n * @param functionName 函数导出名(如 'GET')\n */\nexport function analyzeInjection(code: string, functionName: string): InjectionMeta {\n const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);\n return analyzeInjectionInSourceFile(sourceFile, functionName);\n}\n\n/**\n * 从已有 SourceFile 分析 handler 函数的参数注入元数据\n *\n * 与 analyzeInjection 语义一致,但接受 AST 节点而非源码字符串——\n * 调用方(如 collectRouteSchemaSources)的 program 中已有解析好的 SourceFile,\n * 逐方法重新 parse 同一文件字符串是纯浪费(同文件 N 个方法 = N 次全量重复解析)。\n *\n * @param sourceFile 已解析的 AST\n * @param functionName 函数导出名(如 'GET')\n */\nexport function analyzeInjectionInSourceFile(\n sourceFile: ts.SourceFile,\n functionName: string,\n): InjectionMeta {\n const params: ParamMeta[] = [];\n\n // 遍历 AST 查找目标函数\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {\n // 分析参数\n for (const param of node.parameters) {\n const paramMeta = analyzeParam(param, sourceFile);\n params.push(paramMeta);\n }\n }\n });\n\n return { params };\n}\n\n/**\n * 分析单个参数\n */\nfunction analyzeParam(param: ts.ParameterDeclaration, sourceFile: ts.SourceFile): ParamMeta {\n const name = param.name.getText(sourceFile);\n const type = PARAM_TYPE_MAP[name] || 'unknown';\n\n const result: ParamMeta = { name, type };\n\n // 分析类型\n if (param.type) {\n if (ts.isTypeReferenceNode(param.type)) {\n // 引用类型:Query, Body 等\n result.typeName = param.type.typeName.getText(sourceFile);\n } else if (ts.isTypeLiteralNode(param.type)) {\n // 内联类型:{ page: number }\n result.schema = extractSchema(param.type, sourceFile);\n }\n }\n\n return result;\n}\n\n/**\n * 从类型字面量提取 schema\n */\nfunction extractSchema(typeNode: ts.TypeLiteralNode, sourceFile: ts.SourceFile): PropertyInfo[] {\n const schema: PropertyInfo[] = [];\n\n for (const member of typeNode.members) {\n if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {\n const propName = member.name.text;\n const optional = !!member.questionToken;\n const propType = member.type?.getText(sourceFile) || 'unknown';\n\n schema.push({\n name: propName,\n type: propType,\n optional,\n });\n }\n }\n\n return schema;\n}\n","import { createPrograms } from '../ast/createProgram';\nimport {\n createLazyTypeResolver,\n type HandlerTypeInfo,\n type LazyTypeResolver,\n} from '../ast/extractHandlerTypes';\nimport { getInputTypeForMethod } from '../runtime/inputType';\nimport { getSchemaName } from '../validator/schemaName';\nimport { analyzeInjectionInSourceFile } from '../injection/analyzeInjection';\nimport type { RouteManifest } from '../router/routeTypes';\nimport path from 'node:path';\n\n/**\n * 单个路由的 schema 提取结果\n *\n * key 使用 urlPath(如 '/api/hello')而非 filePath,因为 urlPath 在 dev/prod 完全一致,\n * 无需 remapManifestKeys 桥接 .ts/.js 路径差异。\n */\nexport interface RouteSchemaSource {\n /** 路由 URL 路径(如 '/api/hello'),作为 schema key */\n urlPath: string;\n /** 源文件绝对路径(用于 generateSchemaFiles 按文件分组生成 zod.js) */\n filePath: string;\n schemaName: string;\n typeInfo: HandlerTypeInfo | null;\n /**\n * 是否对 number/boolean 字段生成 z.preprocess 字符串转换(coerce)。\n *\n * - query/params:始终 coerce=true(URL 来源均为 string)\n * - body:始终 coerce=false(JSON 解析已是天然 JS 类型)\n * - form:coerce=true(form-urlencoded 来源均为 string),由本函数在提取时\n * 检测到 handler 声明 `form` 参数时显式设置。schema 名仍为 `POSTBody`\n * (与 body 共享运行时 schema key),运行时 validateInput 无需感知 form/body 差异。\n *\n * 未设置时由 generateSchemaFileSource 回退到 schemaName 后缀正则推断(Query/Params → true)。\n */\n coerce?: boolean;\n}\n\n/**\n * 从路由清单收集 schema 提取所需的原始数据\n *\n * dev 和 prd 共享的核心提取流程:\n * 1. 按文件分组遍历路由\n * 2. 批量共享 Program(同一次提取只按文件组创建少量 Program)\n * 3. 对每个路由用 analyzeInjectionInSourceFile 定位入口参数类型,\n * extractTypeInfo 解析入口类型(入口类型必须严格解析,失败抛 SchemaExtractionError)\n * 4. 返回按文件分组的惰性类型解析器——generateSchemaFiles 生成 zod.js 时\n * 遇到 ref(同文件循环引用)按需解析并缓存\n *\n * 惰性语义:与路由无关的类型(未被任何入口类型引用)不会被解析——文件里\n * 存在一个含不支持语法的无关类型不再拖垮整个 build/reload。\n */\nexport function collectRouteSchemaSources(\n routes: RouteManifest,\n rootDir?: string,\n): {\n sources: RouteSchemaSource[];\n /** 按文件分组的惰性类型解析器(generateSchemaFiles 解析 ref 用) */\n resolversByFile: Map<string, LazyTypeResolver>;\n} {\n // 按文件分组收集方法(去重)\n // key 是文件绝对路径(createProgram 需要),但 schema key 用 urlPath\n const methodsByFile = new Map<string, { urlPath: string; methods: Set<string> }>();\n for (const route of routes) {\n const filePath = rootDir ? path.resolve(rootDir, route.filePath) : route.filePath;\n let entry = methodsByFile.get(filePath);\n if (!entry) {\n entry = { urlPath: route.urlPath, methods: new Set() };\n methodsByFile.set(filePath, entry);\n }\n entry.methods.add(route.method);\n }\n\n // 批量共享 Program:同一次提取只创建一个 Program,避免逐文件全量解析\n const programByFile = createPrograms([...methodsByFile.keys()]);\n\n // 每个文件一个惰性解析器:入口类型立即解析,ref 按需解析(缓存幂等)\n const resolversByFile = new Map<string, LazyTypeResolver>();\n for (const filePath of methodsByFile.keys()) {\n resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath)!, filePath));\n }\n\n // 每个文件提取 schema(key 用 urlPath)\n const sources: RouteSchemaSource[] = [];\n for (const [filePath, entry] of methodsByFile) {\n const program = programByFile.get(filePath)!;\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) continue;\n const resolver = resolversByFile.get(filePath)!;\n for (const method of entry.methods) {\n const inputType = getInputTypeForMethod(method);\n const schemaName = getSchemaName(method, inputType);\n // 复用 program 已解析的 SourceFile,逐方法零重复 parse\n const meta = analyzeInjectionInSourceFile(sourceFile, method);\n // POST/PUT/PATCH(inputType='body'):优先找 body 参数,找不到再找 form 参数。\n // form 与 body 共享 schema 名(POSTBody),运行时 validateInput 仍按 POSTBodySchema 查找;\n // 差异仅在校验:form 声明时通过 source.coerce=true 显式覆盖(form 值均为 string,\n // 需 z.preprocess 转换 number/boolean 字段)。\n const param =\n meta.params.find((p) => p.type === inputType) ??\n (inputType === 'body' ? meta.params.find((p) => p.type === 'form') : undefined);\n const isForm = param?.type === 'form';\n // 入口类型:必须严格解析(失败抛 SchemaExtractionError,带文件/类型上下文)\n const typeInfo = param?.typeName ? resolver.resolve(param.typeName) : null;\n sources.push({\n urlPath: entry.urlPath,\n filePath,\n schemaName,\n typeInfo,\n coerce: isForm || undefined,\n });\n }\n }\n\n return { sources, resolversByFile };\n}\n","import {\n SchemaExtractionError,\n type RuntimeType,\n type TupleElement,\n type TypeConstraint,\n} from './resolveTypeNode';\nimport type { HandlerTypeInfo } from './extractHandlerTypes';\n\n/**\n * 类型解析器:根据类型名获取 RuntimeType\n *\n * 用于解析循环引用中的 ref。调用方负责提供(通常从 AST 提取)。\n */\nexport type TypeResolver = (name: string) => RuntimeType | undefined;\n\n/**\n * 代码生成上下文\n *\n * 跟踪已收集的命名类型,用于生成 z.lazy 声明。\n */\nclass CodeGenContext {\n /** 命名类型集合:name → RuntimeType */\n readonly namedTypes = new Map<string, RuntimeType>();\n /** 类型解析器(用于解析 ref 的实际类型) */\n readonly resolveType: TypeResolver;\n /** 入口类型原始名(typeInfo.name,用于识别入口类型的自引用) */\n entryTypeName = '';\n /** 入口类型导出名(exportName,自引用时用此名生成变量名) */\n entryExportName = '';\n /**\n * 是否生成 coerce 逻辑(query/params 场景,URL 来源均为 string)\n *\n * true 时为 number/boolean 字段包 z.preprocess,把合法的字符串转成对应类型。\n * 嵌套类型(array/object/tuple/union 等)的元素递归处理。\n */\n coerce = false;\n\n constructor(resolveType: TypeResolver) {\n this.resolveType = resolveType;\n }\n}\n\n/**\n * 从 RuntimeType 收集所有命名类型(ref)\n *\n * 递归遍历 RuntimeType,遇到 ref 时解析并加入 namedTypes。\n * 已收集的类型不重复处理(防止循环引用无限递归)。\n */\nexport function collectNamedTypes(type: RuntimeType, ctx: CodeGenContext): void {\n switch (type.kind) {\n case 'string':\n case 'number':\n case 'boolean':\n case 'bigint':\n case 'null':\n case 'undefined':\n case 'any':\n case 'unknown':\n case 'literal':\n case 'date':\n return;\n\n case 'array':\n collectNamedTypes(type.element, ctx);\n return;\n\n case 'tuple':\n for (const el of type.elements) {\n collectNamedTypes(el.type, ctx);\n }\n return;\n\n case 'object':\n for (const prop of type.properties) {\n collectNamedTypes(prop.type, ctx);\n }\n return;\n\n case 'union':\n for (const member of type.members) {\n collectNamedTypes(member, ctx);\n }\n return;\n\n case 'record':\n collectNamedTypes(type.key, ctx);\n collectNamedTypes(type.value, ctx);\n return;\n\n case 'map':\n collectNamedTypes(type.key, ctx);\n collectNamedTypes(type.value, ctx);\n return;\n\n case 'set':\n collectNamedTypes(type.element, ctx);\n return;\n\n case 'ref': {\n if (ctx.namedTypes.has(type.name)) return;\n // 先占位,防止循环引用无限递归\n ctx.namedTypes.set(type.name, { kind: 'any' });\n const resolved = ctx.resolveType(type.name);\n if (resolved) {\n ctx.namedTypes.set(type.name, resolved);\n collectNamedTypes(resolved, ctx);\n return;\n }\n // ref 只在 AST 提取阶段遇到真实 TS 类型引用时产生(首现内联、二次标 ref),\n // 声明必然存在于某个源文件中;解析不到说明查找能力不足(如命名空间内声明),\n // 按框架约定显式抛错而非静默降级为 z.unknown()(AGENTS.md §6.3)\n throw new SchemaExtractionError(\n type.name,\n `无法解析命名类型引用 \"${type.name}\"(handler 文件与 program 源文件中均未找到同名顶层声明)`,\n );\n }\n }\n}\n\n/**\n * RuntimeType → zod 表达式字符串(不含声明)\n *\n * @param type 类型描述\n * @param ctx 代码生成上下文\n * @param constraints 字段级约束(仅 object 字段传入,影响最外层 zod 链)\n */\nexport function runtimeTypeToZodExpression(\n type: RuntimeType,\n ctx: CodeGenContext,\n constraints?: TypeConstraint[],\n): string {\n const expr = baseExpression(type, ctx);\n // 字段级约束链(@max/@min/@regex 等)追加到基础表达式后\n // coerce 模式下约束需作用在 preprocess 内部的 z.X() 上,故先应用约束再包裹 preprocess\n const withConstraints =\n constraints && constraints.length > 0 ? applyConstraints(expr, constraints, type.kind) : expr;\n // coerce 模式下,number/boolean 在外层包 z.preprocess\n // 嵌套类型(array/object/tuple/union 等)内部已递归处理,外层不再包裹\n if (ctx.coerce && (type.kind === 'number' || type.kind === 'boolean')) {\n return wrapCoercePreprocess(type.kind, withConstraints);\n }\n // coerce 模式下,数字/布尔字面量同样需要字符串转换:query 声明 `status: 1 | 2`\n // 时 URL 传来 \"1\"(string),裸 z.literal(1) 必然校验失败。union 成员经本函数\n // 递归生成,字面量成员逐个包裹(string 字面量天然命中,无需转换)\n if (\n ctx.coerce &&\n type.kind === 'literal' &&\n (typeof type.value === 'number' || typeof type.value === 'boolean')\n ) {\n return wrapCoercePreprocess(\n typeof type.value === 'number' ? 'number' : 'boolean',\n withConstraints,\n );\n }\n return withConstraints;\n}\n\n/**\n * 在 zod 表达式上追加约束链\n *\n * 约束与字段类型的匹配在 AST 提取阶段已校验,这里直接生成链式调用。\n *\n * coerce 模式下调用方应先 applyConstraints 再 wrapCoercePreprocess,\n * 使约束作用在 preprocess 内部的 z.X() 上,而非 preprocess 外壳。\n *\n * @param baseExpr 基础 zod 表达式(未包裹 preprocess)\n * @param constraints 约束数组\n * @param typeKind 字段类型 kind\n */\nfunction applyConstraints(\n baseExpr: string,\n constraints: TypeConstraint[],\n typeKind: RuntimeType['kind'],\n): string {\n const suffix = constraints.map((c) => constraintToZodChain(c, typeKind)).join('');\n return `${baseExpr}${suffix}`;\n}\n\n/**\n * 单个约束 → zod 链式方法字符串\n *\n * @param constraint 约束\n * @param typeKind 字段类型 kind(用于区分 string/array 的 max/min 含义)\n */\nfunction constraintToZodChain(constraint: TypeConstraint, _typeKind: RuntimeType['kind']): string {\n switch (constraint.kind) {\n case 'max':\n return `.max(${constraint.value})`;\n case 'min':\n return `.min(${constraint.value})`;\n case 'int':\n return '.int()';\n case 'positive':\n return '.positive()';\n case 'negative':\n return '.negative()';\n case 'nonnegative':\n return '.nonnegative()';\n case 'nonpositive':\n return '.nonpositive()';\n case 'maxLength':\n return `.max(${constraint.value})`;\n case 'minLength':\n return `.min(${constraint.value})`;\n case 'length':\n return `.length(${constraint.value})`;\n case 'regex': {\n const flags = constraint.flags ?? '';\n // 用 new RegExp 构造,避免 pattern 中含 / 字符导致的字面量语法错误\n return `.regex(new RegExp(${JSON.stringify(constraint.pattern)}${flags ? `, ${JSON.stringify(flags)}` : ''}))`;\n }\n case 'email':\n return '.email()';\n case 'url':\n return '.url()';\n case 'uuid':\n return '.uuid()';\n }\n}\n\n/**\n * 生成基础 zod 表达式(不含 coerce 包裹)\n */\nfunction baseExpression(type: RuntimeType, ctx: CodeGenContext): string {\n switch (type.kind) {\n case 'string':\n return 'z.string()';\n case 'number':\n return 'z.number()';\n case 'boolean':\n return 'z.boolean()';\n case 'bigint':\n // AST 提取阶段已抛错,这里不会到达\n return 'z.never()';\n case 'null':\n return 'z.null()';\n case 'undefined':\n return 'z.undefined()';\n case 'any':\n case 'unknown':\n return 'z.unknown()';\n case 'literal':\n return `z.literal(${JSON.stringify(type.value)})`;\n case 'array':\n return `z.array(${runtimeTypeToZodExpression(type.element, ctx)})`;\n case 'tuple':\n return generateTupleExpression(type.elements, ctx);\n case 'object':\n return generateObjectExpression(type, ctx);\n case 'union':\n return generateUnionExpression(type.members, ctx);\n case 'date':\n // z.preprocess 限制只接受 string/Date:string → new Date(string),其他原样传入\n // z.coerce.date() 会把 number 也转为 Date(new Date(123)),不符合需求\n // date 类型本身已用 preprocess,coerce 模式下无需再包一层\n return 'z.preprocess((v) => (typeof v === \"string\" ? new Date(v) : v), z.date())';\n case 'record':\n return `z.record(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)})`;\n case 'map':\n // Map<K,V> — JSON 序列化为 entries 数组,coerceMap 还原为 Map 实例后再交给 z.map 校验\n // coerce=false(body)场景也需要 preprocess:JSON.parse 出来是数组,不是 Map 实例\n return `z.preprocess(coerceMap, z.map(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)}))`;\n case 'set':\n // Set<T> — JSON 序列化为数组,coerceSet 还原为 Set 实例后再交给 z.set 校验\n // coerce=false(body)场景也需要 preprocess:JSON.parse 出来是数组,不是 Set 实例\n return `z.preprocess(coerceSet, z.set(${runtimeTypeToZodExpression(type.element, ctx)}))`;\n case 'ref':\n // ref 引用命名类型,直接用 NameSchema 变量\n // 入口类型的自引用需用 exportName(exportName 可能与 typeInfo.name 不同)\n if (type.name === ctx.entryTypeName) {\n return `${ctx.entryExportName}Schema`;\n }\n return `${type.name}Schema`;\n }\n}\n\n/**\n * coerceNumber 公用函数源码(ESM export 格式)\n *\n * 用于 query/params 的 string → number 转换:\n * - 合法数字字符串(\"1\"、\"3.14\")转为 number\n * - 空串/NaN 保留原值,让 z.number() 报错(避免 Number(\"\") = 0 的陷阱)\n *\n * 写入 dist 根部的 faapi-helpers.js,各 zod.js 通过相对路径 import 复用。\n */\nexport const COERCE_NUMBER_HELPER =\n 'export const coerceNumber = (v) => typeof v === \"string\" && v.trim() !== \"\" && !isNaN(Number(v)) ? Number(v) : v;';\n\n/**\n * coerceBoolean 公用函数源码(ESM export 格式)\n *\n * 用于 query/params 的 string → boolean 转换:\n * - 'true'/'1' → true\n * - 'false'/'0' → false\n * - 其他值保留原值,让 z.boolean() 报错\n *\n * 写入 dist 根部的 faapi-helpers.js,各 zod.js 通过相对路径 import 复用。\n */\nexport const COERCE_BOOLEAN_HELPER =\n 'export const coerceBoolean = (v) => {\\n' +\n ' const lower = typeof v === \"string\" ? v.toLowerCase() : v;\\n' +\n ' return lower === \"true\" || lower === \"1\" ? true : lower === \"false\" || lower === \"0\" ? false : v;\\n' +\n '};';\n\n/**\n * coerceMap 公用函数源码(ESM export 格式)\n *\n * 用于 Map<K,V> 的 JSON 还原:JSON 序列化为 entries 数组,JSON.parse 后还原为 Map 实例\n * - entries 数组 [[\"k\",v],...] → new Map(entries)\n * - Map 实例 → 原样返回\n * - 普通对象 { k: v } → new Map(Object.entries(v))\n * - 其他值保留原值,让 z.map() 报错\n *\n * 与 coerceNumber/coerceBoolean 不同,coerceMap 在 coerce=false(body 场景)下也使用:\n * JSON.parse 出来的是数组/对象,不是 Map 实例,必须还原才能交给 z.map() 校验。\n */\nexport const COERCE_MAP_HELPER =\n 'export const coerceMap = (v) => Array.isArray(v) ? new Map(v) : v instanceof Map ? v : (v && typeof v === \"object\" ? new Map(Object.entries(v)) : v);';\n\n/**\n * coerceSet 公用函数源码(ESM export 格式)\n *\n * 用于 Set<T> 的 JSON 还原:JSON 序列化为数组,JSON.parse 后还原为 Set 实例\n * - Set 实例 → 原样返回\n * - 数组 [item,...] → new Set(array)\n * - 其他值保留原值,让 z.set() 报错\n *\n * 与 coerceNumber/coerceBoolean 不同,coerceSet 在 coerce=false(body 场景)下也使用:\n * JSON.parse 出来的是数组,不是 Set 实例,必须还原才能交给 z.set() 校验。\n */\nexport const COERCE_SET_HELPER =\n 'export const coerceSet = (v) => v instanceof Set ? v : (Array.isArray(v) ? new Set(v) : v);';\n\n/**\n * faapi-helpers.js 文件名(生成在 dist 根部)\n */\nexport const HELPERS_FILENAME = 'faapi-helpers.js';\n\n/**\n * 生成 faapi-helpers.js 文件源码\n *\n * 包含 coerceNumber / coerceBoolean / coerceMap / coerceSet 四个公用函数(ESM export)。\n * 由 generateSchemaFiles 按需生成:query/params 场景的 number/boolean 字段触发 coerceNumber/coerceBoolean;\n * Map/Set 字段(query/params 与 body 均包含)触发 coerceMap/coerceSet。\n */\nexport function generateHelpersFileSource(): string {\n return [\n '// faapi-helpers.js — faapi 自动生成的公用函数(请勿手动编辑)',\n COERCE_NUMBER_HELPER,\n COERCE_BOOLEAN_HELPER,\n COERCE_MAP_HELPER,\n COERCE_SET_HELPER,\n '',\n ].join('\\n');\n}\n\n/**\n * 检测代码是否引用了 coerceNumber / coerceBoolean / coerceMap / coerceSet 变量\n *\n * 用于 generateSchemaFiles 判断是否需要生成 faapi-helpers.js。\n * 注意:Map/Set 在 coerce=false(body 场景)下也会引用 coerceMap/coerceSet,\n * 故仅凭此检测即可覆盖所有需要生成 helpers 的场景。\n */\nexport function usesCoerceHelpers(code: string): boolean {\n return (\n code.includes('coerceNumber') ||\n code.includes('coerceBoolean') ||\n code.includes('coerceMap') ||\n code.includes('coerceSet')\n );\n}\n\n/**\n * 为 number/boolean 表达式包裹 z.preprocess(用于 query coerce)\n *\n * 引用从 faapi-helpers.js import 的 coerceNumber / coerceBoolean 变量,\n * 避免每个字段都内联一长串函数。\n */\nfunction wrapCoercePreprocess(kind: 'number' | 'boolean', inner: string): string {\n if (kind === 'number') {\n return `z.preprocess(coerceNumber, ${inner})`;\n }\n return `z.preprocess(coerceBoolean, ${inner})`;\n}\n\n/**\n * 生成元组 zod 表达式\n *\n * zod 的 tuple 不直接支持可选元素,需要用 union 模拟:\n * - 全部必填:`z.tuple([e0, e1, ...])`\n * - 含可选元素:`z.union([z.tuple([e0]), z.tuple([e0, e1]), ...])`(从短到长)\n * - 含 rest:`z.tuple([e0, ...]).rest(eRest)`\n */\nfunction generateTupleExpression(elements: TupleElement[], ctx: CodeGenContext): string {\n const fixedExprs: string[] = [];\n const fixedOptional: boolean[] = [];\n let restExpression = '';\n let restStarted = false;\n\n for (const el of elements) {\n if (el.rest) {\n restExpression = runtimeTypeToZodExpression(el.type, ctx);\n restStarted = true;\n } else if (!restStarted) {\n fixedExprs.push(runtimeTypeToZodExpression(el.type, ctx));\n fixedOptional.push(el.optional);\n }\n }\n\n // 含 rest 元素:z.tuple([fixed...]).rest(restType)。\n // 可选前缀保留(zod v4 原生支持 z.tuple([X.optional()]).rest(...))——\n // 此前丢弃 fixedOptional 导致 [string?, ...number[]] 的空数组被误拒\n if (restExpression) {\n const fixedWithOptional = fixedExprs.map((expr, i) =>\n fixedOptional[i] ? `${expr}.optional()` : expr,\n );\n return `z.tuple([${fixedWithOptional.join(', ')}]).rest(${restExpression})`;\n }\n\n // 无可选元素:z.tuple([e0, e1, ...])\n const hasOptional = fixedOptional.some((o) => o);\n if (!hasOptional) {\n return `z.tuple([${fixedExprs.join(', ')}])`;\n }\n\n // 含可选元素:从后往前截断可选元素,生成 union\n // TS 语法要求可选元素在必填元素之后,所以只需从后往前去掉可选元素\n const variants: string[] = [];\n for (let len = fixedExprs.length; len >= 0; len--) {\n // len === 0 时只有全部可选才有效(空元组)\n // len > 0 时,第 len-1 个元素必须存在(必填或可选都行)\n // 截断点:去掉 fixedExprs[len..end],这些必须都是可选的\n const removed = fixedOptional.slice(len);\n if (removed.length > 0 && removed.some((o) => !o)) {\n // 被截断的元素中有必填元素,无效截断\n break;\n }\n const subset = fixedExprs.slice(0, len);\n variants.push(`z.tuple([${subset.join(', ')}])`);\n }\n\n // 从短到长排序(更自然的校验顺序)\n variants.reverse();\n if (variants.length === 1) {\n return variants[0]!;\n }\n return `z.union([${variants.join(', ')}])`;\n}\n\n/**\n * 生成对象 zod 表达式\n *\n * 可选字段用 .optional(),约束链在 .optional() 之前生成。\n * 带索引签名的对象(catchall)生成 z.object({...}).catchall(valueSchema)——\n * 索引签名与属性共存时属性保留(resolveTypeNode 不再丢弃)。\n */\nfunction generateObjectExpression(\n type: Extract<RuntimeType, { kind: 'object' }>,\n ctx: CodeGenContext,\n): string {\n const fields = type.properties.map((prop) => {\n const expr = runtimeTypeToZodExpression(prop.type, ctx, prop.constraints);\n const finalExpr = prop.optional ? `${expr}.optional()` : expr;\n return `${JSON.stringify(prop.name)}: ${finalExpr}`;\n });\n const objectExpr = `z.object({ ${fields.join(', ')} })`;\n return type.catchall !== undefined\n ? `${objectExpr}.catchall(${runtimeTypeToZodExpression(type.catchall, ctx)})`\n : objectExpr;\n}\n\n/**\n * 生成联合类型 zod 表达式\n *\n * 优化:如果联合中包含 null,用 .nullable() 简化\n * 否则用 z.union([m0, m1, ...])\n */\nfunction generateUnionExpression(members: RuntimeType[], ctx: CodeGenContext): string {\n const hasNull = members.some((m) => m.kind === 'null');\n const nonNull = members.filter((m) => m.kind !== 'null');\n\n // 只有一个非 null 成员 + null → .nullable()\n if (hasNull && nonNull.length === 1) {\n return `${runtimeTypeToZodExpression(nonNull[0]!, ctx)}.nullable()`;\n }\n\n // 多个成员 + null → z.union([...]).nullable()\n if (hasNull) {\n const unionInner = nonNull.map((m) => runtimeTypeToZodExpression(m, ctx)).join(', ');\n return `z.union([${unionInner}]).nullable()`;\n }\n\n // 无 null → z.union([...])\n const unionInner = members.map((m) => runtimeTypeToZodExpression(m, ctx)).join(', ');\n return `z.union([${unionInner}])`;\n}\n\n/**\n * 生成命名类型的 zod schema 声明\n *\n * - 无循环引用:`const NameSchema = z.object({ ... })`\n * - 有循环引用:`const NameSchema = z.lazy(() => z.object({ ... }))`\n */\nfunction generateNamedTypeDeclaration(\n name: string,\n type: RuntimeType,\n ctx: CodeGenContext,\n): string {\n const expr = runtimeTypeToZodExpression(type, ctx);\n const hasRef = containsRef(type, new Set([name]));\n if (hasRef) {\n // 循环引用:用 z.lazy 延迟求值\n return `const ${name}Schema = z.lazy(() => ${expr});`;\n }\n return `const ${name}Schema = ${expr};`;\n}\n\n/**\n * 检查 RuntimeType 是否包含对自身的 ref(循环引用)\n */\nfunction containsRef(type: RuntimeType, visited: Set<string>): boolean {\n switch (type.kind) {\n case 'ref':\n return visited.has(type.name);\n case 'array':\n return containsRef(type.element, visited);\n case 'tuple':\n return type.elements.some((el) => containsRef(el.type, visited));\n case 'object':\n return type.properties.some((prop) => containsRef(prop.type, visited));\n case 'union':\n return type.members.some((m) => containsRef(m, visited));\n case 'record':\n return containsRef(type.key, visited) || containsRef(type.value, visited);\n case 'map':\n return containsRef(type.key, visited) || containsRef(type.value, visited);\n case 'set':\n return containsRef(type.element, visited);\n default:\n return false;\n }\n}\n\n/**\n * 生成单个类型的 zod schema 代码(含命名类型声明)\n *\n * 返回的代码包含:\n * - `import { z } from 'zod'`\n * - 内部命名类型的 const 声明(带 z.lazy 循环引用保护)\n * - `export const NameSchema = ...` 入口导出\n *\n * @param typeInfo 类型信息\n * @param resolveType 类型解析器(用于解析 ref 的实际类型)\n * @param exportName 导出的 schema 变量名(不含 Schema 后缀)。默认用 typeInfo.name。\n * validateInput 按 `${schemaName}Schema` 查找,需与导出名一致。\n * @param coerce 是否生成 coerce 逻辑(query/params 场景)。默认 false。\n * true 时为 number/boolean 字段(含嵌套元素)包 z.preprocess。\n */\nexport function generateZodSchemaSource(\n typeInfo: HandlerTypeInfo,\n resolveType: TypeResolver,\n exportName?: string,\n coerce = false,\n): string {\n const { namedTypeDeclarations, entryDeclaration } = generateZodSchemaSourceParts(\n typeInfo,\n resolveType,\n exportName,\n coerce,\n );\n\n const lines: string[] = [];\n lines.push(\"import { z } from 'zod';\");\n lines.push('');\n\n for (const { declaration } of namedTypeDeclarations) {\n lines.push(declaration);\n }\n if (namedTypeDeclarations.length > 0) lines.push('');\n\n lines.push(entryDeclaration);\n\n return lines.join('\\n');\n}\n\n/**\n * 生成单个类型的 zod schema 代码片段(结构化形式)\n *\n * 与 generateZodSchemaSource 的区别:不拼 import 头,命名类型声明与入口导出分开返回,\n * 供文件级组装按名去重——同一 handler 文件的多个方法引用同一命名类型时,命名类型\n * 声明只允许出现一次,否则拼接产物含重复 const 声明,import 即 SyntaxError。\n *\n * 语义与 generateZodSchemaSource 完全一致(含 coerce / z.lazy 循环引用处理)。\n */\nexport interface ZodSchemaSourceParts {\n /** 命名类型声明(按首次引用顺序排列;name 为不含 Schema 后缀的类型名) */\n namedTypeDeclarations: Array<{ name: string; declaration: string }>;\n /** 入口 schema 导出(`export const NameSchema = ...;`) */\n entryDeclaration: string;\n}\n\nexport function generateZodSchemaSourceParts(\n typeInfo: HandlerTypeInfo,\n resolveType: TypeResolver,\n exportName?: string,\n coerce = false,\n): ZodSchemaSourceParts {\n const ctx = new CodeGenContext(resolveType);\n const name = exportName ?? typeInfo.name;\n ctx.entryTypeName = typeInfo.name;\n ctx.entryExportName = name;\n ctx.coerce = coerce;\n\n // 收集入口类型中的所有命名类型(不含入口类型本身,避免重复声明)\n collectNamedTypes(typeInfo.runtimeType, ctx);\n ctx.namedTypes.delete(typeInfo.name);\n\n const namedTypeDeclarations = [...ctx.namedTypes].map(([n, type]) => ({\n name: n,\n declaration: generateNamedTypeDeclaration(n, type, ctx),\n }));\n\n // 入口 schema 导出(含循环引用时用 z.lazy)\n const entryExpr = runtimeTypeToZodExpression(typeInfo.runtimeType, ctx);\n const hasSelfRef = containsRef(typeInfo.runtimeType, new Set([typeInfo.name]));\n const entryDeclaration = hasSelfRef\n ? `export const ${name}Schema = z.lazy(() => ${entryExpr});`\n : `export const ${name}Schema = ${entryExpr};`;\n\n return { namedTypeDeclarations, entryDeclaration };\n}\n","import path from 'node:path';\nimport { atomicWriteFile } from '../utils/atomicWrite';\nimport type { RouteManifest } from '../router/routeTypes';\nimport type { RouteSchemaSource } from './collectRouteSchemaSources';\nimport type { RuntimeType } from '../ast/resolveTypeNode';\nimport { collectRouteSchemaSources } from './collectRouteSchemaSources';\nimport {\n generateZodSchemaSourceParts,\n generateHelpersFileSource,\n usesCoerceHelpers,\n HELPERS_FILENAME,\n} from '../ast/generateZodSchema';\n\n/**\n * 源文件路径 → 产物 zod.js 路径\n *\n * 每个 handler 目录下生成一个 `zod.js`(与 handler.js 同级,文件名固定为 zod.js):\n * - `src/api/hello/handler.ts` → `<dist>/api/hello/zod.js`\n *\n * 路由源码目录写死为 src,剥离 `src/` 前缀。\n *\n * @param sourceFile 源文件相对路径(相对 rootDir,如 'src/api/hello/handler.ts')\n * @param dist 输出目录(如 'dist' 或 '.faapi')\n * @param rootDir 项目根目录\n */\nexport function getSchemaOutputPath(sourceFile: string, dist: string, rootDir: string): string {\n let rel = sourceFile.replace(/\\\\/g, '/');\n // 去掉 src/ 前缀(打平产物结构)\n if (rel.startsWith('src/')) {\n rel = rel.slice(4);\n }\n // 取目录部分,basename 固定为 zod.js(每个 handler 目录一个 zod.js)\n const idx = rel.lastIndexOf('/');\n const relDir = idx >= 0 ? rel.slice(0, idx) : '';\n return path.resolve(rootDir, dist, relDir, 'zod.js');\n}\n\n/**\n * 运行时从 route.filePath 计算对应 zod.js 绝对路径\n *\n * 每个 handler 目录下的 zod.js(文件名固定为 zod.js,与 handler.js 同级):\n * - dev 模式:route.filePath 是源码路径(如 'src/api/hello/handler.ts'),\n * zod.js 在 `<rootDir>/<dist>/api/hello/zod.js`\n * - prod 模式:route.filePath 是产物路径(如 'dist/api/hello/handler.js'),\n * zod.js 在 `<rootDir>/dist/api/hello/zod.js`(与 handler.js 同级)\n *\n * 通过同时检查 src 和 dist 前缀,统一处理两种模式:\n * - dev:strip 'src/' 前缀,join dist\n * - prod:strip 'dist/' 前缀,join dist(dist='dist',结果与 rootDir 一致)\n *\n * @param filePath route.filePath(dev 为源码路径,prod 为产物路径)\n * @param dist 输出目录(如 'dist' 或 '.faapi')\n * @param rootDir 项目根目录\n */\nexport function getRuntimeSchemaPath(filePath: string, dist: string, rootDir: string): string {\n let rel = filePath.replace(/\\\\/g, '/');\n // strip src/ 前缀(dev 模式:filePath = 'src/...')\n if (rel.startsWith('src/')) {\n rel = rel.slice(4);\n }\n // strip dist 前缀(prod 模式:filePath = 'dist/...')\n else if (rel.startsWith(`${dist}/`)) {\n rel = rel.slice(dist.length + 1);\n }\n // 取目录部分,basename 固定为 zod.js\n const idx = rel.lastIndexOf('/');\n const relDir = idx >= 0 ? rel.slice(0, idx) : '';\n return path.resolve(rootDir, dist, relDir, 'zod.js');\n}\n\n/**\n * 计算 zod.js 到 faapi-helpers.js 的相对 import 路径\n *\n * faapi-helpers.js 固定在 dist 根部,zod.js 可能在子目录(如 'api/hello/zod.js')。\n * 根据子目录深度计算 `../` 数量:\n * - zod.js 在 `api/hello/`(深度 2)→ `../../faapi-helpers.js`\n * - zod.js 在 `api/`(深度 1)→ `../faapi-helpers.js`\n * - zod.js 在 dist 根(深度 0,理论不会发生)→ `./faapi-helpers.js`\n *\n * @param relDir zod.js 所在目录相对 dist 的路径(如 'api/hello' 或 '')\n * @returns ESM import 路径(如 '../../faapi-helpers.js')\n */\nexport function getHelpersImportPath(relDir: string): string {\n if (!relDir) return `./${HELPERS_FILENAME}`;\n const depth = relDir.split('/').filter(Boolean).length;\n return `${'../'.repeat(depth)}${HELPERS_FILENAME}`;\n}\n\n/**\n * 生成单个 handler 文件的 zod.js 源码\n *\n * 自包含:extractAllTypes 在 AST 阶段已通过 TypeScript checker 内联跨文件类型,\n * 每个 zod.js 无需 import 其他 zod.js。ref 仅用于同文件内的循环引用(通过 z.lazy 处理)。\n *\n * 导出格式:\n * - `<SchemaName>Schema`:zod schema 对象(用于 safeParse 校验)\n *\n * 无类型声明的方法不导出对应 Schema(validateInput 检测到 undefined 跳过校验)。\n *\n * coerce 逻辑:query/params 来源均为 string,schemaName 以 \"Query\" 或 \"Params\" 结尾时\n * 生成 coerce=true 的 schema(number/boolean 字段用 z.preprocess 包裹字符串转换)。\n * body 是 JSON 解析的天然 JS 类型,不需要 coerce。form 与 body 共享 schema 名(POSTBody),\n * 但 `RouteSchemaSource.coerce=true` 显式覆盖(form 值均为 string,需 coerce)。\n * Map/Set 字段在两种场景下都生成 z.preprocess 包裹(JSON.parse 出来的是数组/对象,需还原为 Map/Set 实例)。\n *\n * 命名类型声明文件级去重:同一文件的多个方法引用同一命名类型时,`const XSchema` 只在\n * 文件头声明一次(按首次引用顺序),避免重复 const 声明导致 zod.js import 即 SyntaxError。\n *\n * 公用函数复用:schema 引用 coerceNumber / coerceBoolean / coerceMap / coerceSet 变量时,\n * 这些变量从 dist 根部的 faapi-helpers.js import(跨文件复用,仅一份声明)。\n *\n * @param sources 同一文件的 schema 提取结果(含多个方法,如 GETQuery + POSTBody)\n * @param resolveType ref 解析函数(解析循环引用中的 ref):名称 → 运行时类型,\n * 未声明返回 undefined。惰性实现由 collectRouteSchemaSources 的 resolversByFile 提供。\n * @param helpersImportPath 到 faapi-helpers.js 的相对 import 路径(如 '../../faapi-helpers.js')。\n * 传空字符串表示不注入 coerce helpers 的 import(用于无 coerce schema 的文件或不支持外部 import 的测试场景)。\n * @returns zod.js 源码字符串\n */\nexport function generateSchemaFileSource(\n sources: RouteSchemaSource[],\n resolveType: (name: string) => RuntimeType | undefined,\n helpersImportPath: string,\n): string {\n const lines: string[] = [\"import { z } from 'zod';\"];\n\n // 文件级命名类型声明去重:同一文件的多个方法引用同一命名类型(如 GETQuery 与\n // POSTBody 都引用 Item)时,const 声明只提升到文件头生成一次——否则拼接产物含\n // 重复 const 声明,zod.js import 即 SyntaxError,该文件所有路由 500。\n const namedTypeDeclarations: string[] = [];\n const seenNamedTypes = new Set<string>();\n\n // 先生成所有 schema 入口导出,暂存到 schemaBlocks\n const schemaBlocks: string[] = [];\n for (const source of sources) {\n const { schemaName, typeInfo } = source;\n if (!typeInfo) {\n // 无类型声明,不导出对应 Schema\n continue;\n }\n\n // 推断 coerce:\n // - source.coerce 显式设置时优先使用(form 声明时由 collectRouteSchemaSources 设置为 true)\n // - 否则回退到 schemaName 后缀正则:query/params 需要 coerce(URL 来源均为 string),body 不需要\n const coerce = source.coerce ?? /(?:Query|Params)$/.test(schemaName);\n\n // 生成结构化片段:命名类型声明按名去重后提升到文件头,入口导出留在方法 block 内\n const { namedTypeDeclarations: decls, entryDeclaration } = generateZodSchemaSourceParts(\n typeInfo,\n resolveType,\n schemaName,\n coerce,\n );\n for (const { name, declaration } of decls) {\n if (seenNamedTypes.has(name)) continue;\n seenNamedTypes.add(name);\n namedTypeDeclarations.push(declaration);\n }\n\n schemaBlocks.push([`// ${schemaName}`, entryDeclaration, ''].join('\\n'));\n }\n\n if (namedTypeDeclarations.length > 0) {\n lines.push(...namedTypeDeclarations);\n lines.push('');\n }\n\n // 检测是否有 schema 引用了 coerce 公用函数,若有则注入 import 语句\n // 公用函数包含 coerceNumber / coerceBoolean(query/params 的 string 转换)和\n // coerceMap / coerceSet(Map/Set 的 JSON 还原,body 场景也会引用)\n const allSchemaCode = [...namedTypeDeclarations, ...schemaBlocks].join('\\n');\n if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {\n lines.push(\n `import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`,\n );\n }\n lines.push('');\n\n lines.push(...schemaBlocks);\n\n return lines.join('\\n').replace(/\\n+$/, '\\n');\n}\n\n/**\n * 为路由清单中每个 handler 生成 zod.js\n *\n * 输出路径与 handler.js 同级(文件名固定为 zod.js):\n * - dev:`.faapi/api/hello/zod.js`\n * - prod:`dist/api/hello/zod.js`\n *\n * 公用函数复用:若项目中存在 coerce schema(query/params)或 Map/Set 字段,\n * 在 dist 根部生成 `faapi-helpers.js`(导出 coerceNumber / coerceBoolean / coerceMap / coerceSet),\n * 各 zod.js 通过相对路径 import。\n *\n * 内部流程:\n * 1. collectRouteSchemaSources 从路由清单提取 schema 源数据(AST 从源码 .ts)\n * 2. 按 filePath 分组 sources\n * 3. 为每个文件生成 zod.js 源码(暂存,用于检测是否需要 helpers)\n * 4. 检测是否需要 helpers,需要则生成 faapi-helpers.js\n * 5. 并行写入所有 zod.js\n *\n * @param routes 排序后的路由清单\n * @param rootDir 项目根目录\n * @param dist 输出目录(如 '.faapi' 或 'dist')\n */\nexport async function generateSchemaFiles(\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n): Promise<void> {\n if (routes.length === 0) return;\n\n const { sources, resolversByFile } = collectRouteSchemaSources(routes, rootDir);\n\n // 按 filePath 分组 sources(同一 handler 的多个方法合并到一个 zod.js)\n const sourcesByFile = new Map<string, RouteSchemaSource[]>();\n for (const source of sources) {\n let list = sourcesByFile.get(source.filePath);\n if (!list) {\n list = [];\n sourcesByFile.set(source.filePath, list);\n }\n list.push(source);\n }\n\n // 为每个文件生成 zod.js 源码(先暂存,用于检测是否需要 helpers)\n const fileEntries: { outputPath: string; source: string }[] = [];\n for (const [filePath, fileSources] of sourcesByFile) {\n // filePath 是绝对路径,转为相对 rootDir 的路径用于计算输出路径\n const relFile = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const outputPath = getSchemaOutputPath(relFile, dist, rootDir);\n // 计算 zod.js 所在目录相对 dist 的路径(用于 import helpers)\n // 与 getSchemaOutputPath 的目录计算逻辑一致:strip src/ 前缀后取目录部分\n let relForDir = relFile;\n if (relForDir.startsWith('src/')) {\n relForDir = relForDir.slice(4);\n }\n const dirIdx = relForDir.lastIndexOf('/');\n const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : '';\n const helpersImportPath = getHelpersImportPath(zodRelDir);\n\n const resolver = resolversByFile.get(filePath);\n const source = generateSchemaFileSource(\n fileSources,\n (name) => resolver?.resolve(name)?.runtimeType,\n helpersImportPath,\n );\n fileEntries.push({ outputPath, source });\n }\n\n // 检测是否需要生成 faapi-helpers.js\n const allSourceCode = fileEntries.map((e) => e.source).join('\\n');\n if (usesCoerceHelpers(allSourceCode)) {\n const helpersPath = path.resolve(rootDir, dist, HELPERS_FILENAME);\n await writeSchemaFile(helpersPath, generateHelpersFileSource());\n }\n\n // 并行写入所有 zod.js\n await Promise.all(\n fileEntries.map(({ outputPath, source }) => writeSchemaFile(outputPath, source)),\n );\n}\n\n/**\n * 写入 zod.js 文件(确保目录存在)\n */\nasync function writeSchemaFile(outputPath: string, source: string): Promise<void> {\n // 原子写:dev watch 重建与在途请求并发时,请求 import zod.js 不能读到半成品\n await atomicWriteFile(outputPath, source);\n}\n","import path from 'node:path';\nimport { existsSync } from 'node:fs';\nimport type { ToolManifestList } from '../tools/toolTypes';\nimport type { ToolMetadata } from '../ast/extractToolMetadata';\nimport { extractToolMetadata } from '../ast/extractToolMetadata';\nimport { createPrograms } from '../ast/createProgram';\nimport { toProdFilePath } from '../utils/prodPaths';\nimport { atomicWriteFile } from '../utils/atomicWrite';\nimport { getSchemaOutputPath } from './generateSchemaFiles';\nimport {\n extractTypeInfo,\n createLazyTypeResolver,\n type HandlerTypeInfo,\n type LazyTypeResolver,\n} from '../ast/extractHandlerTypes';\nimport type { RuntimeType } from '../ast/resolveTypeNode';\nimport {\n generateZodSchemaSource,\n generateHelpersFileSource,\n usesCoerceHelpers,\n HELPERS_FILENAME,\n} from '../ast/generateZodSchema';\nimport { getHelpersImportPath } from './generateSchemaFiles';\n\n/**\n * 序列化的 tool manifest 记录(可写入 JS 模块,无函数引用)\n *\n * 与 [ToolMetadata](../ast/extractToolMetadata.md) 字段一一对应,仅 `filePath`\n * 由源码形式(`src/...`)转为产物形式(`<dist>/...`,打平 `src/` 前缀 + dist 前缀 + `.js`)。\n *\n * `undefined` 字段(description / inputTypeName)在 JSON.stringify 时自动省略,\n * 水合时通过 `??` 兜底为 undefined。\n */\nexport interface SerializedToolRecord {\n /** tool 名(`@tool` 覆盖值 或 路径推导值) */\n name: string;\n /** 源码中的导出函数名(供 loadToolModule 在 handler.js 中按名 import) */\n functionName: string;\n /** JSDoc 描述(对 LLM 可见) */\n description?: string;\n /** 第一个参数 interface 名(用于从 zod.js 加载 schema) */\n inputTypeName?: string;\n /** 产物形式路径(如 `dist/tools/weather/handler.js`),供运行时 import tool.js */\n filePath: string;\n}\n\n/**\n * 单个 tool 的 schema 提取结果\n *\n * 与 [RouteSchemaSource](./collectRouteSchemaSources.md) 对称,但 schema key 用 tool 名而非 urlPath。\n */\nexport interface ToolSchemaSource {\n /** tool 名(含 `@tool` 覆盖),用作注释标识 */\n name: string;\n /** 源文件绝对路径(用于 generateToolArtifacts 按文件分组生成 zod.js) */\n filePath: string;\n /** schema 名 = inputTypeName(导出 `${inputTypeName}Schema`) */\n schemaName: string;\n /** AST 提取的类型信息,null 时不导出 Schema */\n typeInfo: HandlerTypeInfo | null;\n}\n\n/**\n * faapi-tools.js 文件名(与 faapi-routes.js 同构)\n */\nconst TOOLS_FILE = 'faapi-tools.js';\n\n/**\n * tool zod.js 输出路径 / 运行时路径计算\n *\n * 与路由 schema 的路径规则完全一致(strip src/ 前缀 + dist + 固定 zod.js 文件名,\n * 见 generateSchemaFiles.ts 的实现),此处别名 re-export 保持 tool 侧语义命名。\n * 此前两份逐字同构实现已合并。\n */\nexport {\n getSchemaOutputPath as getToolSchemaOutputPath,\n getRuntimeSchemaPath as getRuntimeToolSchemaPath,\n} from './generateSchemaFiles';\n/**\n * 序列化 tool 清单为可写入 JS 模块的结构\n *\n * - `filePath` 转为产物形式(打平 `src/` 前缀 + dist 前缀 + `.js`)\n * - 其他字段(name/functionName/description/inputTypeName)直接透传\n * - `undefined` 字段在 JSON.stringify 时自动省略\n *\n * @param tools AST 增强后的 ToolMetadata[](由 generateToolArtifacts 内部从 ToolManifest 增强)\n * @param dist 产物目录(默认 `dist`),用于转换 filePath\n */\nexport function serializeTools(\n tools: ToolMetadata[],\n dist: string = 'dist',\n): SerializedToolRecord[] {\n return tools.map((t) => ({\n name: t.name,\n functionName: t.functionName,\n description: t.description,\n inputTypeName: t.inputTypeName,\n filePath: toProdFilePath(t.filePath, dist),\n }));\n}\n\n/**\n * 把序列化的 tool 清单写入 faapi-tools.js\n *\n * 生成 ESM 模块,运行时 `createAppCore` 通过 `importWithCacheBust` 加载。\n * 用 JSON.stringify 嵌入,保证字符串转义安全(与 `writeRoutesModule` 一致)。\n */\nexport async function writeToolsModule(\n manifest: SerializedToolRecord[],\n outputPath: string,\n): Promise<void> {\n const content = `// 自动生成,请勿手动编辑(faapi build/dev 产物)\nexport const tools = ${JSON.stringify(manifest, null, 2)};\n`;\n await atomicWriteFile(outputPath, content);\n}\n\n/**\n * 从序列化清单水合还原 ToolMetadata[]\n *\n * 字段一一对应(无函数引用需还原)。`undefined` 字段在 JSON.parse 时缺失,\n * 通过 `??` 兜底为 undefined(保证 `ToolMetadata` 类型完整)。\n */\nexport function hydrateTools(manifest: SerializedToolRecord[]): ToolMetadata[] {\n return manifest.map((t) => ({\n name: t.name,\n functionName: t.functionName,\n description: t.description ?? undefined,\n inputTypeName: t.inputTypeName ?? undefined,\n filePath: t.filePath,\n }));\n}\n\n/**\n * 从 ToolMetadata[] 收集 schema 提取所需的原始数据\n *\n * 与 [collectRouteSchemaSources](./collectRouteSchemaSources.ts) 对称,但:\n * - 按 tool handler.ts 文件分组(同一 handler.ts 多个 tool 合并到一个 zod.js)\n * - schema 名 = inputTypeName(导出 `${inputTypeName}Schema`,与路由的 `<METHOD><InputType>Schema` 不同)\n * - coerce=false(tool input 来自 LLM JSON 调用,与 body 一致,无需字符串转换)\n *\n * 跳过 inputTypeName 为 undefined 的 tool(无参数/无类型标注/内联类型字面量),\n * 不生成对应 schema(与路由的\"无类型声明的方法不导出 Schema\"对齐)。\n *\n * 与路由的 collectRouteSchemaSources 一致采用惰性解析:入口类型(inputTypeName)\n * 立即解析且必须严格校验,ref 按需解析并缓存——与 tool 无关的类型零开销。\n *\n * @returns sources(用于生成 zod.js) + resolversByFile(用于解析 ref)\n */\nfunction collectToolSchemaSources(\n tools: ToolMetadata[],\n rootDir: string,\n): {\n sources: ToolSchemaSource[];\n resolversByFile: Map<string, LazyTypeResolver>;\n} {\n // 按文件分组 tool(同一 handler.ts 多个 tool 合并到一个 zod.js)\n const toolsByFile = new Map<string, ToolMetadata[]>();\n for (const tool of tools) {\n if (!tool.inputTypeName) continue; // 跳过无 inputTypeName 的 tool\n const absPath = path.resolve(rootDir, tool.filePath);\n let list = toolsByFile.get(absPath);\n if (!list) {\n list = [];\n toolsByFile.set(absPath, list);\n }\n list.push(tool);\n }\n\n // 批量共享 Program:同一次提取只创建一个 Program,避免逐文件全量解析\n const programByFile = createPrograms([...toolsByFile.keys()]);\n const resolversByFile = new Map<string, LazyTypeResolver>();\n for (const filePath of toolsByFile.keys()) {\n resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath)!, filePath));\n }\n\n // 为每个 tool 提取 schema\n const sources: ToolSchemaSource[] = [];\n for (const [filePath, fileTools] of toolsByFile) {\n const program = programByFile.get(filePath)!;\n for (const tool of fileTools) {\n // inputTypeName 已在外层过滤,这里一定非空(防御性兜底)\n const inputTypeName = tool.inputTypeName!;\n const typeInfo = extractTypeInfo(program, filePath, inputTypeName); // 入口类型,严格解析\n sources.push({\n name: tool.name,\n filePath,\n schemaName: inputTypeName, // schema 名 = inputTypeName\n typeInfo,\n });\n }\n }\n\n return { sources, resolversByFile };\n}\n\n/**\n * 生成单个 tool handler.ts 的 zod.js 源码\n *\n * 自包含:[extractAllTypes](../ast/extractHandlerTypes.md) 在 AST 阶段已通过 TypeScript checker\n * 内联跨文件类型,每个 zod.js 无需 import 其他 zod.js。`ref` 仅用于同文件内的循环引用\n * (通过 `z.lazy` 处理)。\n *\n * 导出格式:\n * - `${inputTypeName}Schema`:zod schema 对象(用于 safeParse 校验)\n *\n * 与 [generateSchemaFileSource](./generateSchemaFiles.ts) 的差异:\n * - schema 名直接取自 `inputTypeName`(无 HTTP 方法前缀)\n * - coerce=false(tool input 来自 LLM JSON 调用,与 body 一致)\n *\n * 无 inputTypeName 的 tool 不导出对应 Schema(collectToolSchemaSources 已过滤)。\n *\n * @param sources 同一文件的 schema 提取结果(含多个 tool)\n * @param resolveType ref 解析函数(解析循环引用中的 ref):名称 → 运行时类型,未声明返回 undefined\n * @param helpersImportPath 到 faapi-helpers.js 的相对 import 路径(如 `../../faapi-helpers.js`)。\n * 传空字符串表示不注入 coerce helpers 的 import(用于无 helpers 引用的文件或测试场景)。\n */\nexport function generateToolSchemaFileSource(\n sources: ToolSchemaSource[],\n resolveType: (name: string) => RuntimeType | undefined,\n helpersImportPath: string,\n): string {\n const lines: string[] = [\"import { z } from 'zod';\"];\n\n // 先生成所有 schema 代码,暂存到 schemaBlocks\n const schemaBlocks: string[] = [];\n for (const source of sources) {\n const { schemaName, typeInfo } = source;\n if (!typeInfo) {\n // 无类型声明,不导出对应 Schema\n continue;\n }\n\n // tool schema coerce=false(input 来自 LLM JSON 调用,与 body 一致)\n const coerce = false;\n\n const block = [`// ${source.name} → ${schemaName}`];\n // generateZodSchemaSource 自带 import 语句,剥离后由本函数统一管理 import\n // 传入 schemaName 作为 exportName,确保导出名与运行时查找一致(`${inputTypeName}Schema`)\n const schemaCode = generateZodSchemaSource(typeInfo, resolveType, schemaName, coerce).replace(\n /^import \\{ z \\} from 'zod';\\s*\\n\\s*\\n/,\n '',\n );\n block.push(schemaCode);\n block.push('');\n schemaBlocks.push(block.join('\\n'));\n }\n\n // 检测是否有 schema 引用了 coerce 公用函数(Map/Set 还原,即使 coerce=false 也可能引用)\n const allSchemaCode = schemaBlocks.join('\\n');\n if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {\n lines.push(\n `import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`,\n );\n }\n lines.push('');\n\n lines.push(...schemaBlocks);\n\n return lines.join('\\n').replace(/\\n+$/, '\\n');\n}\n\n/**\n * 检测并生成 faapi-helpers.js(若不存在且 schema 引用了公用函数)\n *\n * tool schema 在两种场景下会引用 faapi-helpers.js 的公用变量:\n * 1. Map/Set 字段:即使 coerce=false 也引用 `coerceMap` / `coerceSet`(JSON.parse 出来的是数组/对象,需还原)\n * 2. number/boolean 字段:coerce=true 时引用 `coerceNumber` / `coerceBoolean`\n * (tool schema coerce=false,不会触发此场景)\n *\n * 与 [generateSchemaFiles](./generateSchemaFiles.ts) 共享同一份 `faapi-helpers.js`——\n * 若文件已存在则不重复生成(路由 schema 生成阶段可能已生成)。\n */\nasync function maybeGenerateHelpers(allSourceCode: string, distDir: string): Promise<void> {\n if (!usesCoerceHelpers(allSourceCode)) return;\n const helpersPath = path.resolve(distDir, HELPERS_FILENAME);\n if (existsSync(helpersPath)) return;\n await atomicWriteFile(helpersPath, generateHelpersFileSource());\n}\n\n/**\n * 主入口:从 ToolManifest[] 生成 faapi-tools.js + 每个 tool 的 zod.js\n *\n * 内部流程:\n * 1. 对每个 ToolManifest 调 `createProgram` + `extractToolMetadata` → ToolMetadata[]\n * (AST 增强:补全 description / inputTypeName / `@tool` 覆盖后的 name)\n * 2. `serializeTools(metadata, dist)` → SerializedToolRecord[](filePath 转产物形式)\n * 3. `writeToolsModule(serialized, faapiToolsPath)` → 写入 `<dist>/faapi-tools.js`\n * 4. 若 `skipSchema=true`(dev 按需模式)→ 返回,不生成 zod.js\n * 5. `collectToolSchemaSources` 从 ToolMetadata[] 收集 schema 源数据(按文件分组)\n * 6. 为每个文件计算 `helpersImportPath` + 生成 zod.js 源码(暂存)\n * 7. `maybeGenerateHelpers` 检测并生成/复用 `faapi-helpers.js`\n * 8. 并行写入所有 zod.js\n *\n * @param tools scanTools 产出的 ToolManifest[](仅路径推导字段)\n * @param rootDir 项目根目录\n * @param dist 产物目录(`.faapi` 或 `dist`)\n * @param options.skipSchema 跳过 zod.js 生成(dev 按需模式启动时仅生成清单)\n * @returns AST 增强后的 ToolMetadata[](供调用方日志/调试)\n */\nexport async function generateToolArtifacts(\n tools: ToolManifestList,\n rootDir: string,\n dist: string,\n options?: { skipSchema?: boolean },\n): Promise<ToolMetadata[]> {\n // 1. AST 增强:对每个 manifest 调 extractToolMetadata(批量共享 Program)\n const metadata: ToolMetadata[] = [];\n const programByFile = createPrograms(tools.map((m) => path.resolve(rootDir, m.filePath)));\n for (const manifest of tools) {\n const absPath = path.resolve(rootDir, manifest.filePath);\n const program = programByFile.get(absPath)!;\n const result = extractToolMetadata(program, absPath, manifest.functionName, {\n name: manifest.name,\n filePath: manifest.filePath,\n });\n if (result) {\n metadata.push(result);\n }\n }\n\n // 2. 序列化 + 写入 faapi-tools.js\n const serialized = serializeTools(metadata, dist);\n const toolsPath = path.resolve(rootDir, dist, TOOLS_FILE);\n await writeToolsModule(serialized, toolsPath);\n\n // 4. dev 按需模式:跳过 zod.js 生成(首次请求时由 ensureToolSchemaGenerated 按需生成)\n if (options?.skipSchema) {\n return metadata;\n }\n\n // 5-8. 生成 zod.js\n if (metadata.length === 0) {\n return metadata;\n }\n\n const { sources, resolversByFile } = collectToolSchemaSources(metadata, rootDir);\n if (sources.length === 0) {\n return metadata; // 无 inputTypeName 的 tool,不生成 zod.js\n }\n\n // 按文件分组 sources(同一 handler.ts 多个 tool 合并到一个 zod.js)\n const sourcesByFile = new Map<string, ToolSchemaSource[]>();\n for (const source of sources) {\n let list = sourcesByFile.get(source.filePath);\n if (!list) {\n list = [];\n sourcesByFile.set(source.filePath, list);\n }\n list.push(source);\n }\n\n // 为每个文件生成 zod.js 源码(先暂存,用于检测是否需要 helpers)\n const fileEntries: { outputPath: string; source: string }[] = [];\n for (const [filePath, fileSources] of sourcesByFile) {\n const relFile = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const outputPath = getSchemaOutputPath(relFile, dist, rootDir);\n const resolver = resolversByFile.get(filePath);\n\n // 计算 zod.js 所在目录相对 dist 的路径(用于 import helpers)\n let relForDir = relFile;\n if (relForDir.startsWith('src/')) {\n relForDir = relForDir.slice(4);\n }\n const dirIdx = relForDir.lastIndexOf('/');\n const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : '';\n const helpersImportPath = getHelpersImportPath(zodRelDir);\n\n const source = generateToolSchemaFileSource(\n fileSources,\n (name) => resolver?.resolve(name)?.runtimeType,\n helpersImportPath,\n );\n fileEntries.push({ outputPath, source });\n }\n\n // 检测是否需要生成 faapi-helpers.js\n const allSourceCode = fileEntries.map((e) => e.source).join('\\n');\n const distDir = path.resolve(rootDir, dist);\n await maybeGenerateHelpers(allSourceCode, distDir);\n\n // 并行写入所有 zod.js\n await Promise.all(\n fileEntries.map(({ outputPath, source }) => writeToolSchemaFile(outputPath, source)),\n );\n\n return metadata;\n}\n\n/**\n * 写入 tool zod.js 文件(确保目录存在)\n */\nasync function writeToolSchemaFile(outputPath: string, source: string): Promise<void> {\n await atomicWriteFile(outputPath, source);\n}\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { AgentManifestList } from './agentTypes';\n\n/**\n * 默认 agent 扫描 patterns\n *\n * 与 tool 扫描([TOOL_PATTERNS](../tools/scanTools.md))对称——\n * agent 定义文件约定放在 `src/agents/<agentName>/handler.ts`(一级目录)。\n *\n * 由 devCommand / buildCommand / createDevApp.reloadAgents(Phase 1.9)共享,\n * 避免多处重复定义。\n */\nexport const DEFAULT_AGENT_PATTERNS = ['src/agents/*/handler.ts'];\n\n/**\n * 检测 `run` 函数导出\n *\n * 匹配:\n * - `export function run() { ... }`\n * - `export async function run() { ... }`\n * - `export const run = () => { ... }`\n * - `export const run = async () => { ... }`\n *\n * 不匹配(`\\b` 词边界保证精确匹配 `run`):\n * - `export const runtime = ...`(run 后跟 `time`)\n *\n * > `config` 导出检测已移除(`hasConfig` 字段已废弃,见 [agentTypes](./agentTypes.md))。\n * > AST 提取阶段 [extractAgentMetadata](../ast/extractAgentMetadata.md) 仍会查找 config\n * > 导出(用于提取 JSDoc 描述 + config 块字段),但运行时不再需要 `hasConfig` 标志。\n */\nconst RUN_EXPORT_RE = /export\\s+(?:async\\s+)?(?:function\\s+|const\\s+)run\\b/;\n\n/**\n * 从文件路径提取 agent 名\n *\n * 匹配 `src/agents/<agentName>/handler.ts` 模式,提取 `<agentName>`。\n *\n * - `src/agents/researcher/handler.ts` → `researcher`\n * - `backup/agents/researcher/handler.ts` → `researcher`(任意前缀,只要匹配 agents/<name>/handler.ts)\n *\n * @throws 路径不匹配 agent 模式时抛错(不应发生——glob pattern 已限制)\n */\nfunction extractAgentNameFromPath(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n const match = normalized.match(/(?:^|\\/)agents\\/([^/]+)\\/handler\\.ts$/);\n if (!match) {\n throw new Error(\n `Not an agent path: \"${filePath}\". Expected pattern: src/agents/<name>/handler.ts`,\n );\n }\n return match[1]!;\n}\n\n/**\n * 从源码检测 `run` 导出是否存在\n *\n * 不 import 模块——正则匹配源码文本(Vite 风格,与 [scanTools](../tools/scanTools.md) 的\n * `extractToolExportsFromSource` 同构)。\n */\nfunction detectAgentExports(source: string): { hasRun: boolean } {\n return {\n hasRun: RUN_EXPORT_RE.test(source),\n };\n}\n\n/**\n * 扫描 agents 目录,生成 agent 清单\n *\n * Vite 风格:启动时只读源码 + 正则检测 `run` 导出,不 import agent.js。\n * agent.js 加载延后到 [loadAgentModule](../loader/loadAgentModule.md) 请求阶段(Phase 1.9)。\n *\n * agent 文件格式:`src/agents/<agentName>/handler.ts`,导出 `config` 块(可选)和\n * `run` 函数(可选)。一个目录一份 handler.ts = 一个 agent。\n *\n * 重名检测:同 agent 名出现在多个文件 → 抛错(agent 名全局唯一,无作用域维度)。\n *\n * @param rootDir 项目根目录\n * @param patterns glob patterns(源码 .ts 路径,匹配 agent handler 文件)\n * @returns `AgentManifestList`\n */\nexport async function scanAgents(rootDir: string, patterns: string[]): Promise<AgentManifestList> {\n const files = await fg(patterns, {\n cwd: rootDir,\n onlyFiles: true,\n absolute: false,\n });\n\n const agents: AgentManifestList = [];\n // 重名检测:agentName → filePath\n const seen = new Map<string, string>();\n\n for (const file of files) {\n const normalizedFile = file.replace(/\\\\/g, '/');\n const fileName = normalizedFile.split('/').pop()!;\n\n // 只处理 handler.ts(与路由/tool handler.ts 对称),其他 .ts 文件跳过\n if (fileName !== 'handler.ts' && fileName !== 'handler.js') {\n continue;\n }\n\n const absPath = path.resolve(rootDir, normalizedFile);\n const source = await fs.promises.readFile(absPath, 'utf8').catch(() => '');\n const { hasRun } = detectAgentExports(source);\n const name = extractAgentNameFromPath(normalizedFile);\n\n // 重名检测:同 agent 名报错\n const prevFile = seen.get(name);\n if (prevFile) {\n throw new Error(\n `Agent conflict: \"${name}\" declared in both ${prevFile} and ${normalizedFile}`,\n );\n }\n seen.set(name, normalizedFile);\n\n agents.push({\n name,\n filePath: normalizedFile,\n hasRun,\n });\n }\n\n return agents;\n}\n","import ts from 'typescript';\nimport {\n extractDescription,\n extractJSDocTagValue,\n getJSDocFromNode,\n hasExportModifier,\n} from './jsDocMetadata';\n\n/**\n * Agent 的 LLM 可见核心字段\n *\n * 描述\"agent 是什么\"——LLM 真正需要消费的字段,**不含**代码本体加载细节\n * (filePath / hasRun)。文件型 agent 与 DB-driven skill 都实现此接口。\n *\n * - 文件型 agent:由 [AgentMetadata](./extractAgentMetadata.md) 继承扩展,\n * 额外含 `filePath` / `hasRun`(代码本体加载用)\n * - DB-driven skill:业务方 plugin 从 DB 字段映射到本接口即可,无需填占位值\n * (skill 无源文件,不走 `loadAgentModule`,自然不读 filePath / hasRun)\n *\n * `@faapi/agent` 子包的 `Agent` 类、`agentRegistry` 查询入口、`asTool` 包装\n * 都消费 `AgentCore`,实现\"agent 与 skill 走同一运行时链路\"。\n */\nexport interface AgentCore {\n /** agent 名(`@agent` JSDoc 覆盖值 或 目录推导值) */\n name: string;\n /** JSDoc 描述(agent 描述,对 LLM 可见),无 JSDoc 或 JSDoc 无自由文本时为 `undefined` */\n description?: string;\n /** 系统提示词(config 块字面量提取),无/非字面量时为 `undefined` */\n systemPrompt?: string;\n /** agent 显式声明可用的 tool 引用列表(config 块字面量提取),无/含非字面量元素时为 `undefined` */\n tools?: string[];\n /** 可调用的其他 agent 名列表(config 块字面量提取),无/含非字面量元素时为 `undefined` */\n agents?: string[];\n /** LLM 模型名(config 块字面量提取),无/非字面量时为 `undefined` */\n model?: string;\n /** 最大对话轮数(config 块字面量提取),无/非字面量时为 `undefined` */\n maxTurns?: number;\n}\n\n/**\n * Agent 完整元数据(文件型 agent)\n *\n * 继承 [AgentCore](./extractAgentMetadata.md) 的 LLM 字段,额外扩展**代码本体加载细节**:\n * - `filePath` — `loadAgentModule` 加载 `handler.js` 产物提取 `run` 函数用\n * - `hasRun` — 是否导出 `run` 函数(`Agent.executeSubAgent` 据此决定走自定义 run\n * 还是默认 reactLoop)\n *\n * DB-driven skill 不实现此接口(无源文件,无需加载),只实现 `AgentCore`。\n *\n * 由 [extractAgentMetadata](./extractAgentMetadata.md) 产出,合并路径推导字段\n * (来自 [scanAgents](../agents/scanAgents.md) 的 `AgentManifest`)与 AST 提取字段\n * (JSDoc 描述、`@agent` 覆盖名、config 块字段)。\n *\n * 字段来源:\n * - `name` — `@agent` JSDoc 覆盖值,或 `pathMeta.name`(目录推导)\n * - `filePath` / `hasRun` — 由 `pathMeta` 透传\n * - `description` — JSDoc 注释块自由文本(对 LLM 可见)\n * - `systemPrompt` / `tools` / `agents` / `model` / `maxTurns` — config 块字面量提取\n */\nexport interface AgentMetadata extends AgentCore {\n /** 源码相对路径(从 `pathMeta` 透传),`loadAgentModule` 据此加载 `handler.js` 提取 `run` */\n filePath: string;\n /** 是否导出 `run` 函数(从 `pathMeta` 透传),`Agent.executeSubAgent` 据此选择自定义 run / 默认 reactLoop */\n hasRun: boolean;\n}\n\n/**\n * 路径推导的 agent 元数据(由 [scanAgents](../agents/scanAgents.ts) 计算)\n *\n * 透传到 [AgentMetadata](./extractAgentMetadata.ts) 输出,与 AST 提取字段合并。\n * 与 [ToolPathMeta](./extractToolMetadata.md) 对称。\n */\nexport interface AgentPathMeta {\n /** 目录推导的 agent 名(如 `researcher`) */\n name: string;\n /** 源码相对路径(如 `src/agents/researcher/handler.ts`) */\n filePath: string;\n /** 是否导出 `run` 函数(scanAgents 正则检测) */\n hasRun: boolean;\n}\n\n/** config 导出查找结果:JSDoc 持有节点 + 对象字面量(可能为 null) */\ninterface FoundConfig {\n /** JSDoc 持有节点(VariableStatement 或 FunctionDeclaration) */\n jsDocOwner: ts.Node;\n /** config 对象字面量(export const config = {...} 的 initializer 或 export function config() 的 return 值) */\n objectLiteral: ts.ObjectLiteralExpression | null;\n}\n\n/**\n * 从 agent handler.ts 提取 agent 的完整元数据\n *\n * 提取内容:\n * 1. **JSDoc 描述** — config 导出的 JSDoc 自由文本(无 config 时从 run 导出提取)\n * 2. **`@agent` 覆盖名** — JSDoc 中 `@agent` 标签后的文本,覆盖目录推导的 `name`\n * 3. **config 块字段** — systemPrompt / tools / agents / model / maxTurns\n *\n * 不提取(由 `pathMeta` 透传):`filePath` / `hasRun`\n *\n * config 查找支持两种导出形式,与 [scanAgents](../agents/scanAgents.md) 的 `CONFIG_EXPORT_RE` 正则同构:\n * - `export const config = { ... }` — 对象字面量(最常见)\n * - `export function config() { return { ... } }` — 函数返回对象\n *\n * JSDoc 查找对箭头函数/函数表达式自动回溯到外层 `VariableStatement`\n * (JSDoc 通常写在 `export const` 上方,而非箭头函数本身)。\n * 与 [extractToolMetadata](./extractToolMetadata.md) 的 JSDoc 查找同构。\n *\n * config 块字段提取仅处理字面量值——变量引用/Spread/模板字符串等非静态值返回 `undefined`。\n *\n * @param program TypeScript Program\n * @param filePath 源文件**绝对路径**(AST 用,需与 `program.getSourceFile` 一致)\n * @param pathMeta 路径推导的元数据(scanAgents 已计算)\n * @returns `AgentMetadata` 或 `null`(源文件不在 Program 中)\n */\nexport function extractAgentMetadata(\n program: ts.Program,\n filePath: string,\n pathMeta: AgentPathMeta,\n): AgentMetadata | null {\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n // 查找 config 导出(优先提取 JSDoc + config 块字段)\n const configFound = findConfigExport(sourceFile);\n let jsDocOwner: ts.Node | null = null;\n let objectLiteral: ts.ObjectLiteralExpression | null = null;\n\n if (configFound) {\n jsDocOwner = configFound.jsDocOwner;\n objectLiteral = configFound.objectLiteral;\n } else if (pathMeta.hasRun) {\n // 无 config 时从 run 导出提取 JSDoc\n const runNode = findRunExport(sourceFile);\n if (runNode) {\n jsDocOwner = runNode;\n }\n }\n\n const jsDoc = jsDocOwner ? getJSDocFromNode(jsDocOwner) : undefined;\n const description = extractDescription(jsDoc);\n const agentNameOverride = extractJSDocTagValue(jsDoc, 'agent');\n\n // config 块字段提取\n let systemPrompt: string | undefined;\n let tools: string[] | undefined;\n let agents: string[] | undefined;\n let model: string | undefined;\n let maxTurns: number | undefined;\n\n if (objectLiteral) {\n const fields = extractConfigFields(objectLiteral);\n systemPrompt = fields.systemPrompt;\n tools = fields.tools;\n agents = fields.agents;\n model = fields.model;\n maxTurns = fields.maxTurns;\n }\n\n return {\n name: agentNameOverride ?? pathMeta.name,\n description,\n filePath: pathMeta.filePath,\n hasRun: pathMeta.hasRun,\n systemPrompt,\n tools,\n agents,\n model,\n maxTurns,\n };\n}\n\n/**\n * 在源文件中查找 `config` 导出\n *\n * 支持两种形式:\n * - `export const config = { ... }` → VariableStatement,对象字面量是 initializer\n * - `export function config() { return { ... } }` → FunctionDeclaration,对象字面量是 return 表达式\n *\n * 返回 JSDoc 持有节点和对象字面量(可能为 null——函数无 return / return 非对象字面量)。\n *\n * 不依赖 `node.parent` 链——`ts.createProgram` 配置 `noEmit: true` 时不会设置父指针。\n */\nfunction findConfigExport(sourceFile: ts.SourceFile): FoundConfig | null {\n let result: FoundConfig | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n // export const config = { ... }\n if (ts.isVariableStatement(node) && hasExportModifier(node)) {\n for (const decl of node.declarationList.declarations) {\n if (result) break;\n const nameText = ts.isIdentifier(decl.name) ? decl.name.text : '';\n if (nameText !== 'config' || !decl.initializer) continue;\n\n // 对象字面量:export const config = { ... }\n if (ts.isObjectLiteralExpression(decl.initializer)) {\n result = { jsDocOwner: node, objectLiteral: decl.initializer };\n }\n // 箭头函数返回对象:export const config = () => ({ ... })\n else if (ts.isArrowFunction(decl.initializer)) {\n const returnObj = getReturnObjectLiteral(decl.initializer);\n result = { jsDocOwner: node, objectLiteral: returnObj };\n }\n }\n }\n\n // export function config() { return { ... } }\n if (ts.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === 'config') {\n const returnObj = getReturnObjectLiteral(node);\n result = { jsDocOwner: node, objectLiteral: returnObj };\n }\n });\n\n return result;\n}\n\n/**\n * 在源文件中查找 `run` 导出(用于 JSDoc 回退)\n *\n * 支持:`export function run` / `export async function run` / `export const run = () =>`\n * 返回 JSDoc 持有节点(FunctionDeclaration 本身 或 VariableStatement)。\n */\nfunction findRunExport(sourceFile: ts.SourceFile): ts.Node | null {\n let result: ts.Node | null = null;\n\n ts.forEachChild(sourceFile, (node) => {\n if (result) return;\n\n // export function run() / export async function run()\n if (ts.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === 'run') {\n result = node;\n return;\n }\n\n // export const run = () => {}\n if (ts.isVariableStatement(node) && hasExportModifier(node)) {\n for (const decl of node.declarationList.declarations) {\n if (result) break;\n const nameText = ts.isIdentifier(decl.name) ? decl.name.text : '';\n if (nameText !== 'run' || !decl.initializer) continue;\n if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {\n result = node; // JSDoc 持有者是 VariableStatement\n }\n }\n }\n });\n\n return result;\n}\n\n/**\n * 从函数体中提取 return 的对象字面量\n *\n * - `() => ({ ... })` — 箭头函数直接返回对象字面量(body 是 ObjectLiteralExpression)\n * - `() => { return { ... }; }` — block body 中的 return 语句\n * - `function f() { return { ... }; }` — block body 中的 return 语句\n *\n * 无 return / return 非对象字面量 → null\n */\nfunction getReturnObjectLiteral(\n fn: ts.FunctionDeclaration | ts.ArrowFunction,\n): ts.ObjectLiteralExpression | null {\n const body = fn.body;\n if (!body) return null;\n\n // 箭头函数直接返回对象字面量:() => ({ ... })\n if (ts.isObjectLiteralExpression(body)) {\n return body;\n }\n\n // block body:查找 return 语句\n if (ts.isBlock(body)) {\n for (const stmt of body.statements) {\n if (\n ts.isReturnStatement(stmt) &&\n stmt.expression &&\n ts.isObjectLiteralExpression(stmt.expression)\n ) {\n return stmt.expression;\n }\n }\n }\n\n return null;\n}\n\n/**\n * 从 config 对象字面量提取 config 块字段\n *\n * 遍历对象属性,按属性名匹配提取对应字段。仅处理字面量值——\n * 变量引用/Spread/模板字符串等非静态值返回 `undefined`。\n *\n * 属性名匹配支持 Identifier 和 StringLiteral 两种形式:\n * - `{ systemPrompt: 'x' }` — Identifier 属性名\n * - `{ 'systemPrompt': 'x' }` — StringLiteral 属性名\n *\n * SpreadAssignment(`...other`)跳过——无法静态求值。\n */\nfunction extractConfigFields(objLit: ts.ObjectLiteralExpression): {\n systemPrompt?: string;\n tools?: string[];\n agents?: string[];\n model?: string;\n maxTurns?: number;\n} {\n const result: {\n systemPrompt?: string;\n tools?: string[];\n agents?: string[];\n model?: string;\n maxTurns?: number;\n } = {};\n\n for (const prop of objLit.properties) {\n // 跳过 SpreadAssignment(...other)\n if (!ts.isPropertyAssignment(prop)) continue;\n\n const propName = getPropertyName(prop.name);\n if (!propName) continue;\n\n switch (propName) {\n case 'systemPrompt':\n result.systemPrompt = extractStringValue(prop.initializer);\n break;\n case 'tools':\n result.tools = extractStringArrayValue(prop.initializer);\n break;\n case 'agents':\n result.agents = extractStringArrayValue(prop.initializer);\n break;\n case 'model':\n result.model = extractStringValue(prop.initializer);\n break;\n case 'maxTurns':\n result.maxTurns = extractNumberValue(prop.initializer);\n break;\n }\n }\n\n return result;\n}\n\n/**\n * 从属性名节点提取字符串名\n *\n * 支持:\n * - Identifier(`systemPrompt` → `'systemPrompt'`)\n * - StringLiteral(`'systemPrompt'` → `'systemPrompt'`)\n *\n * ComputedPropertyName / NumericLiteral / PrivateIdentifier → null\n */\nfunction getPropertyName(name: ts.PropertyName): string | null {\n if (ts.isIdentifier(name)) return name.text;\n if (ts.isStringLiteral(name)) return name.text;\n return null;\n}\n\n/**\n * 从表达式提取字符串值(仅 StringLiteral)\n *\n * - `'hello'` → `'hello'`\n * - `\"hello\"` → `'hello'`\n * - 模板字符串 / 变量引用 / 数字 → `undefined`\n */\nfunction extractStringValue(expr: ts.Expression): string | undefined {\n if (ts.isStringLiteral(expr)) return expr.text;\n return undefined;\n}\n\n/**\n * 从表达式提取数字值(仅 NumericLiteral)\n *\n * - `10` → `10`\n * - `'10'` / 变量引用 → `undefined`\n */\nfunction extractNumberValue(expr: ts.Expression): number | undefined {\n if (ts.isNumericLiteral(expr)) {\n const num = Number(expr.text);\n return Number.isNaN(num) ? undefined : num;\n }\n return undefined;\n}\n\n/**\n * 从表达式提取字符串数组(ArrayLiteralExpression,全 StringLiteral 元素)\n *\n * - `['a', 'b']` → `['a', 'b']`\n * - `['a', someVar]` → `undefined`(含非 StringLiteral 元素)\n * - `[]` → `[]`(空数组)\n * - 非数组 → `undefined`\n */\nfunction extractStringArrayValue(expr: ts.Expression): string[] | undefined {\n if (!ts.isArrayLiteralExpression(expr)) return undefined;\n const values: string[] = [];\n for (const element of expr.elements) {\n if (!ts.isStringLiteral(element)) return undefined;\n values.push(element.text);\n }\n return values;\n}\n","import path from 'node:path';\nimport type { AgentManifestList } from '../agents/agentTypes';\nimport type { AgentMetadata } from '../ast/extractAgentMetadata';\nimport { extractAgentMetadata } from '../ast/extractAgentMetadata';\nimport { createPrograms } from '../ast/createProgram';\nimport { toProdFilePath } from '../utils/prodPaths';\nimport { atomicWriteFile } from '../utils/atomicWrite';\n\n/**\n * 序列化的 agent manifest 记录(可写入 JS 模块,无函数引用)\n *\n * 与 [AgentMetadata](../ast/extractAgentMetadata.md) 字段一一对应,仅 `filePath`\n * 由源码形式(`src/...`)转为产物形式(`<dist>/...`,打平 `src/` 前缀 + dist 前缀 + `.js`)。\n *\n * `undefined` 字段(description / systemPrompt / tools / agents / model / maxTurns)\n * 在 JSON.stringify 时自动省略,水合时通过 `??` 兜底为 undefined。\n *\n * > `hasConfig` 字段已移除——它原本用于控制 `loadAgentModule` 是否提取 `config` 对象,\n * > 但 `AgentModule.config` 已废弃(`executeSubAgent` 拿到后从不读取),属于死链路。\n */\nexport interface SerializedAgentRecord {\n /** agent 名(`@agent` 覆盖值 或 目录推导值) */\n name: string;\n /** JSDoc 描述(对 LLM 可见),无则省略 */\n description?: string;\n /** 是否导出 run 函数(从 manifest 透传) */\n hasRun: boolean;\n /** 系统提示词(config 块字面量提取),无/非字面量时省略 */\n systemPrompt?: string;\n /** agent 显式声明可用的 tool 引用列表(config 块字面量提取),无/含非字面量元素时省略 */\n tools?: string[];\n /** 可调用的其他 agent 名列表(config 块字面量提取),无/含非字面量元素时省略 */\n agents?: string[];\n /** LLM 模型名(config 块字面量提取),无/非字面量时省略 */\n model?: string;\n /** 最大对话轮数(config 块字面量提取),无/非字面量时省略 */\n maxTurns?: number;\n /** 产物形式路径(如 `dist/agents/researcher/handler.js`),供运行时 import agent.js */\n filePath: string;\n}\n\n/**\n * faapi-agents.js 文件名(与 faapi-routes.js / faapi-tools.js 同构)\n */\nconst AGENTS_FILE = 'faapi-agents.js';\n\n/**\n * 序列化 agent 清单为可写入 JS 模块的结构\n *\n * - `filePath` 转为产物形式(打平 `src/` 前缀 + dist 前缀 + `.js`)\n * - 其他字段(name/description/hasRun/systemPrompt/tools/agents/model/maxTurns)直接透传\n * - `undefined` 字段在 JSON.stringify 时自动省略\n *\n * @param agents AST 增强后的 AgentMetadata[](由 generateAgentArtifacts 内部从 AgentManifest 增强)\n * @param dist 产物目录(默认 `dist`),用于转换 filePath\n */\nexport function serializeAgents(\n agents: AgentMetadata[],\n dist: string = 'dist',\n): SerializedAgentRecord[] {\n return agents.map((a) => ({\n name: a.name,\n description: a.description,\n hasRun: a.hasRun,\n systemPrompt: a.systemPrompt,\n tools: a.tools,\n agents: a.agents,\n model: a.model,\n maxTurns: a.maxTurns,\n filePath: toProdFilePath(a.filePath, dist),\n }));\n}\n\n/**\n * 把序列化的 agent 清单写入 faapi-agents.js\n *\n * 生成 ESM 模块,运行时 `createAppCore` 通过 `importWithCacheBust` 加载。\n * 用 JSON.stringify 嵌入,保证字符串转义安全(与 `writeRoutesModule` / `writeToolsModule` 一致)。\n */\nexport async function writeAgentsModule(\n manifest: SerializedAgentRecord[],\n outputPath: string,\n): Promise<void> {\n const content = `// 自动生成,请勿手动编辑(faapi build/dev 产物)\nexport const agents = ${JSON.stringify(manifest, null, 2)};\n`;\n await atomicWriteFile(outputPath, content);\n}\n\n/**\n * 从序列化清单水合还原 AgentMetadata[]\n *\n * 字段一一对应(无函数引用需还原)。`undefined` 字段在 JSON.parse 时缺失,\n * 通过 `??` 兜底为 undefined(保证 `AgentMetadata` 类型完整)。\n */\nexport function hydrateAgents(manifest: SerializedAgentRecord[]): AgentMetadata[] {\n return manifest.map((a) => ({\n name: a.name,\n description: a.description ?? undefined,\n filePath: a.filePath,\n hasRun: a.hasRun,\n systemPrompt: a.systemPrompt ?? undefined,\n tools: a.tools ?? undefined,\n agents: a.agents ?? undefined,\n model: a.model ?? undefined,\n maxTurns: a.maxTurns ?? undefined,\n }));\n}\n\n/**\n * 主入口:从 AgentManifest[] 生成 faapi-agents.js\n *\n * agent **不生成 zod.js**——与 tool 不同,agent 没有用户输入参数(config 块字段\n * 已在 [extractAgentMetadata](../ast/extractAgentMetadata.md) AST 阶段提取为字面量),\n * 运行时无需 schema 校验。`run` 函数参数由 [agentRegistry](../injection/agentRegistry.md)\n * 的 `asTool()` 在 Phase 2.2 处理(若需输入校验,由 agent 子包在 Phase 3.x 自行生成)。\n *\n * 内部流程:\n * 1. 对每个 AgentManifest 调 `createProgram` + `extractAgentMetadata` → AgentMetadata[]\n * (AST 增强:补全 description / `@agent` 覆盖名 / config 块字段)\n * 2. `serializeAgents(metadata, dist)` → SerializedAgentRecord[](filePath 转产物形式)\n * 3. `writeAgentsModule(serialized, faapiAgentsPath)` → 写入 `<dist>/faapi-agents.js`\n *\n * 与 [generateToolArtifacts](./generateToolArtifacts.md) 的差异:\n * - 不生成 zod.js(agent 无输入参数)\n * - 无 `skipSchema` 选项(没有 schema 可跳过)\n * - 文件名常量为 `faapi-agents.js`,导出 `agents` 而非 `tools`\n *\n * @param agents scanAgents 产出的 AgentManifest[](仅路径推导字段)\n * @param rootDir 项目根目录\n * @param dist 产物目录(`.faapi` 或 `dist`)\n * @returns AST 增强后的 AgentMetadata[](供调用方日志/调试)\n */\nexport async function generateAgentArtifacts(\n agents: AgentManifestList,\n rootDir: string,\n dist: string,\n): Promise<AgentMetadata[]> {\n // 1. AST 增强:对每个 manifest 调 extractAgentMetadata(批量共享 Program)\n const metadata: AgentMetadata[] = [];\n const programByFile = createPrograms(agents.map((m) => path.resolve(rootDir, m.filePath)));\n for (const manifest of agents) {\n const absPath = path.resolve(rootDir, manifest.filePath);\n const program = programByFile.get(absPath)!;\n const result = extractAgentMetadata(program, absPath, {\n name: manifest.name,\n filePath: manifest.filePath,\n hasRun: manifest.hasRun,\n });\n if (result) {\n metadata.push(result);\n }\n }\n\n // 2. 序列化 + 写入 faapi-agents.js\n const serialized = serializeAgents(metadata, dist);\n const agentsPath = path.resolve(rootDir, dist, AGENTS_FILE);\n await writeAgentsModule(serialized, agentsPath);\n\n return metadata;\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport type { FaapiConfig } from './configTypes';\n\n/**\n * 配置产物文件名(compileConfig 生成)\n */\nconst CONFIG_PRODUCT_FILE = 'faapi-config.js';\n\n/**\n * 加载 faapi 配置文件\n *\n * 统一读取 `<dist>/faapi-config.js` 产物:\n * - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/faapi-config.js`\n * - prod 模式:`faapi build` 时由 `compileConfig` 生成 `dist/faapi-config.js`\n *\n * 产物由 `compileConfig` 在构建阶段编译,运行时不读源码、不现场编译。\n * 环境变量由 `loadEnv` 从 `.env` 系列文件加载到 `process.env`,配置文件中通过 `process.env.XXX` 读取。\n *\n * - 产物存在 → import 并返回 default\n * - 产物不存在但源码有配置文件 → 抛错(强制 rebuild)\n * - 源码也无配置文件 → 返回 `null`(配置可选)\n *\n * @param rootDir 项目根目录\n * @param dist 产物目录(如 'dist' 或 '.faapi')\n * @returns 配置对象,无配置文件时返回 null\n */\nexport async function loadConfig(\n rootDir: string,\n dist: string,\n): Promise<Partial<FaapiConfig> | null> {\n const configProductPath = path.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);\n\n if (fs.existsSync(configProductPath)) {\n const module = (await importWithCacheBust(configProductPath)) as {\n default?: Partial<FaapiConfig>;\n };\n return module.default ?? {};\n }\n\n // 产物不存在:检查源码是否有配置文件\n const hasSourceConfig =\n fs.existsSync(path.join(rootDir, 'faapi.config.ts')) ||\n fs.existsSync(path.join(rootDir, 'faapi.config.js'));\n\n if (hasSourceConfig) {\n throw new Error(\n `[faapi] ${dist}/${CONFIG_PRODUCT_FILE} 不存在,请先执行 \\`faapi build\\`(或 \\`faapi dev\\`)生成产物。`,\n );\n }\n\n return null;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * env 决定规则:NODE_ENV || 'development'\n *\n * 调用方应在调 loadEnv 之前自行兜底 NODE_ENV(dev 设 'development',prod 设 'production')。\n */\nfunction resolveEnv(): string {\n return process.env.NODE_ENV || 'development';\n}\n\n/**\n * .env 文件加载顺序(从低到高优先级,后加载的覆盖先加载的)\n *\n * 1. .env — 所有环境共享\n * 2. .env.local — 本地覆盖(不提交 git)\n * 3. .env.{env} — 按环境覆盖\n * 4. .env.{env}.local — 按环境本地覆盖\n */\nfunction getEnvFiles(env: string): string[] {\n return ['.env', '.env.local', `.env.${env}`, `.env.${env}.local`];\n}\n\n/**\n * 解析 .env 文件内容为键值对象\n *\n * 格式:\n * - `KEY=VALUE` 基本格式\n * - `# 注释` 行首注释\n * - `export KEY=VALUE` 支持 export 前缀\n * - 单引号:字面量,不展开变量,不处理转义\n * - 双引号:支持变量展开($VAR / ${VAR})和转义(\\n \\t \\r \\\\ \\\")\n * - 无引号:字面量,` #` 后为行内注释\n *\n * @param content .env 文件内容\n * @param fileVars 本文件之前已解析的变量(用于变量展开)\n * @returns 解析出的键值对象\n */\nfunction parseEnvFile(content: string, fileVars: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n // 兼容 CRLF / LF\n const lines = content.replace(/\\r\\n/g, '\\n').split('\\n');\n\n for (const line of lines) {\n const trimmed = line.trim();\n // 空行 / 注释行跳过\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n // 匹配 KEY=VALUE 或 export KEY=VALUE\n // KEY 必须符合 [A-Za-z_][A-Za-z0-9_]*\n const match = /^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/.exec(trimmed);\n if (!match) continue;\n\n // 正则有两个捕获组,匹配成功则必非空\n const key = match[1]!;\n const rawValue = match[2]!;\n const value = parseValue(rawValue, { ...fileVars, ...result });\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * 解析单个值的引号/展开/转义\n *\n * - 单引号:字面量(去除首尾单引号)\n * - 双引号:处理转义 + 展开变量\n * - 无引号:字面量,去除行内注释(` #` 后为注释)\n */\nfunction parseValue(raw: string, env: Record<string, string>): string {\n if (raw === '') return '';\n\n // 单引号:字面量(raw 非空已由调用方保证)\n if (raw[0] === \"'\") {\n const end = raw.indexOf(\"'\", 1);\n return end === -1 ? raw.slice(1) : raw.slice(1, end);\n }\n\n // 双引号:转义 + 展开变量(支持 \\\" 转义引号,不作为结束符)\n if (raw[0] === '\"') {\n const match = /^\"((?:\\\\.|[^\"\\\\])*)\"/.exec(raw);\n // 匹配失败时 slice(1) 兜底(无闭合引号)\n const inner = match ? match[1]! : raw.slice(1);\n return expandEscapesAndVars(inner, env);\n }\n\n // 无引号:字面量,处理行内注释\n // ` #` 前必须有空白才算注释;# 紧贴值(如 url#anchor)不算注释\n const commentMatch = /^(.*?)(\\s+#.*)$/.exec(raw);\n const value = commentMatch ? commentMatch[1]! : raw;\n return value.trim();\n}\n\n/**\n * 双引号值:处理转义字符 + 展开变量\n *\n * 转义:\\n \\r \\t \\\\ \\\" (未知转义保留字符本身)\n * 展开:$VAR / ${VAR},优先 env 参数,其次 process.env,未定义返回空字符串\n */\nfunction expandEscapesAndVars(str: string, env: Record<string, string>): string {\n // 先处理转义(避免变量值中的 $ 被二次展开)\n const escaped = str.replace(/\\\\(.)/g, (_, ch: string) => {\n switch (ch) {\n case 'n':\n return '\\n';\n case 'r':\n return '\\r';\n case 't':\n return '\\t';\n case '\\\\':\n return '\\\\';\n case '\"':\n return '\"';\n default:\n return ch; // 未知转义保留字符\n }\n });\n\n // 展开变量:$VAR 或 ${VAR}\n return escaped.replace(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n (_, braced, plain) => {\n const varName = braced || plain;\n return env[varName] ?? process.env[varName] ?? '';\n },\n );\n}\n\n/**\n * 加载 `.env` 系列文件到 `process.env`\n *\n * 按 Next.js 约定加载四级文件(从低到高):\n * 1. `.env` — 所有环境共享\n * 2. `.env.local` — 本地覆盖\n * 3. `.env.{env}` — 按环境覆盖\n * 4. `.env.{env}.local` — 按环境本地覆盖\n *\n * env 由 `NODE_ENV || 'development'` 决定。调用方应在调 loadEnv 之前自行兜底 NODE_ENV\n * (dev 设 'development',prod 设 'production')。\n *\n * 合并规则:\n * - 后加载的文件覆盖先加载的同名变量\n * - **shell 已设置的变量不被覆盖**(`process.env` 已有的值优先)\n *\n * @param rootDir 项目根目录(`.env` 文件所在目录)\n */\nexport function loadEnv(rootDir: string): void {\n const env = resolveEnv();\n const files = getEnvFiles(env);\n\n // 收集所有文件解析结果,按优先级合并(后面的覆盖前面的)\n const merged: Record<string, string> = {};\n for (const file of files) {\n const filePath = path.join(rootDir, file);\n if (!fs.existsSync(filePath)) continue;\n const content = fs.readFileSync(filePath, 'utf-8');\n const parsed = parseEnvFile(content, merged);\n Object.assign(merged, parsed);\n }\n\n // 写入 process.env(不覆盖已有的 shell 变量)\n for (const [key, value] of Object.entries(merged)) {\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n","import { stat, lstat, readdir, realpath } from 'node:fs/promises';\nimport { Readable } from 'node:stream';\nimport { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from 'node:path';\nexport const EntryTypes = {\n FILE_TYPE: 'files',\n DIR_TYPE: 'directories',\n FILE_DIR_TYPE: 'files_directories',\n EVERYTHING_TYPE: 'all',\n};\nconst defaultOptions = {\n root: '.',\n fileFilter: (_entryInfo) => true,\n directoryFilter: (_entryInfo) => true,\n type: EntryTypes.FILE_TYPE,\n lstat: false,\n depth: 2147483648,\n alwaysStat: false,\n highWaterMark: 4096,\n};\nObject.freeze(defaultOptions);\nconst RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';\nconst NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);\nconst ALL_TYPES = [\n EntryTypes.DIR_TYPE,\n EntryTypes.EVERYTHING_TYPE,\n EntryTypes.FILE_DIR_TYPE,\n EntryTypes.FILE_TYPE,\n];\nconst DIR_TYPES = new Set([\n EntryTypes.DIR_TYPE,\n EntryTypes.EVERYTHING_TYPE,\n EntryTypes.FILE_DIR_TYPE,\n]);\nconst FILE_TYPES = new Set([\n EntryTypes.EVERYTHING_TYPE,\n EntryTypes.FILE_DIR_TYPE,\n EntryTypes.FILE_TYPE,\n]);\nconst isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);\nconst wantBigintFsStats = process.platform === 'win32';\nconst emptyFn = (_entryInfo) => true;\nconst normalizeFilter = (filter) => {\n if (filter === undefined)\n return emptyFn;\n if (typeof filter === 'function')\n return filter;\n if (typeof filter === 'string') {\n const fl = filter.trim();\n return (entry) => entry.basename === fl;\n }\n if (Array.isArray(filter)) {\n const trItems = filter.map((item) => item.trim());\n return (entry) => trItems.some((f) => entry.basename === f);\n }\n return emptyFn;\n};\n/** Readable readdir stream, emitting new files as they're being listed. */\nexport class ReaddirpStream extends Readable {\n constructor(options = {}) {\n super({\n objectMode: true,\n autoDestroy: true,\n highWaterMark: options.highWaterMark,\n });\n const opts = { ...defaultOptions, ...options };\n const { root, type } = opts;\n this._fileFilter = normalizeFilter(opts.fileFilter);\n this._directoryFilter = normalizeFilter(opts.directoryFilter);\n const statMethod = opts.lstat ? lstat : stat;\n // Use bigint stats if it's windows and stat() supports options (node 10+).\n if (wantBigintFsStats) {\n this._stat = (path) => statMethod(path, { bigint: true });\n }\n else {\n this._stat = statMethod;\n }\n this._maxDepth = opts.depth ?? defaultOptions.depth;\n this._wantsDir = type ? DIR_TYPES.has(type) : false;\n this._wantsFile = type ? FILE_TYPES.has(type) : false;\n this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;\n this._root = presolve(root);\n this._isDirent = !opts.alwaysStat;\n this._statsProp = this._isDirent ? 'dirent' : 'stats';\n this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };\n // Launch stream with one parent, the root dir.\n this.parents = [this._exploreDir(root, 1)];\n this.reading = false;\n this.parent = undefined;\n }\n async _read(batch) {\n if (this.reading)\n return;\n this.reading = true;\n try {\n while (!this.destroyed && batch > 0) {\n const par = this.parent;\n const fil = par && par.files;\n if (fil && fil.length > 0) {\n const { path, depth } = par;\n const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));\n const awaited = await Promise.all(slice);\n for (const entry of awaited) {\n if (!entry)\n continue;\n if (this.destroyed)\n return;\n const entryType = await this._getEntryType(entry);\n if (entryType === 'directory' && this._directoryFilter(entry)) {\n if (depth <= this._maxDepth) {\n this.parents.push(this._exploreDir(entry.fullPath, depth + 1));\n }\n if (this._wantsDir) {\n this.push(entry);\n batch--;\n }\n }\n else if ((entryType === 'file' || this._includeAsFile(entry)) &&\n this._fileFilter(entry)) {\n if (this._wantsFile) {\n this.push(entry);\n batch--;\n }\n }\n }\n }\n else {\n const parent = this.parents.pop();\n if (!parent) {\n this.push(null);\n break;\n }\n this.parent = await parent;\n if (this.destroyed)\n return;\n }\n }\n }\n catch (error) {\n this.destroy(error);\n }\n finally {\n this.reading = false;\n }\n }\n async _exploreDir(path, depth) {\n let files;\n try {\n files = await readdir(path, this._rdOptions);\n }\n catch (error) {\n this._onError(error);\n }\n return { files, depth, path };\n }\n async _formatEntry(dirent, path) {\n let entry;\n const basename = this._isDirent ? dirent.name : dirent;\n try {\n const fullPath = presolve(pjoin(path, basename));\n entry = { path: prelative(this._root, fullPath), fullPath, basename };\n entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);\n }\n catch (err) {\n this._onError(err);\n return;\n }\n return entry;\n }\n _onError(err) {\n if (isNormalFlowError(err) && !this.destroyed) {\n this.emit('warn', err);\n }\n else {\n this.destroy(err);\n }\n }\n async _getEntryType(entry) {\n // entry may be undefined, because a warning or an error were emitted\n // and the statsProp is undefined\n if (!entry && this._statsProp in entry) {\n return '';\n }\n const stats = entry[this._statsProp];\n if (stats.isFile())\n return 'file';\n if (stats.isDirectory())\n return 'directory';\n if (stats && stats.isSymbolicLink()) {\n const full = entry.fullPath;\n try {\n const entryRealPath = await realpath(full);\n const entryRealPathStats = await lstat(entryRealPath);\n if (entryRealPathStats.isFile()) {\n return 'file';\n }\n if (entryRealPathStats.isDirectory()) {\n const len = entryRealPath.length;\n if (full.startsWith(entryRealPath) && full.substr(len, 1) === psep) {\n const recursiveError = new Error(`Circular symlink detected: \"${full}\" points to \"${entryRealPath}\"`);\n // @ts-ignore\n recursiveError.code = RECURSIVE_ERROR_CODE;\n return this._onError(recursiveError);\n }\n return 'directory';\n }\n }\n catch (error) {\n this._onError(error);\n return '';\n }\n }\n }\n _includeAsFile(entry) {\n const stats = entry && entry[this._statsProp];\n return stats && this._wantsEverything && !stats.isDirectory();\n }\n}\n/**\n * Streaming version: Reads all files and directories in given root recursively.\n * Consumes ~constant small amount of RAM.\n * @param root Root directory\n * @param options Options to specify root (start directory), filters and recursion depth\n */\nexport function readdirp(root, options = {}) {\n // @ts-ignore\n let type = options.entryType || options.type;\n if (type === 'both')\n type = EntryTypes.FILE_DIR_TYPE; // backwards-compatibility\n if (type)\n options.type = type;\n if (!root) {\n throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');\n }\n else if (typeof root !== 'string') {\n throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');\n }\n else if (type && !ALL_TYPES.includes(type)) {\n throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);\n }\n options.root = root;\n return new ReaddirpStream(options);\n}\n/**\n * Promise version: Reads all files and directories in given root recursively.\n * Compared to streaming version, will consume a lot of RAM e.g. when 1 million files are listed.\n * @returns array of paths and their entry infos\n */\nexport function readdirpPromise(root, options = {}) {\n return new Promise((resolve, reject) => {\n const files = [];\n readdirp(root, options)\n .on('data', (entry) => files.push(entry))\n .on('end', () => resolve(files))\n .on('error', (error) => reject(error));\n });\n}\nexport default readdirp;\n","import { watchFile, unwatchFile, watch as fs_watch } from 'fs';\nimport { open, stat, lstat, realpath as fsrealpath } from 'fs/promises';\nimport * as sysPath from 'path';\nimport { type as osType } from 'os';\nexport const STR_DATA = 'data';\nexport const STR_END = 'end';\nexport const STR_CLOSE = 'close';\nexport const EMPTY_FN = () => { };\nexport const IDENTITY_FN = (val) => val;\nconst pl = process.platform;\nexport const isWindows = pl === 'win32';\nexport const isMacos = pl === 'darwin';\nexport const isLinux = pl === 'linux';\nexport const isFreeBSD = pl === 'freebsd';\nexport const isIBMi = osType() === 'OS400';\nexport const EVENTS = {\n ALL: 'all',\n READY: 'ready',\n ADD: 'add',\n CHANGE: 'change',\n ADD_DIR: 'addDir',\n UNLINK: 'unlink',\n UNLINK_DIR: 'unlinkDir',\n RAW: 'raw',\n ERROR: 'error',\n};\nconst EV = EVENTS;\nconst THROTTLE_MODE_WATCH = 'watch';\nconst statMethods = { lstat, stat };\nconst KEY_LISTENERS = 'listeners';\nconst KEY_ERR = 'errHandlers';\nconst KEY_RAW = 'rawEmitters';\nconst HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];\n// prettier-ignore\nconst binaryExtensions = new Set([\n '3dm', '3ds', '3g2', '3gp', '7z', 'a', 'aac', 'adp', 'afdesign', 'afphoto', 'afpub', 'ai',\n 'aif', 'aiff', 'alz', 'ape', 'apk', 'appimage', 'ar', 'arj', 'asf', 'au', 'avi',\n 'bak', 'baml', 'bh', 'bin', 'bk', 'bmp', 'btif', 'bz2', 'bzip2',\n 'cab', 'caf', 'cgm', 'class', 'cmx', 'cpio', 'cr2', 'cur', 'dat', 'dcm', 'deb', 'dex', 'djvu',\n 'dll', 'dmg', 'dng', 'doc', 'docm', 'docx', 'dot', 'dotm', 'dra', 'DS_Store', 'dsk', 'dts',\n 'dtshd', 'dvb', 'dwg', 'dxf',\n 'ecelp4800', 'ecelp7470', 'ecelp9600', 'egg', 'eol', 'eot', 'epub', 'exe',\n 'f4v', 'fbs', 'fh', 'fla', 'flac', 'flatpak', 'fli', 'flv', 'fpx', 'fst', 'fvt',\n 'g3', 'gh', 'gif', 'graffle', 'gz', 'gzip',\n 'h261', 'h263', 'h264', 'icns', 'ico', 'ief', 'img', 'ipa', 'iso',\n 'jar', 'jpeg', 'jpg', 'jpgv', 'jpm', 'jxr', 'key', 'ktx',\n 'lha', 'lib', 'lvp', 'lz', 'lzh', 'lzma', 'lzo',\n 'm3u', 'm4a', 'm4v', 'mar', 'mdi', 'mht', 'mid', 'midi', 'mj2', 'mka', 'mkv', 'mmr', 'mng',\n 'mobi', 'mov', 'movie', 'mp3',\n 'mp4', 'mp4a', 'mpeg', 'mpg', 'mpga', 'mxu',\n 'nef', 'npx', 'numbers', 'nupkg',\n 'o', 'odp', 'ods', 'odt', 'oga', 'ogg', 'ogv', 'otf', 'ott',\n 'pages', 'pbm', 'pcx', 'pdb', 'pdf', 'pea', 'pgm', 'pic', 'png', 'pnm', 'pot', 'potm',\n 'potx', 'ppa', 'ppam',\n 'ppm', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'psd', 'pya', 'pyc', 'pyo', 'pyv',\n 'qt',\n 'rar', 'ras', 'raw', 'resources', 'rgb', 'rip', 'rlc', 'rmf', 'rmvb', 'rpm', 'rtf', 'rz',\n 's3m', 's7z', 'scpt', 'sgi', 'shar', 'snap', 'sil', 'sketch', 'slk', 'smv', 'snk', 'so',\n 'stl', 'suo', 'sub', 'swf',\n 'tar', 'tbz', 'tbz2', 'tga', 'tgz', 'thmx', 'tif', 'tiff', 'tlz', 'ttc', 'ttf', 'txz',\n 'udf', 'uvh', 'uvi', 'uvm', 'uvp', 'uvs', 'uvu',\n 'viv', 'vob',\n 'war', 'wav', 'wax', 'wbmp', 'wdp', 'weba', 'webm', 'webp', 'whl', 'wim', 'wm', 'wma',\n 'wmv', 'wmx', 'woff', 'woff2', 'wrm', 'wvx',\n 'xbm', 'xif', 'xla', 'xlam', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', 'xm',\n 'xmind', 'xpi', 'xpm', 'xwd', 'xz',\n 'z', 'zip', 'zipx',\n]);\nconst isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());\n// TODO: emit errors properly. Example: EMFILE on Macos.\nconst foreach = (val, fn) => {\n if (val instanceof Set) {\n val.forEach(fn);\n }\n else {\n fn(val);\n }\n};\nconst addAndConvert = (main, prop, item) => {\n let container = main[prop];\n if (!(container instanceof Set)) {\n main[prop] = container = new Set([container]);\n }\n container.add(item);\n};\nconst clearItem = (cont) => (key) => {\n const set = cont[key];\n if (set instanceof Set) {\n set.clear();\n }\n else {\n delete cont[key];\n }\n};\nconst delFromSet = (main, prop, item) => {\n const container = main[prop];\n if (container instanceof Set) {\n container.delete(item);\n }\n else if (container === item) {\n delete main[prop];\n }\n};\nconst isEmptySet = (val) => (val instanceof Set ? val.size === 0 : !val);\nconst FsWatchInstances = new Map();\n/**\n * Instantiates the fs_watch interface\n * @param path to be watched\n * @param options to be passed to fs_watch\n * @param listener main event handler\n * @param errHandler emits info about errors\n * @param emitRaw emits raw event data\n * @returns {NativeFsWatcher}\n */\nfunction createFsWatchInstance(path, options, listener, errHandler, emitRaw) {\n const handleEvent = (rawEvent, evPath) => {\n listener(path);\n emitRaw(rawEvent, evPath, { watchedPath: path });\n // emit based on events occurring for files from a directory's watcher in\n // case the file's watcher misses it (and rely on throttling to de-dupe)\n if (evPath && path !== evPath) {\n fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));\n }\n };\n try {\n return fs_watch(path, {\n persistent: options.persistent,\n }, handleEvent);\n }\n catch (error) {\n errHandler(error);\n return undefined;\n }\n}\n/**\n * Helper for passing fs_watch event data to a collection of listeners\n * @param fullPath absolute path bound to fs_watch instance\n */\nconst fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {\n const cont = FsWatchInstances.get(fullPath);\n if (!cont)\n return;\n foreach(cont[listenerType], (listener) => {\n listener(val1, val2, val3);\n });\n};\n/**\n * Instantiates the fs_watch interface or binds listeners\n * to an existing one covering the same file system entry\n * @param path\n * @param fullPath absolute path\n * @param options to be passed to fs_watch\n * @param handlers container for event listener functions\n */\nconst setFsWatchListener = (path, fullPath, options, handlers) => {\n const { listener, errHandler, rawEmitter } = handlers;\n let cont = FsWatchInstances.get(fullPath);\n let watcher;\n if (!options.persistent) {\n watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter);\n if (!watcher)\n return;\n return watcher.close.bind(watcher);\n }\n if (cont) {\n addAndConvert(cont, KEY_LISTENERS, listener);\n addAndConvert(cont, KEY_ERR, errHandler);\n addAndConvert(cont, KEY_RAW, rawEmitter);\n }\n else {\n watcher = createFsWatchInstance(path, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, // no need to use broadcast here\n fsWatchBroadcast.bind(null, fullPath, KEY_RAW));\n if (!watcher)\n return;\n watcher.on(EV.ERROR, async (error) => {\n const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);\n if (cont)\n cont.watcherUnusable = true; // documented since Node 10.4.1\n // Workaround for https://github.com/joyent/node/issues/4337\n if (isWindows && error.code === 'EPERM') {\n try {\n const fd = await open(path, 'r');\n await fd.close();\n broadcastErr(error);\n }\n catch (err) {\n // do nothing\n }\n }\n else {\n broadcastErr(error);\n }\n });\n cont = {\n listeners: listener,\n errHandlers: errHandler,\n rawEmitters: rawEmitter,\n watcher,\n };\n FsWatchInstances.set(fullPath, cont);\n }\n // const index = cont.listeners.indexOf(listener);\n // removes this instance's listeners and closes the underlying fs_watch\n // instance if there are no more listeners left\n return () => {\n delFromSet(cont, KEY_LISTENERS, listener);\n delFromSet(cont, KEY_ERR, errHandler);\n delFromSet(cont, KEY_RAW, rawEmitter);\n if (isEmptySet(cont.listeners)) {\n // Check to protect against issue gh-730.\n // if (cont.watcherUnusable) {\n cont.watcher.close();\n // }\n FsWatchInstances.delete(fullPath);\n HANDLER_KEYS.forEach(clearItem(cont));\n // @ts-ignore\n cont.watcher = undefined;\n Object.freeze(cont);\n }\n };\n};\n// fs_watchFile helpers\n// object to hold per-process fs_watchFile instances\n// (may be shared across chokidar FSWatcher instances)\nconst FsWatchFileInstances = new Map();\n/**\n * Instantiates the fs_watchFile interface or binds listeners\n * to an existing one covering the same file system entry\n * @param path to be watched\n * @param fullPath absolute path\n * @param options options to be passed to fs_watchFile\n * @param handlers container for event listener functions\n * @returns closer\n */\nconst setFsWatchFileListener = (path, fullPath, options, handlers) => {\n const { listener, rawEmitter } = handlers;\n let cont = FsWatchFileInstances.get(fullPath);\n // let listeners = new Set();\n // let rawEmitters = new Set();\n const copts = cont && cont.options;\n if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {\n // \"Upgrade\" the watcher to persistence or a quicker interval.\n // This creates some unlikely edge case issues if the user mixes\n // settings in a very weird way, but solving for those cases\n // doesn't seem worthwhile for the added complexity.\n // listeners = cont.listeners;\n // rawEmitters = cont.rawEmitters;\n unwatchFile(fullPath);\n cont = undefined;\n }\n if (cont) {\n addAndConvert(cont, KEY_LISTENERS, listener);\n addAndConvert(cont, KEY_RAW, rawEmitter);\n }\n else {\n // TODO\n // listeners.add(listener);\n // rawEmitters.add(rawEmitter);\n cont = {\n listeners: listener,\n rawEmitters: rawEmitter,\n options,\n watcher: watchFile(fullPath, options, (curr, prev) => {\n foreach(cont.rawEmitters, (rawEmitter) => {\n rawEmitter(EV.CHANGE, fullPath, { curr, prev });\n });\n const currmtime = curr.mtimeMs;\n if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {\n foreach(cont.listeners, (listener) => listener(path, curr));\n }\n }),\n };\n FsWatchFileInstances.set(fullPath, cont);\n }\n // const index = cont.listeners.indexOf(listener);\n // Removes this instance's listeners and closes the underlying fs_watchFile\n // instance if there are no more listeners left.\n return () => {\n delFromSet(cont, KEY_LISTENERS, listener);\n delFromSet(cont, KEY_RAW, rawEmitter);\n if (isEmptySet(cont.listeners)) {\n FsWatchFileInstances.delete(fullPath);\n unwatchFile(fullPath);\n cont.options = cont.watcher = undefined;\n Object.freeze(cont);\n }\n };\n};\n/**\n * @mixin\n */\nexport class NodeFsHandler {\n constructor(fsW) {\n this.fsw = fsW;\n this._boundHandleError = (error) => fsW._handleError(error);\n }\n /**\n * Watch file for changes with fs_watchFile or fs_watch.\n * @param path to file or dir\n * @param listener on fs change\n * @returns closer for the watcher instance\n */\n _watchWithNodeFs(path, listener) {\n const opts = this.fsw.options;\n const directory = sysPath.dirname(path);\n const basename = sysPath.basename(path);\n const parent = this.fsw._getWatchedDir(directory);\n parent.add(basename);\n const absolutePath = sysPath.resolve(path);\n const options = {\n persistent: opts.persistent,\n };\n if (!listener)\n listener = EMPTY_FN;\n let closer;\n if (opts.usePolling) {\n const enableBin = opts.interval !== opts.binaryInterval;\n options.interval = enableBin && isBinaryPath(basename) ? opts.binaryInterval : opts.interval;\n closer = setFsWatchFileListener(path, absolutePath, options, {\n listener,\n rawEmitter: this.fsw._emitRaw,\n });\n }\n else {\n closer = setFsWatchListener(path, absolutePath, options, {\n listener,\n errHandler: this._boundHandleError,\n rawEmitter: this.fsw._emitRaw,\n });\n }\n return closer;\n }\n /**\n * Watch a file and emit add event if warranted.\n * @returns closer for the watcher instance\n */\n _handleFile(file, stats, initialAdd) {\n if (this.fsw.closed) {\n return;\n }\n const dirname = sysPath.dirname(file);\n const basename = sysPath.basename(file);\n const parent = this.fsw._getWatchedDir(dirname);\n // stats is always present\n let prevStats = stats;\n // if the file is already being watched, do nothing\n if (parent.has(basename))\n return;\n const listener = async (path, newStats) => {\n if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))\n return;\n if (!newStats || newStats.mtimeMs === 0) {\n try {\n const newStats = await stat(file);\n if (this.fsw.closed)\n return;\n // Check that change event was not fired because of changed only accessTime.\n const at = newStats.atimeMs;\n const mt = newStats.mtimeMs;\n if (!at || at <= mt || mt !== prevStats.mtimeMs) {\n this.fsw._emit(EV.CHANGE, file, newStats);\n }\n if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats.ino) {\n this.fsw._closeFile(path);\n prevStats = newStats;\n const closer = this._watchWithNodeFs(file, listener);\n if (closer)\n this.fsw._addPathCloser(path, closer);\n }\n else {\n prevStats = newStats;\n }\n }\n catch (error) {\n // Fix issues where mtime is null but file is still present\n this.fsw._remove(dirname, basename);\n }\n // add is about to be emitted if file not already tracked in parent\n }\n else if (parent.has(basename)) {\n // Check that change event was not fired because of changed only accessTime.\n const at = newStats.atimeMs;\n const mt = newStats.mtimeMs;\n if (!at || at <= mt || mt !== prevStats.mtimeMs) {\n this.fsw._emit(EV.CHANGE, file, newStats);\n }\n prevStats = newStats;\n }\n };\n // kick off the watcher\n const closer = this._watchWithNodeFs(file, listener);\n // emit an add event if we're supposed to\n if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {\n if (!this.fsw._throttle(EV.ADD, file, 0))\n return;\n this.fsw._emit(EV.ADD, file, stats);\n }\n return closer;\n }\n /**\n * Handle symlinks encountered while reading a dir.\n * @param entry returned by readdirp\n * @param directory path of dir being read\n * @param path of this item\n * @param item basename of this item\n * @returns true if no more processing is needed for this entry.\n */\n async _handleSymlink(entry, directory, path, item) {\n if (this.fsw.closed) {\n return;\n }\n const full = entry.fullPath;\n const dir = this.fsw._getWatchedDir(directory);\n if (!this.fsw.options.followSymlinks) {\n // watch symlink directly (don't follow) and detect changes\n this.fsw._incrReadyCount();\n let linkPath;\n try {\n linkPath = await fsrealpath(path);\n }\n catch (e) {\n this.fsw._emitReady();\n return true;\n }\n if (this.fsw.closed)\n return;\n if (dir.has(item)) {\n if (this.fsw._symlinkPaths.get(full) !== linkPath) {\n this.fsw._symlinkPaths.set(full, linkPath);\n this.fsw._emit(EV.CHANGE, path, entry.stats);\n }\n }\n else {\n dir.add(item);\n this.fsw._symlinkPaths.set(full, linkPath);\n this.fsw._emit(EV.ADD, path, entry.stats);\n }\n this.fsw._emitReady();\n return true;\n }\n // don't follow the same symlink more than once\n if (this.fsw._symlinkPaths.has(full)) {\n return true;\n }\n this.fsw._symlinkPaths.set(full, true);\n }\n _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {\n // Normalize the directory name on Windows\n directory = sysPath.join(directory, '');\n throttler = this.fsw._throttle('readdir', directory, 1000);\n if (!throttler)\n return;\n const previous = this.fsw._getWatchedDir(wh.path);\n const current = new Set();\n let stream = this.fsw._readdirp(directory, {\n fileFilter: (entry) => wh.filterPath(entry),\n directoryFilter: (entry) => wh.filterDir(entry),\n });\n if (!stream)\n return;\n stream\n .on(STR_DATA, async (entry) => {\n if (this.fsw.closed) {\n stream = undefined;\n return;\n }\n const item = entry.path;\n let path = sysPath.join(directory, item);\n current.add(item);\n if (entry.stats.isSymbolicLink() &&\n (await this._handleSymlink(entry, directory, path, item))) {\n return;\n }\n if (this.fsw.closed) {\n stream = undefined;\n return;\n }\n // Files that present in current directory snapshot\n // but absent in previous are added to watch list and\n // emit `add` event.\n if (item === target || (!target && !previous.has(item))) {\n this.fsw._incrReadyCount();\n // ensure relativeness of path is preserved in case of watcher reuse\n path = sysPath.join(dir, sysPath.relative(dir, path));\n this._addToNodeFs(path, initialAdd, wh, depth + 1);\n }\n })\n .on(EV.ERROR, this._boundHandleError);\n return new Promise((resolve, reject) => {\n if (!stream)\n return reject();\n stream.once(STR_END, () => {\n if (this.fsw.closed) {\n stream = undefined;\n return;\n }\n const wasThrottled = throttler ? throttler.clear() : false;\n resolve(undefined);\n // Files that absent in current directory snapshot\n // but present in previous emit `remove` event\n // and are removed from @watched[directory].\n previous\n .getChildren()\n .filter((item) => {\n return item !== directory && !current.has(item);\n })\n .forEach((item) => {\n this.fsw._remove(directory, item);\n });\n stream = undefined;\n // one more time for any missed in case changes came in extremely quickly\n if (wasThrottled)\n this._handleRead(directory, false, wh, target, dir, depth, throttler);\n });\n });\n }\n /**\n * Read directory to add / remove files from `@watched` list and re-read it on change.\n * @param dir fs path\n * @param stats\n * @param initialAdd\n * @param depth relative to user-supplied path\n * @param target child path targeted for watch\n * @param wh Common watch helpers for this path\n * @param realpath\n * @returns closer for the watcher instance.\n */\n async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {\n const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));\n const tracked = parentDir.has(sysPath.basename(dir));\n if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {\n this.fsw._emit(EV.ADD_DIR, dir, stats);\n }\n // ensure dir is tracked (harmless if redundant)\n parentDir.add(sysPath.basename(dir));\n this.fsw._getWatchedDir(dir);\n let throttler;\n let closer;\n const oDepth = this.fsw.options.depth;\n if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {\n if (!target) {\n await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);\n if (this.fsw.closed)\n return;\n }\n closer = this._watchWithNodeFs(dir, (dirPath, stats) => {\n // if current directory is removed, do nothing\n if (stats && stats.mtimeMs === 0)\n return;\n this._handleRead(dirPath, false, wh, target, dir, depth, throttler);\n });\n }\n return closer;\n }\n /**\n * Handle added file, directory, or glob pattern.\n * Delegates call to _handleFile / _handleDir after checks.\n * @param path to file or ir\n * @param initialAdd was the file added at watch instantiation?\n * @param priorWh depth relative to user-supplied path\n * @param depth Child path actually targeted for watch\n * @param target Child path actually targeted for watch\n */\n async _addToNodeFs(path, initialAdd, priorWh, depth, target) {\n const ready = this.fsw._emitReady;\n if (this.fsw._isIgnored(path) || this.fsw.closed) {\n ready();\n return false;\n }\n const wh = this.fsw._getWatchHelpers(path);\n if (priorWh) {\n wh.filterPath = (entry) => priorWh.filterPath(entry);\n wh.filterDir = (entry) => priorWh.filterDir(entry);\n }\n // evaluate what is at the path we're being asked to watch\n try {\n const stats = await statMethods[wh.statMethod](wh.watchPath);\n if (this.fsw.closed)\n return;\n if (this.fsw._isIgnored(wh.watchPath, stats)) {\n ready();\n return false;\n }\n const follow = this.fsw.options.followSymlinks;\n let closer;\n if (stats.isDirectory()) {\n const absPath = sysPath.resolve(path);\n const targetPath = follow ? await fsrealpath(path) : path;\n if (this.fsw.closed)\n return;\n closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);\n if (this.fsw.closed)\n return;\n // preserve this symlink's target path\n if (absPath !== targetPath && targetPath !== undefined) {\n this.fsw._symlinkPaths.set(absPath, targetPath);\n }\n }\n else if (stats.isSymbolicLink()) {\n const targetPath = follow ? await fsrealpath(path) : path;\n if (this.fsw.closed)\n return;\n const parent = sysPath.dirname(wh.watchPath);\n this.fsw._getWatchedDir(parent).add(wh.watchPath);\n this.fsw._emit(EV.ADD, wh.watchPath, stats);\n closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);\n if (this.fsw.closed)\n return;\n // preserve this symlink's target path\n if (targetPath !== undefined) {\n this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);\n }\n }\n else {\n closer = this._handleFile(wh.watchPath, stats, initialAdd);\n }\n ready();\n if (closer)\n this.fsw._addPathCloser(path, closer);\n return false;\n }\n catch (error) {\n if (this.fsw._handleError(error)) {\n ready();\n return path;\n }\n }\n }\n}\n","/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */\nimport { stat as statcb } from 'fs';\nimport { stat, readdir } from 'fs/promises';\nimport { EventEmitter } from 'events';\nimport * as sysPath from 'path';\nimport { readdirp } from 'readdirp';\nimport { NodeFsHandler, EVENTS as EV, isWindows, isIBMi, EMPTY_FN, STR_CLOSE, STR_END, } from './handler.js';\nconst SLASH = '/';\nconst SLASH_SLASH = '//';\nconst ONE_DOT = '.';\nconst TWO_DOTS = '..';\nconst STRING_TYPE = 'string';\nconst BACK_SLASH_RE = /\\\\/g;\nconst DOUBLE_SLASH_RE = /\\/\\//;\nconst DOT_RE = /\\..*\\.(sw[px])$|~$|\\.subl.*\\.tmp/;\nconst REPLACER_RE = /^\\.[/\\\\]/;\nfunction arrify(item) {\n return Array.isArray(item) ? item : [item];\n}\nconst isMatcherObject = (matcher) => typeof matcher === 'object' && matcher !== null && !(matcher instanceof RegExp);\nfunction createPattern(matcher) {\n if (typeof matcher === 'function')\n return matcher;\n if (typeof matcher === 'string')\n return (string) => matcher === string;\n if (matcher instanceof RegExp)\n return (string) => matcher.test(string);\n if (typeof matcher === 'object' && matcher !== null) {\n return (string) => {\n if (matcher.path === string)\n return true;\n if (matcher.recursive) {\n const relative = sysPath.relative(matcher.path, string);\n if (!relative) {\n return false;\n }\n return !relative.startsWith('..') && !sysPath.isAbsolute(relative);\n }\n return false;\n };\n }\n return () => false;\n}\nfunction normalizePath(path) {\n if (typeof path !== 'string')\n throw new Error('string expected');\n path = sysPath.normalize(path);\n path = path.replace(/\\\\/g, '/');\n let prepend = false;\n if (path.startsWith('//'))\n prepend = true;\n const DOUBLE_SLASH_RE = /\\/\\//;\n while (path.match(DOUBLE_SLASH_RE))\n path = path.replace(DOUBLE_SLASH_RE, '/');\n if (prepend)\n path = '/' + path;\n return path;\n}\nfunction matchPatterns(patterns, testString, stats) {\n const path = normalizePath(testString);\n for (let index = 0; index < patterns.length; index++) {\n const pattern = patterns[index];\n if (pattern(path, stats)) {\n return true;\n }\n }\n return false;\n}\nfunction anymatch(matchers, testString) {\n if (matchers == null) {\n throw new TypeError('anymatch: specify first argument');\n }\n // Early cache for matchers.\n const matchersArray = arrify(matchers);\n const patterns = matchersArray.map((matcher) => createPattern(matcher));\n if (testString == null) {\n return (testString, stats) => {\n return matchPatterns(patterns, testString, stats);\n };\n }\n return matchPatterns(patterns, testString);\n}\nconst unifyPaths = (paths_) => {\n const paths = arrify(paths_).flat();\n if (!paths.every((p) => typeof p === STRING_TYPE)) {\n throw new TypeError(`Non-string provided as watch path: ${paths}`);\n }\n return paths.map(normalizePathToUnix);\n};\n// If SLASH_SLASH occurs at the beginning of path, it is not replaced\n// because \"//StoragePC/DrivePool/Movies\" is a valid network path\nconst toUnix = (string) => {\n let str = string.replace(BACK_SLASH_RE, SLASH);\n let prepend = false;\n if (str.startsWith(SLASH_SLASH)) {\n prepend = true;\n }\n while (str.match(DOUBLE_SLASH_RE)) {\n str = str.replace(DOUBLE_SLASH_RE, SLASH);\n }\n if (prepend) {\n str = SLASH + str;\n }\n return str;\n};\n// Our version of upath.normalize\n// TODO: this is not equal to path-normalize module - investigate why\nconst normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));\n// TODO: refactor\nconst normalizeIgnored = (cwd = '') => (path) => {\n if (typeof path === 'string') {\n return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));\n }\n else {\n return path;\n }\n};\nconst getAbsolutePath = (path, cwd) => {\n if (sysPath.isAbsolute(path)) {\n return path;\n }\n return sysPath.join(cwd, path);\n};\nconst EMPTY_SET = Object.freeze(new Set());\n/**\n * Directory entry.\n */\nclass DirEntry {\n constructor(dir, removeWatcher) {\n this.path = dir;\n this._removeWatcher = removeWatcher;\n this.items = new Set();\n }\n add(item) {\n const { items } = this;\n if (!items)\n return;\n if (item !== ONE_DOT && item !== TWO_DOTS)\n items.add(item);\n }\n async remove(item) {\n const { items } = this;\n if (!items)\n return;\n items.delete(item);\n if (items.size > 0)\n return;\n const dir = this.path;\n try {\n await readdir(dir);\n }\n catch (err) {\n if (this._removeWatcher) {\n this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));\n }\n }\n }\n has(item) {\n const { items } = this;\n if (!items)\n return;\n return items.has(item);\n }\n getChildren() {\n const { items } = this;\n if (!items)\n return [];\n return [...items.values()];\n }\n dispose() {\n this.items.clear();\n this.path = '';\n this._removeWatcher = EMPTY_FN;\n this.items = EMPTY_SET;\n Object.freeze(this);\n }\n}\nconst STAT_METHOD_F = 'stat';\nconst STAT_METHOD_L = 'lstat';\nexport class WatchHelper {\n constructor(path, follow, fsw) {\n this.fsw = fsw;\n const watchPath = path;\n this.path = path = path.replace(REPLACER_RE, '');\n this.watchPath = watchPath;\n this.fullWatchPath = sysPath.resolve(watchPath);\n this.dirParts = [];\n this.dirParts.forEach((parts) => {\n if (parts.length > 1)\n parts.pop();\n });\n this.followSymlinks = follow;\n this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;\n }\n entryPath(entry) {\n return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));\n }\n filterPath(entry) {\n const { stats } = entry;\n if (stats && stats.isSymbolicLink())\n return this.filterDir(entry);\n const resolvedPath = this.entryPath(entry);\n // TODO: what if stats is undefined? remove !\n return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);\n }\n filterDir(entry) {\n return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);\n }\n}\n/**\n * Watches files & directories for changes. Emitted events:\n * `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`\n *\n * new FSWatcher()\n * .add(directories)\n * .on('add', path => log('File', path, 'was added'))\n */\nexport class FSWatcher extends EventEmitter {\n // Not indenting methods for history sake; for now.\n constructor(_opts = {}) {\n super();\n this.closed = false;\n this._closers = new Map();\n this._ignoredPaths = new Set();\n this._throttled = new Map();\n this._streams = new Set();\n this._symlinkPaths = new Map();\n this._watched = new Map();\n this._pendingWrites = new Map();\n this._pendingUnlinks = new Map();\n this._readyCount = 0;\n this._readyEmitted = false;\n const awf = _opts.awaitWriteFinish;\n const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };\n const opts = {\n // Defaults\n persistent: true,\n ignoreInitial: false,\n ignorePermissionErrors: false,\n interval: 100,\n binaryInterval: 300,\n followSymlinks: true,\n usePolling: false,\n // useAsync: false,\n atomic: true, // NOTE: overwritten later (depends on usePolling)\n ..._opts,\n // Change format\n ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),\n awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === 'object' ? { ...DEF_AWF, ...awf } : false,\n };\n // Always default to polling on IBM i because fs.watch() is not available on IBM i.\n if (isIBMi)\n opts.usePolling = true;\n // Editor atomic write normalization enabled by default with fs.watch\n if (opts.atomic === undefined)\n opts.atomic = !opts.usePolling;\n // opts.atomic = typeof _opts.atomic === 'number' ? _opts.atomic : 100;\n // Global override. Useful for developers, who need to force polling for all\n // instances of chokidar, regardless of usage / dependency depth\n const envPoll = process.env.CHOKIDAR_USEPOLLING;\n if (envPoll !== undefined) {\n const envLower = envPoll.toLowerCase();\n if (envLower === 'false' || envLower === '0')\n opts.usePolling = false;\n else if (envLower === 'true' || envLower === '1')\n opts.usePolling = true;\n else\n opts.usePolling = !!envLower;\n }\n const envInterval = process.env.CHOKIDAR_INTERVAL;\n if (envInterval)\n opts.interval = Number.parseInt(envInterval, 10);\n // This is done to emit ready only once, but each 'add' will increase that?\n let readyCalls = 0;\n this._emitReady = () => {\n readyCalls++;\n if (readyCalls >= this._readyCount) {\n this._emitReady = EMPTY_FN;\n this._readyEmitted = true;\n // use process.nextTick to allow time for listener to be bound\n process.nextTick(() => this.emit(EV.READY));\n }\n };\n this._emitRaw = (...args) => this.emit(EV.RAW, ...args);\n this._boundRemove = this._remove.bind(this);\n this.options = opts;\n this._nodeFsHandler = new NodeFsHandler(this);\n // You’re frozen when your heart’s not open.\n Object.freeze(opts);\n }\n _addIgnoredPath(matcher) {\n if (isMatcherObject(matcher)) {\n // return early if we already have a deeply equal matcher object\n for (const ignored of this._ignoredPaths) {\n if (isMatcherObject(ignored) &&\n ignored.path === matcher.path &&\n ignored.recursive === matcher.recursive) {\n return;\n }\n }\n }\n this._ignoredPaths.add(matcher);\n }\n _removeIgnoredPath(matcher) {\n this._ignoredPaths.delete(matcher);\n // now find any matcher objects with the matcher as path\n if (typeof matcher === 'string') {\n for (const ignored of this._ignoredPaths) {\n // TODO (43081j): make this more efficient.\n // probably just make a `this._ignoredDirectories` or some\n // such thing.\n if (isMatcherObject(ignored) && ignored.path === matcher) {\n this._ignoredPaths.delete(ignored);\n }\n }\n }\n }\n // Public methods\n /**\n * Adds paths to be watched on an existing FSWatcher instance.\n * @param paths_ file or file list. Other arguments are unused\n */\n add(paths_, _origAdd, _internal) {\n const { cwd } = this.options;\n this.closed = false;\n this._closePromise = undefined;\n let paths = unifyPaths(paths_);\n if (cwd) {\n paths = paths.map((path) => {\n const absPath = getAbsolutePath(path, cwd);\n // Check `path` instead of `absPath` because the cwd portion can't be a glob\n return absPath;\n });\n }\n paths.forEach((path) => {\n this._removeIgnoredPath(path);\n });\n this._userIgnored = undefined;\n if (!this._readyCount)\n this._readyCount = 0;\n this._readyCount += paths.length;\n Promise.all(paths.map(async (path) => {\n const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, undefined, 0, _origAdd);\n if (res)\n this._emitReady();\n return res;\n })).then((results) => {\n if (this.closed)\n return;\n results.forEach((item) => {\n if (item)\n this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));\n });\n });\n return this;\n }\n /**\n * Close watchers or start ignoring events from specified paths.\n */\n unwatch(paths_) {\n if (this.closed)\n return this;\n const paths = unifyPaths(paths_);\n const { cwd } = this.options;\n paths.forEach((path) => {\n // convert to absolute path unless relative path already matches\n if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {\n if (cwd)\n path = sysPath.join(cwd, path);\n path = sysPath.resolve(path);\n }\n this._closePath(path);\n this._addIgnoredPath(path);\n if (this._watched.has(path)) {\n this._addIgnoredPath({\n path,\n recursive: true,\n });\n }\n // reset the cached userIgnored anymatch fn\n // to make ignoredPaths changes effective\n this._userIgnored = undefined;\n });\n return this;\n }\n /**\n * Close watchers and remove all listeners from watched paths.\n */\n close() {\n if (this._closePromise) {\n return this._closePromise;\n }\n this.closed = true;\n // Memory management.\n this.removeAllListeners();\n const closers = [];\n this._closers.forEach((closerList) => closerList.forEach((closer) => {\n const promise = closer();\n if (promise instanceof Promise)\n closers.push(promise);\n }));\n this._streams.forEach((stream) => stream.destroy());\n this._userIgnored = undefined;\n this._readyCount = 0;\n this._readyEmitted = false;\n this._watched.forEach((dirent) => dirent.dispose());\n this._closers.clear();\n this._watched.clear();\n this._streams.clear();\n this._symlinkPaths.clear();\n this._throttled.clear();\n this._closePromise = closers.length\n ? Promise.all(closers).then(() => undefined)\n : Promise.resolve();\n return this._closePromise;\n }\n /**\n * Expose list of watched paths\n * @returns for chaining\n */\n getWatched() {\n const watchList = {};\n this._watched.forEach((entry, dir) => {\n const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;\n const index = key || ONE_DOT;\n watchList[index] = entry.getChildren().sort();\n });\n return watchList;\n }\n emitWithAll(event, args) {\n this.emit(event, ...args);\n if (event !== EV.ERROR)\n this.emit(EV.ALL, event, ...args);\n }\n // Common helpers\n // --------------\n /**\n * Normalize and emit events.\n * Calling _emit DOES NOT MEAN emit() would be called!\n * @param event Type of event\n * @param path File or directory path\n * @param stats arguments to be passed with event\n * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag\n */\n async _emit(event, path, stats) {\n if (this.closed)\n return;\n const opts = this.options;\n if (isWindows)\n path = sysPath.normalize(path);\n if (opts.cwd)\n path = sysPath.relative(opts.cwd, path);\n const args = [path];\n if (stats != null)\n args.push(stats);\n const awf = opts.awaitWriteFinish;\n let pw;\n if (awf && (pw = this._pendingWrites.get(path))) {\n pw.lastChange = new Date();\n return this;\n }\n if (opts.atomic) {\n if (event === EV.UNLINK) {\n this._pendingUnlinks.set(path, [event, ...args]);\n setTimeout(() => {\n this._pendingUnlinks.forEach((entry, path) => {\n this.emit(...entry);\n this.emit(EV.ALL, ...entry);\n this._pendingUnlinks.delete(path);\n });\n }, typeof opts.atomic === 'number' ? opts.atomic : 100);\n return this;\n }\n if (event === EV.ADD && this._pendingUnlinks.has(path)) {\n event = EV.CHANGE;\n this._pendingUnlinks.delete(path);\n }\n }\n if (awf && (event === EV.ADD || event === EV.CHANGE) && this._readyEmitted) {\n const awfEmit = (err, stats) => {\n if (err) {\n event = EV.ERROR;\n args[0] = err;\n this.emitWithAll(event, args);\n }\n else if (stats) {\n // if stats doesn't exist the file must have been deleted\n if (args.length > 1) {\n args[1] = stats;\n }\n else {\n args.push(stats);\n }\n this.emitWithAll(event, args);\n }\n };\n this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);\n return this;\n }\n if (event === EV.CHANGE) {\n const isThrottled = !this._throttle(EV.CHANGE, path, 50);\n if (isThrottled)\n return this;\n }\n if (opts.alwaysStat &&\n stats === undefined &&\n (event === EV.ADD || event === EV.ADD_DIR || event === EV.CHANGE)) {\n const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;\n let stats;\n try {\n stats = await stat(fullPath);\n }\n catch (err) {\n // do nothing\n }\n // Suppress event when fs_stat fails, to avoid sending undefined 'stat'\n if (!stats || this.closed)\n return;\n args.push(stats);\n }\n this.emitWithAll(event, args);\n return this;\n }\n /**\n * Common handler for errors\n * @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag\n */\n _handleError(error) {\n const code = error && error.code;\n if (error &&\n code !== 'ENOENT' &&\n code !== 'ENOTDIR' &&\n (!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))) {\n this.emit(EV.ERROR, error);\n }\n return error || this.closed;\n }\n /**\n * Helper utility for throttling\n * @param actionType type being throttled\n * @param path being acted upon\n * @param timeout duration of time to suppress duplicate actions\n * @returns tracking object or false if action should be suppressed\n */\n _throttle(actionType, path, timeout) {\n if (!this._throttled.has(actionType)) {\n this._throttled.set(actionType, new Map());\n }\n const action = this._throttled.get(actionType);\n if (!action)\n throw new Error('invalid throttle');\n const actionPath = action.get(path);\n if (actionPath) {\n actionPath.count++;\n return false;\n }\n // eslint-disable-next-line prefer-const\n let timeoutObject;\n const clear = () => {\n const item = action.get(path);\n const count = item ? item.count : 0;\n action.delete(path);\n clearTimeout(timeoutObject);\n if (item)\n clearTimeout(item.timeoutObject);\n return count;\n };\n timeoutObject = setTimeout(clear, timeout);\n const thr = { timeoutObject, clear, count: 0 };\n action.set(path, thr);\n return thr;\n }\n _incrReadyCount() {\n return this._readyCount++;\n }\n /**\n * Awaits write operation to finish.\n * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.\n * @param path being acted upon\n * @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished\n * @param event\n * @param awfEmit Callback to be called when ready for event to be emitted.\n */\n _awaitWriteFinish(path, threshold, event, awfEmit) {\n const awf = this.options.awaitWriteFinish;\n if (typeof awf !== 'object')\n return;\n const pollInterval = awf.pollInterval;\n let timeoutHandler;\n let fullPath = path;\n if (this.options.cwd && !sysPath.isAbsolute(path)) {\n fullPath = sysPath.join(this.options.cwd, path);\n }\n const now = new Date();\n const writes = this._pendingWrites;\n function awaitWriteFinishFn(prevStat) {\n statcb(fullPath, (err, curStat) => {\n if (err || !writes.has(path)) {\n if (err && err.code !== 'ENOENT')\n awfEmit(err);\n return;\n }\n const now = Number(new Date());\n if (prevStat && curStat.size !== prevStat.size) {\n writes.get(path).lastChange = now;\n }\n const pw = writes.get(path);\n const df = now - pw.lastChange;\n if (df >= threshold) {\n writes.delete(path);\n awfEmit(undefined, curStat);\n }\n else {\n timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);\n }\n });\n }\n if (!writes.has(path)) {\n writes.set(path, {\n lastChange: now,\n cancelWait: () => {\n writes.delete(path);\n clearTimeout(timeoutHandler);\n return event;\n },\n });\n timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);\n }\n }\n /**\n * Determines whether user has asked to ignore this path.\n */\n _isIgnored(path, stats) {\n if (this.options.atomic && DOT_RE.test(path))\n return true;\n if (!this._userIgnored) {\n const { cwd } = this.options;\n const ign = this.options.ignored;\n const ignored = (ign || []).map(normalizeIgnored(cwd));\n const ignoredPaths = [...this._ignoredPaths];\n const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];\n this._userIgnored = anymatch(list, undefined);\n }\n return this._userIgnored(path, stats);\n }\n _isntIgnored(path, stat) {\n return !this._isIgnored(path, stat);\n }\n /**\n * Provides a set of common helpers and properties relating to symlink handling.\n * @param path file or directory pattern being watched\n */\n _getWatchHelpers(path) {\n return new WatchHelper(path, this.options.followSymlinks, this);\n }\n // Directory helpers\n // -----------------\n /**\n * Provides directory tracking objects\n * @param directory path of the directory\n */\n _getWatchedDir(directory) {\n const dir = sysPath.resolve(directory);\n if (!this._watched.has(dir))\n this._watched.set(dir, new DirEntry(dir, this._boundRemove));\n return this._watched.get(dir);\n }\n // File helpers\n // ------------\n /**\n * Check for read permissions: https://stackoverflow.com/a/11781404/1358405\n */\n _hasReadPermissions(stats) {\n if (this.options.ignorePermissionErrors)\n return true;\n return Boolean(Number(stats.mode) & 0o400);\n }\n /**\n * Handles emitting unlink events for\n * files and directories, and via recursion, for\n * files and directories within directories that are unlinked\n * @param directory within which the following item is located\n * @param item base path of item/directory\n */\n _remove(directory, item, isDirectory) {\n // if what is being deleted is a directory, get that directory's paths\n // for recursive deleting and cleaning of watched object\n // if it is not a directory, nestedDirectoryChildren will be empty array\n const path = sysPath.join(directory, item);\n const fullPath = sysPath.resolve(path);\n isDirectory =\n isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);\n // prevent duplicate handling in case of arriving here nearly simultaneously\n // via multiple paths (such as _handleFile and _handleDir)\n if (!this._throttle('remove', path, 100))\n return;\n // if the only watched file is removed, watch for its return\n if (!isDirectory && this._watched.size === 1) {\n this.add(directory, item, true);\n }\n // This will create a new entry in the watched object in either case\n // so we got to do the directory check beforehand\n const wp = this._getWatchedDir(path);\n const nestedDirectoryChildren = wp.getChildren();\n // Recursively remove children directories / files.\n nestedDirectoryChildren.forEach((nested) => this._remove(path, nested));\n // Check if item was on the watched list and remove it\n const parent = this._getWatchedDir(directory);\n const wasTracked = parent.has(item);\n parent.remove(item);\n // Fixes issue #1042 -> Relative paths were detected and added as symlinks\n // (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),\n // but never removed from the map in case the path was deleted.\n // This leads to an incorrect state if the path was recreated:\n // https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553\n if (this._symlinkPaths.has(fullPath)) {\n this._symlinkPaths.delete(fullPath);\n }\n // If we wait for this file to be fully written, cancel the wait.\n let relPath = path;\n if (this.options.cwd)\n relPath = sysPath.relative(this.options.cwd, path);\n if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {\n const event = this._pendingWrites.get(relPath).cancelWait();\n if (event === EV.ADD)\n return;\n }\n // The Entry will either be a directory that just got removed\n // or a bogus entry to a file, in either case we have to remove it\n this._watched.delete(path);\n this._watched.delete(fullPath);\n const eventName = isDirectory ? EV.UNLINK_DIR : EV.UNLINK;\n if (wasTracked && !this._isIgnored(path))\n this._emit(eventName, path);\n // Avoid conflicts if we later create another file with the same name\n this._closePath(path);\n }\n /**\n * Closes all watchers for a path\n */\n _closePath(path) {\n this._closeFile(path);\n const dir = sysPath.dirname(path);\n this._getWatchedDir(dir).remove(sysPath.basename(path));\n }\n /**\n * Closes only file-specific watchers\n */\n _closeFile(path) {\n const closers = this._closers.get(path);\n if (!closers)\n return;\n closers.forEach((closer) => closer());\n this._closers.delete(path);\n }\n _addPathCloser(path, closer) {\n if (!closer)\n return;\n let list = this._closers.get(path);\n if (!list) {\n list = [];\n this._closers.set(path, list);\n }\n list.push(closer);\n }\n _readdirp(root, opts) {\n if (this.closed)\n return;\n const options = { type: EV.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };\n let stream = readdirp(root, options);\n this._streams.add(stream);\n stream.once(STR_CLOSE, () => {\n stream = undefined;\n });\n stream.once(STR_END, () => {\n if (stream) {\n this._streams.delete(stream);\n stream = undefined;\n }\n });\n return stream;\n }\n}\n/**\n * Instantiates watcher with paths to be tracked.\n * @param paths file / directory paths\n * @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others\n * @returns an instance of FSWatcher for chaining.\n * @example\n * const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });\n * watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })\n */\nexport function watch(paths, options = {}) {\n const watcher = new FSWatcher(options);\n watcher.add(paths);\n return watcher;\n}\nexport default { watch, FSWatcher };\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport fg from 'fast-glob';\nimport { buildAliasPlugins } from './aliasPlugin';\nimport { APP_DIR } from '../utils/prodPaths';\n\n/**\n * 编译结果\n */\nexport interface CompileResult {\n /** 已编译的文件列表(绝对路径) */\n compiledFiles: string[];\n}\n\nexport interface CompileSourceFilesOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(dev 为 `.faapi`,build 为 `dist`) */\n dist: string;\n /**\n * 增量编译:传入要编译的文件列表(绝对路径)。\n * 不传则全量编译 src 下所有 .ts(排除测试文件和声明文件)。\n */\n files?: string[];\n /** 是否输出 esbuild 日志(默认静默) */\n logLevel?: 'silent' | 'info';\n /**\n * prod 语义:`define` 把 `process.env.NODE_ENV` 编译期替换为 `\"production\"` +\n * `minifySyntax` 删除死分支(两者在 `bundle: false` 下均生效,单文件级别优化)。\n */\n production?: boolean;\n /**\n * dev 语义:原子写(write: false + 临时文件 + rename),避免 watch 模式下\n * esbuild 非原子写期间运行时 import 读到半成品产物。\n */\n atomicWrite?: boolean;\n}\n\n/**\n * dev/prod 共用的逐文件 TypeScript 编译实现\n *\n * 每个 `.ts` 独立编译为 `.js`,不分析 import 关系(`bundle: false`——\n * 保证 config 与 routes 共享同一运行时对象,`instanceof` 跨边界生效)。\n * 产物**打平 src/ 前缀**:`src/api/hello/handler.ts` → `<dist>/api/hello/handler.js`。\n *\n * 别名在编译时重写为相对路径,运行时无需 loader。\n *\n * 通过 `compileDevRoutes` / `compileBuildRoutes` 调用,见 ./compileSourceFiles.md\n * 的选项矩阵。\n */\nexport async function compileSourceFiles(\n options: CompileSourceFilesOptions,\n): Promise<CompileResult> {\n const { rootDir, dist, files, logLevel = 'silent', production, atomicWrite } = options;\n\n // 收集要编译的文件:files 优先,否则全量扫描 src 下所有 .ts\n const entryPoints =\n files ??\n (await fg([`${APP_DIR}/**/*.ts`], {\n cwd: rootDir,\n onlyFiles: true,\n absolute: true,\n ignore: ['**/*.test.ts', '**/*.e2e.test.ts', '**/*.d.ts'],\n }));\n\n if (entryPoints.length === 0) {\n return { compiledFiles: [] };\n }\n\n // 确保输出目录存在\n const absDist = path.resolve(rootDir, dist);\n await fs.promises.mkdir(absDist, { recursive: true });\n\n // 构造别名重写插件(无 tsconfig/paths 时为空,相对路径重写仍生效)\n const plugins = buildAliasPlugins(rootDir);\n\n // 逐文件编译,outbase 设为 src 以打平产物结构:\n // `src/api/hello/handler.ts` → `<dist>/api/hello/handler.js`(去掉 src/ 前缀)\n const esbuild = await import('esbuild');\n const outbase = path.resolve(rootDir, APP_DIR);\n const result = await esbuild.build({\n entryPoints,\n outdir: absDist,\n outbase,\n bundle: false,\n platform: 'node',\n format: 'esm',\n sourcemap: true,\n packages: 'external',\n plugins,\n // build 语义:编译期 NODE_ENV 替换 + 死分支删除(见 AGENTS.md §5.3)\n ...(production\n ? { define: { 'process.env.NODE_ENV': '\"production\"' }, minifySyntax: true }\n : {}),\n logLevel,\n // dev 语义:esbuild 返回内存内容,由下方原子写落盘\n ...(atomicWrite ? { write: false as const } : {}),\n });\n\n // 原子写:逐个写临时文件 + rename。rename 在同一文件系统上是原子的(POSIX),\n // HTTP 请求要么看到旧文件要么看到新文件\n if (atomicWrite && result.outputFiles) {\n await Promise.all(\n result.outputFiles.map(async (file) => {\n await fs.promises.mkdir(path.dirname(file.path), { recursive: true });\n const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;\n await fs.promises.writeFile(tmp, file.contents);\n await fs.promises.rename(tmp, file.path);\n }),\n );\n }\n\n return { compiledFiles: entryPoints };\n}\n\n/**\n * 全量扫描 src 下的 .ts 文件(排除测试文件和声明文件)\n *\n * 与 compileSourceFiles 的全量分支使用同一 ignore 规则,供 watch 等场景独立扫描。\n */\nexport async function collectSourceFiles(rootDir: string): Promise<string[]> {\n return fg([`${APP_DIR}/**/*.ts`], {\n cwd: rootDir,\n onlyFiles: true,\n absolute: true,\n ignore: ['**/*.test.ts', '**/*.e2e.test.ts', '**/*.d.ts'],\n });\n}\n","import { compileSourceFiles, collectSourceFiles, type CompileResult } from './compileSourceFiles';\n\nexport { collectSourceFiles };\nexport type { CompileResult };\n\n/**\n * dev 编译选项\n */\nexport interface CompileDevOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(dev 模式为 `.faapi`) */\n dist: string;\n /**\n * 增量编译:传入要编译的文件列表(绝对路径)。\n * 不传则全量编译 src 下所有 .ts(排除测试文件和声明文件)。\n */\n files?: string[];\n /** 是否输出 esbuild 日志(dev 模式默认静默) */\n logLevel?: 'silent' | 'info';\n}\n\n/**\n * dev 模式编译 TypeScript:逐文件编译,启动快、增量编译友好\n *\n * 共享实现见 [compileSourceFiles](./compileSourceFiles.ts);dev 差异仅两点:\n * 不传 `define`(`process.env.NODE_ENV` 运行时读取环境变量,便于热替换)+\n * **原子写**(写临时文件 + rename,避免 watch 模式下运行时 import 读到半成品产物,\n * 详见 compileDevRoutes.md 的\"dev 原子写\"章节)。\n *\n * @example\n * // 全量编译\n * await compileDevRoutes({ rootDir, dist: '.faapi' });\n * // watch 增量:只编译变化的文件\n * await compileDevRoutes({ rootDir, dist: '.faapi', files: changedFiles });\n */\nexport async function compileDevRoutes(options: CompileDevOptions): Promise<CompileResult> {\n return compileSourceFiles({ ...options, atomicWrite: true });\n}\n","/**\n * watcher 重建调度器\n *\n * 收敛 watcher 的调度逻辑:debounce 合并、重入保护、失败回灌。\n * 行为定义见 ./rebuildScheduler.md。\n */\n\nexport interface RebuildScheduler {\n /** 文件变化/新增:加入待编译集合并调度重建 */\n addFiles(files: string[]): void;\n /** 仅调度重建(如 unlink,无文件可编译) */\n schedule(): void;\n}\n\nexport interface RebuildSchedulerOptions {\n /**\n * 重建回调,接收本轮待编译文件列表(可能为空数组——纯结构变化场景)\n *\n * 抛错时该轮文件回灌待编译集合(不主动重试),错误经 onError 上报。\n */\n rebuild: (files: string[]) => Promise<void>;\n /** debounce 窗口(毫秒),默认 100 */\n debounceMs?: number;\n /** 重建失败回调(如 console.error) */\n onError?: (err: unknown) => void;\n}\n\n/**\n * 创建重建调度器\n *\n * - debounce 窗口内的事件合并为一轮重建\n * - 重建进行中不重入:新事件累积文件并标记补跑,当前轮结束后串行补跑\n * - 回调抛错:文件回灌 + onError 上报,等待下一次事件自然触发(不主动重试,\n * 避免语法错误场景每 100ms 刷一次错误)\n */\nexport function createRebuildScheduler(options: RebuildSchedulerOptions): RebuildScheduler {\n const { rebuild, debounceMs = 100, onError } = options;\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let pendingFiles: string[] = [];\n let isRebuilding = false;\n let needsAnotherRun = false;\n\n async function run(): Promise<void> {\n // 重入保护:重建进行中只标记补跑,当前轮结束时串行补跑\n if (isRebuilding) {\n needsAnotherRun = true;\n return;\n }\n isRebuilding = true;\n try {\n // 串行补跑循环:每轮取走当前累积的文件,期间新事件再标记补跑\n do {\n needsAnotherRun = false;\n const files = pendingFiles;\n pendingFiles = [];\n try {\n await rebuild(files);\n } catch (err) {\n // 失败回灌:合并回待编译集合,等下次文件事件一起编译(不主动重试)\n pendingFiles = [...files, ...pendingFiles];\n onError?.(err);\n }\n } while (needsAnotherRun);\n } finally {\n isRebuilding = false;\n }\n }\n\n function scheduleDebounce(): void {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n void run();\n }, debounceMs);\n }\n\n function requestRun(): void {\n // 重入保护:重建进行中只标记补跑(不等 debounce——重建耗时已天然\n // 合并连续事件),当前轮 while 循环结束时立即串行补跑\n if (isRebuilding) {\n needsAnotherRun = true;\n return;\n }\n scheduleDebounce();\n }\n\n return {\n addFiles(files) {\n // 去重:编辑器保存常连续触发多次 change,同一文件在同一轮里只编译一次\n for (const file of files) {\n if (!pendingFiles.includes(file)) pendingFiles.push(file);\n }\n requestRun();\n },\n schedule() {\n requestRun();\n },\n };\n}\n","import chokidar from 'chokidar';\nimport path from 'node:path';\nimport { compileDevRoutes } from './compileDevRoutes';\nimport { compileConfig } from './compileConfig';\nimport type { DevApp } from './createDevApp';\nimport { createRebuildScheduler } from './rebuildScheduler';\n\nexport interface WatchOptions {\n /** 项目根目录 */\n rootDir: string;\n /** dev 应用实例(调用 app.reloadRoutes 热替换) */\n app: DevApp;\n /** dev 产物目录(如 .faapi),用于增量编译与配置重生成 */\n devDist: string;\n}\n\n/**\n * 启动文件 watcher(仅 dev 模式)\n *\n * 监听源码 `.ts` 变化,增量编译 + 重生成 config/schema 产物 + 调 `app.reloadRoutes()` 热替换。\n *\n * 与 `app.reloadRoutes()` 的分工:\n * - watcher:增量编译变化文件 + 重生成 `faapi-config.js`(compileConfig 内部有 mtime 短路,无变化时跳过编译)\n * - `reloadRoutes()`:重新扫描路由 + 重新生成 schema + 清缓存 + 更新 server 路由引用\n *\n * 注意:`faapi-routes.js` 不在 watcher 中重生成——reloadRoutes 直接调 scanRoutes 重新扫描,\n * 不依赖重新 import faapi-routes.js(ESM 模块缓存难以可靠绕过)。\n *\n * chokidar v4 移除了 glob 模式支持,改为监听整个 `src` 目录 + `ignored` 函数过滤。\n * 监听整个 src 比 glob 更合理:handler.ts 引用的 util.ts 变化也能触发重建。\n *\n * 重建调度(createRebuildScheduler,debounce 100ms):\n * 1. 增量编译变化的文件(add/change 事件累积的文件)\n * 2. 重生成 `faapi-config.js`(compileConfig 内部 mtime 短路:源文件无变化时跳过编译)\n * 3. 调 `app.reloadRoutes()`(scanRoutes + generateSchemaFiles + 更新引用)\n *\n * 重建进行中不重入:新事件只累积文件,当前轮结束后自动串行补跑(见 rebuildScheduler.md)。\n * 重建失败时待编译文件回灌,等待下次文件事件一起重编译(不主动重试)。\n *\n * unlink(文件删除)不增量编译(无文件可编译),但触发 reloadRoutes(路由结构变化)。\n */\nexport function startWatcher(options: WatchOptions): void {\n const { rootDir, app, devDist } = options;\n\n async function rebuildRoutes(files: string[]): Promise<void> {\n // 1. 增量编译变化的文件(add/change 事件累积的文件)\n if (files.length > 0) {\n await compileDevRoutes({\n rootDir,\n dist: devDist,\n files,\n });\n }\n\n // 2. 重生成 faapi-config.js(compileConfig 内部 mtime 短路,配置源无变化时跳过)\n await compileConfig({ rootDir, dist: devDist });\n\n // 3. 调 app.reloadRoutes()(scanRoutes + generateSchemaFiles + 清缓存 + 更新引用)\n await app.reloadRoutes();\n // 4. 调 app.reloadTools()(scanTools + 重生成 faapi-tools.js + 清缓存)\n // 与 reloadRoutes 分离——tool 清单独立重建,无 tool 文件时 scanTools 返回空(快速跳过)\n await app.reloadTools();\n // 5. 调 app.reloadAgents()(scanAgents + 重生成 faapi-agents.js + 清缓存)\n // 与 reloadTools 分离——agent 清单独立重建,无 agent 文件时 scanAgents 返回空(快速跳过)\n await app.reloadAgents();\n\n console.log(\n `- Routes rebuilt${files.length > 0 ? `, ${files.length} file(s) recompiled` : ''}`,\n );\n }\n\n // 重建调度器:debounce 合并 + 重入保护 + 失败回灌(见 rebuildScheduler.md)\n const scheduler = createRebuildScheduler({\n rebuild: rebuildRoutes,\n onError: (err) => {\n // esbuild 抛错带结构化 errors 数组(file/line/text/frame)——逐条输出,\n // 压扁成 err.message 会让用户只剩一句摘要找不到位置\n console.error('- Error rebuilding routes:');\n const esbuildErr = err as {\n errors?: Array<{ location?: { file?: string; line?: number }; text?: string }>;\n };\n if (Array.isArray(esbuildErr?.errors) && esbuildErr.errors.length > 0) {\n for (const e of esbuildErr.errors) {\n const loc = e.location?.file\n ? `${e.location.file}${e.location.line != null ? `:${e.location.line}` : ''}: `\n : '';\n console.error(` ${loc}${e.text ?? 'compile error'}`);\n }\n } else {\n console.error(' ', err instanceof Error ? err.message : String(err));\n }\n },\n });\n\n // 监听文件变化\n // chokidar v4 移除了 glob 模式支持,改为监听 src 整个目录 + ignored 函数过滤\n // 监听整个 src 比 glob 更合理:handler.ts 引用的 util.ts 变化也能触发重建\n // 同时监听根目录的 faapi.config.{ts,js}(配置变化时重生成 faapi-config.js)\n const CONFIG_FILES = ['faapi.config.ts', 'faapi.config.js'];\n const configAbsPaths = new Set(CONFIG_FILES.map((f) => path.resolve(rootDir, f)));\n // tsconfig.json 变化影响 alias 重写与 mtime 缓存输入(compileConfig 明确把\n // tsconfig 计入缓存输入),不监听的话改 paths 后要等下一个无关文件变化才生效\n const watchPaths = ['src', 'tsconfig.json', ...CONFIG_FILES];\n const scheduleOnly = new Set([...configAbsPaths, path.resolve(rootDir, 'tsconfig.json')]);\n const watcher = chokidar.watch(watchPaths, {\n cwd: rootDir,\n ignoreInitial: true,\n ignored: (filePath, stats) => {\n // 按路径段判断,避免子串误伤(如 src/lib/node_modules-helper.ts、src/api/x.faapi.ts)\n const parts = filePath.split(/[\\\\/]/);\n if (\n parts.includes('node_modules') ||\n parts.includes('.git') ||\n parts.includes('.faapi') ||\n (devDist && parts.includes(devDist))\n ) {\n return true;\n }\n // 无 stats 时不忽略(chokidar 会再次调用并传入 stats)\n if (!stats) return false;\n // 目录不忽略(chokidar 需要递归进入子目录)\n if (stats.isDirectory()) return false;\n // 只监听 .ts/.js 文件(.js 用于 faapi.config.js)\n return !filePath.endsWith('.ts') && !filePath.endsWith('.js');\n },\n });\n\n // config 文件事件只触发重生成(compileConfig mtime 短路:无变化时跳过),\n // 不进增量编译——config 在 src/outbase 之外,喂给 compileDevRoutes 会让 esbuild\n // 把 `..` 段转义成 `_.._` 目录,在 .faapi 下堆积垃圾产物\n watcher.on('add', (file) => handleFileEvent(file));\n watcher.on('change', (file) => handleFileEvent(file));\n\n /**\n * 文件事件分流:\n * - config / tsconfig:只 schedule(compileConfig mtime 短路,无变化跳过)\n * - test/d.ts:完全跳过(build/dev 全量编译都不含它们,喂给增量编译会让\n * 测试文件的语法错误打断整轮重建)\n * - 其余源码:进增量编译\n */\n function handleFileEvent(file: string): void {\n const abs = path.resolve(rootDir, file);\n if (scheduleOnly.has(abs)) {\n scheduler.schedule();\n return;\n }\n if (/\\.(test|e2e\\.test)\\.tsx?$/.test(abs.replace(/\\\\/g, '/')) || abs.endsWith('.d.ts')) {\n return;\n }\n scheduler.addFiles([abs]);\n }\n watcher.on('unlink', () => {\n // 文件删除:不增量编译(无文件可编译),但触发重生成产物 + reloadRoutes(路由结构变化)\n scheduler.schedule();\n });\n watcher.on('error', (err) => {\n console.error('- Watcher error:', err instanceof Error ? err.message : String(err));\n });\n watcher.on('ready', () => {\n const watched = watcher.getWatched();\n const dirCount = Object.keys(watched).length;\n const fileCount = Object.values(watched).reduce((sum, files) => sum + files.length, 0);\n console.log(`- Watcher ready: ${dirCount} dir(s), ${fileCount} file(s) watched`);\n });\n\n console.log('- Watch mode enabled');\n}\n","import type { RouteManifest } from './routeTypes';\n\nexport interface RouteConflict {\n method: string;\n urlPath: string;\n files: string[];\n}\n\n/**\n * 检测路由冲突(相同 method + urlPath 的多个文件)\n */\nexport function detectRouteConflicts(routes: RouteManifest): RouteConflict[] {\n const map = new Map<string, RouteConflict>();\n\n for (const route of routes) {\n const key = `${route.method} ${route.urlPath}`;\n const existing = map.get(key);\n if (existing) {\n existing.files.push(route.filePath);\n } else {\n map.set(key, {\n method: route.method,\n urlPath: route.urlPath,\n files: [route.filePath],\n });\n }\n }\n\n // 只返回有冲突的(文件数 > 1)\n const conflicts: RouteConflict[] = [];\n for (const conflict of map.values()) {\n if (conflict.files.length > 1) {\n conflicts.push(conflict);\n }\n }\n\n return conflicts;\n}\n","import type { RouteManifest, RouteMatch, WsRouteManifest, WsRouteMatch } from './routeTypes';\n\n/**\n * 路由匹配索引——把 O(n) 线性扫描降为静态路由 O(1) 命中\n *\n * 每个请求都要做路由匹配,原实现逐条遍历清单(静态路由字符串比较、动态路由\n * split('/') 比对),大清单下是热路径开销。索引把清单分为两部分:\n * - **静态路由**:`method|urlPath` → route 的 Map,一次查找命中\n * - **动态路由**:保持清单顺序的数组(`sortRoutes` 保证静态恒在动态之前,\n * 且同一 `(method, path)` 至多一条静态路由,静态优先查与原 first-match\n * 语义完全等价)\n *\n * 索引按**清单数组身份**缓存(WeakMap):`reloadRoutes` / `hydrateRoutes`\n * 整体替换清单数组(`routesRef.current = newRoutes`),旧数组被替换后索引\n * 自动失效可被 GC,无需手动清理——无生命周期侵入。\n *\n * 详见 [matchRoute.md](./matchRoute.md)。\n */\n\n/**\n * HTTP 路由匹配索引\n */\ninterface HttpRoutesIndex {\n /** 静态路由:`method|urlPath` → route */\n static: Map<string, RouteManifest[number]>;\n /** 静态路径 → 方法集合(405 findAllowedMethods 反查用,免再扫静态段) */\n methodsByStaticPath: Map<string, Set<string>>;\n /** 动态路由(含 catch-all),保持清单顺序,pattern 已预编译 */\n dynamics: DynamicEntry[];\n}\n\n/** 动态路由条目(pattern segments 预编译,免去每请求对固定 pattern 重复 split) */\ninterface DynamicEntry {\n route: RouteManifest[number];\n /** 预编译的模式段(pattern.split('/').filter(Boolean),对固定 route 恒定) */\n segments: string[];\n}\n\n/** WS 路由匹配索引(无 HTTP 方法维度) */\ninterface WsRoutesIndex {\n /** 静态路由:urlPath → route */\n static: Map<string, WsRouteManifest[number]>;\n /** 动态路由(含 catch-all),保持清单顺序 */\n dynamics: WsRouteManifest;\n}\n\nconst httpIndexCache = new WeakMap<RouteManifest, HttpRoutesIndex>();\nconst wsIndexCache = new WeakMap<WsRouteManifest, WsRoutesIndex>();\n\n/** 构建 HTTP 路由索引(惰性,每个清单数组只构建一次) */\nfunction getHttpIndex(routes: RouteManifest): HttpRoutesIndex {\n let index = httpIndexCache.get(routes);\n if (index) return index;\n\n index = { static: new Map(), methodsByStaticPath: new Map(), dynamics: [] };\n for (const route of routes) {\n if (route.isDynamic) {\n index.dynamics.push({\n route,\n segments: route.urlPath.split('/').filter(Boolean),\n });\n } else {\n index.static.set(`${route.method}|${route.urlPath}`, route);\n let methods = index.methodsByStaticPath.get(route.urlPath);\n if (!methods) {\n methods = new Set();\n index.methodsByStaticPath.set(route.urlPath, methods);\n }\n methods.add(route.method);\n }\n }\n\n httpIndexCache.set(routes, index);\n return index;\n}\n\n/** 构建 WS 路由索引(惰性,每个清单数组只构建一次) */\nfunction getWsIndex(routes: WsRouteManifest): WsRoutesIndex {\n let index = wsIndexCache.get(routes);\n if (index) return index;\n\n index = { static: new Map(), dynamics: [] };\n for (const route of routes) {\n if (route.isDynamic) {\n index.dynamics.push(route);\n } else {\n index.static.set(route.urlPath, route);\n }\n }\n\n wsIndexCache.set(routes, index);\n return index;\n}\n\n/**\n * 根据请求路径和方法匹配路由\n *\n * HEAD 回退:无显式 HEAD 路由时复用 GET handler(Node ServerResponse 对 HEAD\n * 自动丢弃 body)。探活、CDN 健康检查、HTTP 客户端预检常用 HEAD——没有回退时\n * 只定义 GET 的路由对 HEAD 返回 405,监控大面积误报。\n *\n * @param routes 已排序的路由清单\n * @param method HTTP 方法\n * @param path 请求路径\n * @returns 匹配结果,包含路由记录和参数;无匹配返回 null\n */\nexport function matchRoute(routes: RouteManifest, method: string, path: string): RouteMatch | null {\n const index = getHttpIndex(routes);\n const upper = method.toUpperCase();\n\n const hit = matchByMethod(index, upper, path);\n if (hit) return hit;\n\n if (upper === 'HEAD') {\n return matchByMethod(index, 'GET', path);\n }\n\n return null;\n}\n\n/** 按方法匹配(静态 O(1) → 动态线性),matchRoute 内部复用(HEAD 回退 GET) */\nfunction matchByMethod(index: HttpRoutesIndex, method: string, path: string): RouteMatch | null {\n // 静态路由 O(1) 命中(sortRoutes 保证静态优先,等价于原遍历的首个命中)\n const staticHit = index.static.get(`${method}|${path}`);\n if (staticHit) {\n return { route: staticHit, params: {} };\n }\n\n // 动态路由按清单顺序匹配(pattern segments 已预编译)\n for (const entry of index.dynamics) {\n const route = entry.route;\n if (route.method !== method) {\n continue;\n }\n const params = matchSegments(entry.segments, path, route.paramNames, route.isCatchAll);\n if (params !== null) {\n return { route, params };\n }\n }\n\n return null;\n}\n\n/**\n * 匹配 WebSocket 路由(无 HTTP 方法维度)\n *\n * WS 路由只按路径匹配,协议升级时调用。\n * @param wsRoutes WebSocket 路由清单\n * @param path 请求路径\n */\nexport function matchWsRoute(wsRoutes: WsRouteManifest, path: string): WsRouteMatch | null {\n const index = getWsIndex(wsRoutes);\n\n const staticHit = index.static.get(path);\n if (staticHit) {\n return { route: staticHit, params: {} };\n }\n\n for (const route of index.dynamics) {\n const params = matchDynamicPath(route.urlPath, path, route.paramNames, route.isCatchAll);\n if (params !== null) {\n return { route, params };\n }\n }\n\n return null;\n}\n\n/**\n * 查找路径的所有允许方法(用于 405 响应)\n *\n * 静态段直接查索引(O(1)),仅动态段线性扫描——原实现对全部路由做\n * O(n) 遍历 + 静态路径比较,404/405 高频场景(扫描器、探活探测)开销显著。\n */\nexport function findAllowedMethods(routes: RouteManifest, path: string): string[] {\n const index = getHttpIndex(routes);\n const methods = new Set<string>();\n\n // 静态路由:索引直查\n const staticMethods = index.methodsByStaticPath.get(path);\n if (staticMethods) {\n for (const method of staticMethods) {\n methods.add(method);\n }\n }\n\n // 动态路由:线性扫描(segments 已预编译)\n for (const entry of index.dynamics) {\n const params = matchSegments(\n entry.segments,\n path,\n entry.route.paramNames,\n entry.route.isCatchAll,\n );\n if (params !== null) {\n methods.add(entry.route.method);\n }\n }\n\n // RFC 9110:GET 允许时 HEAD 必然允许(matchRoute 有 HEAD→GET 回退),\n // 405 的 Allow 头如实反映,避免客户端误判 HEAD 不可用\n if (methods.has('GET')) {\n methods.add('HEAD');\n }\n\n return Array.from(methods);\n}\n\n/**\n * 动态路径匹配\n * 将路由模式(如 /user/:id)与请求路径(如 /user/123)匹配\n * 支持 catch-all 路由(如 /shop/:...slug 匹配 /shop/clothes/tops)\n * 返回提取的参数对象,不匹配返回 null\n */\nexport function matchDynamicPath(\n pattern: string,\n path: string,\n paramNames: string[],\n isCatchAll?: boolean,\n): Record<string, string> | null {\n return matchSegments(pattern.split('/').filter(Boolean), path, paramNames, isCatchAll);\n}\n\n/**\n * 基于**预编译 segments** 的动态匹配(热路径内部用)\n *\n * 与 matchDynamicPath 语义一致,但模式段由索引构建期一次性 split,\n * 免去每请求对固定 pattern 重复字符串分割\n */\nfunction matchSegments(\n patternSegments: string[],\n path: string,\n paramNames: string[],\n isCatchAll?: boolean,\n): Record<string, string> | null {\n const pathSegments = path.split('/').filter(Boolean);\n\n // catch-all 路由:最后一个模式段为 :...slug\n if (isCatchAll) {\n // catch-all 前面的静态/动态段必须匹配\n // catch-all 段至少匹配一个路径段\n const nonCatchAllCount = patternSegments.length - 1;\n if (pathSegments.length <= nonCatchAllCount) {\n return null;\n }\n\n const params: Record<string, string> = {};\n\n for (let i = 0; i < nonCatchAllCount; i++) {\n // i < nonCatchAllCount ≤ 两数组长度,索引必然存在\n const patternSeg = patternSegments[i]!;\n const pathSeg = pathSegments[i]!;\n\n if (patternSeg.startsWith(':')) {\n const paramName = patternSeg.slice(1);\n params[paramName] = pathSeg;\n } else if (patternSeg !== pathSeg) {\n return null;\n }\n }\n\n // catch-all 段:剩余所有路径段用 / 连接\n const catchAllValue = pathSegments.slice(nonCatchAllCount).join('/');\n // 前置检查 pathSegments.length > nonCatchAllCount 保证 catch-all 段存在\n const catchAllParamName = patternSegments[nonCatchAllCount]!.slice(4); // 去掉 ':...'\n params[catchAllParamName] = catchAllValue;\n\n if (Object.keys(params).length !== paramNames.length) {\n return null;\n }\n\n return params;\n }\n\n // 普通动态路由:段数必须一致\n if (patternSegments.length !== pathSegments.length) {\n return null;\n }\n\n const params: Record<string, string> = {};\n\n for (let i = 0; i < patternSegments.length; i++) {\n // 段数一致性检查后索引必然存在\n const patternSeg = patternSegments[i]!;\n const pathSeg = pathSegments[i]!;\n\n if (patternSeg.startsWith(':')) {\n // 动态段,提取参数值\n const paramName = patternSeg.slice(1);\n params[paramName] = pathSeg;\n } else if (patternSeg !== pathSeg) {\n // 静态段不匹配\n return null;\n }\n }\n\n // 确保所有参数名都被提取到\n if (Object.keys(params).length !== paramNames.length) {\n return null;\n }\n\n return params;\n}\n","/**\n * 从模块中解析出指定名称的导出\n *\n * 解析顺序:\n * 1. 具名导出:export function GET() {}\n * 2. 默认导出的对象属性:export default { GET() {} }\n *\n * @param module 动态 import 得到的模块对象\n * @param exportName 导出名(如 'GET')\n */\nexport function resolveExport(module: Record<string, unknown>, exportName: string): unknown {\n // 1. 具名导出\n if (exportName in module && typeof module[exportName] !== 'undefined') {\n return module[exportName];\n }\n\n // 2. 默认导出的对象属性\n const defaultExport = module.default;\n if (defaultExport !== null && typeof defaultExport === 'object') {\n const value = (defaultExport as Record<string, unknown>)[exportName];\n if (value !== undefined) {\n return value;\n }\n }\n\n return undefined;\n}\n","/**\n * 校验导出值是否为合法的 handler 函数\n */\nexport function validateRouteModule(\n value: unknown,\n method: string,\n filePath: string,\n): asserts value is (...args: unknown[]) => unknown {\n if (typeof value !== 'function') {\n throw new Error(\n `Route module \"${filePath}\" does not export a valid handler for method \"${method}\". ` +\n `Expected a function, got ${typeof value}.`,\n );\n }\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { compileDevRoutes } from './compileDevRoutes';\nimport { generateSchemaFiles } from './generateSchemaFiles';\nimport { getRuntimeSchemaPath } from './generateSchemaFiles';\nimport { collectRelativeImports } from './collectImports';\nimport type { RouteManifest } from '../router/routeTypes';\n\n/**\n * 按需编译(Vite 风格):仅在请求时编译被访问的 handler.ts 及其依赖闭包 + 生成 zod.js\n *\n * 设计目标:dev 启动时零编译、零 schema 生成,handler.js / zod.js 首次被需要时才触发。\n * 与阶段 1(scanRoutes 去 import)配合,dev 冷启动近乎瞬开。\n *\n * 依赖闭包:`bundle: false` 逐文件编译不分析 import 关系,只编译 handler 单文件时,\n * handler 引用的共享模块(如 `../../lib/db`)没有产物,首次请求 import 即\n * ERR_MODULE_NOT_FOUND。因此 ensureCompiled 先收集依赖闭包\n * (collectRelativeImports,相对 + tsconfig paths 别名),跳过产物已新鲜的文件后\n * 批量编译。src 外依赖不收集(outbase=src 语义限制,与 watcher 增量编译一致)。\n *\n * 四种触发路径:\n * 1. `loadRouteModule` 先调 `ensureCompiled` 编译源码 → 再 import handler.js\n * 2. `loadWsHandler` 同上(WS handler.js)\n * 3. `createServer` 调 `ensureSchemaGenerated` 检查 zod.js 是否存在/最新 → 不存在则生成\n * 4. watcher 文件变化 → 增量编译该文件(保留现有逻辑)+ 删除 stale zod.js\n * 5. `createServer` / `handleWsUpgrade` 首次加载目录中间件前调 `ensureMiddlewaresCompiled`\n *\n * mtime 缓存(阶段 4):\n * - `ensureCompiled`:handler.js 存在且 mtime ≥ 源码 mtime → 跳过编译\n * - `ensureSchemaGenerated`:zod.js 存在且 mtime ≥ 源码 mtime → 跳过生成\n * - 首次调用后写入内存 Set,后续直接跳过(避免 fs.statSync 调用)\n * - watcher 触发时清除内存 Set,mtime 重新评估\n *\n * 并发去重(mutex):\n * - 同一文件的并发编译请求会共享同一个 in-flight Promise,避免重复触发 esbuild\n * - 同一 schemaPath 的并发生成请求同理\n * - watcher 触发 clear 时同步清空 in-flight Map\n */\n\n/**\n * 比较源文件和产物文件的 mtime\n *\n * @returns true 表示产物是最新的(可复用),false 表示需要重新生成\n */\nfunction isProductFresh(sourceAbsPath: string, productAbsPath: string): boolean {\n try {\n const srcStat = fs.statSync(sourceAbsPath);\n const prodStat = fs.statSync(productAbsPath);\n return prodStat.mtimeMs >= srcStat.mtimeMs;\n } catch {\n return false;\n }\n}\n\n// ─── DevOnDemand 状态封装 ────────────────────────────────────────────\n\n/**\n * 按需编译运行时状态\n *\n * 封装原本散落的 4 个模块级可变状态 + 2 个 mutex Map,避免全局污染 +\n * 便于测试隔离(_resetDevOnDemandState 重置回初始状态)。\n *\n * faapi 单进程单 server 设计下,单例 state 已足够。多实例测试场景通过\n * _resetDevOnDemandState 在 beforeEach / afterEach 中清空状态。\n */\ninterface DevOnDemandState {\n /** 是否启用 dev 按需编译模式(prod 始终 false) */\n enabled: boolean;\n /** dev 模式产物目录(仅 enabled 时使用) */\n distDir: string | undefined;\n /** 已编译文件缓存(源码绝对路径) */\n compiledFiles: Set<string>;\n /** 已生成 schema 缓存(schemaPath 绝对路径) */\n generatedSchemas: Set<string>;\n /** handler.js 编译 mutex:同一 sourceAbsPath 的并发请求共享同一 Promise */\n inFlightCompilations: Map<string, Promise<void>>;\n /** zod.js 生成 mutex:同一 schemaPath 的并发请求共享同一 Promise */\n inFlightSchemaGenerations: Map<string, Promise<void>>;\n}\n\nfunction createDevOnDemandState(): DevOnDemandState {\n return {\n enabled: false,\n distDir: undefined,\n compiledFiles: new Set(),\n generatedSchemas: new Set(),\n inFlightCompilations: new Map(),\n inFlightSchemaGenerations: new Map(),\n };\n}\n\nconst state: DevOnDemandState = createDevOnDemandState();\n\n/**\n * 重置 dev on demand 状态(仅测试用)\n *\n * 清空所有缓存 + mutex + 标记位,让下一次测试从干净状态开始。\n * 生产代码不应调用此函数——会丢失已编译产物的缓存。\n */\nexport function _resetDevOnDemandState(): void {\n state.enabled = false;\n state.distDir = undefined;\n state.compiledFiles.clear();\n state.generatedSchemas.clear();\n state.inFlightCompilations.clear();\n state.inFlightSchemaGenerations.clear();\n}\n\n// ─── handler.js 按需编译 ────────────────────────────────────────────\n\n/**\n * 清空所有按需编译缓存(reloadRoutes 时调用)\n *\n * 同时清空 in-flight mutex Map + 产物→源码映射缓存(源文件新建/删除后映射需重建),\n * 避免 watcher 重置后旧 Promise 永久阻塞。\n */\nexport function clearCompiledFiles(): void {\n state.compiledFiles.clear();\n state.inFlightCompilations.clear();\n sourcePathCache.clear();\n}\n\n/**\n * 确保源文件及其依赖闭包已编译为产物,未编译则触发批量编译\n *\n * 调用方(`loadRouteModule` / `loadWsHandler`)在 import 产物之前调用此函数,确保产物已生成。\n *\n * 依赖闭包:handler 引用的项目内共享模块(相对 import + tsconfig paths 别名)一并编译,\n * 跳过产物已新鲜的文件(watcher 已编译或此前请求已编译),只补缺口。\n *\n * mtime 缓存(阶段 4):\n * 1. 内存 Set 命中 → 跳过(最快路径)\n * 2. 产物存在且 mtime ≥ 源码 mtime → 跳过(复用已有产物,如 watcher 已编译)\n * 3. 产物不存在或 stale → 编译(含 stale 的依赖闭包文件) → 加入内存 Set\n *\n * 并发去重(mutex):\n * - 同一 sourceAbsPath 的并发请求共享同一 in-flight Promise\n * - 第二个请求 await 后返回 false(表示「别的请求已触发编译,本次不需要再触发」)\n *\n * @param sourceAbsPath 源码 .ts 绝对路径\n * @param rootDir 项目根目录\n * @param dist 产物目录(dev 模式为 '.faapi')\n * @returns 是否实际触发了编译(false 表示已编译过或产物已最新或源文件不存在)\n * @throws 编译失败时抛错(带原始 cause),调用方可据此给出精确错误信息\n */\nexport async function ensureCompiled(\n sourceAbsPath: string,\n rootDir: string,\n dist: string,\n): Promise<boolean> {\n // mutex: 同一文件正在被别的请求编译 → 等待并返回 false\n const inFlight = state.inFlightCompilations.get(sourceAbsPath);\n if (inFlight) {\n await inFlight.catch(() => {\n // 别的请求编译失败时不在这里抛——让本请求按正常流程自己重试\n });\n return false;\n }\n\n // 内存缓存命中:跳过\n if (state.compiledFiles.has(sourceAbsPath)) {\n return false;\n }\n\n // 源文件不存在:无法编译\n if (!fs.existsSync(sourceAbsPath)) {\n return false;\n }\n\n // mtime 缓存:产物已存在且最新 → 复用(watcher 已编译过的情况)\n const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);\n if (productPath && isProductFresh(sourceAbsPath, productPath)) {\n state.compiledFiles.add(sourceAbsPath);\n return false;\n }\n\n // 触发编译:注册 in-flight Promise 防止并发重复编译\n const compilePromise = (async () => {\n // 依赖闭包:入口本身 + src 内全部传递依赖;产物已新鲜的文件跳过(只补缺口)\n const { insideFiles } = await collectRelativeImports([sourceAbsPath], rootDir);\n const files = [sourceAbsPath];\n for (const dep of insideFiles) {\n if (state.compiledFiles.has(dep)) continue;\n const depProduct = prodSourcePathToProductPath(dep, rootDir, dist);\n if (depProduct && isProductFresh(dep, depProduct)) continue;\n files.push(dep);\n }\n\n await compileDevRoutes({\n rootDir,\n dist,\n files,\n logLevel: 'silent',\n });\n for (const file of files) {\n state.compiledFiles.add(file);\n }\n state.compiledFiles.add(sourceAbsPath);\n })();\n\n state.inFlightCompilations.set(sourceAbsPath, compilePromise);\n try {\n await compilePromise;\n return true;\n } finally {\n state.inFlightCompilations.delete(sourceAbsPath);\n }\n}\n\n/**\n * 确保目录中间件文件已编译(dev 按需模式)\n *\n * middlewarePaths 是产物绝对路径(`.faapi/api/users/middlewares.js`),dev 按需模式下\n * 产物在首次请求前不存在——若不先编译,`loadMergedMiddlewares` import 即\n * ERR_MODULE_NOT_FOUND。通过 `prodPathToSourcePath` 反推源码路径后走 `ensureCompiled`\n * (含依赖闭包)。prod 模式(按需开关关闭)直接返回——产物由 build 固化。\n *\n * @param middlewarePaths 中间件产物绝对路径列表(scanRoutes 收集)\n * @param rootDir 项目根目录\n */\nexport async function ensureMiddlewaresCompiled(\n middlewarePaths: string[],\n rootDir: string,\n): Promise<void> {\n if (!isDevOnDemandEnabled() || middlewarePaths.length === 0) return;\n const dist = getDevDist();\n if (!dist) return;\n\n for (const mwPath of middlewarePaths) {\n const sourcePath = prodPathToSourcePath(mwPath, rootDir, dist);\n try {\n await ensureCompiled(sourcePath, rootDir, dist);\n } catch (err) {\n // 中间件编译失败不阻断请求——loadMiddlewaresFile 对 import 失败已有\n // console.error + 空 bundle 降级语义(鉴权失效由 onError 感知),此处保持一致\n console.error(`[faapi] Failed to compile middleware source ${sourcePath}:`, err);\n }\n }\n}\n\n/**\n * 从源码绝对路径推算产物绝对路径\n *\n * 源码 `<rootDir>/src/api/hello/handler.ts` → 产物 `<rootDir>/<dist>/api/hello/handler.js`\n */\nfunction prodSourcePathToProductPath(\n sourceAbsPath: string,\n rootDir: string,\n dist: string,\n): string | null {\n const rel = path.relative(rootDir, sourceAbsPath).replace(/\\\\/g, '/');\n if (!rel.startsWith('src/')) return null;\n const relWithoutSrc = rel.slice(4); // 去掉 src/\n const jsRel = relWithoutSrc.replace(/\\.ts$/, '.js');\n return path.resolve(rootDir, dist, jsRel);\n}\n\n// ─── zod.js 按需生成 ────────────────────────────────────────────────\n\n/**\n * 清空所有 schema 生成缓存(reloadRoutes 时调用)\n *\n * 同时清空 in-flight mutex Map。\n */\nexport function clearGeneratedSchemas(): void {\n state.generatedSchemas.clear();\n state.inFlightSchemaGenerations.clear();\n}\n\n/**\n * 确保 zod.js 已生成,未生成则触发单文件 schema 生成\n *\n * 调用方(createServer)在 validateInput 之前调用此函数。\n *\n * route.filePath 在 dev/prod 模式下均为产物路径(如 '.faapi/api/hello/handler.js'),\n * 需通过 prodPathToSourcePath 反推源码 .ts 路径用于 AST 分析和 mtime 比较。\n *\n * mtime 缓存(阶段 4):\n * 1. 内存 Set 命中 → 跳过\n * 2. zod.js 存在且 mtime ≥ 源码 mtime → 跳过(复用已有产物)\n * 3. zod.js 不存在或 stale → 生成 → 加入内存 Set\n *\n * 并发去重(mutex):\n * - 同一 schemaPath 的并发请求共享同一 in-flight Promise\n * - 第二个请求 await 后返回 false\n *\n * @param schemaPath zod.js 绝对路径\n * @param routeFilePath route.filePath(产物路径,如 '.faapi/api/hello/handler.js')\n * @param routes 完整路由清单(用于过滤同文件的所有方法)\n * @param rootDir 项目根目录\n * @param dist 产物目录\n * @returns 是否实际触发了生成(false 表示已生成过或产物已最新或源文件不存在)\n * @throws schema 生成失败时抛错(带原始 cause),由 createServer 错误处理链接管\n */\nexport async function ensureSchemaGenerated(\n schemaPath: string,\n routeFilePath: string,\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n): Promise<boolean> {\n // mutex: 同一 schemaPath 正在被别的请求生成 → 等待并返回 false\n const inFlight = state.inFlightSchemaGenerations.get(schemaPath);\n if (inFlight) {\n await inFlight.catch(() => {\n // 别的请求生成失败时不在这里抛——让本请求按正常流程自己重试\n });\n return false;\n }\n\n // 内存缓存命中:跳过\n if (state.generatedSchemas.has(schemaPath)) {\n return false;\n }\n\n // route.filePath 是产物路径,反推源码绝对路径(用于 mtime 比较和 AST 分析)\n const prodAbsPath = path.resolve(rootDir, routeFilePath);\n const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);\n if (!fs.existsSync(sourceAbsPath)) {\n return false;\n }\n\n // mtime 缓存:zod.js 已存在且最新 → 复用\n if (isProductFresh(sourceAbsPath, schemaPath)) {\n state.generatedSchemas.add(schemaPath);\n return false;\n }\n\n // 过滤同文件的所有路由(一个 handler.ts 可能有 GET/POST/WS 多个方法)\n const fileRoutes = routes.filter((r) => r.filePath === routeFilePath);\n if (fileRoutes.length === 0) {\n return false;\n }\n\n // 把产物 filePath 转为源码 filePath(generateSchemaFiles → collectRouteSchemaSources\n // 需要源码 .ts 路径做 AST 分析)\n const sourceRelPath = path.relative(rootDir, sourceAbsPath).replace(/\\\\/g, '/');\n const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));\n\n // 触发生成:注册 in-flight Promise 防止并发重复生成\n const generatePromise = (async () => {\n // 生成失败时抛错(不吞错误),由 createServer 错误处理链接管\n await generateSchemaFiles(sourceRoutes, rootDir, dist);\n state.generatedSchemas.add(schemaPath);\n })();\n\n state.inFlightSchemaGenerations.set(schemaPath, generatePromise);\n try {\n await generatePromise;\n return true;\n } finally {\n state.inFlightSchemaGenerations.delete(schemaPath);\n }\n}\n\n/**\n * 删除指定路由清单对应的所有 zod.js 文件(reloadRoutes 时调用)\n *\n * watcher 文件变化后,旧 zod.js 可能 stale(类型引用变化等)。\n * 删除后下次请求触发 ensureSchemaGenerated 重新生成。\n *\n * 仅删除文件,不删除目录(faapi-helpers.js 保留,内容确定性不变)。\n */\nexport async function deleteSchemaFiles(\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n): Promise<void> {\n const deleted = new Set<string>();\n for (const route of routes) {\n const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);\n if (deleted.has(schemaPath)) continue;\n deleted.add(schemaPath);\n try {\n await fs.promises.unlink(schemaPath);\n } catch {\n // 文件不存在:忽略\n }\n }\n}\n\n// ─── 路径转换 ────────────────────────────────────────────────────────\n\n/**\n * 从产物绝对路径反推源码绝对路径(带映射缓存)\n *\n * 源码 `<rootDir>/src/api/hello/handler.ts` → 产物 `<rootDir>/<dist>/api/hello/handler.js`\n *\n * dev 按需模式下**每请求**都会调此函数(loadRouteModule / ensureSchemaGenerated),\n * 缓存映射后稳态请求零 fs 访问。映射仅取决于源文件的存在性——源文件新建/删除\n * 通过 `clearCompiledFiles()`(reloadRoutes 时调用)清缓存。\n */\nconst sourcePathCache = new Map<string, string>();\n\nexport function prodPathToSourcePath(prodAbsPath: string, rootDir: string, dist: string): string {\n const cached = sourcePathCache.get(prodAbsPath);\n if (cached) return cached;\n\n const rel = path.relative(rootDir, prodAbsPath).replace(/\\\\/g, '/');\n // 去掉 <dist>/ 前缀\n let relWithoutDist = rel;\n if (relWithoutDist.startsWith(`${dist}/`)) {\n relWithoutDist = relWithoutDist.slice(dist.length + 1);\n }\n // 加 src/ 前缀\n const srcRel = `src/${relWithoutDist}`;\n // .js → .ts(.ts 不存在时回退 .js)\n const tsRel = srcRel.replace(/\\.js$/, '.ts');\n const tsAbs = path.resolve(rootDir, tsRel);\n let result: string;\n if (fs.existsSync(tsAbs)) {\n result = tsAbs;\n } else {\n // .ts 不存在,保持 .js(极少见,可能是用户直接放 .js 源码)\n result = path.resolve(rootDir, srcRel);\n }\n sourcePathCache.set(prodAbsPath, result);\n return result;\n}\n\n// ─── Dev 按需模式标记 ────────────────────────────────────────────────\n\n/**\n * Dev 按需编译模式标记\n *\n * 由 devCommand 在按需模式启动时设置为 true,loadRouteModule / loadWsHandler /\n * createServer 通过此标记判断是否启用按需编译/schema 生成回退。\n *\n * prod 模式(node dist/main)始终为 false——产物在 build 阶段已固化,import 失败即报错。\n */\nexport function setDevOnDemandEnabled(enabled: boolean): void {\n state.enabled = enabled;\n}\n\nexport function isDevOnDemandEnabled(): boolean {\n return state.enabled;\n}\n\n/**\n * Dev 模式产物目录(仅 devOnDemandEnabled 时使用)\n *\n * 由 devCommand 设置,loadRouteModule / loadWsHandler / createServer 通过 getDevDist() 读取。\n */\nexport function setDevDist(dist: string): void {\n state.distDir = dist;\n}\n\nexport function getDevDist(): string | undefined {\n return state.distDir;\n}\n","import type { HttpMethod } from '../router/constants';\nimport { resolveExport } from './resolveExports';\nimport { validateRouteModule } from './validateRouteModule';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport {\n ensureCompiled,\n isDevOnDemandEnabled,\n getDevDist,\n prodPathToSourcePath,\n} from '../cli/compileOnDemand';\n\nexport interface RouteModule {\n handler: (...args: unknown[]) => unknown;\n method: HttpMethod;\n}\n\n/**\n * 动态 import 路由文件并提取 handler\n *\n * Dev 按需编译模式(Vite 风格):先确保编译再 import,避免 import 不存在的文件。\n * Prod 模式:产物在 build 阶段已固化,直接 import,失败即报错。\n *\n * 错误传递:\n * - 编译失败 → 抛出编译错误(\"Failed to compile route module\")\n * - import 失败 → 抛出加载错误(\"Failed to load route module\")\n *\n * @param filePath 路由文件的绝对路径\n * @param method HTTP 方法名(也是导出名)\n * @param rootDir 项目根目录(按需编译模式用,可选)\n */\nexport async function loadRouteModule(\n filePath: string,\n method: HttpMethod,\n rootDir?: string,\n): Promise<RouteModule> {\n // Dev 按需模式:先确保编译再 import\n // 避免\"import 失败 → 编译 → 重试 import\"模式——首次 import 不存在的文件会污染\n // Vite SSR 内部状态,导致编译创建文件后重试 import 仍失败(CI Linux 上复现)\n //\n // 不在此处 existsSync(sourcePath):ensureCompiled 内部对缺失源文件直接返回 false,\n // 外层检查是冗余的每请求同步 IO(编译完成一次后内存 Set 命中即短路)\n if (isDevOnDemandEnabled() && rootDir) {\n const dist = getDevDist();\n if (dist) {\n const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);\n if (sourcePath) {\n try {\n await ensureCompiled(sourcePath, rootDir, dist);\n } catch (compileErr) {\n const reason = compileErr instanceof Error ? compileErr.message : String(compileErr);\n throw new Error(`Failed to compile route module \"${sourcePath}\": ${reason}`, {\n cause: compileErr,\n });\n }\n }\n }\n }\n\n // import 模块(dev 按需模式下走 Node 原生 import 绕过 Vite SSR 缓存)\n let module: Record<string, unknown>;\n try {\n module = await importWithCacheBust(filePath, isDevOnDemandEnabled());\n } catch (err: unknown) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load route module \"${filePath}\": ${reason}`, { cause: err });\n }\n\n const handler = resolveExport(module, method);\n validateRouteModule(handler, method, filePath);\n\n return { handler, method };\n}\n","/**\n * SSE(Server-Sent Events)支持\n *\n * 让 handler 能向客户端推送流式事件,用于 LLM token 流、进度通知等场景。\n *\n * 核心导出:\n * - `encodeSseEvent(event)`:把 SSE 事件对象编码为符合 HTML5 SSE 规范的字符串\n * - `createSseWriter()`:创建一个 SseWriter,封装 ReadableStream + Response,提供 send/close/sendError API\n * - `SseWriter`:writer 类型,ctx.sse() 返回此类型\n *\n * 设计要点:\n * - writer 内部用 ReadableStream + TextEncoder,send 时 enqueue,close 时 close controller\n * - response 预设 text/event-stream、no-cache、keep-alive 头,状态码默认 200\n * - close 后再 send 静默忽略,避免 handler 异步流程中误写已关闭的流\n * - sendError 向流写入 event: error 后关闭,用于流式输出中报错的优雅终止\n *\n * 与 ctx 的集成:\n * - ctx.sse() 调用 createSseWriter(),并把 response 缓存到 ctx 内部字段\n * - invokeHandler 在 handler 返回后检查 ctx 是否持有 SSE response,有则优先使用\n */\n\n/**\n * SSE 事件字段\n *\n * 遵循 HTML5 SSE 规范:\n * - `data`:消息数据,多行时每行加 `data: ` 前缀;对象自动 JSON.stringify\n * - `event`:事件类型,客户端可用 addEventListener(event) 监听\n * - `id`:事件 ID,客户端断线重连时通过 Last-Event-ID 头发送\n * - `retry`:重连等待时间(毫秒)\n * - `comment`:注释行(以 `:` 开头),用于 keep-alive 心跳,不传递给客户端消息\n */\nexport interface SseEvent {\n /** 消息数据。字符串原样输出;对象自动 JSON.stringify;多行时每行加 data: 前缀 */\n data?: unknown;\n /** 事件类型,客户端可用 addEventListener 监听 */\n event?: string;\n /** 事件 ID,客户端断线重连时通过 Last-Event-ID 头发送 */\n id?: string | number;\n /** 重连等待时间(毫秒) */\n retry?: number;\n /** 注释行(以 : 开头),用于 keep-alive 心跳 */\n comment?: string;\n}\n\n/**\n * 把 SSE 事件对象编码为符合 HTML5 SSE 规范的字符串\n *\n * 字段顺序固定:comment > event > id > retry > data,末尾空行分隔事件。\n *\n * @param event SSE 事件对象\n * @returns 编码后的字符串(含末尾空行)\n */\nexport function encodeSseEvent(event: SseEvent): string {\n let out = '';\n\n // comment(注释行,以 : 开头)\n if (event.comment !== undefined) {\n out += `: ${event.comment}\\n`;\n }\n\n // event\n if (event.event !== undefined) {\n out += `event: ${event.event}\\n`;\n }\n\n // id\n if (event.id !== undefined) {\n out += `id: ${event.id}\\n`;\n }\n\n // retry\n if (event.retry !== undefined) {\n out += `retry: ${event.retry}\\n`;\n }\n\n // data:对象 JSON.stringify,多行每行加前缀\n if (event.data !== undefined) {\n let dataStr: string;\n if (typeof event.data === 'string') {\n dataStr = event.data;\n } else if (event.data === null) {\n dataStr = 'null';\n } else {\n dataStr = JSON.stringify(event.data);\n }\n // 多行 data:每行加 data: 前缀\n const lines = dataStr.split('\\n');\n for (const line of lines) {\n out += `data: ${line}\\n`;\n }\n }\n\n // 空行结束事件\n out += '\\n';\n return out;\n}\n\n/**\n * SSE writer:封装流式推送 API\n *\n * 通过 `ctx.sse()` 创建,handler 调用 `send` 推送事件,`close` 关闭流。\n * 框架在 handler 返回后,自动使用 writer.response 作为 HTTP 响应。\n */\nexport interface SseWriter {\n /** 推送一个 SSE 事件 */\n send(event: SseEvent): void;\n /**\n * 直接写入原始字节/字符串,不做任何 SSE 序列化\n *\n * 用于透传上游已有的 SSE 原文(如 LLM 中转平台逐 chunk 转发 OpenAI 响应)。\n * 调用方负责保证内容符合 HTML5 SSE 规范;`send` 会再次加 `data: ` 前缀,\n * 不适用于原文透传场景。\n *\n * 接受 string 或 Uint8Array(Buffer 是 Uint8Array 子类,自然兼容)。\n * 与 `send` 一致:close/aborted 后静默忽略,不抛错。\n */\n sendRaw(chunk: string | Uint8Array): void;\n /** 推送一个 error 事件并关闭流(用于流式输出中报错的优雅终止) */\n sendError(error: unknown): void;\n /** 关闭流(多次调用安全) */\n close(): void;\n /** 流是否已关闭(handler 主动 close 或框架自动 close) */\n readonly closed: boolean;\n /** 客户端是否已断开(ReadableStream 被 cancel) */\n readonly aborted: boolean;\n /** 对应的 HTTP Response(由框架使用,用户一般不需要直接访问) */\n readonly response: Response;\n}\n\n/**\n * 创建一个 SSE writer\n *\n * 内部用 ReadableStream + TextEncoder 实现,send 时把编码后的事件 enqueue 到流,\n * close 时关闭 controller。response 预设标准 SSE 头。\n *\n * aborted 检测:监听 ReadableStream 的 cancel 钩子,客户端断开(cancel)时置为 true。\n * 此时 send 静默忽略,handler 可通过 writer.aborted 退出循环。\n */\nexport function createSseWriter(): SseWriter {\n const encoder = new TextEncoder();\n let controller: ReadableStreamDefaultController<Uint8Array> | null = null;\n let closed = false;\n let aborted = false;\n\n const stream = new ReadableStream<Uint8Array>({\n start(c) {\n controller = c;\n },\n cancel() {\n // 客户端断开连接(cancel ReadableStream)\n aborted = true;\n closed = true;\n controller = null;\n },\n });\n\n const response = new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n\n const writer: SseWriter = {\n send(event: SseEvent): void {\n if (closed || !controller) return;\n const text = encodeSseEvent(event);\n controller.enqueue(encoder.encode(text));\n },\n\n sendRaw(chunk: string | Uint8Array): void {\n if (closed || !controller) return;\n const bytes = typeof chunk === 'string' ? encoder.encode(chunk) : chunk;\n controller.enqueue(bytes);\n },\n\n sendError(error: unknown): void {\n if (closed || !controller) return;\n const message = error instanceof Error ? error.message : String(error);\n const text = encodeSseEvent({ event: 'error', data: message });\n try {\n controller.enqueue(encoder.encode(text));\n } finally {\n writer.close();\n }\n },\n\n close(): void {\n if (closed) return;\n closed = true;\n if (controller) {\n try {\n controller.close();\n } catch {\n // controller 可能已关闭,忽略\n }\n controller = null;\n }\n },\n\n get closed(): boolean {\n return closed;\n },\n\n get aborted(): boolean {\n return aborted;\n },\n\n get response(): Response {\n return response;\n },\n };\n\n return writer;\n}\n","import type { ErrorCode } from './errorCodes';\n\nexport class FaapiError extends Error {\n constructor(\n public readonly code: ErrorCode,\n message: string,\n public readonly statusCode: number,\n ) {\n super(message);\n this.name = 'FaapiError';\n }\n}\n","import { FaapiError } from './FaapiError';\n\n/**\n * 根据 issues 中的 code 推导 HTTP 状态码\n *\n * 语义参考 RFC 7807 / Rails / Laravel / Spring:\n * - 400 Bad Request:请求语法错误或结构不完整\n * - INVALID_FORMAT:JSON 格式错误、Date 非 ISO 8601\n * - MISSING_FIELD:缺少必填字段\n * - 422 Unprocessable Entity:请求语法正确但语义错误\n * - TYPE_MISMATCH:类型不匹配(string 期望 number)\n * - INVALID_VALUE:值不在允许范围(字面量/enum 不匹配)\n * - COERCE_FAILED:query 字符串转换失败\n *\n * 多个 issue 时取最高严重度(400 优先于 422)。\n */\nfunction deriveStatusCode(issues: ValidationIssue[]): number {\n // 有 400 类 issue → 400;否则 422\n const has400 = issues.some((i) => i.code === 'INVALID_FORMAT' || i.code === 'MISSING_FIELD');\n return has400 ? 400 : 422;\n}\n\nexport class ValidationError extends FaapiError {\n constructor(\n message: string,\n public readonly issues: ValidationIssue[],\n ) {\n super('VALIDATION_ERROR', message, deriveStatusCode(issues));\n this.name = 'ValidationError';\n }\n}\n\nexport class RouteNotFoundError extends FaapiError {\n constructor(path: string) {\n super('ROUTE_NOT_FOUND', `Route not found: ${path}`, 404);\n this.name = 'RouteNotFoundError';\n }\n}\n\nexport class MethodNotAllowedError extends FaapiError {\n constructor(\n method: string,\n path: string,\n public readonly allowedMethods: string[],\n ) {\n super('METHOD_NOT_ALLOWED', `Method ${method} not allowed for ${path}`, 405);\n this.name = 'MethodNotAllowedError';\n }\n}\n\nexport class InternalError extends FaapiError {\n constructor(message: string) {\n super('INTERNAL_ERROR', message, 500);\n this.name = 'InternalError';\n }\n}\n\nexport class ModuleLoadError extends FaapiError {\n constructor(filePath: string, reason: string) {\n super('MODULE_LOAD_ERROR', `Failed to load module ${filePath}: ${reason}`, 500);\n this.name = 'ModuleLoadError';\n }\n}\n\n/**\n * 请求体超过大小限制(413 Payload Too Large)\n *\n * 由 createServer 的 limitStreamSize 在流式读取阶段抛出,\n * 经 formatErrorResponse 转换为 413 响应,而非兜底 500。\n */\nexport class PayloadTooLargeError extends FaapiError {\n constructor(maxSize: number) {\n super('PAYLOAD_TOO_LARGE', `Request body exceeds size limit of ${maxSize} bytes`, 413);\n this.name = 'PayloadTooLargeError';\n }\n}\n\n/**\n * 校验问题类型\n *\n * 结构化错误信息,便于上层(全局错误中间件/前端)按 code 做不同处理,\n * 不依赖字符串解析。message 仅为人类可读的兜底描述。\n *\n * code 与 HTTP 状态码的映射(由 ValidationError 推导):\n * - INVALID_FORMAT / MISSING_FIELD → 400 Bad Request\n * - TYPE_MISMATCH / INVALID_VALUE / COERCE_FAILED → 422 Unprocessable Entity\n */\nexport interface ValidationIssue {\n /** 字段路径,如 'user.address.city' */\n path: string;\n /** 错误码,机器可读的契约 */\n code: ValidationErrorCode;\n /** 期望类型/值,如 'number' / '\"admin\" | \"user\"' */\n expected: string;\n /** 实际类型/值,如 'string' / 'undefined' */\n received: string;\n /** 人类可读的本地化消息(兜底,不保证稳定) */\n message: string;\n}\n\nexport type ValidationErrorCode =\n | 'TYPE_MISMATCH' // 类型不匹配(string 期望 number) → 422\n | 'MISSING_FIELD' // 缺少必填字段 → 400\n | 'INVALID_FORMAT' // 格式错误(JSON 解析失败、Date 非 ISO 等) → 400\n | 'INVALID_VALUE' // 值不在允许范围(字面量/enum 不匹配) → 422\n | 'COERCE_FAILED'; // 类型转换失败(query 字符串转 number/boolean 失败) → 422\n","import type { ResponseConfig } from '../config/configTypes';\nimport { FaapiError } from '../errors/FaapiError';\nimport { ValidationError, MethodNotAllowedError, PayloadTooLargeError } from '../errors/httpErrors';\n\n/**\n * 统一响应格式化中心\n *\n * 集中所有响应包装规则,确保 handler return 值的自动包裹、ctx.fail() 主动错误响应、\n * formatErrorResponse 抛错兜底 三条路径共享同一套 ok/fail 函数,避免响应格式漂移。\n *\n * 路径分工:\n * - 成功路径: handler `return data` → wrapOkResult → toResponse 多类型序列化\n * - 显式成功: handler `return ctx.ok(data)` → wrapOkResult → jsonOk 构造 Response\n * - 主动错误: handler `return ctx.fail({...})` → formatFailResponse → jsonOk\n * - 抛错兜底: handler throw → formatErrorResponse(err, config) → jsonOk\n *\n * 业务方通过 faapi.config.ts 的 `response.ok` / `response.fail` 自定义包装函数,\n * 三条路径自动一致生效。\n */\n\n/** 默认 ok 包装:(data) => ({ data }) */\nexport function defaultOk(data: unknown): Record<string, unknown> {\n return { data };\n}\n\n/**\n * 默认 fail 包装:省略的字段不放入 error 对象\n *\n * `code` 为 undefined 时不放入响应,保持 body 紧凑。\n */\nexport function defaultFail(e: {\n status?: number;\n code?: string;\n message: string;\n}): Record<string, unknown> {\n const error: Record<string, unknown> = { message: e.message };\n if (e.code !== undefined) error.code = e.code;\n return { error };\n}\n\n/** 从 ctx.config 读取 ResponseConfig(业务方在 faapi.config.ts 中配置的 response 字段) */\nexport function getResponseConfig(\n config: Record<string, unknown> | undefined,\n): ResponseConfig | undefined {\n return (config as { response?: ResponseConfig } | undefined)?.response;\n}\n\n/** 解析 ok 函数:业务方自定义 ?? 框架默认 */\nexport function resolveOkFn(config: Record<string, unknown> | undefined) {\n return getResponseConfig(config)?.ok ?? defaultOk;\n}\n\n/** 解析 fail 函数:业务方自定义 ?? 框架默认 */\nexport function resolveFailFn(config: Record<string, unknown> | undefined) {\n return getResponseConfig(config)?.fail ?? defaultFail;\n}\n\n/**\n * 构造 JSON 成功响应(不包裹,原样序列化)\n *\n * 用于 ctx.json / ctx.ok 等显式响应场景。\n */\nexport function jsonOk(body: unknown, status = 200, extraHeaders?: HeadersInit): Response {\n return jsonRaw(body, status, extraHeaders);\n}\n\n/** 内部:构造 JSON Response,不包外层 */\nfunction jsonRaw(body: unknown, status: number, extraHeaders?: HeadersInit): Response {\n const headers = new Headers({ 'Content-Type': 'application/json' });\n if (extraHeaders) {\n const extra = new Headers(extraHeaders);\n extra.forEach((value, key) => headers.set(key, value));\n }\n return new Response(JSON.stringify(body), { status, headers });\n}\n\n/**\n * 成功响应自动包装:handler return 值 → okFn(data) 包裹\n *\n * 规则:\n * - Response 对象:不包裹(ctx.ok/ctx.fail/ctx.json 等返回的 Response 原样透传)\n * - 其他值(含 null/undefined):用 config.response.ok(或默认 (data) => ({ data })) 包裹\n *\n * 注:null/undefined 也会被包裹为 { data: null } / { data: undefined }。\n *\n * 不构造 Response,只返回包装后的值——最终序列化交给 toResponse 处理(支持多类型分发 +\n * 合并 ctx.meta 的 setStatus/setHeader/setCookie)。\n */\nexport function wrapOkResult(\n result: unknown,\n config: Record<string, unknown> | undefined,\n): unknown {\n if (result instanceof Response) return result;\n return resolveOkFn(config)(result);\n}\n\n/**\n * 业务方主动错误响应:FailOptions → Response\n *\n * handler `return ctx.fail({ status, code, message })` 的实现。\n * 用 config.response.fail(或默认实现)包装为 `{ error: { ... } }` 结构。\n *\n * `status` 省略时默认 500;`code` 省略时由 fail 函数决定是否放入 body。\n */\nexport function formatFailResponse(\n options: { status?: number; code?: string; message: string },\n config: Record<string, unknown> | undefined,\n): Response {\n const failFn = resolveFailFn(config);\n const body = failFn({\n status: options.status,\n code: options.code,\n message: options.message,\n });\n return jsonOk(body, options.status ?? 500);\n}\n\n/**\n * 抛错兜底响应:把 handler 抛出的错误转换为统一格式的 Response\n *\n * 覆盖的 FaapiError 子类:\n * - ValidationError → 400/422 + issues\n * - MethodNotAllowedError → 405 + Allow header\n * - PayloadTooLargeError → 413\n * - 其他 FaapiError 子类 → 用 error.statusCode\n * - 未知 Error / 非 Error → 500 INTERNAL_ERROR\n *\n * 与 [formatFailResponse] 共享同一套 fail 函数:业务方在 faapi.config.ts 自定义的\n * `response.fail` 会同时应用到「主动错误响应」和「抛错兜底响应」,确保错误格式一致。\n *\n * @param error handler 抛出的错误\n * @param config 业务方配置(用于读取 response.fail 自定义包装函数,可选)\n */\nexport function formatErrorResponse(\n error: unknown,\n config?: Record<string, unknown> | undefined,\n): Response {\n const failFn = resolveFailFn(config);\n\n if (error instanceof ValidationError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n // ValidationError 需要附加 issues 字段。业务方 failFn 可能返回共享/复用对象\n // (性能优化写法)或冻结对象——就地改写会让 issues 跨请求残留污染或静默失败,\n // 因此浅拷贝后再扩展,不改写业务方返回的原对象\n const bodyObj =\n typeof body === 'object' && body !== null\n ? ({ ...(body as Record<string, unknown>) } as Record<string, unknown>)\n : { error: body };\n const existingError = bodyObj.error as Record<string, unknown> | undefined;\n if (existingError && typeof existingError === 'object') {\n bodyObj.error = { ...existingError, issues: error.issues };\n } else if (typeof body === 'object' && body !== null) {\n // 无 error 包装层:body 本身就是错误对象\n bodyObj.issues = error.issues;\n } else {\n bodyObj.error = { issues: error.issues };\n }\n return jsonOk(bodyObj, error.statusCode);\n }\n\n if (error instanceof MethodNotAllowedError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n return jsonOk(body, error.statusCode, { Allow: error.allowedMethods.join(', ') });\n }\n\n if (error instanceof PayloadTooLargeError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n return jsonOk(body, error.statusCode);\n }\n\n if (error instanceof FaapiError) {\n const body = failFn({\n status: error.statusCode,\n code: error.code,\n message: error.message,\n });\n return jsonOk(body, error.statusCode);\n }\n\n // 未知错误:500 INTERNAL_ERROR\n const message = error instanceof Error ? error.message : 'An unknown error occurred';\n const body = failFn({ status: 500, code: 'INTERNAL_ERROR', message });\n return jsonOk(body, 500);\n}\n","import type { FaapiContext, ResponseMeta, CookieOptions, FailOptions } from './contextTypes';\nimport { createSseWriter, type SseWriter } from './sse';\nimport { wrapOkResult, formatFailResponse, jsonOk } from '../response/responseFormatter';\n\n/**\n * 解析 Cookie 请求头为 Map\n */\nfunction parseCookies(cookieHeader: string): Map<string, string> {\n const cookies = new Map<string, string>();\n if (!cookieHeader) return cookies;\n for (const pair of cookieHeader.split(';')) {\n const [name, ...rest] = pair.split('=');\n const trimmed = name?.trim();\n if (trimmed) {\n cookies.set(trimmed, rest.join('=').trim());\n }\n }\n return cookies;\n}\n\n/**\n * 格式化 Set-Cookie 值\n */\nfunction formatSetCookie(name: string, value: string, options?: CookieOptions): string {\n let cookie = `${name}=${value}`;\n if (options?.domain) cookie += `; Domain=${options.domain}`;\n if (options?.path) cookie += `; Path=${options.path}`;\n if (options?.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`;\n if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;\n if (options?.httpOnly) cookie += `; HttpOnly`;\n if (options?.secure) cookie += `; Secure`;\n if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;\n return cookie;\n}\n\n/**\n * 从 Request 对象创建 FaapiContext\n * @param request Web Request 对象\n * @param params 动态路由参数\n * @param config 自定义业务配置(来自 faapi.config.ts)\n * @param ip 客户端 IP(由调用方从 IncomingMessage 提取,HTTP/WS 握手均通过 utils/getClientIp)\n *\n * ua 不作为参数传入:User-Agent 是标准 HTTP 请求头,createContext 内部直接从\n * request.headers 读取(与 ip 不同,ip 需要从 IncomingMessage 提取故由调用方传入)。\n */\nexport function createContext(\n request: Request,\n params: Record<string, string>,\n config: Record<string, unknown> = {},\n ip: string = '',\n registries?: FaapiContext['registries'],\n): FaapiContext {\n return createContextFromUrl(request, new URL(request.url), params, config, ip, registries);\n}\n\n/**\n * createContext 的热路径变体:URL 由调用方解析一次后传入\n *\n * createServer 每请求需要 pathname/searchParams,若各自 `new URL(request.url)`\n * 一次请求要重复解析 3~4 次。此变体接收已解析的 URL 对象,searchParams 与\n * 调用方共享(只读)。普通场景用 {@link createContext}。\n */\nexport function createContextFromUrl(\n request: Request,\n url: URL,\n params: Record<string, string>,\n config: Record<string, unknown> = {},\n ip: string = '',\n registries?: FaapiContext['registries'],\n): FaapiContext {\n const meta: ResponseMeta = { headers: {}, setCookies: [] };\n const parsedCookies = parseCookies(request.headers.get('cookie') ?? '');\n const cookiesObj: Record<string, string> = {};\n for (const [key, val] of parsedCookies) {\n cookiesObj[key] = val;\n }\n\n const ctx = {\n request,\n params,\n query: url.searchParams,\n headers: request.headers,\n method: request.method,\n path: url.pathname,\n ip,\n ua: request.headers.get('user-agent') ?? '',\n cookies: cookiesObj,\n config,\n meta,\n\n setStatus(status: number) {\n meta.status = status;\n },\n\n setHeader(key: string, value: string) {\n meta.headers[key] = value;\n },\n\n setETag(value: string) {\n meta.headers['etag'] = value;\n },\n\n redirect(url: string, status = 302): Response {\n return new Response(null, {\n status,\n headers: { Location: url },\n });\n },\n\n json(data: unknown, status?: number): Response {\n return jsonOk(data, status ?? 200);\n },\n\n html(html: string, status?: number): Response {\n const headers: Record<string, string> = { 'Content-Type': 'text/html; charset=utf-8' };\n return new Response(html, {\n status: status ?? 200,\n headers,\n });\n },\n\n getCookie(name: string): string | undefined {\n return parsedCookies.get(name);\n },\n\n setCookie(name: string, value: string, options?: CookieOptions): void {\n meta.setCookies.push(formatSetCookie(name, value, options));\n },\n\n deleteCookie(name: string): void {\n meta.setCookies.push(formatSetCookie(name, '', { maxAge: 0 }));\n },\n\n /**\n * 创建 SSE writer,用于流式推送事件\n *\n * handler 调用此方法后,通过返回的 writer 推送事件,框架自动把 writer.response\n * 作为 HTTP 响应(Content-Type: text/event-stream)。\n *\n * 与 ctx.json / ctx.html 互斥:一个 handler 只能用一种响应方式。\n */\n sse(): SseWriter {\n const writer = createSseWriter();\n const ctxWithSse = ctx as FaapiContext & {\n __sseResponse?: Response;\n __sseWriter?: SseWriter;\n };\n ctxWithSse.__sseResponse = writer.response;\n ctxWithSse.__sseWriter = writer;\n return writer;\n },\n\n /**\n * 显式包装成功响应(返回 Response,不会被自动包裹再次包装)\n *\n * 实现委托给 [responseFormatter.wrapOkResult](../response/responseFormatter.ts),\n * 与 handler `return data` 走的自动包裹路径共享同一套 ok 函数。\n *\n * 用 config.response.ok(或默认 (data) => ({ data })) 包裹 data 并返回 JSON Response。\n * handler 也可直接 return data,框架会自动用 ok 包裹,两者等价。\n */\n ok(data: unknown): Response {\n const body = wrapOkResult(data, config);\n // wrapOkResult 对 Response 对象直接透传(不会包裹),ctx.ok 接收到的是 okFn(data) 的对象\n // 业务方不应在 ctx.ok 里传 Response,这里 fallback 调 jsonOk 序列化\n return jsonOk(body, 200);\n },\n\n /**\n * 返回错误响应(对象形式参数,status 和 code 均可省略)\n *\n * 实现委托给 [responseFormatter.formatFailResponse](../response/responseFormatter.ts),\n * 与 formatErrorResponse(handler 抛错兜底)共享同一套 fail 函数,确保错误格式一致。\n *\n * - status 省略时 HTTP 状态码默认 500\n * - code 省略时响应 body 里不含 code 字段(默认 fail 函数只放非 undefined 的字段)\n * - status 和 code 独立无关联\n *\n * body 用 config.response.fail(或默认实现)包装。\n */\n fail(options: FailOptions): Response {\n return formatFailResponse(options, config);\n },\n } as FaapiContext & { meta: ResponseMeta; __sseResponse?: Response; __sseWriter?: SseWriter };\n\n // app 级注册表(方案 A 实例化)——框架请求链路的 tool/agent/agentHandle 读取来源\n if (registries) {\n ctx.registries = registries;\n }\n\n // 执行用户自定义的 ctx 扩展钩子(config.extendContext)\n const extend = config?.extendContext;\n if (typeof extend === 'function') {\n extend(ctx);\n }\n\n return ctx;\n}\n\n/**\n * 测试专用:从选项对象创建 FaapiContext,免去手写 `new Request(url)` 的样板代码\n *\n * 与 createContext 的关系:createTestContext 内部构造 Request 后调 createContext,\n * 语义完全一致,仅是测试场景的语法糖——不写无意义的 host、query 用对象形式、headers 直接传对象。\n *\n * 为什么不合并进 createContext:createContext 运行时也从真实 HTTP 请求构造 Request,\n * 保持 `(request: Request)` 签名使运行时与测试同构;createTestContext 是纯测试便捷封装,\n * 不引入运行时分支。\n *\n * body 不在此处理:createContext 本身不读 `request.body`,body 注入由 `invokeHandler`\n * 的第 3 个参数负责。POST/PUT/PATCH 测试时 body 单独传给 invokeHandler,避免在两处传 body 产生混淆。\n *\n * @param options 请求选项(path 必填,其余可选)\n * @returns FaapiContext\n */\nexport function createTestContext(options: CreateTestContextOptions): FaapiContext {\n const {\n method = 'GET',\n path,\n query,\n headers,\n params = {},\n config = {},\n ip = '',\n registries,\n } = options;\n\n // 用 URL 解析 + 拼 query,避免手动拼接字符串的转义问题\n const url = new URL(`http://localhost${path}`);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n url.searchParams.append(key, String(item));\n }\n } else {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const request = new Request(url.toString(), {\n method,\n headers: headers as HeadersInit | undefined,\n });\n\n return createContext(request, params, config, ip, registries);\n}\n\n/**\n * createTestContext 的选项\n */\nexport interface CreateTestContextOptions {\n /** app 级注册表(测试 handler 声明 agent/agents 参数时注入用,可选) */\n registries?: FaapiContext['registries'];\n /** 请求方法,默认 'GET' */\n method?: string;\n /** 请求路径,必填,如 '/api/user'(无需写 host) */\n path: string;\n /** 查询参数,对象形式,自动拼接到 URL(值会被 String() 转换;数组生成同名多值参数) */\n query?: Record<string, string | number | boolean | Array<string | number | boolean>>;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 动态路由参数,默认 {} */\n params?: Record<string, string>;\n /** 业务配置(来自 faapi.config.ts),默认 {} */\n config?: Record<string, unknown>;\n /** 客户端 IP,默认 '' */\n ip?: string;\n}\n","/**\n * URLSearchParams → query 对象\n *\n * 重复 key 聚合为数组(对齐 Express qs / Hono getAll 语义):\n * - `?page=1` → `{ page: '1' }`\n * - `?tag=a&tag=b` → `{ tag: ['a', 'b'] }`\n *\n * 此前 last-wins 静默丢弃前面的值(`?ids=1&ids=2` 得 `'2'`),且 query schema\n * 的 coerce 管线对 array 元素同样生成 preprocess——声明 `ids: number[]` 的字段\n * 此前永远校验失败(解析端从未产出数组),属于校验管线输入端漏洞。\n */\nexport function queryToObject(params: URLSearchParams): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of params) {\n const existing = result[key];\n if (existing === undefined) {\n result[key] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n result[key] = [existing, value];\n }\n }\n return result;\n}\n","export type JsonParseResult = { success: true; data: unknown } | { success: false; error: string };\n\nexport function parseJsonBody(text: string): JsonParseResult {\n try {\n const data = JSON.parse(text);\n return { success: true, data };\n } catch {\n return { success: false, error: 'Invalid JSON body' };\n }\n}\n","/**\n * 解析 multipart/form-data 请求\n * 使用 Web 标准 Request.formData() API\n */\n\nexport interface UploadedFile {\n name: string; // 字段名\n filename: string; // 原始文件名\n type: string; // MIME 类型\n size: number; // 文件大小(字节)\n arrayBuffer: () => Promise<ArrayBuffer>; // 获取文件内容\n}\n\nexport interface MultipartResult {\n /** 表单字段(同名字段用数组收集,单值保持 string) */\n fields: Record<string, string | string[]>;\n files: UploadedFile[];\n}\n\n/**\n * 解析 multipart/form-data\n *\n * 同名字段的处理:\n * - 第一次出现:string\n * - 第二次出现:string[](包含所有同名字段的值)\n * - 文件始终收集到 files 数组(通过 name 字段标识归属)\n */\nexport async function parseMultipart(request: Request): Promise<MultipartResult> {\n const formData = await request.formData();\n const fields: Record<string, string | string[]> = {};\n const files: UploadedFile[] = [];\n\n for (const [key, value] of formData.entries()) {\n if (value instanceof File) {\n files.push({\n name: key,\n filename: value.name,\n type: value.type,\n size: value.size,\n arrayBuffer: () => value.arrayBuffer(),\n });\n } else {\n // 同名字段收集为数组\n if (key in fields) {\n const existing = fields[key];\n if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n // in 检查已证明存在\n fields[key] = [existing!, value];\n }\n } else {\n fields[key] = value;\n }\n }\n }\n\n return { fields, files };\n}\n","import { queryToObject } from '../utils/queryToObject';\nimport { parseJsonBody } from '../utils/parseJsonBody';\nimport { parseMultipart } from '../utils/parseMultipart';\nimport { getInputTypeForMethod } from './inputType';\nimport { ValidationError } from '../errors/httpErrors';\n\n/**\n * 根据 HTTP 方法解析主输入\n *\n * - GET / DELETE / HEAD:主输入是 query(URL 查询参数)\n * - POST / PUT / PATCH:主输入是 body(请求体)\n *\n * 注意:DELETE 也可能有 body,但主输入(用于校验)是 query。\n * body 由 createServer 调 resolveBodyForQueryMethod 单独解析后注入\n * (同时消费请求体流,保证 keep-alive 连接可复用)。\n *\n * HTTP 传输语义:\n * - 空请求体(或纯空白)视为无 body,返回 null(handler 可不声明 body 参数)\n * - 请求体非空但 JSON 格式非法,抛 ValidationError(code=INVALID_FORMAT),\n * 不再静默返回 null 导致后续报\"字段缺失\"\n *\n * @param method HTTP 方法\n * @param request Request 对象\n * @returns 解析后的输入值(query 对象或 body 对象)\n * @throws {ValidationError} 当请求体非空但 JSON 格式非法时\n */\nexport async function resolveInput(method: string, request: Request): Promise<unknown> {\n return resolveInputFromUrl(method, request, new URL(request.url));\n}\n\n/**\n * resolveInput 的热路径变体:URL 由调用方解析一次后传入\n *\n * createServer 每请求已持有解析好的 URL(pathname/searchParams 复用),\n * 通过此变体避免 query 分支的重复 `new URL(request.url)`。\n */\nexport async function resolveInputFromUrl(\n method: string,\n request: Request,\n url: URL,\n): Promise<unknown> {\n const inputType = getInputTypeForMethod(method);\n\n // 主输入是 body 的方法:解析请求体\n if (inputType === 'body') {\n const contentType = request.headers.get('content-type') ?? '';\n\n // multipart/form-data:字段 + 文件\n if (contentType.includes('multipart/form-data')) {\n return parseMultipart(request);\n }\n\n // application/x-www-form-urlencoded:表单字段\n if (contentType.includes('application/x-www-form-urlencoded')) {\n const text = await request.text();\n if (isBlankText(text)) return null;\n const params = new URLSearchParams(text);\n const obj: Record<string, string> = {};\n for (const [key, value] of params) {\n obj[key] = value;\n }\n return obj;\n }\n\n // 默认按 JSON 解析(application/json 及其它未明确类型)\n const text = await request.text();\n\n // 空请求体:视为无 body(handler 可不声明 body 参数)\n if (isBlankText(text)) {\n return null;\n }\n\n // 非空请求体:必须能解析为 JSON,否则是格式错误\n const result = parseJsonBody(text);\n if (!result.success) {\n throw new ValidationError('请求体不是合法的 JSON', [\n {\n path: 'body',\n code: 'INVALID_FORMAT',\n expected: 'JSON',\n received: 'text',\n message: '请求体不是合法的 JSON',\n },\n ]);\n }\n return result.data;\n }\n\n // 主输入是 query 的方法(GET / DELETE / HEAD):从 URL 提取 query\n return queryToObject(url.searchParams);\n}\n\n/** 空白文本判断(length 短路 + 正则扫描,避免 trim 的全量字符串拷贝) */\nfunction isBlankText(text: string): boolean {\n return text.length === 0 || !/\\S/.test(text);\n}\n\n/**\n * 解析\"主输入是 query 但允许携带 body\"的方法(DELETE)的请求体\n *\n * DELETE 的主输入(用于校验)是 query,但请求体流必须被消费(keep-alive 连接\n * 上有未读 body 时 Node 只能断开连接,无法复用),且 handler 声明 `body` 参数\n * 时应注入真正的请求体而非 query。\n *\n * 与主输入 body 的方法不同:DELETE body 无对应 schema(schema 提取只产出\n * DELETEQuery),不做校验,原样注入。空请求体返回 undefined(handler 未声明\n * body 参数时注入 undefined,行为一致)。\n *\n * @throws {ValidationError} 当请求体非空但 JSON 格式非法时\n */\nexport async function resolveBodyForQueryMethod(request: Request): Promise<unknown> {\n const text = await request.text();\n if (isBlankText(text)) return undefined;\n const result = parseJsonBody(text);\n if (!result.success) {\n throw new ValidationError('请求体不是合法的 JSON', [\n {\n path: 'body',\n code: 'INVALID_FORMAT',\n expected: 'JSON',\n received: 'text',\n message: '请求体不是合法的 JSON',\n },\n ]);\n }\n return result.data;\n}\n","export function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n // 数组不是普通对象\n if (Array.isArray(value)) {\n return false;\n }\n\n // 检查原型链:普通对象的 prototype 是 Object.prototype 或 null(Object.create(null))\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n}\n","import type { ResponseMeta } from '../runtime/contextTypes';\n\n/**\n * 仅 headers 型 meta 的延迟落头通道\n *\n * Web Response 一旦构造不可变——此前 `ctx.ok()` / `ctx.fail()` 等返回 Response\n * 的场景,即使 meta 只有 headers(helmet 开启后每请求 ~13 个静态头),也要\n * `new Headers()` 拷贝 + `new Response()` 重建一次。改为把 headers 记入\n * WeakMap(按响应对象弱键,响应被 GC 自动清理),由 Node 发送层\n * (sendNodeResponse)最后一次性 `setHeader`——热路径零重建。\n *\n * 中间件若重建 Response(如 compression / etag 的大 body 分支),需用\n * `consumePendingMetaHeaders` 把延迟头并入新 Response 的 headers,避免丢失。\n */\n\nconst pending = new WeakMap<Response, Record<string, string>>();\n\n/**\n * 记录延迟落头(headers-only meta 场景)\n *\n * 同一响应多次记录按 key 合并(后写覆盖,与 mergeMeta 的 headers.set 语义一致)。\n */\nexport function deferMetaHeaders(response: Response, headers: Record<string, string>): void {\n const existing = pending.get(response);\n if (existing) {\n Object.assign(existing, headers);\n return;\n }\n pending.set(response, { ...headers });\n}\n\n/**\n * 取出并清除响应的延迟头(发送层 / 重建方消费)\n */\nexport function consumePendingMetaHeaders(response: Response): Record<string, string> | undefined {\n const headers = pending.get(response);\n if (headers) pending.delete(response);\n return headers;\n}\n\n/** meta 是否仅含 headers(无 status / setCookies,可走延迟通道) */\nexport function isHeadersOnlyMeta(meta: ResponseMeta): boolean {\n return meta.status === undefined && meta.setCookies.length === 0;\n}\n","import { isPlainObject } from '../utils/isPlainObject';\nimport type { ResponseMeta } from '../runtime/contextTypes';\nimport { deferMetaHeaders, isHeadersOnlyMeta } from './pendingMeta';\n\n/**\n * 将 handler 返回值统一转换为 Response\n *\n * 规则:\n * - Response 对象:原样返回\n * - ReadableStream:直接作为 body\n * - Buffer / Uint8Array:作为二进制 body\n * - 普通对象/数组:JSON.stringify,Content-Type: application/json\n * - string:text/plain\n * - number/boolean:text/plain,String(value)\n * - null/undefined:204 No Content\n * - Promise:await 后再处理\n *\n * 如果传入了 meta,会将 ctx.setStatus / ctx.setHeader 的设置合并到 Response 中\n */\nexport async function toResponse(value: unknown, meta?: ResponseMeta): Promise<Response> {\n // Promise:await 后再处理\n if (value instanceof Promise) {\n return toResponse(await value, meta);\n }\n\n // 辅助函数:将 meta headers 和 setCookies 合并到 Headers 对象\n const applyMeta = (headers: Headers): void => {\n if (!meta) return;\n for (const [key, val] of Object.entries(meta.headers)) {\n headers.set(key, val);\n }\n for (const cookie of meta.setCookies ?? []) {\n headers.append('set-cookie', cookie);\n }\n };\n\n // Response 对象:原样返回(但合并 meta headers)\n if (value instanceof Response) {\n if (\n meta &&\n (meta.status !== undefined ||\n Object.keys(meta.headers).length > 0 ||\n meta.setCookies.length > 0)\n ) {\n // 仅 headers 时延迟到发送层落头(避免每请求 Headers 拷贝 + Response 重建,\n // helmet 开启后 meta.headers 恒非空)。status/cookie 需要 Response 级变更,仍重建\n if (isHeadersOnlyMeta(meta)) {\n deferMetaHeaders(value, meta.headers);\n return value;\n }\n const headers = new Headers(value.headers);\n applyMeta(headers);\n return new Response(value.body, {\n status: meta.status ?? value.status,\n headers,\n });\n }\n return value;\n }\n\n // null/undefined:204 No Content\n if (value === null || value === undefined) {\n const status = meta?.status ?? 204;\n const headers = new Headers();\n applyMeta(headers);\n return new Response(null, { status, headers });\n }\n\n // ReadableStream:直接作为 body\n if (typeof ReadableStream !== 'undefined' && value instanceof ReadableStream) {\n const headers = new Headers({ 'Content-Type': 'application/octet-stream' });\n applyMeta(headers);\n return new Response(value, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // Buffer / Uint8Array:作为二进制 body\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n const headers = new Headers({ 'Content-Type': 'application/octet-stream' });\n applyMeta(headers);\n return new Response(value as BodyInit, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n if (value instanceof Uint8Array) {\n const headers = new Headers({ 'Content-Type': 'application/octet-stream' });\n applyMeta(headers);\n return new Response(value as BodyInit, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // 普通对象/数组:JSON.stringify,Content-Type: application/json\n if (isPlainObject(value) || Array.isArray(value)) {\n const body = JSON.stringify(value);\n const headers = new Headers({ 'Content-Type': 'application/json' });\n applyMeta(headers);\n return new Response(body, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // string:text/plain\n if (typeof value === 'string') {\n const headers = new Headers({ 'Content-Type': 'text/plain' });\n applyMeta(headers);\n return new Response(value, {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // number/boolean:text/plain,String(value)\n if (typeof value === 'number' || typeof value === 'boolean') {\n const headers = new Headers({ 'Content-Type': 'text/plain' });\n applyMeta(headers);\n return new Response(String(value), {\n status: meta?.status ?? 200,\n headers,\n });\n }\n\n // 其他类型 fallback:JSON.stringify\n const body = JSON.stringify(value);\n const headers = new Headers({ 'Content-Type': 'application/json' });\n applyMeta(headers);\n return new Response(body, {\n status: meta?.status ?? 200,\n headers,\n });\n}\n","import type { ToolMetadata } from '../ast/extractToolMetadata';\nimport type { AgentMetadata, AgentCore } from '../ast/extractAgentMetadata';\nimport type { FaapiContext } from '../runtime/contextTypes';\n\n/**\n * app 级注册表(方案 A:注册表实例化)\n *\n * 每个应用实例(`createAppBase`)持有一套独立的注册表,随 app 创建、随\n * `app.close()` 销毁——多 app 同进程(测试 / 嵌入 / 多租户)互不串台。\n * 此前注册表是模块级全局单例 + hydrate 整体替换语义,后创建的 app 会覆盖\n * 先创建的 app 的清单,任一 app close 会清空全部(详见各模块 .md)。\n *\n * 原四个模块(toolRegistry / agentRegistry / skillRegistry / agentHandle)\n * 保留同名全局函数作为**默认实例的便捷访问器**(供编程式直调 / 单元测试 /\n * 无 app 上下文的场景),内部全部路由到本模块的 `defaultRegistries`。\n * 框架自身链路(hydrate / 请求注入 / `@faapi/agent` 插件 / lifecycle 钩子)\n * 一律走 app 实例,不再读写全局默认实例。\n */\n\n// ─── Tool 注册表 ─────────────────────────────────────────────────────\n\nexport interface ToolRegistry {\n /** 全量替换(tool 清单来自编译期产物,reload 时整体重新生成) */\n hydrate(tools: ToolMetadata[]): void;\n /** 按全名查找(如 `weather.getWeather`) */\n get(name: string): ToolMetadata | undefined;\n /** 所有已注册 tool(副本) */\n list(): ToolMetadata[];\n clear(): void;\n}\n\nexport function createToolRegistry(): ToolRegistry {\n let registry = new Map<string, ToolMetadata>();\n return {\n hydrate(tools) {\n const next = new Map<string, ToolMetadata>();\n for (const tool of tools) {\n next.set(tool.name, tool);\n }\n registry = next;\n },\n get(name) {\n return registry.get(name);\n },\n list() {\n return Array.from(registry.values());\n },\n clear() {\n registry = new Map();\n },\n };\n}\n\n// ─── Agent 注册表 ────────────────────────────────────────────────────\n\n/** agent 包装为 tool 的描述符(reactLoop 据此识别 sub-agent 递归) */\nexport interface AgentToolDescriptor {\n kind: 'agent';\n name: string;\n agentName: string;\n description?: string;\n metadata: AgentCore;\n}\n\nexport interface AgentRegistry {\n /** 全量替换(与 hydrateToolRegistry 同构) */\n hydrate(agents: AgentMetadata[]): void;\n /** LLM 可见元数据(AgentCore) */\n getAgent(name: string): AgentCore | undefined;\n /** 完整元数据(含 filePath / hasRun,供加载 handler.js 执行 run) */\n getAgentEntry(name: string): AgentMetadata | undefined;\n /** 所有已注册 agent 的 LLM 可见元数据(副本) */\n listAgents(): AgentCore[];\n /** 包装为 tool 描述符 */\n asTool(name: string): AgentToolDescriptor | undefined;\n /**\n * 解析 agent 显式声明的 tool 集合。\n * 跨注册表依赖:经由构造时绑定的 tool 注册表查找(同属一套 AppRegistries,\n * 由 createAppBase 在同一启动阶段水合)\n */\n resolveAgentTools(name: string): ToolMetadata[];\n /** 解析 agent 可调用的子 agent 集合 */\n resolveSubAgents(name: string): AgentCore[];\n clear(): void;\n}\n\nexport function createAgentRegistry(tool: ToolRegistry): AgentRegistry {\n let registry = new Map<string, AgentMetadata>();\n\n const getAgent = (name: string): AgentCore | undefined => registry.get(name);\n\n return {\n hydrate(agents) {\n const next = new Map<string, AgentMetadata>();\n for (const agent of agents) {\n next.set(agent.name, agent);\n }\n registry = next;\n },\n getAgent,\n getAgentEntry(name) {\n return registry.get(name);\n },\n listAgents() {\n const merged = new Map<string, AgentCore>();\n for (const agent of registry.values()) merged.set(agent.name, agent);\n return Array.from(merged.values());\n },\n asTool(name) {\n const agent = getAgent(name);\n if (!agent) return undefined;\n return {\n kind: 'agent',\n name: `agent.${agent.name}`,\n agentName: agent.name,\n description: agent.description,\n metadata: agent,\n };\n },\n resolveAgentTools(name) {\n const agent = getAgent(name);\n if (!agent) return [];\n\n const result = new Map<string, ToolMetadata>();\n if (agent.tools) {\n for (const toolName of agent.tools) {\n const resolved = tool.get(toolName);\n if (resolved) result.set(resolved.name, resolved);\n // 未找到的 tool 名静默跳过(tool 可选可用,不强制存在)\n }\n }\n return Array.from(result.values());\n },\n resolveSubAgents(name) {\n const agent = getAgent(name);\n if (!agent || !agent.agents) return [];\n\n const result: AgentCore[] = [];\n for (const subName of agent.agents) {\n const sub = getAgent(subName);\n if (sub) result.push(sub);\n // 未注册的 agent 名跳过(agent 可选可用,不强制存在)\n }\n return result;\n },\n clear() {\n registry = new Map();\n },\n };\n}\n\n// ─── Skill 注册表 ────────────────────────────────────────────────────\n\nexport interface SkillRegistry {\n /** 全量替换(DB change stream 场景也可用 upsert 增量) */\n hydrate(skills: AgentCore[]): void;\n /** 增量注册 / 覆盖(业务方监听 DB 单条变更) */\n upsert(skill: AgentCore): void;\n remove(name: string): void;\n get(name: string): AgentCore | undefined;\n list(): AgentCore[];\n clear(): void;\n}\n\nexport function createSkillRegistry(): SkillRegistry {\n let registry = new Map<string, AgentCore>();\n return {\n hydrate(skills) {\n const next = new Map<string, AgentCore>();\n for (const skill of skills) {\n next.set(skill.name, skill);\n }\n registry = next;\n },\n upsert(skill) {\n registry.set(skill.name, skill);\n },\n remove(name) {\n registry.delete(name);\n },\n get(name) {\n return registry.get(name);\n },\n list() {\n return Array.from(registry.values());\n },\n clear() {\n registry = new Map();\n },\n };\n}\n\n// ─── Agent handle 工厂 ───────────────────────────────────────────────\n\n/** agent handle 工厂函数(由 `@faapi/agent` 插件注册) */\nexport type AgentHandleFactory = (ctx: FaapiContext) => unknown;\n\nexport interface AgentHandleStore {\n /** 注册工厂(null 清理);二次注册覆盖 */\n register(factory: AgentHandleFactory | null): void;\n /** 工厂已注册时返回 AgentHandle 实例,未注册返回 undefined */\n get(ctx: FaapiContext): unknown;\n clear(): void;\n}\n\nexport function createAgentHandleStore(): AgentHandleStore {\n let currentFactory: AgentHandleFactory | null = null;\n return {\n register(factory) {\n currentFactory = factory;\n },\n get(ctx) {\n if (currentFactory === null) return undefined;\n return currentFactory(ctx);\n },\n clear() {\n currentFactory = null;\n },\n };\n}\n\n// ─── App 级集合 ──────────────────────────────────────────────────────\n\n/** 一个 app 实例持有的全套注册表 */\nexport interface AppRegistries {\n tool: ToolRegistry;\n agent: AgentRegistry;\n skill: SkillRegistry;\n agentHandle: AgentHandleStore;\n}\n\n/** 创建一套 app 级注册表(`createAppBase` 每次调用创建独立实例) */\nexport function createAppRegistries(): AppRegistries {\n const tool = createToolRegistry();\n const agent = createAgentRegistry(tool);\n const skill = createSkillRegistry();\n const agentHandle = createAgentHandleStore();\n return { tool, agent, skill, agentHandle };\n}\n\n/**\n * 默认实例:原全局函数的退路(编程式直调 / 单元测试 / 无 app 上下文场景)。\n *\n * 框架自身链路不读写此实例——app 创建自己的 AppRegistries 并在请求链路 /\n * 插件 / lifecycle 钩子中传递。多 app 场景下默认实例无隔离语义(等同旧全局行为)。\n */\nexport const defaultRegistries: AppRegistries = createAppRegistries();\n","import type { AgentCore, AgentMetadata } from '../ast/extractAgentMetadata';\nimport { defaultRegistries } from './registries';\n\n// 类型从 registries 转发(AgentToolDescriptor 的定义源已随实例化迁入 registries)\nexport type { AgentToolDescriptor } from './registries';\n\n/**\n * agent 注册表全局访问器(默认实例便捷入口)\n *\n * 框架自身链路(`createAppBase` 水合 / `@faapi/agent` 插件 deps / `agents`\n * 参数注入)已改为读写 **app 实例级注册表**(见 [registries.md](./registries.md))——\n * agent 注册表实例在构造时绑定同套 tool 注册表实例(`resolveAgentTools` 跨表查找),\n * 多 app 同进程互不串台。\n *\n * 本模块的同名函数保留为**默认实例**的便捷访问器(语义约束不变:不 fallback 到\n * skillRegistry、Core / Entry 双查询入口),供编程式直调 / 单元测试使用。\n *\n * ## 与 skillRegistry 物理隔离\n *\n * agent 负责核心流程,skill 用于拓展——查询函数不 fallback 到 skillRegistry,\n * 实例化后依旧如此(两套注册表实例互不引用)。\n *\n * 详见 [agentRegistry.md](./agentRegistry.md)。\n */\n\n/**\n * 水合默认实例的 agent 注册表(全量替换)\n *\n * 框架路径请使用 app 实例:`ctx.registries.agent.hydrate(agents)`。\n *\n * @param agents 从 `faapi-agents.js` 水合还原的 `AgentMetadata[]`\n */\nexport function hydrateAgentRegistry(agents: AgentMetadata[]): void {\n defaultRegistries.agent.hydrate(agents);\n}\n\n/**\n * 清空默认实例的 agent 注册表\n *\n * app close 清理的是 app 自己的实例,不经过此函数。\n */\nexport function clearAgentRegistry(): void {\n defaultRegistries.agent.clear();\n}\n\n/**\n * 按名查找单个 agent 的 LLM 可见元数据(默认实例)\n *\n * 返回 [AgentCore](../ast/extractAgentMetadata.md)(不含 filePath / hasRun)。\n * 加载 handler.js 执行 `run` 函数请用 [getAgentEntry](#getAgentEntry)。\n *\n * @param name agent 名(如 `researcher`,含 `@agent` 覆盖值)\n */\nexport function getAgent(name: string): AgentCore | undefined {\n return defaultRegistries.agent.getAgent(name);\n}\n\n/**\n * 按名查找单个 agent 的完整元数据(默认实例,含 filePath / hasRun)\n */\nexport function getAgentEntry(name: string): AgentMetadata | undefined {\n return defaultRegistries.agent.getAgentEntry(name);\n}\n\n/**\n * 返回默认实例所有已注册 agent 的 LLM 可见元数据(副本,仅文件型 agent)\n */\nexport function listAgents(): AgentCore[] {\n return defaultRegistries.agent.listAgents();\n}\n\n/**\n * 把 agent 包装为 tool 描述符(默认实例)\n */\nexport function asTool(name: string) {\n return defaultRegistries.agent.asTool(name);\n}\n\n/**\n * 解析 agent 显式声明的 tool 集合(默认实例,跨查默认 tool 注册表)\n */\nexport function resolveAgentTools(name: string) {\n return defaultRegistries.agent.resolveAgentTools(name);\n}\n\n/**\n * 解析 agent 可调用的子 agent 集合(默认实例,按声明顺序)\n */\nexport function resolveSubAgents(name: string): AgentCore[] {\n return defaultRegistries.agent.resolveSubAgents(name);\n}\n","import type { FaapiContext } from '../runtime/contextTypes';\n\n/**\n * agent handle 工厂注册表(单例)\n *\n * 让 `@faapi/agent` 插件在启动时注册「请求级 agent handle 工厂」,\n * [injectParams](./injectParams.md) 在 `agent` 参数注入时调工厂拿到 `AgentHandle` 实例。\n *\n * 解耦设计:faapi 核心不依赖 `@faapi/agent`——核心只提供注册 / 查询点,\n * 工厂返回 `unknown`,具体类型由 `@faapi/agent` 的 `AgentHandle` 接口定义。\n *\n * 详见 [agentHandle.md](./agentHandle.md)。\n */\n\n/** agent handle 工厂函数(由 `@faapi/agent` 插件注册) */\nexport type AgentHandleFactory = (ctx: FaapiContext) => unknown;\n\nimport { defaultRegistries } from './registries';\n\n/**\n * agent handle 工厂全局访问器(默认实例便捷入口)\n *\n * 框架路径已改为 app 实例:`@faapi/agent` 插件在 setup 时经\n * `ctx.registries.agentHandle.register(factory)` 注册到**当前 app 的实例**,\n * 请求注入经 `ctx.registries.agentHandle.get(ctx)` 读取——多 app 同进程互不覆盖。\n * 此前模块级单值会让第二个 app 的插件注册覆盖第一个。\n *\n * 本模块的同名函数保留为默认实例便捷访问器(向后兼容)。\n *\n * 详见 [agentHandle.md](./agentHandle.md) 与 [registries.md](./registries.md)。\n */\n\n/**\n * 注册默认实例的 agent handle 工厂\n *\n * 框架路径请使用 `ctx.registries.agentHandle.register(factory)`(PluginContext 提供)。\n * 传入 `null` 等效于清理。\n */\nexport function registerAgentHandleFactory(factory: AgentHandleFactory | null): void {\n defaultRegistries.agentHandle.register(factory);\n}\n\n/**\n * 从默认实例获取 agent handle(工厂未注册时返回 undefined)\n */\nexport function getAgentHandle(ctx: FaapiContext): unknown {\n return defaultRegistries.agentHandle.get(ctx);\n}\n\n/**\n * 清空默认实例的工厂注册\n */\nexport function clearAgentHandleFactory(): void {\n defaultRegistries.agentHandle.clear();\n}\n","import type { FaapiContext } from '../runtime/contextTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { resolveInjection, type InjectionType } from './resolveInjection';\nimport { queryToObject } from '../utils/queryToObject';\nimport type { MultipartResult } from '../utils/parseMultipart';\nimport { listAgents } from './agentRegistry';\nimport { getAgentHandle } from './agentHandle';\n\n/**\n * 根据注入类型获取对应的值(内置)\n *\n * Phase 2.3 扩展 `agent` / `agents`:\n * - `agents` → `listAgents()`(所有已注册 agent 元数据列表)\n * - `agent` → `getAgentHandle(ctx)`(Phase 3.5:调 `@faapi/agent` 插件注册的工厂)\n *\n * Phase 3.5 的 `@faapi/agent` 插件通过 [agentHandle](./agentHandle.md) 工厂注册机制\n * 提供 `AgentHandle`(含可调用 `run` / `stream`)。未注册工厂时返回 `undefined`。\n */\nfunction getBuiltinInjectionValue(type: InjectionType, ctx: FaapiContext, body?: unknown): unknown {\n switch (type) {\n case 'query':\n return queryToObject(ctx.query);\n case 'params':\n return ctx.params;\n case 'headers':\n return ctx.headers;\n case 'context':\n return ctx;\n case 'cookies':\n return ctx.cookies;\n case 'ip':\n return ctx.ip;\n case 'ua':\n return ctx.ua;\n case 'body':\n return body;\n // form 与 body 共享解析结果(resolveInput 已按 Content-Type 解析 form-urlencoded)\n // 差异仅在 schema 校验(form coerce=true,由 collectRouteSchemaSources 标记)\n case 'form':\n return body;\n case 'files':\n if (body && typeof body === 'object' && 'files' in body) {\n return (body as MultipartResult).files;\n }\n return [];\n case 'fields':\n if (body && typeof body === 'object' && 'fields' in body) {\n return (body as MultipartResult).fields;\n }\n return {};\n // Phase 2.3:注入所有已注册 agent 元数据列表\n // 方案 A:优先读 app 实例注册表,无实例(编程式直调 ctx)回退默认全局实例\n case 'agents':\n return ctx.registries ? ctx.registries.agent.listAgents() : listAgents();\n // Phase 3.5:调 @faapi/agent 插件注册的工厂获取 AgentHandle\n case 'agent':\n return ctx.registries ? ctx.registries.agentHandle.get(ctx) : getAgentHandle(ctx);\n default:\n return undefined;\n }\n}\n\n/**\n * 根据注入信息,准备参数值并调用 handler(异步版本)\n *\n * 支持 async handler。内置注入优先于注入器(避免 query/body 等被覆盖)。\n * 非内置参数从注入器注册表按参数名查找,按需执行。\n */\nexport async function injectParamsAsync(\n handler: (...args: unknown[]) => unknown,\n ctx: FaapiContext,\n body?: unknown,\n injectors?: InjectorMap,\n): Promise<unknown> {\n const injections = resolveInjection(handler);\n\n if (injections.length === 0) {\n return await handler();\n }\n\n const args = await Promise.all(\n injections.map(async (injection) => {\n // 内置注入优先\n if (injection.type !== 'unknown') {\n return getBuiltinInjectionValue(injection.type, ctx, body);\n }\n // 注入器按参数名匹配\n if (injectors && injection.name in injectors) {\n // in 检查已证明存在\n return await injectors[injection.name]!(ctx);\n }\n return undefined;\n }),\n );\n\n return await handler(...args);\n}\n","import type { FaapiContext } from './contextTypes';\nimport type { ResponseMeta } from './contextTypes';\nimport type { SseWriter } from './sse';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { toResponse } from '../response/toResponse';\nimport { deferMetaHeaders, isHeadersOnlyMeta } from '../response/pendingMeta';\nimport { wrapOkResult } from '../response/responseFormatter';\nimport { injectParamsAsync } from '../injection/injectParams';\n\n/**\n * 自动包裹 handler 返回值(统一响应包装)\n *\n * 实现已移至 [response/responseFormatter.ts](../response/responseFormatter.ts) 的\n * wrapOkResult,与 ctx.ok() / formatFailResponse / formatErrorResponse 共享同一套\n * ok/fail 函数,确保响应格式在所有路径一致。\n *\n * 规则:\n * - Response 对象:不包裹(ctx.ok/ctx.fail/ctx.json 等返回的 Response 原样透传)\n * - 其他值(含 null/undefined):用 config.response.ok(或默认 (data) => ({ data })) 包裹\n *\n * ctx.ok() 返回的是 Response 对象,所以 handler 用 `return ctx.ok(data)` 或 `return data`\n * 最终响应一致(都是 ok(data) 的结果),不会双重包裹。\n *\n * 注意:null/undefined 也会被包裹为 { data: null } / { data: undefined }(JSON.stringify\n * 后者得到 \"{}\"),不再返回 204 No Content。如需 204,显式返回 Response 对象。\n */\nfunction wrapResult(result: unknown, ctx: FaapiContext): unknown {\n return wrapOkResult(result, ctx.config);\n}\n\n/**\n * 将 ctx.meta(setStatus/setHeader/setCookie 设置的响应元数据)合并到 Response\n *\n * 用于中间件返回 Response 时,确保用户通过 ctx.setStatus/setHeader 设置的\n * 状态码、响应头、cookie 能正确附加到中间件返回的 Response 上。\n */\nexport function mergeMeta(response: Response, meta: ResponseMeta): Response {\n const hasMeta =\n meta.status !== undefined || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;\n if (!hasMeta) return response;\n\n // 仅 headers 时延迟到发送层落头(零 Response 重建,见 pendingMeta.ts)\n if (isHeadersOnlyMeta(meta)) {\n deferMetaHeaders(response, meta.headers);\n return response;\n }\n\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(meta.headers)) {\n headers.set(key, value);\n }\n for (const cookie of meta.setCookies) {\n headers.append('set-cookie', cookie);\n }\n return new Response(response.body, {\n status: meta.status ?? response.status,\n headers,\n });\n}\n\n/**\n * 洋葱模型调度:将中间件链包装成 next 函数\n *\n * 每个中间件的 `await next()` 会执行下一个中间件并返回其 Response;\n * 最内层的 next 执行 handler(含参数注入)。\n *\n * 中间件返回 Response 的语义:\n * - 在 await next() 之前返回:拦截请求(不执行后续中间件和 handler)\n * - 在 await next() 之后返回:替换内层返回的响应\n * - 在 catch 块中返回:作为错误响应\n *\n * 中间件返回 void/undefined:使用 await next() 返回的内层响应。\n * 中间件不调用 next() 也不返回 Response:抛错(语义模糊,禁止使用)。\n *\n * 中间件返回 Response 时会合并 ctx.meta(setStatus/setHeader/setCookie 的设置),\n * 确保 ctx 便捷方法在中间件拦截场景下也生效。\n */\nexport async function compose(\n middlewares: FaapiMiddleware[],\n ctx: FaapiContext,\n finalHandler: () => Promise<Response>,\n): Promise<Response> {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n let index = -1;\n\n async function dispatch(i: number): Promise<Response> {\n if (i <= index) {\n throw new Error('next() called multiple times');\n }\n index = i;\n\n // 所有中间件执行完,运行 handler\n if (i >= middlewares.length) {\n return await finalHandler();\n }\n\n // i >= middlewares.length 已早退,此处索引必然存在\n const mw = middlewares[i]!;\n // next() 返回内层 Response,中间件可选择使用或替换\n let innerResponse: Response | undefined;\n const next = async (): Promise<Response> => {\n innerResponse = await dispatch(i + 1);\n return innerResponse;\n };\n\n const result = await mw(ctx, next);\n\n // 中间件返回 Response:使用它(拦截或替换),并合并 ctx.meta\n if (result instanceof Response) {\n return mergeMeta(result, meta);\n }\n // 中间件返回 void:使用 next() 返回的内层响应\n if (innerResponse !== undefined) {\n return innerResponse;\n }\n // 中间件既没返回 Response 也没调用 next():语义模糊,禁止\n throw new Error('中间件必须 await next() 或返回 Response');\n }\n\n return await dispatch(0);\n}\n\n/**\n * 调用路由 handler 并将返回值转为 Response\n *\n * 流程(洋葱模型):\n * 1. 中间件按洋葱模型执行:mw1.before → mw2.before → ... → handler → ... → mw2.after → mw1.after\n * 2. 中间件不调用 next() 即拦截请求(必须返回 Response)\n * 3. 中间件可用 try/catch 捕获内层错误\n * 4. 最内层执行注入器(按需)→ handler\n *\n * 注入器与中间件解耦:\n * - 注入器按 handler 参数名匹配,只执行需要的\n * - 注入器可读取中间件塞进 ctx 的值\n */\nexport async function invokeHandler(\n handler: (...args: unknown[]) => unknown,\n ctx: FaapiContext,\n body?: unknown,\n middlewares?: FaapiMiddleware[],\n injectors?: InjectorMap,\n): Promise<Response> {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n\n /**\n * 若 handler 调用了 ctx.sse(),优先使用 SSE Response\n * (handler 返回值被忽略,SSE 流由 writer 控制)\n *\n * 同时执行自动 close 兜底:若 handler 返回时 writer 仍未关闭,自动调用 close(),\n * 避免连接泄漏。handler 异步循环未退出、忘记 close、或抛错的场景由这里兜底。\n */\n const pickSseAndAutoClose = (): Response | null => {\n const sseWriter = (ctx as FaapiContext & { __sseWriter?: SseWriter }).__sseWriter;\n if (!sseWriter) return null;\n // 兜底:handler 未显式 close 时自动关闭\n if (!sseWriter.closed && !sseWriter.aborted) {\n sseWriter.close();\n }\n return mergeMeta(sseWriter.response, meta);\n };\n\n // 抛错时也保证 writer 被关闭(避免流泄漏)\n const autoCloseSseOnError = (): void => {\n const sseWriter = (ctx as FaapiContext & { __sseWriter?: SseWriter }).__sseWriter;\n if (sseWriter && !sseWriter.closed && !sseWriter.aborted) {\n sseWriter.close();\n }\n };\n\n // 无中间件:直接执行 handler\n if (!middlewares || middlewares.length === 0) {\n try {\n const result = await injectParamsAsync(handler, ctx, body, injectors);\n const sseResponse = pickSseAndAutoClose();\n if (sseResponse) return sseResponse;\n return toResponse(wrapResult(result, ctx), meta);\n } catch (err) {\n // handler 抛错时关闭未完成的 SSE 流,避免泄漏\n autoCloseSseOnError();\n throw err;\n }\n }\n\n // 有中间件:洋葱模型调度\n const finalHandler = async (): Promise<Response> => {\n try {\n const result = await injectParamsAsync(handler, ctx, body, injectors);\n const sseResponse = pickSseAndAutoClose();\n if (sseResponse) return sseResponse;\n return toResponse(wrapResult(result, ctx), meta);\n } catch (err) {\n // handler 抛错时关闭未完成的 SSE 流,避免泄漏\n autoCloseSseOnError();\n throw err;\n }\n };\n\n return await compose(middlewares, ctx, finalHandler);\n}\n","import type { ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { consumePendingMetaHeaders } from './pendingMeta';\n\n/**\n * 将 Web Response 写入 Node.js ServerResponse\n *\n * 使用 stream pipe 处理背压,避免高并发下内存膨胀。\n *\n * 断连语义:客户端中断(res 'error' / 提前 'close')时销毁源流并按正常完成收尾——\n * 网络中断不是服务端错误,不进入 500 兜底、不触发 onError;destroy 使底层\n * web ReadableStream 触发 cancel(),上游生产者(如 SseWriter)感知 aborted\n * 停止推送,避免数据堆积在无消费者的流 buffer 中(内存泄漏)。\n * 源流自身错误(handler 流式写失败)仍 reject,走正常错误响应路径。\n */\nexport async function sendNodeResponse(response: Response, res: ServerResponse): Promise<void> {\n // 设置状态码\n res.statusCode = response.status;\n\n // 设置 headers\n for (const [key, value] of response.headers) {\n if (key.toLowerCase() === 'set-cookie') {\n // Set-Cookie 使用 appendHeader 支持多个值\n res.appendHeader(key, value);\n } else {\n res.setHeader(key, value);\n }\n }\n // 延迟落头(headers-only meta 的零重建通道,见 pendingMeta.ts)——\n // 后写覆盖,与 Headers.set 语义一致\n const deferred = consumePendingMetaHeaders(response);\n if (deferred) {\n for (const [key, value] of Object.entries(deferred)) {\n res.setHeader(key, value);\n }\n }\n\n // 写入 body(使用 pipe 处理背压)\n if (response.body) {\n // TS 5.7 lib.dom 的 ReadableStream 与 node:stream/web 的 ReadableStream 是不同类型,cast 绕过\n const nodeStream = Readable.fromWeb(response.body as never);\n await new Promise<void>((resolve, reject) => {\n // 源流错误:真实错误 → reject(handleRequest catch 走错误响应)\n nodeStream.on('error', reject);\n\n // 客户端断开:销毁源流(触发底层 cancel)+ resolve(不当作服务端错误)\n const abort = () => {\n nodeStream.destroy();\n resolve();\n };\n res.on('error', abort);\n res.on('close', () => {\n // 正常完成也会触发 'close'(在 'finish' 之后,此时 writableEnded=true),不视为断连\n if (!res.writableEnded) abort();\n });\n\n res.on('finish', resolve);\n nodeStream.pipe(res);\n });\n return;\n }\n\n res.end();\n}\n","import { getSchemaName } from './schemaName';\nimport { InternalError } from '../errors/httpErrors';\nimport type { ValidationIssue, ValidationErrorCode } from '../errors/httpErrors';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport { isDevOnDemandEnabled } from '../cli/compileOnDemand';\n\n/**\n * 校验结果类型\n *\n * data 为校验/解析后的原始值(通常为对象;数组 body 为数组,\n * 顶层 primitive body——如 `type POSTBody = string`——为对应原始值)。\n */\nexport interface ValidationResult {\n valid: boolean;\n issues: ValidationIssue[];\n data: unknown;\n}\n\n/**\n * zod.js 模块导出格式\n *\n * 每个 handler 目录下的 zod.js 导出:\n * - `<SchemaName>Schema`:zod schema 对象(用于 safeParse 校验)\n *\n * query/params 的 schema 已在代码生成阶段用 z.preprocess 包裹了字符串转换逻辑\n * (number/boolean 字段),无需运行时再做 coerce。\n *\n * 无类型声明的方法不导出对应 Schema(值为 undefined)。\n */\ninterface SchemaModule {\n [key: string]: unknown;\n}\n\n/**\n * schema 模块缓存:schemaPath → SchemaModule\n *\n * dev 模式下 watch 触发 reloadRoutes 时通过 invalidateSchemaCache() 清空,\n * 下次请求重新 import(importWithCacheBust 会拼接时间戳绕过 ESM 缓存)。\n */\nconst moduleCache = new Map<string, SchemaModule>();\n\n/**\n * 清空 schema 模块缓存(dev watch 模式下文件变化时调用)\n */\nexport function invalidateSchemaCache(): void {\n moduleCache.clear();\n}\n\n/**\n * 加载 schema 模块(带缓存)\n *\n * 首次调用时 import zod.js 并缓存,后续直接返回缓存。\n * dev 模式下 watch 触发 invalidateSchemaCache 后,下次调用重新 import。\n */\nasync function loadSchemaModule(schemaPath: string): Promise<SchemaModule> {\n let mod = moduleCache.get(schemaPath);\n if (!mod) {\n // dev 按需模式下走 Node 原生 import 绕过 Vite SSR 缓存\n // (zod.js 是已编译产物,不需要 Vite alias 解析)\n mod = (await importWithCacheBust(schemaPath, isDevOnDemandEnabled())) as SchemaModule;\n moduleCache.set(schemaPath, mod);\n }\n return mod;\n}\n\n/**\n * 校验输入参数\n *\n * 流程:从 schemaPath import zod.js → zod safeParse\n *\n * schema 来源由调用方确保已生成:\n * - dev 模式:createApp 启动时 + watch 时调 generateSchemaFiles 生成 zod.js\n * - prd 模式:faapi build 时调 generateSchemaFiles 生成 zod.js\n *\n * coerce 说明:\n * - query/params 的 schema 在代码生成阶段已用 z.preprocess 包裹字符串转换逻辑\n * (number: \"1\" → 1,boolean: \"true\" → true),运行时直接 safeParse 即可\n * - body 是 JSON 解析的天然 JS 类型,schema 不含 preprocess\n *\n * 三种状态:\n * - Schema 导出存在:执行 zod safeParse 校验\n * - Schema 导出 undefined(无类型声明):跳过校验\n * - zod.js 文件不存在或 import 失败:抛 InternalError\n *\n * @param schemaPath zod.js 文件绝对路径\n * @param method HTTP 方法\n * @param inputType 输入类型\n * @param input 输入值\n */\nexport async function validateInput(\n schemaPath: string,\n method: string,\n inputType: 'query' | 'body' | 'params',\n input: unknown,\n): Promise<ValidationResult> {\n const schemaName = getSchemaName(method, inputType);\n const schemaKey = `${schemaName}Schema`;\n\n let mod: SchemaModule;\n try {\n mod = await loadSchemaModule(schemaPath);\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new InternalError(`Schema 模块加载失败: ${schemaPath}: ${reason}`);\n }\n\n const schema = mod[schemaKey];\n\n // 无类型声明:跳过校验,input 原样透传(数组/原始值 body 不再静默替换为 {})\n if (schema === undefined || schema === null) {\n return { valid: true, issues: [], data: input };\n }\n\n // schema 必须是带 safeParse 的 zod schema\n if (\n typeof schema !== 'object' ||\n typeof (schema as { safeParse?: unknown }).safeParse !== 'function'\n ) {\n throw new InternalError(`Schema 不是有效的 zod schema: ${schemaPath}#${schemaName}`);\n }\n\n // zod safeParse 校验:input 原样传入(schema 生成端支持数组/顶层 primitive body,\n // 预先替换数组会使命中 z.array 的合法 body 永远失败且 issue 误导为 received object)\n // query/params 的 preprocess 已在 schema 生成阶段内联\n const zodSchema = schema as { safeParse: (v: unknown) => ZodSafeParseResult };\n const result = zodSchema.safeParse(input);\n\n if (result.success) {\n // 解析后的数据原样返回(z.array schema 的 data 是数组,顶层 primitive 是原始值)\n return { valid: true, issues: [], data: result.data };\n }\n\n // 将 zod issues 转为 ValidationIssue\n const issues = mapZodIssues(result.error);\n return { valid: false, issues, data: input };\n}\n\n/**\n * zod safeParse 成功结果\n */\ninterface ZodSafeParseSuccess {\n success: true;\n data: unknown;\n}\n\n/**\n * zod safeParse 失败结果\n */\ninterface ZodSafeParseError {\n success: false;\n error: {\n issues: Array<{\n path: (string | number)[];\n code: string;\n expected?: string;\n received?: string;\n message: string;\n }>;\n };\n}\n\ntype ZodSafeParseResult = ZodSafeParseSuccess | ZodSafeParseError;\n\n/**\n * 将 zod v4 error issues 映射为框架的 ValidationIssue\n *\n * zod code → 框架 ValidationErrorCode 映射:\n * - invalid_type(received 非 undefined) / invalid_union → TYPE_MISMATCH(422)\n * - invalid_type(received === 'undefined',即缺失必填字段)→ MISSING_FIELD(400)\n * - unrecognized_keys → INVALID_FORMAT(400)\n * - invalid_value / invalid_format(v4 的 email/url/uuid/regex 等格式检查)/\n * invalid_key / invalid_element(v4 的 record/map 元素检查)/\n * too_small / too_big / invalid_intersection_types / not_multiple_of / custom\n * → INVALID_VALUE(422)\n *\n * path 数组转为 dot 路径(如 ['user', 'address', 'city'] → 'user.address.city')。\n */\nfunction mapZodIssues(error: ZodSafeParseError['error']): ValidationIssue[] {\n return error.issues.map((issue) => {\n const code = mapZodCode(issue);\n const path = issue.path.map(String).join('.') || '';\n return {\n path,\n code,\n expected: issue.expected ?? mapExpectedFromMessage(issue.message),\n received: issue.received ?? mapReceivedFromMessage(issue.message),\n message: issue.message,\n };\n });\n}\n\ninterface ZodIssueLike {\n code: string;\n expected?: string;\n received?: string;\n message: string;\n}\n\n/**\n * 映射 zod v4 issue code 到框架 ValidationErrorCode\n *\n * zod v4 已移除 invalid_string(v3 遗留),格式类检查(email/url/uuid/regex)\n * 产出 invalid_format;record/map 的键/元素检查产出 invalid_key / invalid_element。\n */\nfunction mapZodCode(issue: ZodIssueLike): ValidationErrorCode {\n switch (issue.code) {\n case 'invalid_type':\n // zod v4 对缺失必填字段产出 invalid_type,无独立 missing code;\n // received 字段缺失,\"received undefined\" 只出现在 message 中\n if (issue.received === 'undefined' || /received undefined/i.test(issue.message)) {\n return 'MISSING_FIELD';\n }\n return 'TYPE_MISMATCH';\n case 'invalid_union':\n case 'invalid_union_discriminator':\n return 'TYPE_MISMATCH';\n case 'unrecognized_keys':\n return 'INVALID_FORMAT';\n case 'invalid_value':\n case 'invalid_format':\n case 'invalid_key':\n case 'invalid_element':\n case 'too_small':\n case 'too_big':\n case 'invalid_intersection_types':\n case 'not_multiple_of':\n case 'custom':\n return 'INVALID_VALUE';\n default:\n return 'INVALID_VALUE';\n }\n}\n\n/**\n * 从 zod message 中提取期望类型(兜底)\n */\nfunction mapExpectedFromMessage(message: string): string {\n // zod v3 message 示例:\"Expected string, received number\"\n const match = message.match(/Expected\\s+(\\w+)/i);\n return match ? match[1]!.toLowerCase() : 'unknown';\n}\n\n/**\n * 从 zod message 中提取实际类型(兜底)\n */\nfunction mapReceivedFromMessage(message: string): string {\n const match = message.match(/received\\s+(\\w+)/i);\n return match ? match[1]!.toLowerCase() : 'unknown';\n}\n","import type { IncomingMessage } from 'node:http';\n\n/**\n * 从 IncomingMessage 提取客户端 IP\n *\n * 优先级:\n * 1. `trustedProxy=true` 时,`x-forwarded-for` 第一个 IP(反向代理场景,如 nginx/CDN)\n * 2. `req.socket.remoteAddress`(直连场景,默认路径)\n *\n * `x-forwarded-for` 格式:`client, proxy1, proxy2`,取第一个即客户端真实 IP。\n *\n * **默认不信任该 header**(`trustedProxy=false`):客户端直连时 XFF 可被任意伪造,\n * 污染 `ctx.ip`(影响限流/日志/风控)。部署在受信任的反向代理之后时,通过\n * `faapi.config.ts` 的 `trustedProxy: true` 显式开启。\n *\n * @param trustedProxy 是否信任反向代理头(默认 false——安全默认,直连不可伪造)\n * @returns 客户端 IP,无法获取时返回空字符串\n */\nexport function getClientIp(req: IncomingMessage, trustedProxy = false): string {\n // 1. x-forwarded-for(仅显式信任反向代理时)\n if (trustedProxy) {\n const xff = req.headers['x-forwarded-for'];\n if (typeof xff === 'string' && xff.length > 0) {\n const first = xff.split(',')[0]?.trim();\n if (first) return first;\n }\n }\n\n // 2. 直连 socket IP\n const remote = req.socket?.remoteAddress;\n if (remote) {\n // 去掉 IPv6 前缀 ::ffff:,统一返回 IPv4 形式\n if (remote.startsWith('::ffff:')) {\n return remote.slice(7);\n }\n return remote;\n }\n\n return '';\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\n\nexport interface CorsOptions {\n origin?: string | string[] | true;\n methods?: string[];\n allowedHeaders?: string[];\n exposeHeaders?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\nconst DEFAULT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];\n\n/**\n * 创建 CORS 中间件(洋葱模型)\n *\n * - origin=true: 允许所有来源(反射请求的 Origin)\n * - origin=string: 允许指定来源\n * - origin=string[]: 允许多个来源\n *\n * OPTIONS 预检请求直接返回 204,不调用 next()。\n */\nexport function cors(options: CorsOptions = {}): FaapiMiddleware {\n const {\n origin = true,\n methods = DEFAULT_METHODS,\n allowedHeaders,\n exposeHeaders,\n credentials = false,\n maxAge,\n } = options;\n\n return async (ctx, next) => {\n const reqOrigin = ctx.headers.get('origin');\n if (!reqOrigin) {\n await next();\n return;\n }\n\n // 确定允许的来源\n let allowOrigin: string | null = null;\n if (origin === true) {\n allowOrigin = reqOrigin; // 反射 Origin\n } else if (typeof origin === 'string') {\n allowOrigin = reqOrigin === origin ? origin : null;\n } else if (Array.isArray(origin)) {\n allowOrigin = origin.includes(reqOrigin) ? reqOrigin : null;\n }\n\n // 当 origin 为动态值(true 或数组)时,响应内容(是否带 ACAO)随 Origin 头变化\n // ——不匹配被拒的响应同样必须带 Vary: Origin。缺了它,CDN/浏览器按 URL 缓存\n // \"无 ACAO 的拒绝响应\"后,可能服务给后续合法 Origin 的请求(缓存污染面)\n if (origin === true || Array.isArray(origin)) {\n const existingVary = ctx.headers.get('vary');\n if (existingVary) {\n if (!existingVary.toLowerCase().includes('origin')) {\n ctx.setHeader('Vary', `${existingVary}, Origin`);\n }\n } else {\n ctx.setHeader('Vary', 'Origin');\n }\n }\n\n if (!allowOrigin) {\n await next();\n return;\n }\n\n // 设置 CORS 响应头\n ctx.setHeader('Access-Control-Allow-Origin', allowOrigin);\n\n ctx.setHeader('Access-Control-Allow-Methods', methods.join(', '));\n\n if (allowedHeaders) {\n ctx.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));\n } else {\n // 反射请求头\n const requestHeaders = ctx.headers.get('access-control-request-headers');\n if (requestHeaders) {\n ctx.setHeader('Access-Control-Allow-Headers', requestHeaders);\n }\n }\n\n if (exposeHeaders && exposeHeaders.length > 0) {\n ctx.setHeader('Access-Control-Expose-Headers', exposeHeaders.join(', '));\n }\n\n if (credentials) {\n ctx.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n\n if (maxAge !== undefined) {\n ctx.setHeader('Access-Control-Max-Age', String(maxAge));\n }\n\n // 处理 preflight 请求:直接返回 204,不调用 next()\n if (ctx.method === 'OPTIONS') {\n return new Response(null, { status: 204 });\n }\n\n await next();\n };\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\n\nexport interface HelmetOptions {\n contentSecurityPolicy?: string | false;\n xFrameOptions?: 'DENY' | 'SAMEORIGIN' | false;\n xContentTypeOptions?: boolean;\n referrerPolicy?: string | false;\n strictTransportSecurity?: string | false;\n xDnsPrefetchControl?: boolean;\n xDownloadOptions?: boolean;\n xPermittedCrossDomainPolicies?: string | false;\n crossOriginOpenerPolicy?: string | false;\n crossOriginResourcePolicy?: string | false;\n crossOriginEmbedderPolicy?: string | false;\n originAgentCluster?: boolean;\n xPoweredBy?: boolean;\n}\n\nconst DEFAULTS: Required<Omit<HelmetOptions, 'contentSecurityPolicy'>> & {\n contentSecurityPolicy: string | false;\n} = {\n contentSecurityPolicy: \"default-src 'self'\",\n xFrameOptions: 'SAMEORIGIN',\n xContentTypeOptions: true,\n referrerPolicy: 'no-referrer',\n strictTransportSecurity: 'max-age=31536000; includeSubDomains',\n xDnsPrefetchControl: true,\n xDownloadOptions: true,\n xPermittedCrossDomainPolicies: 'none',\n crossOriginOpenerPolicy: 'same-origin',\n crossOriginResourcePolicy: 'same-origin',\n crossOriginEmbedderPolicy: false,\n originAgentCluster: true,\n xPoweredBy: true,\n};\n\nexport function helmet(options: HelmetOptions = {}): FaapiMiddleware {\n const opts = { ...DEFAULTS, ...options };\n\n return async (ctx, next) => {\n if (opts.contentSecurityPolicy !== false) {\n ctx.setHeader('Content-Security-Policy', opts.contentSecurityPolicy);\n }\n if (opts.xFrameOptions !== false) {\n ctx.setHeader('X-Frame-Options', opts.xFrameOptions);\n }\n if (opts.xContentTypeOptions) {\n ctx.setHeader('X-Content-Type-Options', 'nosniff');\n }\n if (opts.referrerPolicy !== false) {\n ctx.setHeader('Referrer-Policy', opts.referrerPolicy);\n }\n if (opts.strictTransportSecurity !== false) {\n ctx.setHeader('Strict-Transport-Security', opts.strictTransportSecurity);\n }\n if (opts.xDnsPrefetchControl) {\n ctx.setHeader('X-DNS-Prefetch-Control', 'off');\n }\n if (opts.xDownloadOptions) {\n ctx.setHeader('X-Download-Options', 'noopen');\n }\n if (opts.xPermittedCrossDomainPolicies !== false) {\n ctx.setHeader('X-Permitted-Cross-Domain-Policies', opts.xPermittedCrossDomainPolicies);\n }\n if (opts.crossOriginOpenerPolicy !== false) {\n ctx.setHeader('Cross-Origin-Opener-Policy', opts.crossOriginOpenerPolicy);\n }\n if (opts.crossOriginResourcePolicy !== false) {\n ctx.setHeader('Cross-Origin-Resource-Policy', opts.crossOriginResourcePolicy);\n }\n if (opts.crossOriginEmbedderPolicy !== false) {\n ctx.setHeader('Cross-Origin-Embedder-Policy', opts.crossOriginEmbedderPolicy);\n }\n if (opts.originAgentCluster) {\n ctx.setHeader('Origin-Agent-Cluster', '?1');\n }\n if (opts.xPoweredBy) {\n ctx.setHeader('X-Powered-By', 'faapi');\n }\n\n return await next();\n };\n}\n","import zlib from 'node:zlib';\nimport { promisify } from 'node:util';\nimport type { FaapiMiddleware } from './middlewareTypes';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport { consumePendingMetaHeaders } from '../response/pendingMeta';\n\nconst gzipAsync = promisify(zlib.gzip);\nconst deflateAsync = promisify(zlib.deflate);\nconst brotliAsync = promisify(zlib.brotliCompress);\n\nexport interface CompressionOptions {\n /**\n * 最小压缩字节数,body 低于该值不压缩(小 payload 压缩后反而变大)\n * @default 1024\n */\n threshold?: number;\n}\n\nconst DEFAULT_THRESHOLD = 1024;\n\n/** 可压缩的 Content-Type(text/event-stream 除外——流式语义不应缓冲) */\nconst COMPRESSIBLE_CHECKS: Array<(contentType: string) => boolean> = [\n (ct) => ct === 'application/json',\n (ct) => ct === 'application/javascript',\n (ct) => ct === 'image/svg+xml',\n (ct) => ct.startsWith('text/') && ct !== 'text/event-stream',\n];\n\nfunction isCompressible(contentType: string): boolean {\n const ct = contentType.split(';')[0]!.trim().toLowerCase();\n if (ct === '') return false;\n return COMPRESSIBLE_CHECKS.some((check) => check(ct));\n}\n\ninterface AcceptedEncoding {\n encoding: string;\n quality: number;\n}\n\n/**\n * 解析 Accept-Encoding 头(含 q 值)\n *\n * `gzip, br;q=0.8, identity;q=0, *;q=0.5` → [{gzip,1},{br,0.8},{identity,0},{*,0.5}]\n */\nexport function parseAcceptEncoding(header: string): AcceptedEncoding[] {\n return header\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part !== '')\n .map((part) => {\n const [encoding, ...params] = part.split(';');\n let quality = 1;\n for (const param of params) {\n const trimmed = param.trim();\n if (trimmed.startsWith('q=')) {\n const q = Number(trimmed.slice(2));\n if (Number.isFinite(q)) quality = q;\n }\n }\n return { encoding: (encoding ?? '').trim().toLowerCase(), quality };\n })\n .filter((e) => e.encoding !== '' && e.quality > 0);\n}\n\n/**\n * 从已接受的编码中选择压缩算法\n *\n * 服务器偏好 br > gzip > deflate(同等 q 值下);`*` 通配按 gzip 处理。\n * 未命中返回 null(客户端不接受压缩)。\n */\nexport function selectEncoding(accepted: AcceptedEncoding[]): 'br' | 'gzip' | 'deflate' | null {\n const byName = new Map<string, number>();\n for (const { encoding, quality } of accepted) {\n // 同一编码出现多次取最高 q\n byName.set(encoding, Math.max(byName.get(encoding) ?? 0, quality));\n }\n // q=0 表示明确不接受\n for (const [encoding, quality] of byName) {\n if (quality === 0) byName.delete(encoding);\n }\n\n for (const candidate of ['br', 'gzip', 'deflate'] as const) {\n if ((byName.get(candidate) ?? 0) > 0) return candidate;\n }\n if ((byName.get('*') ?? 0) > 0) return 'gzip';\n return null;\n}\n\nasync function compressBody(\n encoding: 'br' | 'gzip' | 'deflate',\n body: string,\n): Promise<Uint8Array> {\n const buf = Buffer.from(body, 'utf-8');\n switch (encoding) {\n case 'br':\n return brotliAsync(buf);\n case 'gzip':\n return gzipAsync(buf);\n case 'deflate':\n return deflateAsync(buf);\n }\n}\n\n/** 向 meta.headers 合并 Vary 值(CORS 等中间件可能已设置 Vary: Origin,不能覆盖) */\nfunction mergeVary(meta: ResponseMeta, value: string): void {\n const existing = meta.headers['Vary'] ?? meta.headers['vary'];\n if (!existing) {\n meta.headers['Vary'] = value;\n return;\n }\n // 大小写不敏感地检查是否已包含\n if (!existing.toLowerCase().includes(value.toLowerCase())) {\n const key = meta.headers['Vary'] !== undefined ? 'Vary' : 'vary';\n meta.headers[key] = `${existing}, ${value}`;\n }\n}\n\n/**\n * 创建响应压缩中间件(内建,默认关闭,`config.compression` 显式启用)\n *\n * 位于外层中间件链最前(包住 CORS/helmet/logger/全局),`await next()` 后拿到\n * 最终 Response:满足压缩条件(可压缩类型 + 大小达标 + 客户端接受)时缓冲 body\n * 异步压缩后构造新 Response 替换;否则透传。`Vary: Accept-Encoding` 通过\n * meta.headers 合并(与 CORS 的 Vary: Origin 共存),两种路径都生效。\n */\nexport function compression(options: CompressionOptions = {}): FaapiMiddleware {\n const threshold = options.threshold ?? DEFAULT_THRESHOLD;\n\n return async (ctx, next) => {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n const response = await next();\n if (!response) return response;\n\n // 无条件协商 Vary(响应内容随 Accept-Encoding 变化,与是否压缩本响应无关)\n mergeVary(meta, 'Accept-Encoding');\n\n // 跳过条件:客户端不接受 / 不可压缩类型 / 已编码 / no-transform / 204、304 等\n const acceptEncoding = ctx.request.headers.get('accept-encoding') ?? '';\n const encoding = selectEncoding(parseAcceptEncoding(acceptEncoding));\n const contentType = response.headers.get('content-type') ?? '';\n if (\n !encoding ||\n !isCompressible(contentType) ||\n response.headers.has('content-encoding') ||\n (response.headers.get('cache-control') ?? '').includes('no-transform') ||\n response.status === 204 ||\n response.status === 304 ||\n response.body === null\n ) {\n return response;\n }\n\n // 缓冲 body(toResponse 的 JSON body 本就是字符串);SSE 已被 content-type 排除\n // 延迟落头(headers-only meta)需并入重建的 Response,否则发送层取不到\n const deferredHeaders = consumePendingMetaHeaders(response);\n\n const bodyText = await response.text();\n if (bodyText.length < threshold) {\n // 未达阈值:body 已被 text() 消费,需重建等价 Response\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n headers.set(key, value);\n }\n return new Response(bodyText, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n const compressed = await compressBody(encoding, bodyText);\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n headers.set(key, value);\n }\n headers.set('Content-Encoding', encoding);\n // Content-Length 由 Response 构造时按压缩后字节数自动设置(先删避免重复)\n headers.delete('Content-Length');\n return new Response(compressed as unknown as BodyInit, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n","import { createHash } from 'node:crypto';\nimport type { FaapiMiddleware } from './middlewareTypes';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport { consumePendingMetaHeaders } from '../response/pendingMeta';\n\nexport interface EtagOptions {\n /**\n * 生成弱 ETag(`W/\"<hash>\"`)。弱校验器允许压缩等表示差异下的 304 协商,\n * 与 compression 中间件配合正确;强 ETag 需对每个表示(编码)单独生成\n * @default true\n */\n weak?: boolean;\n}\n\nconst DEFAULTS: Required<EtagOptions> = { weak: true };\n\n/** 计算 ETag:SHA-1 指纹 base64;weak=true 生成弱校验器(W/ 前缀) */\nfunction computeEtag(body: string, weak: boolean): string {\n const hash = createHash('sha1').update(body).digest('base64');\n return weak ? `W/\"${hash}\"` : `\"${hash}\"`;\n}\n\n/** If-None-Match 比对:weak 模式用 RFC 7232 弱比较(忽略 W/ 前缀);strong 模式精确比对 */\nfunction ifNoneMatchMatches(ifNoneMatch: string, etag: string, weak: boolean): boolean {\n const normalize = (tag: string) => tag.trim().replace(/^W\\//i, '');\n if (weak) {\n const target = normalize(etag);\n return ifNoneMatch.split(',').some((tag) => {\n const trimmed = tag.trim();\n return trimmed === '*' || normalize(trimmed) === target;\n });\n }\n // 强比较:ETag 原样相等(含 W/ 前缀语义,弱 ETag 永不匹配强比较)\n return ifNoneMatch.split(',').some((tag) => tag.trim() === etag || tag.trim() === '*');\n}\n\n/**\n * 创建 ETag/304 协商中间件(内建,默认关闭,`config.etag` 显式启用)\n *\n * `await next()` 后对 GET/HEAD 2xx 响应计算弱 ETag 并写入 meta(handler 显式\n * `ctx.setETag()` 时不覆盖);请求 `If-None-Match` 弱比较命中时返回 304(无 body)。\n *\n * 中间件位置在 compression 内层:先算 ETag/304 再压缩——304 无 body 压缩自动跳过;\n * 弱 ETag 基于未压缩表示计算,语义正确。\n */\nexport function etag(options: EtagOptions = {}): FaapiMiddleware {\n const opts = { ...DEFAULTS, ...options };\n\n return async (ctx, next) => {\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n const response = await next();\n if (!response) return response;\n\n // 仅协商幂等方法的 2xx 响应\n const method = ctx.request.method.toUpperCase();\n if (method !== 'GET' && method !== 'HEAD') return response;\n if (response.status < 200 || response.status >= 300) return response;\n\n // handler 已显式设置 ETag(ctx.setETag 写 meta.headers.etag)→ 控制权归 handler\n if (meta.headers['etag'] !== undefined || response.headers.has('etag')) {\n return response;\n }\n // 流式响应(SSE 等)不缓冲\n if (\n response.body === null ||\n (response.headers.get('content-type') ?? '').includes('text/event-stream')\n ) {\n return response;\n }\n\n const bodyText = await response.text();\n const etagValue = computeEtag(bodyText, opts.weak);\n\n // If-None-Match 弱比较命中 → 304(无 body,携带 ETag)\n const ifNoneMatch = ctx.request.headers.get('if-none-match');\n if (ifNoneMatch && ifNoneMatchMatches(ifNoneMatch, etagValue, opts.weak)) {\n return new Response(null, {\n status: 304,\n statusText: response.statusText,\n headers: { ETag: etagValue },\n });\n }\n\n // 200 路径:body 已被 text() 消费,重建响应;ETag 经 meta 传递(mergeMeta 兜底应用)\n meta.headers['etag'] = etagValue;\n const headers = new Headers(response.headers);\n // 延迟落头并入重建的 Response(etag 中间件位于 compression 内层,先消费)\n const deferredHeaders = consumePendingMetaHeaders(response);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n headers.set(key, value);\n }\n return new Response(bodyText, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n","import type { FaapiMiddleware } from './middlewareTypes';\n\nexport type LoggerFn = (messageOrObj: string | Record<string, unknown>, message?: string) => void;\n\nexport interface LoggerOptions {\n /**\n * 自定义日志函数\n *\n * - 传入 `console.log`(默认):纯文本格式 `GET /api/users 200 12ms`\n * - 传入 pino logger:结构化日志 `logger.info({ method, path, status, durationMs }, 'request completed')`\n * - 传入 winston logger:`logger.info('GET /api/users 200 12ms', { method, path })`\n */\n log?: LoggerFn;\n}\n\n/**\n * 创建请求日志中间件(洋葱模型)\n *\n * 日志格式(文本模式):GET /api/users 200 12ms\n * 错误格式(文本模式):POST /api/users 400 45ms - Error: ...\n *\n * 结构化模式:传入 pino/winston 等 logger 实例时,会自动传递结构化字段。\n *\n * before/after 一体,闭包变量共享开始时间,无需污染 ctx。\n * 错误用 try/catch 捕获,记录后重新抛出(让上层处理)。\n * 成功时从 next() 返回的 Response 读取状态码。\n *\n * log 函数每次请求时读取(options.log ?? console.log),运行时替换 console.log 会生效。\n */\nexport function logger(options: LoggerOptions = {}): FaapiMiddleware {\n return async (ctx, next) => {\n const log = options.log ?? console.log;\n const start = Date.now();\n try {\n const response = await next();\n const duration = Date.now() - start;\n const entry = {\n method: ctx.method,\n path: ctx.path,\n status: response.status,\n durationMs: duration,\n };\n log(entry, `${ctx.method} ${ctx.path} ${response.status} ${duration}ms`);\n return response;\n } catch (err) {\n const duration = Date.now() - start;\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as { statusCode?: number })?.statusCode ?? 500;\n const entry = {\n method: ctx.method,\n path: ctx.path,\n status,\n durationMs: duration,\n error: message,\n };\n log(entry, `${ctx.method} ${ctx.path} ${status} ${duration}ms - ${message}`);\n throw err;\n }\n };\n}\n","/**\n * formatErrorResponse 的 re-export\n *\n * 实现已移至 [response/responseFormatter.ts](../response/responseFormatter.ts),\n * 与 wrapOkResult / formatFailResponse 共享同一套 ok/fail 函数,确保错误响应格式\n * 在「handler 抛错兜底」和「handler return ctx.fail()」两条路径一致。\n *\n * 保留此文件作为 errors/ 模块的入口,便于 errors 模块内部互引 +\n * 不破坏现有 import 路径(formatErrorResponse.test.ts / serverUtils.ts 等)。\n */\nexport { formatErrorResponse } from '../response/responseFormatter';\n","import type { IncomingMessage } from 'node:http';\nimport { formatErrorResponse } from '../errors/formatErrorResponse';\n\n/**\n * 将 Node.js IncomingMessage 的 headers 转为 Web Headers\n *\n * 处理数组型 header 值(如 set-cookie)和 undefined 值。\n */\nexport function nodeHttpToWebHeaders(req: IncomingMessage): Headers {\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const v of value) headers.append(key, v);\n } else {\n headers.set(key, value);\n }\n }\n return headers;\n}\n\n/**\n * 构建错误响应(兜底链)\n *\n * 1. 框架内置 formatErrorResponse 处理(handler 抛错时)——读 config.response.fail\n * 自定义包装函数,确保错误格式与 ctx.fail() 主动错误响应一致\n * 2. 内置兜底仍抛错 → 最简 500 JSON 响应\n *\n * 业务方如需进一步自定义错误响应,在全局中间件中 try/catch next() 即可。\n *\n * @param err handler 抛出的错误\n * @param config 业务方配置(用于读取 response.fail,可选——未传时走框架默认 fail 函数)\n */\nexport function buildErrorResponse(\n err: unknown,\n config?: Record<string, unknown> | undefined,\n): Response {\n try {\n return formatErrorResponse(err, config);\n } catch {\n // 极端情况:内置兜底也失败,返回最简 500\n return new Response(\n JSON.stringify({ error: { code: 'INTERNAL_ERROR', message: 'Internal Server Error' } }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n },\n );\n }\n}\n","/**\n * WebSocket Handler 类型定义与 socket 封装\n *\n * 用户在 handler.ts 中导出 WS 函数,返回事件回调对象。\n * 框架在协议升级成功后调用对应回调,传递封装后的 WsSocket。\n *\n * @see wsHandler.md 功能说明\n */\n\n/**\n * faapi 封装的 WebSocket socket 抽象\n *\n * 不直接暴露 ws 库的原生 socket,提供更安全、易用的 API。\n * send 对象时自动 JSON.stringify。\n */\nexport interface WsSocket {\n /** 发送数据(string/Buffer 直发,对象自动 JSON.stringify) */\n send(data: string | Buffer | object): void;\n /** 关闭连接 */\n close(code?: number, reason?: string | Buffer): void;\n /** 连接状态:0=connecting, 1=open, 2=closing, 3=closed */\n readonly readyState: number;\n}\n\n/**\n * WebSocket 事件回调集合\n *\n * 用户在 WS handler 中返回此对象,框架在对应事件触发时调用。\n * 所有回调可选,未提供则忽略事件。\n */\nexport interface WsEventHandlers {\n /** 连接建立时触发 */\n onOpen?: (ws: WsSocket) => void;\n /** 收到客户端消息时触发 */\n onMessage?: (ws: WsSocket, message: string | Buffer) => void;\n /** 连接关闭时触发 */\n onClose?: (ws: WsSocket, code: number, reason: string) => void;\n /** 发生错误时触发 */\n onError?: (ws: WsSocket, error: Error) => void;\n}\n\n/**\n * WebSocket 上下文(握手阶段构造)\n *\n * 与 HTTP FaapiContext 类似但精简,提供路由参数、查询参数、请求头、配置。\n * 可通过 declare module '@faapi/faapi' 增强自定义字段。\n */\nexport interface WsContext {\n /** 动态路由参数(如 [id] → params.id) */\n params: Record<string, string>;\n /** URL 查询参数 */\n query: URLSearchParams;\n /** 请求头 */\n headers: Headers;\n /** 业务配置(来自 faapi.config.ts) */\n config: Record<string, unknown>;\n /** 中间件塞入的用户信息(鉴权等) */\n user?: unknown;\n /** 允许通过 declare module 扩展 */\n [key: string]: unknown;\n}\n\n/**\n * WS handler 签名:接收 WsContext,返回事件回调对象(或无返回)\n */\nexport type WsHandler = (ctx: WsContext) => WsEventHandlers | void;\n\n/**\n * 将 ws 库的原生 WebSocket 封装为 faapi 的 WsSocket\n *\n * - send 对象时自动 JSON.stringify\n * - close/re.readyState 透传原生 socket\n */\nexport function wrapWsSocket(rawSocket: {\n send: (data: string | Buffer, cb?: (err?: Error) => void) => void;\n close: (code?: number, reason?: string | Buffer) => void;\n readonly readyState: number;\n}): WsSocket {\n return {\n send(data: string | Buffer | object): void {\n const payload =\n typeof data === 'string' || Buffer.isBuffer(data) ? data : JSON.stringify(data);\n rawSocket.send(payload);\n },\n close(code?: number, reason?: string | Buffer): void {\n rawSocket.close(code, reason);\n },\n get readyState(): number {\n return rawSocket.readyState;\n },\n };\n}\n","/**\n * WebSocket 协议升级处理\n *\n * 监听 HTTP server 的 upgrade 事件,匹配 WS 路由后调用 ws 库完成协议升级,\n * 加载 handler.ts 的 WS 导出,绑定事件回调。\n *\n * 握手阶段复用洋葱中间件链(鉴权/CORS/限流/日志等),与同目录 HTTP 路由共享中间件。\n * 中间件塞入 ctx 的值(如 ctx.user)传入 WS handler。连接建立后切到事件模型,不走中间件。\n *\n * @see wsHandler.md WS handler 约定与握手中间件链设计\n */\nimport type { IncomingMessage } from 'node:http';\nimport type { Server } from 'node:http';\nimport type { Socket } from 'node:net';\nimport fs from 'node:fs';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport type { RawData } from 'ws';\nimport path from 'node:path';\nimport type { WsRouteMatch, RoutesRef } from '../router/routeTypes';\nimport { matchWsRoute } from '../router/matchRoute';\nimport { createContext } from '../runtime/createContext';\nimport { compose, mergeMeta } from '../runtime/invokeHandler';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport { getClientIp } from '../utils/getClientIp';\nimport { nodeHttpToWebHeaders, buildErrorResponse } from './serverUtils';\nimport {\n ensureCompiled,\n ensureMiddlewaresCompiled,\n isDevOnDemandEnabled,\n getDevDist,\n prodPathToSourcePath,\n} from '../cli/compileOnDemand';\nimport { loadMergedMiddlewares } from '../middleware/loadMiddlewares';\nimport type { AppRegistries } from '../injection/registries';\nimport { consumePendingMetaHeaders } from '../response/pendingMeta';\nimport {\n wrapWsSocket,\n type WsContext,\n type WsEventHandlers,\n type WsHandler,\n} from '../runtime/wsHandler';\n\n/**\n * 从 IncomingMessage 提取 URL pathname(不含 query)\n */\nfunction getPathname(req: IncomingMessage): string {\n const url = req.url ?? '/';\n const idx = url.indexOf('?');\n return idx >= 0 ? url.slice(0, idx) : url;\n}\n\n/**\n * 加载 WS handler 并执行,返回事件回调对象(可能为空)\n *\n * 直接动态 import handler.ts 并取 `WS` 导出(不经过 loadRouteModule,\n * 因为 WS 不是 HTTP 方法,loadRouteModule 的 method 维度不适用)。\n * watch 模式下通过时间戳 query string 绕过 ESM 缓存。\n *\n * Dev 按需编译模式:先确保编译再 import,避免 import 不存在的文件污染 Vite SSR 状态。\n */\nasync function loadWsHandler(\n filePath: string,\n ctx: WsContext,\n rootDir?: string,\n): Promise<WsEventHandlers | void> {\n // Dev 按需模式:先确保编译再 import(同 loadRouteModule 逻辑)\n if (isDevOnDemandEnabled() && rootDir) {\n const dist = getDevDist();\n if (dist) {\n const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);\n if (sourcePath && fs.existsSync(sourcePath)) {\n await ensureCompiled(sourcePath, rootDir, dist);\n }\n }\n }\n\n const module = (await importWithCacheBust(filePath, isDevOnDemandEnabled())) as Record<\n string,\n unknown\n >;\n\n const handler = module['WS'];\n if (typeof handler !== 'function') {\n throw new Error(`WS export not found in ${filePath}`);\n }\n return (handler as WsHandler)(ctx);\n}\n\n/**\n * 绑定 ws 库原生事件到 faapi 的 WsEventHandlers\n *\n * 注意:wss.handleUpgrade 回调触发时 socket 已经处于 OPEN 状态,\n * 'open' 事件不会再触发,需要直接同步调用 onOpen。\n */\nfunction bindEvents(rawSocket: WebSocket, handlers: WsEventHandlers | void): void {\n if (!handlers) return;\n const ws = wrapWsSocket(rawSocket);\n\n if (handlers.onOpen) {\n // handleUpgrade 回调触发时 socket 已是 OPEN,直接同步调用\n if (rawSocket.readyState === WebSocket.OPEN) {\n handlers.onOpen!(ws);\n } else {\n rawSocket.once('open', () => handlers.onOpen!(ws));\n }\n }\n if (handlers.onMessage) {\n rawSocket.on('message', (data: RawData, isBinary: boolean) => {\n // ws 库 message 事件传 Buffer | ArrayBuffer | Buffer[],归一化为 Buffer;\n // 二进制帧透传 Buffer(utf8 解码不可逆,protobuf/msgpack 等二进制协议会损坏),\n // 文本帧转 utf8 字符串——与 onMessage 类型声明 string | Buffer 一致\n const buf = toWsBuffer(data);\n handlers.onMessage!(ws, isBinary ? buf : buf.toString('utf8'));\n });\n }\n if (handlers.onClose) {\n rawSocket.on('close', (code: number, reason: Buffer) => {\n handlers.onClose!(ws, code, reason.toString('utf8'));\n });\n }\n if (handlers.onError) {\n rawSocket.on('error', (err: Error) => {\n handlers.onError!(ws, err);\n });\n }\n}\n\n/** ws 库 RawData(Buffer | ArrayBuffer | Buffer[])归一化为 Buffer */\nfunction toWsBuffer(data: RawData): Buffer {\n if (Buffer.isBuffer(data)) return data;\n if (Array.isArray(data)) return Buffer.concat(data);\n return Buffer.from(data as ArrayBuffer);\n}\n\n/**\n * 把 Web Response 写回原始 socket(握手被中间件拦截或出错时使用)\n *\n * socket 未升级为 WebSocket 时,按 HTTP 响应格式写回并销毁。\n */\nasync function sendResponseToSocket(socket: Socket, response: Response): Promise<void> {\n const body = await response.text().catch(() => '');\n const statusLine = `HTTP/1.1 ${response.status} ${response.statusText || ''}\\r\\n`;\n const headerLines: string[] = [];\n let hasContentLength = false;\n // 延迟落头(headers-only meta)并入写回的 header 行\n const deferredHeaders = consumePendingMetaHeaders(response);\n for (const [key, value] of Object.entries(deferredHeaders ?? {})) {\n if (key.toLowerCase() === 'content-length') hasContentLength = true;\n headerLines.push(`${key}: ${value}`);\n }\n for (const [key, value] of response.headers) {\n if (key.toLowerCase() === 'content-length') {\n hasContentLength = true;\n }\n headerLines.push(`${key}: ${value}`);\n }\n if (!hasContentLength) {\n headerLines.push(`Content-Length: ${Buffer.byteLength(body)}`);\n }\n socket.write(statusLine + headerLines.join('\\r\\n') + '\\r\\n\\r\\n' + body);\n socket.destroy();\n}\n\nexport interface AttachWsOptions {\n /** HTTP server 实例(已由 createServer 创建) */\n server: Server;\n /** 路由可变引用容器(watch 模式热替换时 routesRef.wsCurrent 被更新) */\n routesRef: RoutesRef;\n /** 项目根目录,用于解析路由文件绝对路径 */\n rootDir: string;\n /** 业务配置,注入到 WsContext.config */\n config?: Record<string, unknown>;\n /** 全局中间件(来自 faapi.config.ts,WS 握手最外层) */\n globalMiddlewares?: FaapiMiddleware[];\n /** 全局注入器(来自 faapi.config.ts,WS handler 可通过 ctx 间接访问全局依赖) */\n globalInjectors?: InjectorMap;\n /**\n * 是否信任反向代理头(X-Forwarded-For),默认 false\n *\n * true 时 `ctx.ip` 取 `x-forwarded-for` 第一个 IP;false(默认)时直取 socket 地址。\n */\n trustedProxy?: boolean;\n /** app 级注册表——经 WsContext 进入 WS 握手链路 */\n registries?: AppRegistries;\n}\n\n/**\n * 在 HTTP server 上挂载 WebSocket 升级处理\n *\n * 创建独立的 WebSocketServer(noServer 模式),监听 server 的 upgrade 事件。\n * 路由匹配成功后,走洋葱中间件链:\n * - 中间件放行(await next):finalHandler 内调用 ws.handleUpgrade 完成协议升级、绑定事件回调\n * - 中间件拦截(返回 Response):把 Response 写回 socket 后销毁,不进行协议升级\n * - 中间件抛错:由 formatErrorResponse/内置兜底生成错误 Response,写回 socket 后销毁\n *\n * 中间件链顺序:全局中间件(外)→ 目录中间件(内)→ finalHandler。\n * watch 模式下 wsRoutes 通过 routesRef 引用更新。\n */\nexport function attachWebSocket(options: AttachWsOptions): WebSocketServer {\n const {\n server,\n routesRef,\n rootDir,\n config,\n globalMiddlewares,\n trustedProxy = false,\n registries,\n } = options;\n\n const wss = new WebSocketServer({ noServer: true });\n\n server.on('upgrade', async (req: IncomingMessage, socket: Socket, head: Buffer) => {\n try {\n await handleUpgradeRequest(req, socket, head);\n } catch (err) {\n // 兜底 catch:upgrade 监听器是 async 函数,路由匹配 / header 构造 / 上下文创建等\n // 抛出的未捕获错误会变成 unhandled rejection(Node 15+ 默认崩掉进程)。\n // HTTP 路径有 catch 兜底,WS 路径保持对称——写回 500 后销毁 socket。\n console.error('[faapi] WS upgrade 处理失败:', err);\n if (!socket.destroyed) {\n try {\n if (!socket.writableEnded) {\n socket.write('HTTP/1.1 500 Internal Server Error\\r\\n\\r\\n');\n }\n } catch {\n // socket 已不可写,忽略\n }\n socket.destroy();\n }\n }\n });\n\n async function handleUpgradeRequest(\n req: IncomingMessage,\n socket: Socket,\n head: Buffer,\n ): Promise<void> {\n const currentWsRoutes = routesRef.wsCurrent;\n const pathname = getPathname(req);\n const match: WsRouteMatch | null = matchWsRoute(currentWsRoutes, pathname);\n\n if (!match) {\n // 路由不匹配:返回 404 并销毁 socket\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n const { route, params } = match;\n const headers = nodeHttpToWebHeaders(req);\n const host = req.headers.host ?? 'localhost';\n const url = `http://${host}${req.url ?? '/'}`;\n\n // 构造 Web Request 与 FaapiContext(与 HTTP 请求一致,供中间件使用)\n const request = new Request(url, { method: 'GET', headers });\n const ctx = createContext(request, params, config, getClientIp(req, trustedProxy), registries);\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n\n // 标记握手是否已完成协议升级(用于判断 socket 是否可写)\n let upgraded = false;\n\n // finalHandler:加载 WS handler + 协议升级 + 绑定事件\n const finalHandler = async (): Promise<Response> => {\n let handlers: WsEventHandlers | void;\n try {\n const absoluteFilePath = path.resolve(rootDir, route.filePath);\n // WsContext 是 FaapiContext 的结构子集,直接复用 ctx 实例\n // 中间件塞入的字段(如 ctx.user)对 WS handler 可见\n handlers = await loadWsHandler(absoluteFilePath, ctx as unknown as WsContext, rootDir);\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.error(`[faapi] WS handler 加载失败 ${route.filePath}: ${reason}`);\n return new Response('Internal Server Error', { status: 500 });\n }\n\n // 协议升级:等 wss.handleUpgrade 回调完成 bindEvents\n await new Promise<void>((resolve, reject) => {\n wss.handleUpgrade(req, socket, head, (rawSocket: WebSocket) => {\n try {\n bindEvents(rawSocket, handlers);\n wss.emit('connection', rawSocket, req);\n upgraded = true;\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n\n // 占位 Response,表示握手成功(socket 已升级,不会被写出)\n return new Response(null, { status: 200 });\n };\n\n // 执行中间件链:全局中间件(外)+ 目录中间件(内)\n let response: Response;\n try {\n // 按需加载中间件:route.middlewares 为 undefined 时从 middlewarePaths 加载(Vite 风格)\n if (route.middlewares === undefined && route.middlewarePaths) {\n // dev 按需模式:先编译中间件源码(含依赖闭包)再 import\n await ensureMiddlewaresCompiled(route.middlewarePaths, rootDir);\n const bundle = await loadMergedMiddlewares(route.middlewarePaths);\n if (bundle) {\n route.middlewares = bundle.middlewares;\n } else {\n route.middlewares = [];\n }\n }\n const dirMiddlewares = route.middlewares ?? [];\n const allMiddlewares =\n globalMiddlewares && globalMiddlewares.length > 0\n ? [...globalMiddlewares, ...dirMiddlewares]\n : dirMiddlewares;\n if (allMiddlewares.length > 0) {\n response = await compose(allMiddlewares, ctx, finalHandler);\n } else {\n response = await finalHandler();\n }\n } catch (err) {\n if (upgraded) {\n // 握手已完成(socket 已被 ws 库接管),中间件 after 阶段抛错无法改写响应\n console.error('[faapi] WS 握手后中间件抛错:', err);\n return;\n }\n // 错误兜底链:内置 formatErrorResponse 兜底\n // 业务方如需自定义错误响应,在全局中间件中 try/catch next() 即可\n response = buildErrorResponse(err);\n }\n\n // 握手成功:socket 已升级,中间件 after 阶段已执行完,无需再写\n if (upgraded) {\n return;\n }\n\n // 中间件拦截或错误:把 Response 写回 socket 后销毁\n await sendResponseToSocket(socket, mergeMeta(response, meta));\n }\n\n return wss;\n}\n","import {\n createServer as createHttpServer,\n type Server,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http';\nimport { createSecureServer as createHttp2SecureServer } from 'node:http2';\nimport { readFileSync } from 'node:fs';\nimport { Readable } from 'node:stream';\nimport path from 'node:path';\nimport type { RouteManifest, RouteMatch, WsRouteManifest, RoutesRef } from '../router/routeTypes';\nimport { matchRoute, findAllowedMethods } from '../router/matchRoute';\nimport { loadRouteModule } from '../loader/loadRouteModule';\nimport { createContextFromUrl } from '../runtime/createContext';\nimport { resolveInputFromUrl, resolveBodyForQueryMethod } from '../runtime/resolveInput';\nimport { invokeHandler, compose, mergeMeta } from '../runtime/invokeHandler';\nimport type { FaapiContext, ResponseMeta } from '../runtime/contextTypes';\nimport { sendNodeResponse } from '../response/sendNodeResponse';\nimport {\n RouteNotFoundError,\n MethodNotAllowedError,\n ValidationError,\n PayloadTooLargeError,\n} from '../errors/httpErrors';\nimport { validateInput } from '../validator/validateInput';\nimport { getInputTypeForMethod, hasBody } from '../runtime/inputType';\nimport { getClientIp } from '../utils/getClientIp';\nimport { cors, type CorsOptions } from '../middleware/cors';\nimport { helmet, type HelmetOptions } from '../middleware/helmet';\nimport { compression, type CompressionOptions } from '../middleware/compression';\nimport { etag, type EtagOptions } from '../middleware/etag';\nimport type { AppRegistries } from '../injection/registries';\nimport { logger as loggerMiddleware, type LoggerOptions } from '../middleware/logger';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport { attachWebSocket } from './handleWsUpgrade';\nimport { nodeHttpToWebHeaders, buildErrorResponse } from './serverUtils';\nimport { getRuntimeSchemaPath } from '../cli/generateSchemaFiles';\nimport {\n ensureSchemaGenerated,\n ensureMiddlewaresCompiled,\n isDevOnDemandEnabled,\n getDevDist,\n} from '../cli/compileOnDemand';\nimport { loadMergedMiddlewares } from '../middleware/loadMiddlewares';\n\n/**\n * 将 Node.js IncomingMessage 转为 Web Request 对象\n *\n * 协议判断:\n * 1. 优先使用 X-Forwarded-Proto 头(反向代理场景)\n * 2. 回退到 http(HTTPS 由外部代理处理)\n */\nconst DEFAULT_BODY_LIMIT = 10 * 1024 * 1024; // 10MB\n\nfunction toWebRequest(\n req: IncomingMessage,\n bodyLimit: number = DEFAULT_BODY_LIMIT,\n requestSignal?: AbortSignal,\n): {\n request: Request;\n /** 已解析的 URL(pathname/searchParams 由调用方复用,免重复 new URL) */\n url: URL;\n} {\n // 协议判断:优先 X-Forwarded-Proto(反向代理),否则 http\n const forwardedProto = req.headers['x-forwarded-proto'];\n const protocol = Array.isArray(forwardedProto)\n ? (forwardedProto[0]?.split(',')[0]?.trim() ?? 'http')\n : (forwardedProto?.split(',')[0]?.trim() ?? 'http');\n const host = req.headers.host ?? 'localhost';\n // 全请求唯一一次 URL 解析——pathname/searchParams 由 ctx/routePipeline 复用\n const url = new URL(req.url ?? '/', `${protocol}://${host}`);\n\n const headers = nodeHttpToWebHeaders(req);\n\n const method = req.method ?? 'GET';\n\n // GET/HEAD 不应该有 body\n if (method === 'GET' || method === 'HEAD') {\n return {\n request: new Request(url.toString(), { method, headers, signal: requestSignal }),\n url,\n };\n }\n\n // content-length 快速判定:声明长度超限直接抛 PayloadTooLargeError,\n // 免去流包装 + 逐 chunk 读取(chunked 无此头,仍走 limitStreamSize 流式限流)\n const contentLength = req.headers['content-length'];\n if (contentLength !== undefined) {\n const declared = Number(Array.isArray(contentLength) ? contentLength[0] : contentLength);\n if (Number.isFinite(declared) && declared > bodyLimit) {\n throw new PayloadTooLargeError(bodyLimit);\n }\n }\n\n // 将 Node.js IncomingMessage 转为 Web ReadableStream\n // 并限制请求体大小(防止 DoS)\n const stream = Readable.toWeb(req) as ReadableStream<Uint8Array>;\n const limitedStream = limitStreamSize(stream, bodyLimit);\n return {\n request: new Request(url.toString(), {\n method,\n headers,\n body: limitedStream,\n duplex: 'half',\n signal: requestSignal,\n } as RequestInit),\n url,\n };\n}\n\n/**\n * 限制 ReadableStream 的总字节数,超过限制时通过 controller.error 抛 PayloadTooLargeError\n *\n * 错误传播路径:controller.error → Request body 读取方(resolveInput)抛 →\n * handleRequest catch → formatErrorResponse(命中 PayloadTooLargeError 分支)→ 413 响应\n *\n * 健壮性处理:\n * - reader.read() 抛错(客户端断开等)→ controller.error + releaseLock,避免泄漏\n * - 超限或异常后释放 reader lock,避免悬挂引用\n * - 取消时同步 cancel 上游 reader\n */\nfunction limitStreamSize(\n stream: ReadableStream<Uint8Array>,\n maxSize: number,\n): ReadableStream<Uint8Array> {\n let totalSize = 0;\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n let errored = false;\n\n const releaseReader = (): void => {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // 锁已释放或 reader 已 closed,忽略\n }\n reader = undefined;\n }\n };\n\n const failStream = (\n controller: ReadableStreamDefaultController<Uint8Array>,\n err: unknown,\n ): void => {\n if (errored) return;\n errored = true;\n controller.error(err instanceof Error ? err : new Error(String(err)));\n releaseReader();\n };\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n if (!reader) reader = stream.getReader();\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n releaseReader();\n return;\n }\n totalSize += value.byteLength;\n if (totalSize > maxSize) {\n // error 让流进入 errored 状态,下游 read() 会 reject\n failStream(controller, new PayloadTooLargeError(maxSize));\n return;\n }\n controller.enqueue(value);\n } catch (err) {\n // reader.read() 抛错(客户端断开、底层流异常等)\n failStream(controller, err);\n }\n },\n cancel(reason) {\n if (reader) {\n try {\n reader.cancel(reason);\n } catch {\n // 忽略上游 cancel 失败\n }\n releaseReader();\n }\n },\n });\n}\n\n/**\n * 查找路径的所有允许方法(用于 405 响应)\n *\n * 实现移至 [matchRoute](../router/matchRoute.ts)(共享路由索引:静态段 O(1) 直查,\n * 仅动态段线性扫描)。404 非热路径,但扫描器/探活探测的 404 高频场景下\n * 索引化仍有收益。\n */\n\nexport interface CreateServerOptions {\n routes: RouteManifest;\n rootDir: string;\n /** 产物输出目录(如 '.faapi' 或 'dist'),用于计算 schema 路径 */\n dist: string;\n cors?: CorsOptions | boolean;\n /** 请求错误钩子(在错误响应生成后调用,用于副作用;不修改已发出的响应) */\n onError?: (error: unknown, ctx: FaapiContext) => Promise<void> | void;\n /** 自定义业务配置(来自 faapi.config.ts,注入到 ctx.config) */\n config?: Record<string, unknown>;\n /** WebSocket 路由清单(空数组则不挂载 WS 支持) */\n wsRoutes?: WsRouteManifest;\n /** 全局中间件(来自 faapi.config.ts,对所有路由生效,最外层) */\n middlewares?: FaapiMiddleware[];\n /** 全局注入器(来自 faapi.config.ts,对所有路由 handler 参数注入生效) */\n injectors?: InjectorMap;\n /** 安全头配置 */\n helmet?: HelmetOptions | boolean;\n /** 响应压缩(gzip/deflate/br 协商),默认关闭,详见 middleware/compression.md */\n compression?: CompressionOptions | boolean;\n /** ETag/304 条件请求协商,默认关闭,详见 middleware/etag.md */\n etag?: EtagOptions | boolean;\n /** app 级注册表(tool/agent/skill/agentHandle)——经 FaapiContext 进入请求链路 */\n registries?: AppRegistries;\n /** 请求日志配置,默认启用(与 cors 一致) */\n logger?: LoggerOptions | boolean;\n /** 请求体大小限制(字节) */\n bodyLimit?: number;\n /** HTTP/2 配置,启用时需提供 SSL 证书路径 */\n http2?: Http2Options | boolean;\n /**\n * 是否信任反向代理头(X-Forwarded-For),默认 false\n *\n * true 时 `ctx.ip` 取 `x-forwarded-for` 第一个 IP(nginx/CDN 场景);\n * false(默认)时直取 socket 地址——直连部署下 XFF 可被伪造,安全默认不信任。\n */\n trustedProxy?: boolean;\n}\n\nexport interface Http2Options {\n key?: string;\n cert?: string;\n}\n\n/**\n * 创建 faapi HTTP server\n *\n * @param options 路由清单、根目录\n * @returns Node.js Server 实例\n */\nexport function createServer(options: CreateServerOptions): {\n server: Server;\n routesRef: RoutesRef;\n} {\n const {\n routes,\n rootDir,\n dist,\n cors: corsOption,\n onError,\n config,\n wsRoutes,\n middlewares: globalMiddlewares,\n injectors: globalInjectors,\n helmet: helmetOption,\n compression: compressionOption,\n etag: etagOption,\n registries,\n logger: loggerOption,\n bodyLimit = DEFAULT_BODY_LIMIT,\n http2: http2Option,\n trustedProxy = false,\n } = options;\n\n // 路由可变引用容器(watch 模式热替换时 reloadRoutes 更新 .current/.wsCurrent)\n const routesRef: RoutesRef = { current: routes, wsCurrent: wsRoutes ?? [] };\n\n // Build middleware chain from config options\n const configMiddlewares: FaapiMiddleware[] = [];\n\n // Compression — 显式启用;链最外层,包住完整链路使最终响应被压缩\n if (compressionOption) {\n const compOpts = typeof compressionOption === 'object' ? compressionOption : {};\n configMiddlewares.push(compression(compOpts));\n }\n\n // CORS\n const corsMiddleware: FaapiMiddleware | null =\n corsOption === false\n ? null\n : corsOption === true || corsOption === undefined\n ? cors()\n : cors(corsOption);\n if (corsMiddleware) configMiddlewares.push(corsMiddleware);\n\n // Helmet — enabled only when explicitly configured\n if (helmetOption) {\n const helmOpts = typeof helmetOption === 'object' ? helmetOption : {};\n configMiddlewares.push(helmet(helmOpts));\n }\n\n // Logger — 默认启用(与 cors 一致),false 禁用,LoggerOptions 自定义\n const loggerMiddlewareInst: FaapiMiddleware | null =\n loggerOption === false\n ? null\n : loggerOption === true || loggerOption === undefined\n ? loggerMiddleware()\n : loggerMiddleware(loggerOption);\n if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);\n\n // ETag — 显式启用;位于 compression 内层:先算 ETag/304 再压缩,\n // 弱 ETag 基于未压缩表示计算(304 无 body 时压缩自动跳过)\n if (etagOption) {\n const etagOpts = typeof etagOption === 'object' ? etagOption : {};\n configMiddlewares.push(etag(etagOpts));\n }\n\n // 外层中间件链启动期组装一次(CORS → helmet → logger → 全局),\n // 每请求不再重复 spread 重组数组\n const outerMiddlewares: FaapiMiddleware[] = [...configMiddlewares];\n if (globalMiddlewares && globalMiddlewares.length > 0) {\n outerMiddlewares.push(...globalMiddlewares);\n }\n\n const server = ((): Server => {\n if (http2Option) {\n const h2Opts = typeof http2Option === 'object' ? http2Option : {};\n return createHttp2SecureServer({\n key: h2Opts.key ? readFileSync(h2Opts.key) : undefined,\n cert: h2Opts.cert ? readFileSync(h2Opts.cert) : undefined,\n allowHTTP1: true,\n }) as unknown as Server;\n }\n return createHttpServer();\n })();\n\n server.on('request', (req: IncomingMessage, res: ServerResponse) => {\n // 每次请求读取最新的路由状态(支持 watch 模式热更新)\n const currentRoutes = routesRef.current;\n\n handleRequest(\n currentRoutes,\n rootDir,\n dist,\n req,\n res,\n outerMiddlewares,\n onError,\n config,\n globalInjectors,\n bodyLimit,\n trustedProxy,\n registries,\n ).catch(() => {\n res.statusCode = 500;\n res.end();\n });\n });\n\n // 挂载 WebSocket 升级处理(无条件挂载:upgrade 处理器内部对无匹配路由返回 404,\n // 空清单成本一次函数调用。若按初始清单条件挂载,dev watch 中新增第一个 WS 路由后\n // upgrade 监听器不会补挂,WS 路由永远 404)\n attachWebSocket({\n server,\n routesRef,\n rootDir,\n config,\n globalMiddlewares,\n trustedProxy,\n registries,\n });\n\n return { server, routesRef };\n}\n\n/**\n * 准备请求上下文:Node IncomingMessage → Web Request + FaapiContext\n *\n * 提取为独立函数,让 handleRequest 主流程聚焦于路由 + 中间件调度,\n * 便于单测与未来扩展(如自定义 context 字段来源)。\n *\n * URL 全请求只解析一次(toWebRequest 内),pathname/searchParams\n * 由 ctx / routePipeline 共享,避免一次请求重复 new URL 3~4 次。\n */\nfunction prepareRequest(\n req: IncomingMessage,\n config: Record<string, unknown> | undefined,\n bodyLimit: number,\n trustedProxy: boolean,\n registries?: AppRegistries,\n requestSignal?: AbortSignal,\n): {\n request: Request;\n url: URL;\n ctx: FaapiContext;\n meta: ResponseMeta;\n method: string;\n urlPath: string;\n} {\n const { request, url } = toWebRequest(req, bodyLimit, requestSignal);\n const method = request.method.toUpperCase();\n const urlPath = url.pathname;\n const ctx = createContextFromUrl(\n request,\n url,\n {},\n config,\n getClientIp(req, trustedProxy),\n registries,\n );\n const meta = (ctx as FaapiContext & { meta: ResponseMeta }).meta;\n return { request, url, ctx, meta, method, urlPath };\n}\n\n/**\n * 路由匹配——命中返回 MatchResult,未命中抛 RouteNotFoundError / MethodNotAllowedError\n *\n * 抛错语义让调用方 try/catch 即可,无需在主流程里分支处理 null。\n */\nfunction resolveRouteOrThrow(routes: RouteManifest, method: string, urlPath: string): RouteMatch {\n const match = matchRoute(routes, method, urlPath);\n if (match) return match;\n\n // 检查是否有其他方法匹配该路径 → 405\n const allowedMethods = findAllowedMethods(routes, urlPath);\n if (allowedMethods.length > 0) {\n throw new MethodNotAllowedError(method, urlPath, allowedMethods);\n }\n\n // 路由未匹配 → 404\n throw new RouteNotFoundError(urlPath);\n}\n\n/**\n * 路由执行管线:作为外层中间件链的 finalHandler\n *\n * 包含:路由匹配 → handler.js 加载 → 参数解析 → zod.js 按需生成 + 校验 →\n * 中间件按需加载 → 注入器合并 → handler 调用。\n *\n * 错误抛出由外层 handleRequest 的 try/catch 接管,经 formatErrorResponse 转换为响应。\n */\n// 路由静态派生路径缓存(绝对路径 / schema 路径对固定 route 恒定,免每请求字符串运算)。\n// WeakMap 按 route 对象弱键——reloadRoutes 整体替换清单后旧对象可 GC,无需手动失效\nconst routePathCache = new WeakMap<\n RouteManifest[number],\n { absFilePath: string; schemaPath: string }\n>();\n\nfunction getRoutePaths(\n route: RouteManifest[number],\n rootDir: string,\n dist: string,\n): { absFilePath: string; schemaPath: string } {\n let cached = routePathCache.get(route);\n if (!cached) {\n cached = {\n absFilePath: path.resolve(rootDir, route.filePath),\n schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir),\n };\n routePathCache.set(route, cached);\n }\n return cached;\n}\n\nfunction createRoutePipeline(opts: {\n routes: RouteManifest;\n method: string;\n urlPath: string;\n url: URL;\n ctx: FaapiContext;\n request: Request;\n rootDir: string;\n dist: string;\n globalInjectors: InjectorMap | undefined;\n}): () => Promise<Response> {\n const { routes, method, urlPath, url, ctx, request, rootDir, dist, globalInjectors } = opts;\n return async () => {\n // 1. 路由匹配(未命中抛 RouteNotFound / MethodNotAllowed)\n const match = resolveRouteOrThrow(routes, method, urlPath);\n ctx.params = match.params;\n const { route } = match;\n const { absFilePath, schemaPath } = getRoutePaths(route, rootDir, dist);\n\n // 2. 加载 handler.js(dev 按需编译 + import,prod 直接 import)\n const routeModule = await loadRouteModule(absFilePath, route.method, rootDir);\n\n // 3. 参数解析(query / body / form / files 等)——复用已解析的 URL\n const input = await resolveInputFromUrl(route.method, request, url);\n\n // 4. schema 校验(运行时按 route.filePath 计算 zod.js 路径 + safeParse)\n const inputType = getInputTypeForMethod(route.method);\n // schemaPath 已在 getRoutePaths 中缓存\n\n // Dev 按需模式:zod.js 不存在或 stale 时触发按需生成\n if (isDevOnDemandEnabled()) {\n const devDist = getDevDist();\n if (devDist) {\n await ensureSchemaGenerated(schemaPath, route.filePath, routes, rootDir, dist);\n }\n }\n\n const result = await validateInput(schemaPath, route.method, inputType, input);\n if (!result.valid) {\n throw new ValidationError('参数校验失败', result.issues);\n }\n // body 计算与主输入分流:\n // - POST/PUT/PATCH:主输入就是 body,用校验后的值\n // - DELETE:主输入是 query(校验 DELETEQuery),body 单独解析注入——\n // 若把校验后的 query 当 body 传入,handler 声明 body 会静默拿到 query;\n // 同时请求体流不被消费,keep-alive 连接无法复用\n // - GET/HEAD:无 body\n const body =\n inputType === 'query' && hasBody(route.method)\n ? await resolveBodyForQueryMethod(request)\n : hasBody(route.method)\n ? result.data\n : undefined;\n\n // 5. 中间件按需加载(Vite 风格):route.middlewares 为 undefined 时从 middlewarePaths 加载\n // 首次请求加载后缓存到 route 上,后续请求直接复用\n if (route.middlewares === undefined && route.injectors === undefined && route.middlewarePaths) {\n // dev 按需模式:先编译中间件源码(含依赖闭包)再 import\n await ensureMiddlewaresCompiled(route.middlewarePaths, rootDir);\n const bundle = await loadMergedMiddlewares(route.middlewarePaths);\n if (bundle) {\n route.middlewares = bundle.middlewares;\n route.injectors = bundle.injectors;\n } else {\n // 标记为已加载(空中间件),避免重复加载\n route.middlewares = [];\n route.injectors = {};\n }\n }\n\n // 6. 注入器合并:全局注入器为基线,目录注入器覆盖同名\n const mergedInjectors = globalInjectors\n ? { ...globalInjectors, ...route.injectors }\n : route.injectors;\n\n // 7. handler 调用(含目录中间件洋葱模型 + 自动响应包装)\n return await invokeHandler(routeModule.handler, ctx, body, route.middlewares, mergedInjectors);\n };\n}\n\n/**\n * 发送成功响应\n */\nasync function sendSuccessResponse(response: Response, res: ServerResponse): Promise<void> {\n await sendNodeResponse(response, res);\n}\n\n/**\n * 发送错误响应 + 触发 onError 副作用\n *\n * 错误处理兜底链(参考 Fastify 语义):\n * 1. 框架内置 formatErrorResponse 兜底(handler 抛错时)——读 ctx.config.response.fail\n * 自定义包装函数,确保错误格式与 ctx.fail() 主动错误响应一致\n * 2. 内置兜底仍抛错 → 最简 500 JSON 响应\n * 3. 响应发出后 → onError 触发副作用(不修改已发出的响应)\n * 注:业务方如需进一步自定义错误响应,在全局中间件中 try/catch next() 即可\n *\n * `ctx` 可为 undefined——请求准备阶段抛错(如 content-length 超限)时尚未构造 ctx,\n * 此时错误格式用默认 fail 包装,onError 不触发(与原\"裸 500\"路径一致)。\n */\nasync function sendErrorResponse(\n err: unknown,\n meta: ResponseMeta,\n res: ServerResponse,\n onError: ((error: unknown, ctx: FaapiContext) => Promise<void> | void) | undefined,\n ctx: FaapiContext | undefined,\n): Promise<void> {\n await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx?.config), meta), res);\n\n // 响应已发出,触发 onError 副作用(日志/告警/链路追踪),自身抛错被忽略\n if (onError && ctx) {\n try {\n await onError(err, ctx);\n } catch {\n // onError 自身抛错不影响已发出的响应\n }\n }\n}\n\nasync function handleRequest(\n routes: RouteManifest,\n rootDir: string,\n dist: string,\n req: IncomingMessage,\n res: ServerResponse,\n outerMiddlewares: FaapiMiddleware[],\n onError: ((error: unknown, ctx: FaapiContext) => Promise<void> | void) | undefined,\n config: Record<string, unknown> | undefined,\n globalInjectors: InjectorMap | undefined,\n bodyLimit: number,\n trustedProxy: boolean,\n registries?: AppRegistries,\n): Promise<void> {\n // meta/ctx 兜底:请求准备阶段抛错(如 content-length 超限的 413)时尚无 ctx\n let meta: ResponseMeta = { headers: {}, setCookies: [] };\n let ctx: FaapiContext | undefined;\n // 客户端断连信号:每请求一个 AbortController,signal 进入 Request(ctx.request.signal)\n // res 'close' 在响应正常完成(keep-alive)时也会触发,用 writableEnded 区分——\n // 响应未写完连接就断开(客户端提前断连)才 abort,正常完成不误触发\n const abortController = new AbortController();\n res.on('close', () => {\n if (!res.writableEnded) abortController.abort();\n });\n try {\n // 1. 准备请求上下文(toWebRequest + createContext)——URL 全请求只解析一次\n const prepared = prepareRequest(\n req,\n config,\n bodyLimit,\n trustedProxy,\n registries,\n abortController.signal,\n );\n ctx = prepared.ctx;\n meta = prepared.meta;\n const { request, url, method, urlPath } = prepared;\n\n // 2. 创建路由执行管线(路由匹配 + 校验 + 中间件加载 + handler 调用)\n const routePipeline = createRoutePipeline({\n routes,\n method,\n urlPath,\n url,\n ctx,\n request,\n rootDir,\n dist,\n globalInjectors,\n });\n\n // 3. 执行外层中间件链(CORS → helmet → logger → 全局 → routePipeline,\n // 数组已在 createServer 启动期组装,此处仅按需 compose)\n const response =\n outerMiddlewares.length > 0\n ? await compose(outerMiddlewares, ctx, routePipeline)\n : await routePipeline();\n // 4. 发送响应\n await sendSuccessResponse(response, res);\n } catch (err: unknown) {\n // 客户端已断开或响应已完成:网络中断/连接销毁不是服务端错误,\n // 不向已销毁的连接写 500(写入无效),也不触发 onError 误报\n if (res.destroyed || res.writableEnded) return;\n await sendErrorResponse(err, meta, res, onError, ctx);\n }\n}\n","import type { Server } from 'node:http';\nimport type { RouteManifest, WsRouteManifest } from '../router/routeTypes';\nimport type { CorsOptions } from '../middleware/cors';\nimport type { FaapiContext } from '../runtime/contextTypes';\nimport type { FaapiMiddleware } from '../middleware/middlewareTypes';\nimport type { InjectorMap } from '../middleware/injectorTypes';\nimport type { RequestHandler, UpgradeHandler } from '../config/pluginTypes';\nimport { createServer } from './createServer';\n\nexport interface StartOptions {\n port: number;\n routes: RouteManifest;\n rootDir: string;\n /** 产物输出目录(如 '.faapi' 或 'dist'),用于计算 schema 路径 */\n dist: string;\n cors?: CorsOptions | boolean;\n /** 请求错误钩子(在错误响应生成后调用,用于副作用,不修改已发出的响应) */\n onError?: (error: unknown, ctx: FaapiContext) => Promise<void> | void;\n /** 自定义业务配置(来自 faapi.config.ts,注入到 ctx.config) */\n config?: Record<string, unknown>;\n /** WebSocket 路由清单 */\n wsRoutes?: WsRouteManifest;\n /** 全局中间件(来自 faapi.config.ts,对所有路由生效,最外层) */\n middlewares?: FaapiMiddleware[];\n /** 全局注入器(来自 faapi.config.ts,对所有路由 handler 参数注入生效) */\n injectors?: InjectorMap;\n /**\n * server.listen 之前的钩子(用于加载插件、应用 handler 包装等)\n *\n * 钩子在 server 创建后、listen 之前调用,可修改 server 的 request/upgrade listener。\n */\n beforeListen?: (server: Server) => Promise<void> | void;\n}\n\n/**\n * 启动 HTTP server 并打印路由表\n *\n * 流程:createServer → beforeListen(可选,加载插件/应用包装)→ listen → 打印路由表\n */\nexport function startServer(options: StartOptions): Promise<Server> {\n const {\n port,\n routes,\n rootDir,\n dist,\n cors,\n onError,\n config,\n wsRoutes,\n middlewares,\n injectors,\n beforeListen,\n } = options;\n const { server } = createServer({\n routes,\n rootDir,\n dist,\n cors,\n onError,\n config,\n wsRoutes,\n middlewares,\n injectors,\n });\n\n return (async () => {\n // beforeListen 钩子:加载插件、应用 handler 包装等\n if (beforeListen) {\n await beforeListen(server);\n }\n\n return new Promise<Server>((resolve) => {\n server.listen(port, () => {\n const address = server.address();\n const actualPort = typeof address === 'object' && address !== null ? address.port : port;\n\n console.log('faapi dev server started');\n console.log(`- Local: http://localhost:${actualPort}`);\n console.log('- Loaded routes:');\n\n for (const route of routes) {\n const method = route.method.padEnd(6);\n console.log(` ${method}${route.urlPath} ${route.filePath}`);\n }\n\n if (wsRoutes && wsRoutes.length > 0) {\n console.log('- WebSocket routes:');\n for (const route of wsRoutes) {\n console.log(` WS ${route.urlPath} ${route.filePath}`);\n }\n }\n\n resolve(server);\n });\n });\n })();\n}\n\n/**\n * 应用 handler / upgrade 包装器到 server\n *\n * 在 server.listen 之前调用,替换 server 的 request/upgrade listener。\n * 包装器按数组顺序嵌套:finalHandler = wrap1(wrap2(originalHandler))。\n *\n * @param server 目标 server(未 listen)\n * @param handlerWrappers HTTP handler 包装器数组\n * @param upgradeWrappers WS upgrade handler 包装器数组\n */\nexport function applyPluginWrappers(\n server: Server,\n handlerWrappers: Array<(original: RequestHandler) => RequestHandler>,\n upgradeWrappers: Array<(original: UpgradeHandler | undefined) => UpgradeHandler>,\n): void {\n // 应用 HTTP handler 包装\n if (handlerWrappers.length > 0) {\n const listeners = server.listeners('request');\n const original = listeners[0] as RequestHandler | undefined;\n if (original) {\n server.removeAllListeners('request');\n let handler: RequestHandler = original;\n for (const wrap of handlerWrappers) {\n handler = wrap(handler);\n }\n server.on('request', handler);\n }\n }\n\n // 应用 WS upgrade handler 包装\n if (upgradeWrappers.length > 0) {\n const listeners = server.listeners('upgrade');\n const original = listeners[0] as UpgradeHandler | undefined;\n server.removeAllListeners('upgrade');\n let upgrade: UpgradeHandler | undefined = original;\n for (const wrap of upgradeWrappers) {\n upgrade = wrap(upgrade);\n }\n if (upgrade) {\n server.on('upgrade', upgrade);\n }\n }\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport type { FaapiPlugin, PluginDeclaration } from '../config/pluginTypes';\nimport type { RequestHandler, UpgradeHandler } from '../config/pluginTypes';\nimport { ensureCompiled, isDevOnDemandEnabled } from './compileOnDemand';\nimport { compileProjectModules } from './compileConfig';\n\n/** loadPlugins 的输入上下文(不含 wrap 能力,由 loadPlugins 内部注入) */\ntype LoadPluginsContext = Omit<\n import('../config/pluginTypes').PluginContext,\n 'wrapHandler' | 'wrapUpgradeHandler'\n>;\n\n/** loadPlugins 返回的包装器收集结果 */\nexport interface PluginLoadResult {\n /** 插件注册的 HTTP handler 包装器(按注册顺序) */\n handlerWrappers: Array<(original: RequestHandler) => RequestHandler>;\n /** 插件注册的 WS upgrade handler 包装器(按注册顺序) */\n upgradeWrappers: Array<(original: UpgradeHandler | undefined) => UpgradeHandler>;\n /** 加载失败的插件(specifier + 原因)——调用方据此汇总报告或 fail-fast */\n failures: Array<{ specifier: string; reason: string }>;\n}\n\n/**\n * 加载并执行插件列表\n *\n * 遍历 config.plugins,动态 import 插件包,调用 setup(ctx)。\n * 插件在 server 创建后、listen 之前按声明顺序执行。\n *\n * 插件可通过 ctx.wrapHandler / ctx.wrapUpgradeHandler 注册包装函数,\n * 本函数收集后返回,由调用方在 listen 之前应用。\n *\n * 错误口径(单一语义,不再静默降级):\n * - 单个插件加载/setup 失败不中断其他插件,但失败明细收集进 `failures` 并\n * 在加载完成后统一 `console.error` 汇总——鉴权/CORS 类插件静默丢失等同裸奔,\n * 必须对业务方可见\n * - 非法声明(resolveDeclaration 失败)与其他插件错误同口径收集,不崩启动\n * - `path` 声明相对项目根目录解析(`pathToFileURL(path.resolve(rootDir, path))`),\n * 此前直接 import 会相对 faapi 包自身产物解析,几乎必然失败且报错指向\n * node_modules 深处\n *\n * 本地 TS 插件(`{ path: './plugins/xxx' }`,`.ts` 源码)的加载路径:\n * - 探测源文件(原样 → `.ts`/`.js` → `/index.ts`/`/index.js`),Node ESM 不做\n * 扩展名补全,无扩展名 file URL 直接 import 必然 `Cannot find module`\n * - 产物存在且不比源码旧 → import 产物(build 固化 / dev 已编译产物复用)\n * - `.ts` 源码 + dev 按需模式 → esbuild 逐文件编译到产物目录后 import 产物\n * (src 内插件走 ensureCompiled 打平产物与 routes 同一运行时对象;src 外插件\n * 走 compileProjectModules,含依赖闭包与 specifier 重写——插件内部\n * `import '../src/xxx'` 无扩展名由此生效)\n * - `.ts` 源码 + 非按需模式(prod / 编程式)且产物 stale → 明确报错指引 `faapi build`,\n * 不静默使用旧产物(与 handler 的 prod 语义一致:产物固化,import 失败即报错)\n *\n * @param declarations 插件声明列表\n * @param ctx 插件上下文(不含 wrap 能力,由本函数注入)\n * @param rootDir 项目根目录(`path` 声明解析基准)\n * @param dist 产物目录(dev 为 `.faapi`,prod 为 `dist`——本地 TS 插件按需编译/产物复用基准)\n * @returns 包装器收集结果 + 失败清单\n */\nexport async function loadPlugins(\n declarations: PluginDeclaration[] | undefined,\n ctx: LoadPluginsContext,\n rootDir?: string,\n dist?: string,\n): Promise<PluginLoadResult> {\n const handlerWrappers: Array<(original: RequestHandler) => RequestHandler> = [];\n const upgradeWrappers: Array<(original: UpgradeHandler | undefined) => UpgradeHandler> = [];\n const failures: Array<{ specifier: string; reason: string }> = [];\n\n if (!declarations || declarations.length === 0) {\n return { handlerWrappers, upgradeWrappers, failures };\n }\n\n // 注入 wrap 能力到 ctx\n const fullCtx = {\n ...ctx,\n wrapHandler: (fn: (original: RequestHandler) => RequestHandler) => {\n handlerWrappers.push(fn);\n },\n wrapUpgradeHandler: (fn: (original: UpgradeHandler | undefined) => UpgradeHandler) => {\n upgradeWrappers.push(fn);\n },\n };\n\n const loaded = new Set<string>();\n\n for (const decl of declarations) {\n // 非法声明与其他插件错误同口径:收集进 failures,不崩启动\n let specifier: string;\n let options: unknown;\n let enable: boolean | undefined;\n try {\n ({ specifier, options, enable } = resolveDeclaration(decl));\n } catch (err) {\n failures.push({\n specifier: JSON.stringify(decl),\n reason: err instanceof Error ? err.message : String(err),\n });\n continue;\n }\n\n // enable 检查\n if (enable === false) continue;\n\n // 去重\n if (loaded.has(specifier)) {\n console.warn(`! Plugin already loaded: ${specifier}, skipping`);\n continue;\n }\n loaded.add(specifier);\n\n try {\n const mod = await importPluginModule(specifier, rootDir ?? process.cwd(), dist);\n const plugin: FaapiPlugin = mod.default ?? (mod as unknown as FaapiPlugin);\n\n if (typeof plugin.setup !== 'function') {\n failures.push({ specifier, reason: 'plugin has no setup function' });\n continue;\n }\n\n await plugin.setup({ ...fullCtx, options });\n console.log(`- Plugin loaded: ${plugin.name ?? specifier}`);\n } catch (err) {\n failures.push({\n specifier,\n reason: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // 失败汇总:单一出口,业务方启动日志一眼可见(鉴权类插件静默丢失不可接受)\n if (failures.length > 0) {\n console.error(\n `[faapi] ${failures.length} plugin(s) failed to load:\\n` +\n failures.map((f) => ` - ${f.specifier}: ${f.reason}`).join('\\n'),\n );\n }\n\n return { handlerWrappers, upgradeWrappers, failures };\n}\n\n/** TS 源文件后缀(需 esbuild 编译才能保证加载) */\nconst TS_EXTS = /\\.(ts|tsx|mts|cts)$/;\n/** JS 源文件后缀(Node ESM 可直接加载) */\nconst JS_EXTS = /\\.(js|mjs|cjs|jsx)$/;\n\n/**\n * 探测本地插件源文件\n *\n * 探测顺序:specifier 原样(已带扩展名)→ `.ts` → `.js` → `/index.ts` → `/index.js`。\n * Node ESM 对 file URL 不做扩展名补全,无扩展名声明必须由框架探测到实际文件。\n *\n * @param specifier 插件声明路径(相对项目根目录或绝对路径)\n * @param baseDir 项目根目录\n * @returns 源文件绝对路径,或 null(未找到)\n */\nexport function resolveLocalPluginSource(specifier: string, baseDir: string): string | null {\n const base = path.isAbsolute(specifier) ? specifier : path.resolve(baseDir, specifier);\n if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs.existsSync(base)) return base;\n for (const ext of ['.ts', '.js']) {\n if (fs.existsSync(base + ext)) return base + ext;\n }\n for (const indexExt of ['/index.ts', '/index.js']) {\n if (fs.existsSync(base + indexExt)) return base + indexExt;\n }\n return null;\n}\n\n/**\n * 从源文件绝对路径推算产物绝对路径\n *\n * - src 内源文件:打平 src/ 前缀(与 compileDevRoutes/compileBuildRoutes 产物同一,\n * 运行时对象同一,instanceof 跨边界生效)\n * - src 外源文件:保留相对 rootDir 的路径结构(compileProjectModules 以 rootDir 为\n * outbase,产物在 `<dist>/` 下保留结构)\n */\nfunction mirrorProductPath(sourcePath: string, baseDir: string, distDir: string): string {\n let rel = path.relative(baseDir, sourcePath).replace(/\\\\/g, '/');\n if (rel.startsWith('src/')) rel = rel.slice(4);\n return path.resolve(distDir, rel.replace(TS_EXTS, '.js'));\n}\n\n/** 插件模块形态:default 导出优先 */\ntype PluginModule = { default?: FaapiPlugin } & Record<string, unknown>;\n\n/** 产物是否比源码旧(stale) */\nfunction isSourceNewer(sourcePath: string, productPath: string): boolean {\n try {\n return fs.statSync(sourcePath).mtimeMs > fs.statSync(productPath).mtimeMs;\n } catch {\n return true;\n }\n}\n\n/**\n * 加载插件模块\n *\n * - 包名(非相对/绝对路径):原样 import,Node 按包解析\n * - 相对/绝对路径:探测源文件与产物,按「产物 fresh → 源码编译/直载」决策,\n * 详见 loadPlugins 注释的「本地 TS 插件的加载路径」\n *\n * @throws 找不到可加载目标或 prod 模式产物 stale 时抛错(带修复指引),进 failures 汇总\n */\nasync function importPluginModule(\n specifier: string,\n baseDir: string,\n dist: string | undefined,\n): Promise<PluginModule> {\n const isRelative = specifier.startsWith('./') || specifier.startsWith('../');\n if (!isRelative && !path.isAbsolute(specifier)) {\n return import(specifier);\n }\n\n const distDir = dist ? path.resolve(baseDir, dist) : null;\n const sourcePath = resolveLocalPluginSource(specifier, baseDir);\n const productPath =\n sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;\n\n // 产物可用:存在 且(源码不存在——部署机只发产物,或 产物不比源码旧)\n if (\n productPath !== null &&\n fs.existsSync(productPath) &&\n (sourcePath === null || !isSourceNewer(sourcePath, productPath))\n ) {\n return import(pathToFileURL(productPath).href);\n }\n\n // TS 源码:dev 按需编译后 import 产物\n if (sourcePath && TS_EXTS.test(sourcePath)) {\n if (!isDevOnDemandEnabled() || !distDir) {\n throw new Error(\n `Local plugin \"${specifier}\" has no up-to-date build artifact (dist is stale or missing). ` +\n `Run \"faapi build\" first, or use \"faapi dev\" for on-demand compilation.`,\n );\n }\n const relFromRoot = path.relative(baseDir, sourcePath).replace(/\\\\/g, '/');\n if (relFromRoot.startsWith('src/')) {\n // src 内插件:打平产物与 routes 同一(同一运行时对象)\n await ensureCompiled(sourcePath, baseDir, dist!);\n } else {\n // src 外插件:编译入口 + 依赖闭包(含插件 import 的 src 内模块)\n await compileProjectModules([sourcePath], baseDir, dist!);\n }\n if (productPath && fs.existsSync(productPath)) {\n return import(pathToFileURL(productPath).href);\n }\n // 编译已完成但产物仍缺失:落到统一报错(理论不可达,防御性兜底)\n }\n\n // JS 源码:Node 可直接加载,无需产物\n if (sourcePath && JS_EXTS.test(sourcePath)) {\n return import(pathToFileURL(sourcePath).href);\n }\n\n // 找不到可加载目标:给明确修复指引(此前仅报 Cannot find module,无法定位原因)\n const candidates = [\n `${specifier}.ts`,\n `${specifier}/index.ts`,\n `${specifier}.js`,\n `${specifier}/index.js`,\n ].map((c) => (path.isAbsolute(c) ? c : path.join(baseDir, c)));\n throw new Error(\n `Cannot find local plugin \"${specifier}\" (resolved from ${baseDir}). ` +\n `Expected one of:\\n ${candidates.join('\\n ')}\\n` +\n `Create the plugin file (export default { name, setup(ctx) {...} }) or fix the path.`,\n );\n}\n\n/**\n * 解析插件声明为统一格式\n */\nfunction resolveDeclaration(decl: PluginDeclaration): {\n specifier: string;\n options?: unknown;\n enable?: boolean;\n} {\n if (typeof decl === 'string') {\n return { specifier: decl };\n }\n\n if (Array.isArray(decl)) {\n const [specifier, options] = decl;\n return { specifier, options };\n }\n\n if ('package' in decl) {\n return { specifier: decl.package, options: decl.options, enable: decl.enable };\n }\n\n if ('path' in decl) {\n return { specifier: decl.path, options: decl.options, enable: decl.enable };\n }\n\n throw new Error(`Invalid plugin declaration: ${JSON.stringify(decl)}`);\n}\n","import type { Server } from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { PassThrough, Readable } from 'node:stream';\nimport type { RouteManifest, WsRouteManifest, RoutesRef } from '../router/routeTypes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { detectRouteConflicts } from '../router/detectRouteConflicts';\nimport { createServer } from '../server/createServer';\nimport { applyPluginWrappers } from '../server/startServer';\nimport { loadConfig } from '../config/loadConfig';\nimport { hydrateRoutes, type SerializedRouteManifest } from './generateRoutes';\nimport { hydrateTools, type SerializedToolRecord } from './generateToolArtifacts';\nimport { hydrateAgents, type SerializedAgentRecord } from './generateAgentArtifacts';\nimport { loadPlugins } from './loadPlugins';\nimport { importWithCacheBust } from '../utils/importWithCacheBust';\nimport {\n createAppRegistries,\n defaultRegistries,\n type AppRegistries,\n} from '../injection/registries';\nimport type { ToolMetadata } from '../ast/extractToolMetadata';\nimport type { AgentMetadata } from '../ast/extractAgentMetadata';\nimport type { FaapiConfig } from '../config/configTypes';\nimport { ROUTE_PATTERNS } from '../utils/prodPaths';\n\nexport interface InjectOptions {\n method?: string;\n path?: string;\n headers?: Record<string, string>;\n query?: Record<string, string>;\n body?: unknown;\n}\n\nexport interface InjectResponse {\n status: number;\n headers: Headers;\n body: unknown;\n}\n\n/** 默认产物目录(prod 模式,对应 `faapi build` 默认输出到 `dist`) */\nconst DEFAULT_DIST = 'dist';\n/** 默认端口 */\nconst DEFAULT_PORT = 3000;\n/** 路由清单文件名(build/dev 启动时生成,必需产物) */\nconst ROUTES_FILE = 'faapi-routes.js';\n/** tool 清单文件名(build/dev 启动时生成,可选产物——无 tool 的项目不生成) */\nconst TOOLS_FILE = 'faapi-tools.js';\n/** agent 清单文件名(build/dev 启动时生成,可选产物——无 agent 的项目不生成) */\nconst AGENTS_FILE = 'faapi-agents.js';\n/** 路由源码目录(写死为 src,路由 .ts 文件位于 src/api/ 下) */\n\n/**\n * 加载 faapi-tools.js 并水合到 toolRegistry(单例)\n *\n * 与路由清单不同,tool 是可选能力——纯 API 项目可能没有 `faapi-tools.js`,\n * 此时不报错,返回空数组,toolRegistry 保持空。\n *\n * dev 按需模式下 `reloadTools` 也会调用此函数重新水合(`setLoadTimestamp` 已在外层设置)。\n *\n * @returns 水合后的 ToolMetadata[](供调用方日志/调试)\n */\nexport async function loadAndHydrateTools(\n rootDir: string,\n dist: string,\n registries: AppRegistries = defaultRegistries,\n): Promise<ToolMetadata[]> {\n const toolsPath = path.resolve(rootDir, dist, TOOLS_FILE);\n if (!fs.existsSync(toolsPath)) {\n return [];\n }\n const serialized = (await importWithCacheBust(toolsPath)) as unknown as {\n tools: SerializedToolRecord[];\n };\n const hydrated = hydrateTools(serialized.tools ?? []);\n registries.tool.hydrate(hydrated);\n return hydrated;\n}\n\n/**\n * 加载 faapi-agents.js 并水合到 agentRegistry(单例)\n *\n * 与 `loadAndHydrateTools` 对称——agent 是可选能力,纯 API 项目可能没有 `faapi-agents.js`,\n * 此时不报错,返回空数组,agentRegistry 保持空。\n *\n * dev 按需模式下 `reloadAgents` 也会调用此函数重新水合(`setLoadTimestamp` 已在外层设置)。\n *\n * @returns 水合后的 AgentMetadata[](供调用方日志/调试)\n */\nexport async function loadAndHydrateAgents(\n rootDir: string,\n dist: string,\n registries: AppRegistries = defaultRegistries,\n): Promise<AgentMetadata[]> {\n const agentsPath = path.resolve(rootDir, dist, AGENTS_FILE);\n if (!fs.existsSync(agentsPath)) {\n return [];\n }\n const serialized = (await importWithCacheBust(agentsPath)) as unknown as {\n agents: SerializedAgentRecord[];\n };\n const hydrated = hydrateAgents(serialized.agents ?? []);\n registries.agent.hydrate(hydrated);\n return hydrated;\n}\n\n/**\n * 当前 app 单例的 globalThis key\n *\n * 用 `Symbol.for` 创建全局 symbol,确保跨模块实例共享同一个 key——\n * Next.js 16 默认用 Turbopack 作为 `next dev` 的 bundler,Turbopack dev server runtime\n * 与主进程的 Node.js 原生 module cache 是两套独立缓存,用模块级变量无法跨实例共享,\n * 必须借助 `globalThis`。\n *\n * 注:生产模式(`node dist/main` + `next build`)不受此问题影响——`next build` 产物是\n * 普通 JS 文件,运行时通过 Node.js 原生 require 加载,与主进程共享 module cache。\n * 此机制主要解决 dev 模式下 RSC 调用 `getApp()` 的问题,对生产模式无副作用。\n */\nconst APP_INSTANCE_KEY = Symbol.for('faapi.app.instance');\n\n/**\n * 读取当前 app 单例(从 globalThis 取,跨模块实例共享)\n *\n * 注意:单例仅指向\"最近一次创建且未关闭的 app\"。测试场景下创建多个临时 app 时,\n * 单例会被覆盖,但 close 时只有当单例仍指向当前 app 才置 null,避免被后续 app 误清。\n */\nfunction getCurrentApp(): AppBase | null {\n return (globalThis as Record<symbol, AppBase | undefined>)[APP_INSTANCE_KEY] ?? null;\n}\n\n/** 设置/清除当前 app 单例(写入 globalThis,跨模块实例共享) */\nfunction setCurrentApp(app: AppBase | null): void {\n if (app === null) {\n delete (globalThis as Record<symbol, AppBase | undefined>)[APP_INSTANCE_KEY];\n } else {\n (globalThis as Record<symbol, AppBase | undefined>)[APP_INSTANCE_KEY] = app;\n }\n}\n\n/** 默认优雅关闭信号 handler 是否已安装(写入 globalThis,跨模块实例共享) */\nconst SHUTDOWN_INSTALLED_KEY = Symbol.for('faapi.defaultShutdownInstalled');\n\n/**\n * 注册默认优雅关闭信号(SIGTERM/SIGINT,进程级仅注册一次)\n *\n * 收到信号时关闭当前 app 单例(app.close() 内部完成 drain + onClose 钩子 +\n * 注册表清理)后退出。faapi 单进程单 app 设计——单例即唯一运行中的 app;\n * 测试场景多次 listen 不会堆积 process 监听器(globalThis 标记防重装)。\n */\nfunction registerDefaultShutdownHandlers(): void {\n const g = globalThis as Record<symbol, unknown>;\n if (g[SHUTDOWN_INSTALLED_KEY]) return;\n g[SHUTDOWN_INSTALLED_KEY] = true;\n\n const shutdown = (signal: string): void => {\n console.log(`\\n- Received ${signal}, shutting down...`);\n void (async () => {\n const app = getCurrentApp();\n if (app) await app.close();\n process.exit(0);\n })();\n };\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('SIGINT', () => shutdown('SIGINT'));\n}\n\n/**\n * 获取当前 faapi app 单例\n *\n * 用于在无法直接拿到 app 引用的场景(如 Next.js Server Component)中访问 app。\n *\n * 通过 `globalThis` 共享单例,确保 Next.js 16 Turbopack dev server runtime 加载的\n * `@faapi/faapi` 模块实例与主进程 `faapi dev` 设置单例的实例能读到同一个 app 引用。\n *\n * @returns 当前 app 实例\n * @throws 未初始化时抛错(需先调 `createProdApp()` / `createDevApp()`,或 `faapi dev` / `node dist/main` 启动)\n *\n * @example\n * ```ts\n * // Next.js RSC 中调用 faapi API(同进程,跳过 HTTP loopback)\n * import { getApp } from '@faapi/faapi';\n * import { headers } from 'next/headers';\n *\n * const app = getApp();\n * const h = await headers();\n * const res = await app.inject({\n * method: 'GET',\n * path: '/api/user',\n * headers: { cookie: h.get('cookie') ?? '', authorization: h.get('authorization') ?? '' },\n * });\n * const data = res.body; // 已解析\n * ```\n */\nexport function getApp(): AppBase {\n const app = getCurrentApp();\n if (!app) {\n throw new Error(\n '[faapi] No app instance. Call createProdApp() / createDevApp() first, or run `faapi dev` / `node dist/main`.',\n );\n }\n return app;\n}\n\n/** FaapiConfig 的内置 key 集合(排除自定义业务配置) */\nconst FAAPI_CONFIG_KEYS = new Set([\n 'cors',\n 'lifecycle',\n 'middlewares',\n 'injectors',\n 'extendContext',\n 'plugins',\n 'helmet',\n 'compression',\n 'etag',\n 'bodyLimit',\n 'logger',\n 'http2',\n 'trustedProxy',\n 'response',\n]);\n\nfunction isFaapiConfigKey(key: string): boolean {\n return FAAPI_CONFIG_KEYS.has(key);\n}\n\nexport interface CreateAppOptions {\n /** 项目根目录,默认 process.cwd() */\n rootDir?: string;\n /** 产物输出目录(如 dist 或 .faapi),覆盖环境变量 FAAPI_DIST,默认 'dist' */\n dist?: string;\n /** 端口号,也可在 listen() 时传入;默认环境变量 PORT 或 3000 */\n port?: number;\n}\n\n/** 应用基础接口(dev/prod 共用,不含 reloadRoutes) */\nexport interface AppBase {\n /** Node.js Server 实例(listen 后可用,close 后置 null) */\n server: Server | null;\n /** app 级注册表(tool/agent/skill/agentHandle 实例,close 时清理) */\n registries: AppRegistries;\n /** 排序后的路由清单 */\n routes: RouteManifest;\n /** WebSocket 路由清单 */\n wsRoutes: WsRouteManifest;\n /** 项目根目录 */\n rootDir: string;\n /** 启动 HTTP server,打印路由表,执行 onReady 钩子 */\n listen(port?: number): Promise<Server>;\n /** 关闭 server,执行 onClose 钩子 */\n close(): Promise<void>;\n /**\n * 无服务器测试注入\n *\n * 构建一个模拟请求直接走完整请求链路(CORS / helmet / logger / 全局中间件 / 路由匹配 /\n * schema 校验 / 目录中间件 / handler),不绑定端口,返回已解析的 `{ status, headers, body }`。\n *\n * `listen()` 前后均可调用——`listen()` 后调用常用于 Next.js Server Component 等同进程场景\n * (配合 `getApp()` 拿到 app 实例)。\n */\n inject(options?: InjectOptions): Promise<InjectResponse>;\n}\n\n/**\n * 内部上下文(供 dev 扩展 reloadRoutes 使用)\n *\n * prod 模式不使用此上下文——createProdApp 直接返回 AppBase。\n */\nexport interface AppContext {\n /** 项目根目录 */\n rootDir: string;\n /** app 级注册表(tool/agent/skill/agentHandle 实例,随 app 生命周期) */\n registries: AppRegistries;\n /** 产物目录 */\n dist: string;\n /** 扫描 patterns(scanRoutes 用) */\n patterns: string[];\n /** Node.js Server 实例(未 listen) */\n server: Server;\n /** 路由可变引用容器(createServer 闭包和 reloadRoutes 共享) */\n routesRef: RoutesRef;\n /** 配置(原始 FaapiConfig 或 null) */\n config: FaapiConfig | null;\n /**\n * 更新路由引用(app.routes/wsRoutes + routesRef + 闭包变量)\n *\n * dev reloadRoutes 调用此方法把新扫描的路由同步到 app 和 server。\n */\n updateRoutes(routes: RouteManifest, wsRoutes: WsRouteManifest): void;\n}\n\n/**\n * 创建应用基础实例(dev/prod 共用逻辑)\n *\n * 完成:配置加载 → 路由清单水合 → 创建 server → 插件加载。\n * 返回 AppBase(listen/close)+ AppContext(供 dev 扩展 reloadRoutes)。\n *\n * dist 由 `process.env.FAAPI_DIST` 决定:\n * - `faapi dev` 启动时固定设为 `.faapi` → 读 dev 产物\n * - `node <dist>/main` 不设 → 默认 `dist`,读 prod 产物\n *\n * 不负责编译 TypeScript——编译由 `faapi dev`(esbuild → `.faapi/`)和 `faapi build`(→ `dist/`)负责。\n * 不负责生成路由清单——`faapi dev`/`faapi build` 启动时生成 `faapi-routes.js`,createAppBase 直接水合。\n */\nexport async function createAppBase(options?: CreateAppOptions): Promise<{\n app: AppBase;\n ctx: AppContext;\n}> {\n const rootDir = options?.rootDir ?? process.cwd();\n const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;\n\n // 校验产物存在性\n const routesPath = path.resolve(rootDir, dist, ROUTES_FILE);\n if (!fs.existsSync(routesPath)) {\n throw new Error(\n `[faapi] ${dist}/${ROUTES_FILE} 不存在,请先执行 \\`faapi build\\`(或 \\`faapi dev\\`)生成产物。`,\n );\n }\n\n // 加载配置(统一读 <dist>/faapi-config.js)\n const config = await loadConfig(rootDir, dist);\n\n // 水合路由清单(统一路径,无 dev/prod 分支)\n const serialized = (await importWithCacheBust(routesPath)) as unknown as SerializedRouteManifest;\n const hydrated = await hydrateRoutes(serialized);\n let sorted = sortRoutes(hydrated.routes);\n let wsRoutes = hydrated.wsRoutes;\n\n // 检测路由冲突\n const conflicts = detectRouteConflicts(sorted);\n if (conflicts.length > 0) {\n for (const conflict of conflicts) {\n console.warn(`! 路由冲突: ${conflict.method} ${conflict.urlPath}`);\n for (const file of conflict.files) {\n console.warn(` - ${file}`);\n }\n }\n }\n\n // app 级注册表(方案 A 实例化):每个 app 持有独立实例,随 app 创建/销毁,\n // 多 app 同进程互不串台。框架链路(请求注入 / 插件 / lifecycle)只读写此实例\n const registries = createAppRegistries();\n\n // 水合 tool 清单(可选产物——无 tool 的项目跳过,tool 注册表保持空)\n const tools = await loadAndHydrateTools(rootDir, dist, registries);\n\n // 水合 agent 清单(可选产物——无 agent 的项目跳过,agent 注册表保持空)\n const agents = await loadAndHydrateAgents(rootDir, dist, registries);\n\n // 自定义业务配置(排除内置 key)\n const pluginConfig: Record<string, unknown> = config\n ? Object.fromEntries(Object.entries(config).filter(([k]) => !isFaapiConfigKey(k)))\n : {};\n\n // 创建 server(不 listen)\n const { server, routesRef } = createServer({\n routes: sorted,\n rootDir,\n dist,\n cors: config?.cors ?? true,\n onError: config?.lifecycle?.onError,\n config: (config as Record<string, unknown> | null) ?? undefined,\n wsRoutes,\n middlewares: config?.middlewares,\n injectors: config?.injectors,\n helmet: config?.helmet,\n compression: config?.compression,\n etag: config?.etag,\n logger: config?.logger,\n bodyLimit: config?.bodyLimit,\n http2: config?.http2,\n trustedProxy: config?.trustedProxy,\n registries,\n });\n\n // 加载插件 + 应用 handler/upgrade 包装器(dist 供本地 TS 插件按需编译/产物复用)\n const { handlerWrappers, upgradeWrappers } = await loadPlugins(\n config?.plugins,\n {\n rootDir,\n registries,\n routes: sorted,\n getRoutes: () => sorted,\n server,\n config: pluginConfig,\n },\n rootDir,\n dist,\n );\n applyPluginWrappers(server, handlerWrappers, upgradeWrappers);\n\n // 关闭状态标记(避免重复关闭)\n let closed = false;\n\n const app: AppBase = {\n server: null,\n registries,\n routes: sorted,\n wsRoutes,\n rootDir,\n\n async listen(listenPort?: number): Promise<Server> {\n // 端口优先级:listen() 参数 > options.port > 环境变量 PORT > 默认 3000\n const envPort = process.env.PORT ? Number(process.env.PORT) : undefined;\n const actualPort = listenPort ?? options?.port ?? envPort ?? DEFAULT_PORT;\n\n // onBoot 生命周期钩子(listen 前):启动校验 / DB 迁移等\"失败即不该暴露端口\"的逻辑。\n // 此时 server 已创建但未监听、路由/tool/agent 清单已水合、插件已加载。\n // 抛错 → listen() 以原始错误 reject,server.listen 不会被调用,端口不暴露。\n if (config?.lifecycle?.onBoot) {\n try {\n await config.lifecycle.onBoot({ rootDir, routes: sorted, server, registries });\n console.log('- onBoot hook executed');\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[faapi] onBoot hook failed: ${message}`);\n throw err;\n }\n }\n\n return new Promise<Server>((resolve, reject) => {\n // listen 阶段错误(端口占用等)转为 Promise reject,避免未监听 'error'\n // 事件以裸堆栈崩掉进程;成功后解除,运行期错误语义不变\n const onListenError = (err: Error): void => {\n if ((err as NodeJS.ErrnoException).code === 'EADDRINUSE') {\n reject(\n new Error(\n `Port ${actualPort} is already in use. ` +\n `Is another faapi instance running? Change the port via the PORT env var.`,\n ),\n );\n return;\n }\n reject(err);\n };\n server.once('error', onListenError);\n server.listen(actualPort, async () => {\n server.off('error', onListenError);\n const address = server.address();\n const p = typeof address === 'object' && address !== null ? address.port : actualPort;\n\n console.log('faapi server started');\n console.log(`- Local: http://localhost:${p}`);\n console.log('- Loaded routes:');\n for (const route of sorted) {\n console.log(` ${route.method.padEnd(6)}${route.urlPath} ${route.filePath}`);\n }\n if (wsRoutes.length > 0) {\n console.log('- WebSocket routes:');\n for (const route of wsRoutes) {\n console.log(` WS ${route.urlPath} ${route.filePath}`);\n }\n }\n if (tools.length > 0) {\n console.log(`- Loaded ${tools.length} tool(s):`);\n for (const tool of tools) {\n console.log(` ${tool.name} ${tool.filePath}`);\n }\n }\n if (agents.length > 0) {\n console.log(`- Loaded ${agents.length} agent(s):`);\n for (const agent of agents) {\n const exports = agent.hasRun ? 'run' : '-';\n console.log(` ${agent.name} [${exports}] ${agent.filePath}`);\n }\n }\n\n // 注册默认优雅关闭信号(进程级仅注册一次,faapi 单进程单 app 设计)\n registerDefaultShutdownHandlers();\n\n // onReady 生命周期钩子\n if (config?.lifecycle?.onReady) {\n await config.lifecycle.onReady({ rootDir, routes: sorted, server, registries });\n console.log('- onReady hook executed');\n }\n\n app.server = server;\n resolve(server);\n });\n });\n },\n\n async inject(injectOpts?: InjectOptions): Promise<InjectResponse> {\n const {\n method = 'GET',\n path: reqPath = '/',\n headers: reqHeaders = {},\n query,\n body,\n } = injectOpts ?? {};\n\n const queryStr = query\n ? '?' +\n new URLSearchParams(Object.entries(query).map(([k, v]) => [k, String(v)])).toString()\n : '';\n\n return new Promise<InjectResponse>((resolve, reject) => {\n // mockRes 需为 Writable Stream(PassThrough)以支持 sendNodeResponse 的 pipe 路径\n // (handler 返回值被自动包裹后产生 JSON body,走 nodeStream.pipe(res))\n const chunks: Buffer[] = [];\n const mockRes = new PassThrough() as PassThrough & {\n statusCode: number;\n _headers: Record<string, string>;\n setHeader(name: string, value: string): void;\n appendHeader(name: string, value: string): void;\n writeHead(status: number, headers?: Record<string, string>): void;\n };\n mockRes.statusCode = 200;\n mockRes._headers = {};\n mockRes.setHeader = function (name: string, value: string) {\n this._headers[name.toLowerCase()] = value;\n };\n mockRes.appendHeader = function (name: string, value: string) {\n const key = name.toLowerCase();\n const existing = this._headers[key];\n this._headers[key] = existing ? `${existing}, ${value}` : value;\n };\n mockRes.writeHead = function (status: number, headers?: Record<string, string>) {\n this.statusCode = status;\n if (headers) {\n Object.assign(this._headers, headers);\n }\n };\n\n mockRes.on('data', (chunk: Buffer) => chunks.push(chunk));\n mockRes.on('error', reject);\n mockRes.on('finish', () => {\n const body = Buffer.concat(chunks);\n let parsed: unknown;\n try {\n parsed = JSON.parse(body.toString());\n } catch {\n parsed = body.toString();\n }\n resolve({\n status: mockRes.statusCode,\n headers: new Headers(mockRes._headers as Record<string, string>),\n body: parsed,\n });\n });\n\n const listeners = server.listeners('request');\n const handler = listeners[listeners.length - 1];\n if (typeof handler !== 'function') {\n reject(new Error('No request handler found'));\n return;\n }\n\n const mockReq: Readable & {\n method?: string;\n url?: string;\n headers?: Record<string, string | undefined>;\n socket?: { remoteAddress?: string };\n } = Readable.from(body !== undefined ? [Buffer.from(JSON.stringify(body))] : []);\n mockReq.method = method;\n mockReq.url = `${reqPath}${queryStr}`;\n mockReq.headers = {\n ...reqHeaders,\n host: 'localhost',\n 'content-type': body !== undefined ? 'application/json' : undefined,\n };\n mockReq.socket = { remoteAddress: '127.0.0.1' };\n\n handler(\n mockReq as unknown as import('node:http').IncomingMessage,\n mockRes as unknown as import('node:http').ServerResponse,\n );\n });\n },\n\n async close(): Promise<void> {\n if (closed) return;\n closed = true;\n\n const s = server as unknown as {\n closeIdleConnections?: () => void;\n closeAllConnections?: () => void;\n };\n\n // 停止接受新连接 + 断开空闲 keep-alive 连接;在途请求继续执行直至完成(drain)。\n // 此前 closeAllConnections 在等待之前调用,会直接掐断在途请求(硬关,非优雅停机)\n s.closeIdleConnections?.();\n\n if (config?.lifecycle?.onClose) {\n await config.lifecycle.onClose({ rootDir, routes: sorted, server, registries });\n }\n\n // 注册表(方案 A 实例化):清理的是 app 自己的实例——多 app 同进程\n // 天然隔离,无需所有权守卫;全局默认实例不被 app 生命周期触碰\n registries.tool.clear();\n registries.agent.clear();\n registries.skill.clear();\n registries.agentHandle.clear();\n\n // server 未 listen 时直接清理状态(避免 ERR_SERVER_NOT_RUNNING 错误)\n if (!server.listening) {\n app.server = null;\n // 清理单例(仅当单例仍指向当前 app 时,避免被后续 app 误清)\n if (getCurrentApp() === app) setCurrentApp(null);\n return;\n }\n\n const drained = new Promise<void>((resolve) => {\n server.close((err) => {\n if (err) console.error('Error closing server:', err);\n resolve();\n });\n });\n\n // drain:等在途请求完成。SSE/WS 长连接永不主动结束,超时后强制断开\n // (默认 10s,FAAPI_SHUTDOWN_TIMEOUT_MS 环境变量可调)\n const drainTimeoutMs = Number(process.env.FAAPI_SHUTDOWN_TIMEOUT_MS ?? 10_000);\n if (\n typeof s.closeAllConnections === 'function' &&\n Number.isFinite(drainTimeoutMs) &&\n drainTimeoutMs >= 0\n ) {\n const forceClose = new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n s.closeAllConnections?.();\n resolve();\n }, drainTimeoutMs);\n // 停机定时器不应阻止进程自然退出\n timer.unref?.();\n });\n await Promise.race([drained, forceClose]);\n // 强制断开后 close 回调随即触发;短兜底等待收尾完成\n const tail = new Promise<void>((resolve) => setTimeout(resolve, 250).unref?.());\n await Promise.race([drained, tail]);\n } else {\n await drained;\n }\n\n app.server = null;\n // 清理单例(仅当单例仍指向当前 app 时,避免被后续 app 误清)\n if (getCurrentApp() === app) setCurrentApp(null);\n },\n };\n\n // 设置单例(覆盖之前的实例;测试场景下多次创建会覆盖,close 时只清自己)\n // 通过 globalThis 存储,确保 Next.js Turbopack dev server runtime 加载的模块实例也能读到\n setCurrentApp(app);\n\n /** 更新路由引用(app + routesRef + 闭包变量) */\n const ctx: AppContext = {\n rootDir,\n registries,\n dist,\n patterns: ROUTE_PATTERNS,\n server,\n routesRef,\n config,\n updateRoutes(newRoutes: RouteManifest, newWsRoutes: WsRouteManifest): void {\n sorted = newRoutes;\n wsRoutes = newWsRoutes;\n app.routes = newRoutes;\n app.wsRoutes = newWsRoutes;\n routesRef.current = newRoutes;\n routesRef.wsCurrent = newWsRoutes;\n },\n };\n\n return { app, ctx };\n}\n","import type { AppBase, CreateAppOptions } from './createAppCore';\nimport { createAppBase, loadAndHydrateTools, loadAndHydrateAgents } from './createAppCore';\nimport { scanRoutes } from '../router/scanRoutes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { scanTools } from '../tools/scanTools';\nimport { TOOL_PATTERNS } from '../tools/scanTools';\nimport { generateToolArtifacts } from './generateToolArtifacts';\nimport { scanAgents } from '../agents/scanAgents';\nimport { DEFAULT_AGENT_PATTERNS } from '../agents/scanAgents';\nimport { generateAgentArtifacts } from './generateAgentArtifacts';\nimport { invalidateMiddlewareCache } from '../middleware/loadMiddlewares';\nimport { invalidateProgramCache } from '../ast/createProgram';\nimport { invalidateSchemaCache } from '../validator/validateInput';\nimport { setLoadTimestamp } from '../utils/importWithCacheBust';\nimport {\n clearCompiledFiles,\n clearGeneratedSchemas,\n deleteSchemaFiles,\n isDevOnDemandEnabled,\n} from './compileOnDemand';\n\n/** dev 应用接口(AppBase + reloadRoutes/reloadTools/reloadAgents 热替换) */\nexport interface DevApp extends AppBase {\n /** 重新水合路由清单 + 清 schema 缓存 + 更新 server 路由引用(dev 热替换用) */\n reloadRoutes(): Promise<void>;\n /** 重新扫描 tools + 重生成 faapi-tools.js + 清缓存(dev 热替换用) */\n reloadTools(): Promise<void>;\n /** 重新扫描 agents + 重生成 faapi-agents.js + 清缓存(dev 热替换用) */\n reloadAgents(): Promise<void>;\n}\n\n/**\n * dev 模式应用启动 API\n *\n * 在 createAppBase(共享逻辑)基础上增加 `reloadRoutes` 热替换能力,供 `faapi dev` watcher 调用。\n *\n * 与 createProdApp 的区别:\n * - dev:含 reloadRoutes(重新扫描路由 + 重新生成 schema + 清缓存 + 更新 server 路由引用)\n * - prod:精简,无 reloadRoutes(产物已固化,运行时不重建)\n *\n * 由 `devCommand` 直接调用,devCommand 持有 app 引用并传给 watcher。\n *\n * @example\n * ```ts\n * // devCommand 内部\n * const app = await createDevApp();\n * await app.listen();\n * startWatcher({ rootDir, app, devDist });\n * ```\n */\nexport async function createDevApp(options?: CreateAppOptions): Promise<DevApp> {\n const { app, ctx } = await createAppBase(options);\n\n const devApp = app as DevApp;\n\n devApp.reloadRoutes = async (): Promise<void> => {\n // 更新模块加载时间戳(ESM import 绕过缓存)\n setLoadTimestamp(Date.now());\n // 清理缓存(中间件/schema 已被 watcher 重新生成)\n invalidateMiddlewareCache();\n invalidateProgramCache();\n invalidateSchemaCache();\n // 清按需编译缓存(让被修改的 handler.js 重新编译)\n clearCompiledFiles();\n // 重新扫描路由\n // 不走 faapi-routes.js 重新 import——ESM 模块缓存难以可靠绕过,直接 scanRoutes 更稳定\n const reScanned = await scanRoutes(ctx.rootDir, ctx.patterns, ctx.dist);\n const sorted = sortRoutes(reScanned.routes);\n\n if (isDevOnDemandEnabled()) {\n // 按需模式:删除 stale zod.js(类型引用变化等),后台批量预生成 + 请求兜底\n // 不在 reload 路径上 await 生成——保持热替换轻快\n await deleteSchemaFiles(sorted, ctx.rootDir, ctx.dist);\n clearGeneratedSchemas();\n // 后台预生成全量 zod.js:此前只删不生成,每次保存后所有路由的首个请求都要\n // 在请求路径上同步付全项目 Program 创建 + schema 生成的代价(p99 尖刺)。\n // 预生成不阻塞 reload 与请求;请求路径的 ensureSchemaGenerated(mtime 缓存 +\n // in-flight mutex + 原子写)与之自然协同——已生成命中 mtime 跳过,并发生成\n // 各自原子落盘内容一致\n void (async (): Promise<void> => {\n try {\n const { generateSchemaFiles } = await import('./generateSchemaFiles');\n await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);\n } catch (err) {\n // 后台失败不崩进程——请求路径的按需生成仍会兜底\n console.error('[faapi] Background schema regeneration failed:', err);\n }\n })();\n } else {\n // 非按需模式(兼容旧路径):全量重新生成 zod.js\n const { generateSchemaFiles } = await import('./generateSchemaFiles');\n await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);\n }\n\n // 更新 app 和 server 路由引用\n ctx.updateRoutes(sorted, reScanned.wsRoutes);\n };\n\n devApp.reloadTools = async (): Promise<void> => {\n // 更新模块加载时间戳(ESM import 绕过缓存,让 faapi-tools.js 重新读取)\n setLoadTimestamp(Date.now());\n // 清 Program 缓存(tool 源码可能变化,AST 需重新分析)\n invalidateProgramCache();\n // 重新扫描 tools(零 import,仅读源码 + 正则提取函数名)\n const tools = await scanTools(ctx.rootDir, TOOL_PATTERNS);\n // 重生成 faapi-tools.js(含 AST 增强:description / inputTypeName)\n // 按需模式跳过 zod.js 生成——首次请求时按需生成(与 reloadRoutes 的策略一致)\n await generateToolArtifacts(tools, ctx.rootDir, ctx.dist, {\n skipSchema: isDevOnDemandEnabled(),\n });\n // 重新水合 faapi-tools.js 到 toolRegistry(reload 后需更新注册表)\n await loadAndHydrateTools(ctx.rootDir, ctx.dist, ctx.registries);\n };\n\n devApp.reloadAgents = async (): Promise<void> => {\n // 更新模块加载时间戳(ESM import 绕过缓存,让 faapi-agents.js 重新读取)\n setLoadTimestamp(Date.now());\n // 清 Program 缓存(agent 源码可能变化,AST 需重新分析)\n invalidateProgramCache();\n // 重新扫描 agents(零 import,仅读源码 + 正则检测 config/run 导出)\n const agents = await scanAgents(ctx.rootDir, DEFAULT_AGENT_PATTERNS);\n // 重生成 faapi-agents.js(含 AST 增强:description / @agent 覆盖 / config 块字段)\n // agent 不生成 zod.js(无输入参数),无 skipSchema 选项\n await generateAgentArtifacts(agents, ctx.rootDir, ctx.dist);\n // 重新水合 faapi-agents.js 到 agentRegistry(reload 后需更新注册表)\n await loadAndHydrateAgents(ctx.rootDir, ctx.dist, ctx.registries);\n };\n\n return devApp;\n}\n","import path from 'node:path';\nimport { compileConfig } from './compileConfig';\nimport { serializeRoutes, writeRoutesModule } from './generateRoutes';\nimport { scanRoutes } from '../router/scanRoutes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { scanTools } from '../tools/scanTools';\nimport { TOOL_PATTERNS } from '../tools/scanTools';\nimport { generateToolArtifacts } from './generateToolArtifacts';\nimport { scanAgents } from '../agents/scanAgents';\nimport { DEFAULT_AGENT_PATTERNS } from '../agents/scanAgents';\nimport { generateAgentArtifacts } from './generateAgentArtifacts';\nimport { loadConfig } from '../config/loadConfig';\nimport { loadEnv } from './loadEnv';\nimport { startWatcher } from './watcher';\nimport { createDevApp } from './createDevApp';\nimport { setDevOnDemandEnabled, setDevDist } from './compileOnDemand';\nimport { ROUTE_PATTERNS } from '../utils/prodPaths';\n\n/** dev 模式产物目录(固定为 .faapi,不可修改) */\nconst DEV_DIST = '.faapi';\n/** 路由清单文件名 */\nconst ROUTES_FILE = 'faapi-routes.js';\n/** 路由源码目录(写死为 src,路由 .ts 文件位于 src/api/ 下) */\n\n/** dev 命令选项(来自 CLI 参数) */\nexport interface DevCommandOptions {\n port?: number;\n}\n\n/**\n * `faapi dev` 命令:编译配置 → 生成路由清单 → 启动 dev 应用 → 启动 watcher\n *\n * **Vite 风格按需编译**(阶段 2+3):启动时只编译配置和路由清单,**不全量编译 handler.js / zod.js**。\n * - handler.js 在首次请求时由 `loadRouteModule` / `loadWsHandler` 触发按需编译\n * - zod.js 在首次请求时由 `ensureSchemaGenerated` 触发按需生成\n *\n * 与 `faapi build`(产线构建)为两套独立代码路径,仅共享工具级函数。\n *\n * 框架元信息通过 CLI 选项或环境变量传入(不放在 faapi.config.ts 内):\n * - `--port` / `PORT`:服务端口,默认 3000\n * - `FAAPI_DIST`:dev 模式由 devCommand 固定设为 `.faapi`\n *\n * 产物(与 `faapi build` 一致,仅目录不同:dev 用 `.faapi/`,build 用 `dist/`):\n * 1. `.faapi/faapi-config.js` — 配置合并产物(compileConfig 生成)\n * 2. `.faapi/faapi-routes.js` — 路由清单(serializeRoutes 生成)\n * 3. `.faapi/` 下各 handler 目录的 `zod.js` — schema 模块(**按需生成**,首次请求触发)\n * 4. `.faapi/` 下的 handler `*.js` — **按需编译**(首次请求时触发,非启动时全量)\n *\n * 流程:\n * 1. 兜底 NODE_ENV(未显式设置时)+ 加载 .env 系列文件到 process.env(loadEnv)\n * 2. 设置 dev 环境标记 + `FAAPI_DIST=.faapi` + 启用按需编译模式\n * 3. 编译配置产物(compileConfig)—— config 引用的项目模块也由 compileConfig 编译\n * 4. 生成路由清单(scanRoutes 读源码 + 正则提取方法名,零 import)\n * 5. 调用 createDevApp() + listen() 启动 dev 应用(含 reloadRoutes 热替换能力)\n * 6. 启动 watcher(增量编译 + 重生成 config + 调 app.reloadRoutes 热替换)\n *\n * **与旧版(启动全量编译)的差异**:\n * - 旧版:步骤 4 之前调 `compileDevRoutes` + `generateSchemaFiles` 全量编译所有 .ts 和生成 zod.js → 启动慢\n * - 新版:跳过全量编译和 schema 生成,handler.js / zod.js 按需生成 → 启动快,首请求有单文件编译延迟(~50ms)\n */\nexport async function devCommand(options?: DevCommandOptions): Promise<void> {\n const rootDir = process.cwd();\n\n // 1. 兜底 NODE_ENV(未显式设置时)+ 加载 .env 系列文件到 process.env\n // loadEnv 读 NODE_ENV 决定加载 .env.{env},需在 loadEnv 之前设置\n if (!process.env.NODE_ENV) process.env.NODE_ENV = 'development';\n loadEnv(rootDir);\n\n // 2. 设置 dist(固定为 .faapi,不可修改)+ 启用按需编译模式\n const devDist = DEV_DIST;\n process.env.FAAPI_DIST = devDist;\n setDevOnDemandEnabled(true);\n setDevDist(devDist);\n console.log('- Development mode (on-demand compile)');\n\n // 3. 编译配置产物(compileConfig 内部编译 config 源 + 引用的项目模块)\n console.log('- Compiling config...');\n await compileConfig({ rootDir, dist: devDist });\n const _config = await loadConfig(rootDir, devDist);\n\n // 4. 生成路由清单 + schema 文件(scanRoutes 不 import,仅读源码 + 正则提取方法名)\n console.log('- Generating route manifest (schema generated on demand)...');\n await generateRouteArtifacts(rootDir, ROUTE_PATTERNS, devDist);\n\n // 5. 生成 tool 清单(scanTools 不 import,仅读源码 + 正则提取函数名)\n // 按需模式跳过 zod.js 生成——首次请求时按需生成(与路由 schema 策略一致)\n console.log('- Generating tool manifest...');\n await generateToolArtifactsForDev(rootDir, devDist);\n\n // 6. 生成 agent 清单(scanAgents 不 import,仅读源码 + 正则检测 config/run)\n // agent 不生成 zod.js(无输入参数),无 skipSchema 选项\n // 无 agent 文件时 scanAgents 返回空列表,generateAgentArtifacts 写入空清单\n console.log('- Generating agent manifest...');\n await generateAgentArtifactsForDev(rootDir, devDist);\n\n // 7. 启动 dev 应用(createDevApp + listen,含 reloadRoutes/reloadTools/reloadAgents 热替换能力)\n console.log('- Starting dev app...');\n const app = await createDevApp({ rootDir, port: options?.port });\n await app.listen();\n\n // 8. 启动 watcher(文件变化时增量编译 + 重生成 config + 调 app.reloadRoutes/reloadTools/reloadAgents)\n startWatcher({ rootDir, app, devDist });\n}\n\n/**\n * 生成路由产物:faapi-routes.js(仅路由清单,不含 zod.js)\n *\n * zod.js 按需生成:首次请求时由 `ensureSchemaGenerated` 触发(阶段 3)。\n * 与 `faapi build` 不同——build 阶段全量生成 zod.js,dev 阶段按需生成。\n */\nexport async function generateRouteArtifacts(\n rootDir: string,\n patterns: string[],\n dist: string,\n): Promise<void> {\n // 扫描路由(读源码 + 正则提取方法名,零 import)\n const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, dist);\n const sorted = sortRoutes(routes);\n\n // 生成路由清单(zod.js 按需生成,不在启动时全量生成)\n const routesPath = path.resolve(rootDir, dist, ROUTES_FILE);\n const serialized = serializeRoutes(sorted, wsRoutes, rootDir, dist);\n await writeRoutesModule(serialized, routesPath);\n}\n\n/**\n * 生成 tool 产物:faapi-tools.js(仅 tool 清单,不含 zod.js)\n *\n * 与 `generateRouteArtifacts` 对称——dev 按需模式跳过 zod.js 生成,\n * 首次请求时按需生成(tool zod.js 的按需生成由 toolRegistry 在 Phase 1.6+ 接入)。\n * 无 tool 文件时 scanTools 返回空列表,generateToolArtifacts 写入空清单。\n */\nexport async function generateToolArtifactsForDev(rootDir: string, dist: string): Promise<void> {\n const tools = await scanTools(rootDir, TOOL_PATTERNS);\n await generateToolArtifacts(tools, rootDir, dist, { skipSchema: true });\n}\n\n/**\n * 生成 agent 产物:faapi-agents.js(仅 agent 清单,不含 zod.js)\n *\n * 与 `generateToolArtifactsForDev` 对称——agent 不生成 zod.js(无输入参数,\n * config 块字段在 AST 阶段已提取为字面量)。无 agent 文件时 scanAgents 返回空列表,\n * generateAgentArtifacts 写入空清单。\n */\nexport async function generateAgentArtifactsForDev(rootDir: string, dist: string): Promise<void> {\n const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);\n await generateAgentArtifacts(agents, rootDir, dist);\n}\n","import { compileSourceFiles, type CompileResult } from './compileSourceFiles';\n\nexport type { CompileResult };\n\n/**\n * build 编译选项\n */\nexport interface CompileBuildOptions {\n /** 项目根目录 */\n rootDir: string;\n /** 输出目录(build 模式为 `dist`) */\n dist: string;\n /**\n * 增量编译:传入要编译的文件列表(绝对路径)。\n * 不传则全量编译 src 下所有 .ts(排除测试文件和声明文件)。\n */\n files?: string[];\n /** 是否输出 esbuild 日志(build 模式默认静默) */\n logLevel?: 'silent' | 'info';\n}\n\n/**\n * build 模式编译 TypeScript:逐文件编译 + 编译期常量替换 + 死分支删除\n *\n * 共享实现见 [compileSourceFiles](./compileSourceFiles.ts);build 差异仅一点:\n * 启用 `define` + `minifySyntax`,在编译期把 `process.env.NODE_ENV` 替换为\n * `\"production\"` 并删除 `if (false) {...}` 死分支。两者在 `bundle: false` 下均生效\n * (单文件级别优化,不需要跨文件分析)。\n *\n * **为什么不用 bundle 模式**:bundle 模式会把 import 的项目模块 inline 进产物,\n * 导致 `faapi.config.ts` 中的 `instanceof` 对项目自定义错误类失效\n * (config 和 routes 各自打包出独立的项目类副本)。\n * 逐文件编译保证每个源文件对应唯一一份产物,config 和 routes 共享同一运行时对象。\n *\n * **tree shaking 不可用**:`bundle: false` 不分析跨文件引用图,未引用的 export 不会被删除。\n * 这恰好符合设计意图——保留所有 export,让 config 和 routes 共享同一运行时对象。\n *\n * 框架采用零入口设计——用户无需编写 main.ts,dev/prod 启动由 CLI 内部编排。\n *\n * @example\n * await compileBuildRoutes({ rootDir, dist: 'dist' });\n */\nexport async function compileBuildRoutes(options: CompileBuildOptions): Promise<CompileResult> {\n return compileSourceFiles({ ...options, production: true });\n}\n","import { scanRoutes } from '../router/scanRoutes';\nimport { sortRoutes } from '../router/sortRoutes';\nimport { detectRouteConflicts } from '../router/detectRouteConflicts';\nimport { scanTools } from '../tools/scanTools';\nimport { TOOL_PATTERNS } from '../tools/scanTools';\nimport { generateToolArtifacts } from './generateToolArtifacts';\nimport { scanAgents } from '../agents/scanAgents';\nimport { DEFAULT_AGENT_PATTERNS } from '../agents/scanAgents';\nimport { generateAgentArtifacts } from './generateAgentArtifacts';\nimport { generateSchemaFiles } from './generateSchemaFiles';\nimport { serializeRoutes, writeRoutesModule } from './generateRoutes';\nimport { compileBuildRoutes } from './compileBuildRoutes';\nimport { ROUTE_PATTERNS } from '../utils/prodPaths';\nimport { compileConfig, compileProjectModules } from './compileConfig';\nimport { loadConfig } from '../config/loadConfig';\nimport { resolveLocalPluginSource } from './loadPlugins';\nimport type { PluginDeclaration } from '../config/pluginTypes';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { isInsideDir, toRealPath } from '../utils/prodPaths';\n\n/** build 模式默认产物目录 */\nconst DEFAULT_DIST = 'dist';\n/** 路由源码目录(写死为 src,路由 .ts 文件位于 src/api/ 下) */\n\n/**\n * 构建命令选项\n */\nexport interface BuildOptions {\n /** 项目根目录,默认 process.cwd() */\n rootDir?: string;\n /** 产物输出目录,默认 dist */\n dist?: string;\n}\n\n/**\n * 执行构建命令\n *\n * `--dist` 直接作为产物输出目录(默认 `dist`),与 Next.js `distDir` 语义一致。\n * 运行时只加载 .js 产物,不依赖 tsx。\n *\n * 应用行为配置从 faapi.config.ts 读取。\n *\n * 框架采用零入口设计——用户无需编写 main.ts,build 阶段自动生成 `<dist>/main.js` 启动入口,\n * 运行时 `node <dist>/main` 直接启动服务,无需 `faapi start` 命令。\n *\n * 端口不通过 build 选项指定——`main.js` 中 `listen()` 无参,运行时由 `PORT` 环境变量\n * 或默认值 3000 决定(与 `next build` 不支持 `--port` 的设计一致)。\n *\n * 流程:\n * 0. 编译配置产物(compileConfig)→ loadConfig 读应用行为配置\n * 1. 编译 TypeScript(逐文件编译,与 dev 一致,打平 src/ 前缀)\n * 2. 重新编译配置文件(确保使用最新源码)\n * 3. 扫描路由(从 <dist> 产物,import .js 拿方法名)\n * 4. 生成 schema 模块(AST 从源码 .ts)\n * 5. 生成路由清单\n * 6. 生成启动入口 <dist>/main.js(import createProdApp + loadEnv + listen)\n *\n * **统一编译模式**:build 与 dev 都采用 `bundle: false` 逐文件编译,差异仅由 `dist` 驱动,\n * 不存在 `if (isDev)` 控制流分支。逐文件编译保证每个源文件对应唯一一份产物,\n * config 和 routes 共享同一运行时对象(`instanceof` 跨边界生效)。\n */\nexport async function buildCommand(options?: BuildOptions): Promise<void> {\n const rootDir = options?.rootDir ?? process.cwd();\n const outdir = options?.dist ?? DEFAULT_DIST;\n\n // 清空输出目录(Vite emptyOutDir 语义):删除路由后 dist 残留旧 handler.js /\n // zod.js / map 文件,体积膨胀且误导排查。防误删保护:outdir 必须严格位于\n // rootDir 内(两侧 realpath 归一化,tmpdir 符号链接不误判)且不等于 rootDir\n const absOut = path.resolve(rootDir, outdir);\n const realRoot = toRealPath(path.resolve(rootDir));\n if (isInsideDir(toRealPath(absOut), realRoot)) {\n fs.rmSync(absOut, { recursive: true, force: true });\n } else {\n console.warn(\n `! Output directory \"${outdir}\" is outside the project root, skipping clean (stale artifacts may remain)`,\n );\n }\n\n // 加载 config\n // build 时无产物,先 compileConfig 生成临时产物到 outdir,再用 loadConfig 读\n await compileConfig({ rootDir, dist: outdir });\n const _config = await loadConfig(rootDir, outdir);\n\n // CJS 项目告警:产物为 ESM(main.js 用 import 语句),缺 type:module 时\n // node dist/main 报 \"Cannot use import statement outside a module\",用户无法关联原因\n const pkgPath = path.resolve(rootDir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { type?: string };\n if (pkg.type !== 'module') {\n console.warn(\n '! package.json is missing \"type\": \"module\" — build output is ESM and `node dist/main` will fail without it',\n );\n }\n } catch {\n // package.json 解析失败不阻断构建\n }\n }\n\n console.log('faapi build started');\n console.log(`- Root: ${rootDir}`);\n console.log(`- Source: src/`);\n console.log(`- Output: ${outdir}`);\n\n // 1. 编译 TypeScript(逐文件编译,与 dev 一致)\n console.log('\\n[1/8] Compiling TypeScript (bundle: false)...');\n const result = await compileBuildRoutes({\n rootDir,\n dist: outdir,\n logLevel: 'silent',\n });\n console.log(` Compiled ${result.compiledFiles.length} file(s)`);\n if (result.compiledFiles.length === 0) {\n console.warn(' ! No source files found, nothing to build');\n return;\n }\n\n // 2. 配置产物已在步骤 0 编译(compileConfig 内部有 mtime 缓存,无源码变化\n // 时重复调用只会命中缓存——此前重复调用还打印 \"Written to\" 撒谎日志)\n\n // 2.5 编译本地 TS 插件(config.plugins 的 path 声明):源文件 + src 外依赖闭包\n // 编译到 `<dist>/` 下(保留相对结构),运行时 loadPlugins 直接 import 产物。\n // 插件引用的 src 内模块已由步骤 1 全量编译,无需重复。\n const localPluginSources = extractLocalPluginSources(_config?.plugins, rootDir);\n if (localPluginSources.length > 0) {\n console.log('\\n[2.5/8] Compiling local plugins...');\n const outside = localPluginSources.filter((p) => {\n const rel = path.relative(rootDir, p).replace(/\\\\/g, '/');\n return !rel.startsWith('src/');\n });\n if (outside.length > 0) {\n await compileProjectModules(outside, rootDir, outdir);\n }\n console.log(` Compiled ${localPluginSources.length} local plugin(s)`);\n }\n\n // 3. 扫描路由(扫描源码 .ts 文件列表,但 import 产物 .js 拿方法名)\n console.log('\\n[3/8] Scanning routes...');\n const { routes, wsRoutes } = await scanRoutes(rootDir, ROUTE_PATTERNS, outdir);\n const sorted = sortRoutes(routes);\n console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);\n\n // 检测路由冲突\n const conflicts = detectRouteConflicts(sorted);\n if (conflicts.length > 0) {\n console.warn('! 检测到路由冲突:');\n for (const conflict of conflicts) {\n console.warn(` ${conflict.method} ${conflict.urlPath}`);\n for (const file of conflict.files) {\n console.warn(` - ${file}`);\n }\n }\n }\n\n // 4. 生成 schema 文件\n console.log('\\n[4/8] Generating schema...');\n await generateSchemaFiles(sorted, rootDir, outdir);\n console.log(` Schema: zod.js files under ${path.resolve(rootDir, outdir)}`);\n\n // 5. 生成路由清单(prd 启动时直接读取,不再 scanRoutes)\n console.log('\\n[5/8] Generating routes manifest...');\n const routesPath = path.resolve(rootDir, outdir, 'faapi-routes.js');\n const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);\n await writeRoutesModule(serialized, routesPath);\n console.log(` Written to ${routesPath}`);\n\n // 6. 生成 tool 清单 + schema(scanTools 读源码 + 正则提取函数名,generateToolArtifacts 做 AST 增强)\n // 与路由对称——生成 faapi-tools.js(tool 清单)+ 每个 tool handler 的 zod.js\n // 无 tool 文件时 scanTools 返回空列表,generateToolArtifacts 写入空清单\n console.log('\\n[6/8] Generating tool manifest and schema...');\n const tools = await scanTools(rootDir, TOOL_PATTERNS);\n const toolMeta = await generateToolArtifacts(tools, rootDir, outdir);\n console.log(` Found ${toolMeta.length} tool(s)`);\n console.log(` Tool manifest: ${path.resolve(rootDir, outdir, 'faapi-tools.js')}`);\n\n // 7. 生成 agent 清单(scanAgents 读源码 + 正则检测 config/run,generateAgentArtifacts 做 AST 增强)\n // agent 不生成 zod.js(无输入参数,config 块字段在 AST 阶段已提取为字面量)\n // 无 agent 文件时 scanAgents 返回空列表,generateAgentArtifacts 写入空清单\n console.log('\\n[7/8] Generating agent manifest...');\n const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);\n const agentMeta = await generateAgentArtifacts(agents, rootDir, outdir);\n console.log(` Found ${agentMeta.length} agent(s)`);\n console.log(` Agent manifest: ${path.resolve(rootDir, outdir, 'faapi-agents.js')}`);\n\n // 8. 生成启动入口 main.js(零入口设计:用户无需编写 main.ts)\n // 内部 import @faapi/faapi 的 createProdApp + loadEnv + listen\n // 运行时 `node <dist>/main` 直接启动:loadEnv 加载 .env → createProdApp 水合产物 → listen\n // --dist 选项写入 main.js(非默认 dist 时),端口由运行时 PORT 环境变量决定\n console.log('\\n[8/8] Generating entry file...');\n const mainPath = path.resolve(rootDir, outdir, 'main.js');\n // 非默认 dist 时写入 createProdApp 参数,让 prod 启动时能定位到产物目录\n // JSON.stringify 生成合法 JS 字符串字面量:Windows 反斜杠路径(.\\build 的 \\b 是退格转义)\n // 与含引号路径不再损坏\n const createProdAppArgs =\n options?.dist && options.dist !== DEFAULT_DIST ? `{ dist: ${JSON.stringify(outdir)} }` : '';\n const mainContent = `// 由 faapi build 自动生成,请勿手动编辑\nimport { createProdApp, loadEnv } from '@faapi/faapi';\n\n// 兜底 NODE_ENV(未显式设置时)+ 加载 .env 系列文件到 process.env\nif (!process.env.NODE_ENV) process.env.NODE_ENV = 'production';\nloadEnv(process.cwd());\n\nconst app = await createProdApp(${createProdAppArgs});\nawait app.listen();\n`;\n await fs.promises.writeFile(mainPath, mainContent, 'utf-8');\n console.log(` Written to ${mainPath}`);\n\n console.log('\\nfaapi build completed');\n}\n\n/**\n * 从 plugins 声明提取本地 TS/JS 插件源文件(build 端编译入口)\n *\n * 只取相对/绝对路径声明(`./x`、`../x`、绝对路径);包名声明跳过(运行时 Node 解析)。\n * 探测不到源文件的声明跳过——运行时 loadPlugins 会报带修复指引的失败,build 不在此\n * 替代报错(文件可能在运行时才出现,或用户笔误由运行时日志定位)。\n */\nfunction extractLocalPluginSources(\n declarations: PluginDeclaration[] | undefined,\n rootDir: string,\n): string[] {\n if (!declarations || declarations.length === 0) return [];\n const sources: string[] = [];\n const seen = new Set<string>();\n for (const decl of declarations) {\n let specifier: string | undefined;\n if (typeof decl === 'string') specifier = decl;\n else if (Array.isArray(decl)) specifier = decl[0];\n else if ('path' in decl) specifier = decl.path;\n if (!specifier) continue;\n if (\n !specifier.startsWith('./') &&\n !specifier.startsWith('../') &&\n !path.isAbsolute(specifier)\n ) {\n continue; // 包名声明\n }\n const source = resolveLocalPluginSource(specifier, rootDir);\n if (source && !seen.has(source)) {\n seen.add(source);\n sources.push(source);\n }\n }\n return sources;\n}\n","import { EventEmitter } from 'events';\n\nfunction toArr(any) {\n\treturn any == null ? [] : Array.isArray(any) ? any : [any];\n}\n\nfunction toVal(out, key, val, opts) {\n\tvar x, old=out[key], nxt=(\n\t\t!!~opts.string.indexOf(key) ? (val == null || val === true ? '' : String(val))\n\t\t: typeof val === 'boolean' ? val\n\t\t: !!~opts.boolean.indexOf(key) ? (val === 'false' ? false : val === 'true' || (out._.push((x = +val,x * 0 === 0) ? x : val),!!val))\n\t\t: (x = +val,x * 0 === 0) ? x : val\n\t);\n\tout[key] = old == null ? nxt : (Array.isArray(old) ? old.concat(nxt) : [old, nxt]);\n}\n\nfunction mri2 (args, opts) {\n\targs = args || [];\n\topts = opts || {};\n\n\tvar k, arr, arg, name, val, out={ _:[] };\n\tvar i=0, j=0, idx=0, len=args.length;\n\n\tconst alibi = opts.alias !== void 0;\n\tconst strict = opts.unknown !== void 0;\n\tconst defaults = opts.default !== void 0;\n\n\topts.alias = opts.alias || {};\n\topts.string = toArr(opts.string);\n\topts.boolean = toArr(opts.boolean);\n\n\tif (alibi) {\n\t\tfor (k in opts.alias) {\n\t\t\tarr = opts.alias[k] = toArr(opts.alias[k]);\n\t\t\tfor (i=0; i < arr.length; i++) {\n\t\t\t\t(opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i=opts.boolean.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.boolean[i]] || [];\n\t\tfor (j=arr.length; j-- > 0;) opts.boolean.push(arr[j]);\n\t}\n\n\tfor (i=opts.string.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.string[i]] || [];\n\t\tfor (j=arr.length; j-- > 0;) opts.string.push(arr[j]);\n\t}\n\n\tif (defaults) {\n\t\tfor (k in opts.default) {\n\t\t\tname = typeof opts.default[k];\n\t\t\tarr = opts.alias[k] = opts.alias[k] || [];\n\t\t\tif (opts[name] !== void 0) {\n\t\t\t\topts[name].push(k);\n\t\t\t\tfor (i=0; i < arr.length; i++) {\n\t\t\t\t\topts[name].push(arr[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst keys = strict ? Object.keys(opts.alias) : [];\n\n\tfor (i=0; i < len; i++) {\n\t\targ = args[i];\n\n\t\tif (arg === '--') {\n\t\t\tout._ = out._.concat(args.slice(++i));\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (j=0; j < arg.length; j++) {\n\t\t\tif (arg.charCodeAt(j) !== 45) break; // \"-\"\n\t\t}\n\n\t\tif (j === 0) {\n\t\t\tout._.push(arg);\n\t\t} else if (arg.substring(j, j + 3) === 'no-') {\n\t\t\tname = arg.substring(j + 3);\n\t\t\tif (strict && !~keys.indexOf(name)) {\n\t\t\t\treturn opts.unknown(arg);\n\t\t\t}\n\t\t\tout[name] = false;\n\t\t} else {\n\t\t\tfor (idx=j+1; idx < arg.length; idx++) {\n\t\t\t\tif (arg.charCodeAt(idx) === 61) break; // \"=\"\n\t\t\t}\n\n\t\t\tname = arg.substring(j, idx);\n\t\t\tval = arg.substring(++idx) || (i+1 === len || (''+args[i+1]).charCodeAt(0) === 45 || args[++i]);\n\t\t\tarr = (j === 2 ? [name] : name);\n\n\t\t\tfor (idx=0; idx < arr.length; idx++) {\n\t\t\t\tname = arr[idx];\n\t\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown('-'.repeat(j) + name);\n\t\t\t\ttoVal(out, name, (idx + 1 < arr.length) || val, opts);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (defaults) {\n\t\tfor (k in opts.default) {\n\t\t\tif (out[k] === void 0) {\n\t\t\t\tout[k] = opts.default[k];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (alibi) {\n\t\tfor (k in out) {\n\t\t\tarr = opts.alias[k] || [];\n\t\t\twhile (arr.length > 0) {\n\t\t\t\tout[arr.shift()] = out[k];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\nconst removeBrackets = (v) => v.replace(/[<[].+/, \"\").trim();\nconst findAllBrackets = (v) => {\n const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;\n const SQUARE_BRACKET_RE_GLOBAL = /\\[([^\\]]+)\\]/g;\n const res = [];\n const parse = (match) => {\n let variadic = false;\n let value = match[1];\n if (value.startsWith(\"...\")) {\n value = value.slice(3);\n variadic = true;\n }\n return {\n required: match[0].startsWith(\"<\"),\n value,\n variadic\n };\n };\n let angledMatch;\n while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) {\n res.push(parse(angledMatch));\n }\n let squareMatch;\n while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) {\n res.push(parse(squareMatch));\n }\n return res;\n};\nconst getMriOptions = (options) => {\n const result = {alias: {}, boolean: []};\n for (const [index, option] of options.entries()) {\n if (option.names.length > 1) {\n result.alias[option.names[0]] = option.names.slice(1);\n }\n if (option.isBoolean) {\n if (option.negated) {\n const hasStringTypeOption = options.some((o, i) => {\n return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === \"boolean\";\n });\n if (!hasStringTypeOption) {\n result.boolean.push(option.names[0]);\n }\n } else {\n result.boolean.push(option.names[0]);\n }\n }\n }\n return result;\n};\nconst findLongest = (arr) => {\n return arr.sort((a, b) => {\n return a.length > b.length ? -1 : 1;\n })[0];\n};\nconst padRight = (str, length) => {\n return str.length >= length ? str : `${str}${\" \".repeat(length - str.length)}`;\n};\nconst camelcase = (input) => {\n return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {\n return p1 + p2.toUpperCase();\n });\n};\nconst setDotProp = (obj, keys, val) => {\n let i = 0;\n let length = keys.length;\n let t = obj;\n let x;\n for (; i < length; ++i) {\n x = t[keys[i]];\n t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(\".\") || !(+keys[i + 1] > -1) ? {} : [];\n }\n};\nconst setByType = (obj, transforms) => {\n for (const key of Object.keys(transforms)) {\n const transform = transforms[key];\n if (transform.shouldTransform) {\n obj[key] = Array.prototype.concat.call([], obj[key]);\n if (typeof transform.transformFunction === \"function\") {\n obj[key] = obj[key].map(transform.transformFunction);\n }\n }\n }\n};\nconst getFileName = (input) => {\n const m = /([^\\\\\\/]+)$/.exec(input);\n return m ? m[1] : \"\";\n};\nconst camelcaseOptionName = (name) => {\n return name.split(\".\").map((v, i) => {\n return i === 0 ? camelcase(v) : v;\n }).join(\".\");\n};\nclass CACError extends Error {\n constructor(message) {\n super(message);\n this.name = this.constructor.name;\n if (typeof Error.captureStackTrace === \"function\") {\n Error.captureStackTrace(this, this.constructor);\n } else {\n this.stack = new Error(message).stack;\n }\n }\n}\n\nclass Option {\n constructor(rawName, description, config) {\n this.rawName = rawName;\n this.description = description;\n this.config = Object.assign({}, config);\n rawName = rawName.replace(/\\.\\*/g, \"\");\n this.negated = false;\n this.names = removeBrackets(rawName).split(\",\").map((v) => {\n let name = v.trim().replace(/^-{1,2}/, \"\");\n if (name.startsWith(\"no-\")) {\n this.negated = true;\n name = name.replace(/^no-/, \"\");\n }\n return camelcaseOptionName(name);\n }).sort((a, b) => a.length > b.length ? 1 : -1);\n this.name = this.names[this.names.length - 1];\n if (this.negated && this.config.default == null) {\n this.config.default = true;\n }\n if (rawName.includes(\"<\")) {\n this.required = true;\n } else if (rawName.includes(\"[\")) {\n this.required = false;\n } else {\n this.isBoolean = true;\n }\n }\n}\n\nconst processArgs = process.argv;\nconst platformInfo = `${process.platform}-${process.arch} node-${process.version}`;\n\nclass Command {\n constructor(rawName, description, config = {}, cli) {\n this.rawName = rawName;\n this.description = description;\n this.config = config;\n this.cli = cli;\n this.options = [];\n this.aliasNames = [];\n this.name = removeBrackets(rawName);\n this.args = findAllBrackets(rawName);\n this.examples = [];\n }\n usage(text) {\n this.usageText = text;\n return this;\n }\n allowUnknownOptions() {\n this.config.allowUnknownOptions = true;\n return this;\n }\n ignoreOptionDefaultValue() {\n this.config.ignoreOptionDefaultValue = true;\n return this;\n }\n version(version, customFlags = \"-v, --version\") {\n this.versionNumber = version;\n this.option(customFlags, \"Display version number\");\n return this;\n }\n example(example) {\n this.examples.push(example);\n return this;\n }\n option(rawName, description, config) {\n const option = new Option(rawName, description, config);\n this.options.push(option);\n return this;\n }\n alias(name) {\n this.aliasNames.push(name);\n return this;\n }\n action(callback) {\n this.commandAction = callback;\n return this;\n }\n isMatched(name) {\n return this.name === name || this.aliasNames.includes(name);\n }\n get isDefaultCommand() {\n return this.name === \"\" || this.aliasNames.includes(\"!\");\n }\n get isGlobalCommand() {\n return this instanceof GlobalCommand;\n }\n hasOption(name) {\n name = name.split(\".\")[0];\n return this.options.find((option) => {\n return option.names.includes(name);\n });\n }\n outputHelp() {\n const {name, commands} = this.cli;\n const {\n versionNumber,\n options: globalOptions,\n helpCallback\n } = this.cli.globalCommand;\n let sections = [\n {\n body: `${name}${versionNumber ? `/${versionNumber}` : \"\"}`\n }\n ];\n sections.push({\n title: \"Usage\",\n body: ` $ ${name} ${this.usageText || this.rawName}`\n });\n const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;\n if (showCommands) {\n const longestCommandName = findLongest(commands.map((command) => command.rawName));\n sections.push({\n title: \"Commands\",\n body: commands.map((command) => {\n return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;\n }).join(\"\\n\")\n });\n sections.push({\n title: `For more info, run any command with the \\`--help\\` flag`,\n body: commands.map((command) => ` $ ${name}${command.name === \"\" ? \"\" : ` ${command.name}`} --help`).join(\"\\n\")\n });\n }\n let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];\n if (!this.isGlobalCommand && !this.isDefaultCommand) {\n options = options.filter((option) => option.name !== \"version\");\n }\n if (options.length > 0) {\n const longestOptionName = findLongest(options.map((option) => option.rawName));\n sections.push({\n title: \"Options\",\n body: options.map((option) => {\n return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? \"\" : `(default: ${option.config.default})`}`;\n }).join(\"\\n\")\n });\n }\n if (this.examples.length > 0) {\n sections.push({\n title: \"Examples\",\n body: this.examples.map((example) => {\n if (typeof example === \"function\") {\n return example(name);\n }\n return example;\n }).join(\"\\n\")\n });\n }\n if (helpCallback) {\n sections = helpCallback(sections) || sections;\n }\n console.log(sections.map((section) => {\n return section.title ? `${section.title}:\n${section.body}` : section.body;\n }).join(\"\\n\\n\"));\n }\n outputVersion() {\n const {name} = this.cli;\n const {versionNumber} = this.cli.globalCommand;\n if (versionNumber) {\n console.log(`${name}/${versionNumber} ${platformInfo}`);\n }\n }\n checkRequiredArgs() {\n const minimalArgsCount = this.args.filter((arg) => arg.required).length;\n if (this.cli.args.length < minimalArgsCount) {\n throw new CACError(`missing required args for command \\`${this.rawName}\\``);\n }\n }\n checkUnknownOptions() {\n const {options, globalCommand} = this.cli;\n if (!this.config.allowUnknownOptions) {\n for (const name of Object.keys(options)) {\n if (name !== \"--\" && !this.hasOption(name) && !globalCommand.hasOption(name)) {\n throw new CACError(`Unknown option \\`${name.length > 1 ? `--${name}` : `-${name}`}\\``);\n }\n }\n }\n }\n checkOptionValue() {\n const {options: parsedOptions, globalCommand} = this.cli;\n const options = [...globalCommand.options, ...this.options];\n for (const option of options) {\n const value = parsedOptions[option.name.split(\".\")[0]];\n if (option.required) {\n const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));\n if (value === true || value === false && !hasNegated) {\n throw new CACError(`option \\`${option.rawName}\\` value is missing`);\n }\n }\n }\n }\n}\nclass GlobalCommand extends Command {\n constructor(cli) {\n super(\"@@global@@\", \"\", {}, cli);\n }\n}\n\nvar __assign = Object.assign;\nclass CAC extends EventEmitter {\n constructor(name = \"\") {\n super();\n this.name = name;\n this.commands = [];\n this.rawArgs = [];\n this.args = [];\n this.options = {};\n this.globalCommand = new GlobalCommand(this);\n this.globalCommand.usage(\"<command> [options]\");\n }\n usage(text) {\n this.globalCommand.usage(text);\n return this;\n }\n command(rawName, description, config) {\n const command = new Command(rawName, description || \"\", config, this);\n command.globalCommand = this.globalCommand;\n this.commands.push(command);\n return command;\n }\n option(rawName, description, config) {\n this.globalCommand.option(rawName, description, config);\n return this;\n }\n help(callback) {\n this.globalCommand.option(\"-h, --help\", \"Display this message\");\n this.globalCommand.helpCallback = callback;\n this.showHelpOnExit = true;\n return this;\n }\n version(version, customFlags = \"-v, --version\") {\n this.globalCommand.version(version, customFlags);\n this.showVersionOnExit = true;\n return this;\n }\n example(example) {\n this.globalCommand.example(example);\n return this;\n }\n outputHelp() {\n if (this.matchedCommand) {\n this.matchedCommand.outputHelp();\n } else {\n this.globalCommand.outputHelp();\n }\n }\n outputVersion() {\n this.globalCommand.outputVersion();\n }\n setParsedInfo({args, options}, matchedCommand, matchedCommandName) {\n this.args = args;\n this.options = options;\n if (matchedCommand) {\n this.matchedCommand = matchedCommand;\n }\n if (matchedCommandName) {\n this.matchedCommandName = matchedCommandName;\n }\n return this;\n }\n unsetMatchedCommand() {\n this.matchedCommand = void 0;\n this.matchedCommandName = void 0;\n }\n parse(argv = processArgs, {\n run = true\n } = {}) {\n this.rawArgs = argv;\n if (!this.name) {\n this.name = argv[1] ? getFileName(argv[1]) : \"cli\";\n }\n let shouldParse = true;\n for (const command of this.commands) {\n const parsed = this.mri(argv.slice(2), command);\n const commandName = parsed.args[0];\n if (command.isMatched(commandName)) {\n shouldParse = false;\n const parsedInfo = __assign(__assign({}, parsed), {\n args: parsed.args.slice(1)\n });\n this.setParsedInfo(parsedInfo, command, commandName);\n this.emit(`command:${commandName}`, command);\n }\n }\n if (shouldParse) {\n for (const command of this.commands) {\n if (command.name === \"\") {\n shouldParse = false;\n const parsed = this.mri(argv.slice(2), command);\n this.setParsedInfo(parsed, command);\n this.emit(`command:!`, command);\n }\n }\n }\n if (shouldParse) {\n const parsed = this.mri(argv.slice(2));\n this.setParsedInfo(parsed);\n }\n if (this.options.help && this.showHelpOnExit) {\n this.outputHelp();\n run = false;\n this.unsetMatchedCommand();\n }\n if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {\n this.outputVersion();\n run = false;\n this.unsetMatchedCommand();\n }\n const parsedArgv = {args: this.args, options: this.options};\n if (run) {\n this.runMatchedCommand();\n }\n if (!this.matchedCommand && this.args[0]) {\n this.emit(\"command:*\");\n }\n return parsedArgv;\n }\n mri(argv, command) {\n const cliOptions = [\n ...this.globalCommand.options,\n ...command ? command.options : []\n ];\n const mriOptions = getMriOptions(cliOptions);\n let argsAfterDoubleDashes = [];\n const doubleDashesIndex = argv.indexOf(\"--\");\n if (doubleDashesIndex > -1) {\n argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);\n argv = argv.slice(0, doubleDashesIndex);\n }\n let parsed = mri2(argv, mriOptions);\n parsed = Object.keys(parsed).reduce((res, name) => {\n return __assign(__assign({}, res), {\n [camelcaseOptionName(name)]: parsed[name]\n });\n }, {_: []});\n const args = parsed._;\n const options = {\n \"--\": argsAfterDoubleDashes\n };\n const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;\n let transforms = Object.create(null);\n for (const cliOption of cliOptions) {\n if (!ignoreDefault && cliOption.config.default !== void 0) {\n for (const name of cliOption.names) {\n options[name] = cliOption.config.default;\n }\n }\n if (Array.isArray(cliOption.config.type)) {\n if (transforms[cliOption.name] === void 0) {\n transforms[cliOption.name] = Object.create(null);\n transforms[cliOption.name][\"shouldTransform\"] = true;\n transforms[cliOption.name][\"transformFunction\"] = cliOption.config.type[0];\n }\n }\n }\n for (const key of Object.keys(parsed)) {\n if (key !== \"_\") {\n const keys = key.split(\".\");\n setDotProp(options, keys, parsed[key]);\n setByType(options, transforms);\n }\n }\n return {\n args,\n options\n };\n }\n runMatchedCommand() {\n const {args, options, matchedCommand: command} = this;\n if (!command || !command.commandAction)\n return;\n command.checkUnknownOptions();\n command.checkOptionValue();\n command.checkRequiredArgs();\n const actionArgs = [];\n command.args.forEach((arg, index) => {\n if (arg.variadic) {\n actionArgs.push(args.slice(index));\n } else {\n actionArgs.push(args[index]);\n }\n });\n actionArgs.push(options);\n return command.commandAction.apply(this, actionArgs);\n }\n}\n\nconst cac = (name = \"\") => new CAC(name);\n\nexport default cac;\nexport { CAC, Command, cac };\n","#!/usr/bin/env node\n\nimport { cac } from 'cac';\nimport { createRequire } from 'node:module';\n\n/**\n * 从最近的 package.json 读版本号\n *\n * src/cli/index.ts 需向上两级到包根,dist/index.js 一级——逐级探测,\n * 命中第一个存在的 package.json(publishConfig 的 files 只发 dist,\n * 探测在 dist 内即命中包根)\n */\nfunction resolveVersion(): string {\n const require = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json']) {\n try {\n const { version } = require(rel) as { version: string };\n if (version) return version;\n } catch {\n // 继续向上一级探测\n }\n }\n return '0.0.0';\n}\n\n/**\n * 创建 faapi CLI 实例(导出供测试调用 `cli.parse(argv)` 显式传参)。\n *\n * cac 在模块加载时捕获 `process.argv` 快照(`const processArgs = process.argv`),\n * `cli.parse()` 无参时用该快照。生产环境 CLI 入口由 shebang 触发,`process.argv` 已就绪;\n * 测试环境需显式传 argv 才能精确控制命令分发。\n */\nexport function createCli() {\n const version = resolveVersion();\n\n const cli = cac('faapi');\n\n cli\n .command('')\n .alias('dev')\n .option('--port <number>', '服务端口(默认 3000)')\n .action(async (options) => {\n const { devCommand } = await import('./devCommand.js');\n await runCommand(() => devCommand(options));\n });\n\n cli\n .command('build', 'Build for production')\n .option('--dist <dir>', '产物输出目录,默认 dist')\n .action(async (options) => {\n const { buildCommand } = await import('./buildCommand.js');\n await runCommand(() => buildCommand(options));\n });\n\n cli.help();\n cli.version(version);\n return cli;\n}\n\n/**\n * 命令顶层错误处理:编译/配置/加载错误以一行友好摘要输出(exit code 1),\n * 不再裸堆栈糊屏。设置 FAAPI_DEBUG=1 时附加完整堆栈供排查。\n */\nasync function runCommand(run: () => Promise<void>): Promise<void> {\n try {\n await run();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`\\nfaapi: ${message}`);\n if (process.env.FAAPI_DEBUG && err instanceof Error && err.stack) {\n console.error(err.stack);\n }\n process.exitCode = 1;\n }\n}\n\n// shebang 入口:直接执行时 parse(用 process.argv 快照)\ncreateCli().parse();\n"],"mappings":"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","names":["path","fs","path","fs","SPEC_RE","path","fs","path","fs","stat","path","fs","fs","path","path","ts","url","path","fs","fg","path","fs","ts","ts","ts","fs","path","ts","ts","ts","ts","path","unionInner","path","path","fg","path","fs","ts","path","path","fs","fs","path","path","basename","stat","lstat","path","rawEmitter","listener","basename","dirname","newStats","closer","resolve","realpath","stats","stat","readdir","EventEmitter","sysPath","relative","normalizePath","path","DOUBLE_SLASH_RE","testString","init_esm","stats","now","path","fs","fg","path","init_esm","path","params","path","fs","inFlight","path","body","url","text","headers","body","Readable","resolve","path","headers","etag","normalize","DEFAULTS","fs","path","resolve","Readable","path","path","fs","pathToFileURL","fs","path","Readable","TOOLS_FILE","AGENTS_FILE","resolve","body","generateSchemaFiles","path","ROUTES_FILE","path","fs","DEFAULT_DIST","require","devCommand","buildCommand"]}
|