@faapi/faapi 0.0.0-canary.0f443f9 → 0.0.0-canary.c6dc0f7

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/index.d.ts CHANGED
@@ -824,7 +824,7 @@ declare function getSchemaProperties(filePath: string, method: string, inputType
824
824
  * 2. faapi.config.ts / faapi.config.js(基础配置)
825
825
  * 3. faapi.config.{env}.ts / faapi.config.{env}.js(环境覆盖,深度合并)
826
826
  *
827
- * 环境由 NODE_ENVFAAPI_ENV 决定,默认 'development'
827
+ * 环境由 FAAPI_ENVNODE_ENV 决定,默认 'development'
828
828
  *
829
829
  * @param rootDir 项目根目录
830
830
  * @param configPath 指定的配置文件路径(可选)
package/dist/index.js CHANGED
@@ -651,7 +651,7 @@ import fs from "fs";
651
651
  import { pathToFileURL } from "url";
652
652
  var BASE_CONFIG_FILES = ["faapi.config.ts", "faapi.config.js"];
653
653
  function getEnv() {
654
- return process.env.NODE_ENV || process.env.FAAPI_ENV || "development";
654
+ return process.env.FAAPI_ENV || process.env.NODE_ENV || "development";
655
655
  }
656
656
  function deepMerge(base, override) {
657
657
  const result = { ...base };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ast/createProgram.ts","../src/ast/extractHandlerTypes.ts","../src/ast/resolveTypeNode.ts","../src/runtime/inputType.ts","../src/validator/schemaRegistry.ts","../src/validator/schemaName.ts","../src/validator/getSchemaProperties.ts","../src/middleware/cors.ts","../src/middleware/logger.ts","../src/config/loadConfig.ts"],"sourcesContent":["import ts from 'typescript';\n\n/**\n * Program 缓存,key 为文件路径\n *\n * watch 模式下通过 invalidateProgramCache() 全量清理。\n * 同一文件多次创建 Program 时复用缓存,避免重复解析。\n */\nconst programCache = new Map<string, ts.Program>();\n\n/**\n * 清理所有 Program 缓存(watch 模式下文件变化时调用)\n *\n * 全量清理而非增量清理,理由:\n * - 简单可靠,无状态一致性问题\n * - 跨文件类型引用需要所有文件的 Program 同步更新\n * - dev 模式文件量有限,全量重建在百毫秒级\n */\nexport function invalidateProgramCache(): void {\n programCache.clear();\n}\n\n/**\n * 为指定文件创建 TypeScript 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 = ts.createProgram([filePath], {\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 programCache.set(filePath, program);\n return program;\n}\n","import ts from 'typescript';\nimport {\n resolveTypeNode,\n resolveInterfaceDeclaration,\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 *\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 // 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 return result;\n}\n\n/**\n * 从源文件中提取所有 interface 和 type 别名的类型信息\n *\n * 用于:\n * - 生成 schema 模块时遍历所有命名类型\n * - 作为 typeResolver 提供给 generateValidatorCode,解析循环引用中的 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 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}\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 );\n throw enriched;\n }\n throw err;\n }\n}\n","import ts from 'typescript';\n\n/**\n * Schema 提取错误\n *\n * 遇到无法解析或不支持运行时校验的类型时抛出,\n * 避免静默降级为 any 导致用户不知情。\n */\nexport class SchemaExtractionError extends Error {\n constructor(\n public readonly typeText: string,\n public readonly reason: string,\n options?: ErrorOptions,\n ) {\n super(`无法解析类型 \"${typeText}\": ${reason}`, options);\n this.name = 'SchemaExtractionError';\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[] }\n | { kind: 'union'; members: RuntimeType[] }\n | { kind: 'date' }\n | { kind: 'record'; key: RuntimeType; value: RuntimeType }\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\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>\n * - 元组类型:[string, number] / [string, number?] / [string, ...number[]](按位置校验)\n * - 对象类型:{ name: string; age?: number }\n * - 联合类型:string | null\n * - 交叉类型:A & B(按对象合并处理)\n * - 引用类型:Date / 其他 interface(递归解析)\n * - 工具类型:Record<K, V> / Partial<T>(best effort)\n * - Pick<T, K> / Omit<T, K>:K 支持字面量联合、类型别名、keyof T\n *\n * @param typeNode TypeScript 类型节点\n * @param checker 类型 checker(用于解析引用类型)\n * @param visited 防止递归循环\n */\nexport function resolveTypeNode(\n typeNode: ts.TypeNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\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),\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);\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),\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),\n optional: true,\n rest: false,\n };\n }\n // 普通元素\n return {\n type: resolveTypeNode(e, checker, visited),\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));\n return { kind: 'union', members };\n }\n\n // 交叉类型:A & B → 合并对象属性\n if (ts.isIntersectionTypeNode(typeNode)) {\n const properties: PropertyType[] = [];\n for (const t of typeNode.types) {\n const resolved = resolveTypeNode(t, checker, visited);\n if (resolved.kind === 'object') {\n properties.push(...resolved.properties);\n }\n }\n return { kind: 'object', properties };\n }\n\n // 内联对象类型:{ name: string; age?: number }\n if (ts.isTypeLiteralNode(typeNode)) {\n return resolveTypeLiteral(typeNode, checker, visited);\n }\n\n // keyof T — 用 checker 解析为字面量联合\n if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.KeyOfKeyword) {\n return resolveKeyOf(typeNode, checker);\n }\n\n // 引用类型:Date / 自定义 interface / Array<T> / Record<K,V> / Partial<T> 等\n if (ts.isTypeReferenceNode(typeNode)) {\n return resolveTypeReference(typeNode, checker, visited);\n }\n\n // 其他无法识别的语法节点\n throw new SchemaExtractionError(typeNode.getText(), '不支持的类型语法');\n}\n\n/**\n * 解析内联对象类型字面量\n */\nfunction resolveTypeLiteral(\n typeNode: ts.TypeLiteralNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n): RuntimeType {\n const properties: PropertyType[] = [];\n\n for (const member of typeNode.members) {\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)\n : { kind: 'any' as const };\n properties.push({ name, type, optional });\n }\n // 索引签名:[key: string]: T → 转为 record\n if (ts.isIndexSignatureDeclaration(member)) {\n const keyType = member.parameters[0]?.type\n ? resolveTypeNode(member.parameters[0].type, checker, visited)\n : { kind: 'any' as const };\n const valueType = member.type\n ? resolveTypeNode(member.type, checker, visited)\n : { kind: 'any' as const };\n return { kind: 'record', key: keyType, value: valueType };\n }\n }\n\n return { 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 * 解析类型引用(Date / 自定义 interface / Array<T> / Record<K,V> 等)\n */\nfunction resolveTypeReference(\n typeNode: ts.TypeReferenceNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n): RuntimeType {\n const typeName = typeNode.typeName.getText();\n\n // Date 类型\n if (typeName === 'Date') {\n return { kind: 'date' };\n }\n\n // Array<T>\n if (typeName === 'Array' && typeNode.typeArguments?.length === 1) {\n return {\n kind: 'array',\n element: resolveTypeNode(typeNode.typeArguments[0], checker, visited),\n };\n }\n\n // Record<K, V>\n if (typeName === 'Record' && typeNode.typeArguments?.length === 2) {\n return {\n kind: 'record',\n key: resolveTypeNode(typeNode.typeArguments[0], checker, visited),\n value: resolveTypeNode(typeNode.typeArguments[1], checker, visited),\n };\n }\n\n // Partial<T> / Required<T> / Readonly<T> — best effort,解析内部类型\n if (\n (typeName === 'Partial' || typeName === 'Required' || typeName === 'Readonly') &&\n typeNode.typeArguments?.length === 1\n ) {\n const inner = resolveTypeNode(typeNode.typeArguments[0], checker, visited);\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 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);\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));\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> / Set<T> — 运行时无法校验\n if (\n typeName === 'Map' ||\n typeName === 'Set' ||\n typeName === 'WeakMap' ||\n typeName === 'WeakSet'\n ) {\n throw new SchemaExtractionError(\n typeNode.getText(),\n `${typeName} 运行时无法校验,请改用对象或数组`,\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 typeNode.typeName.kind === ts.SyntaxKind.Identifier\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(declaration, checker, visited);\n }\n // type 别名声明\n if (ts.isTypeAliasDeclaration(declaration)) {\n return resolveTypeNode(declaration.type, checker, visited);\n }\n // enum 声明 → 字面量联合\n if (ts.isEnumDeclaration(declaration)) {\n return resolveEnumDeclaration(declaration);\n }\n }\n }\n }\n\n throw new SchemaExtractionError(typeNode.getText(), `无法解析的引用类型 \"${typeName}\"`);\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 */\nfunction 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 */\nexport function resolveInterfaceDeclaration(\n node: ts.InterfaceDeclaration,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n): RuntimeType {\n const properties: PropertyType[] = [];\n const propMap = new Map<string, PropertyType>();\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);\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 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)\n : { kind: 'any' as const };\n propMap.set(name, { name, type, optional });\n }\n // 索引签名 → record\n if (ts.isIndexSignatureDeclaration(member)) {\n const keyType = member.parameters[0]?.type\n ? resolveTypeNode(member.parameters[0].type, checker, visited)\n : { kind: 'any' as const };\n const valueType = member.type\n ? resolveTypeNode(member.type, checker, visited)\n : { kind: 'any' as const };\n return { kind: 'record', key: keyType, value: valueType };\n }\n }\n\n for (const prop of propMap.values()) {\n properties.push(prop);\n }\n\n return { kind: 'object', properties };\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","import type { ValidatorFn } from '../ast/generateValidatorCode';\nimport type { PropertyType } from '../ast/resolveTypeNode';\n\n/**\n * 单个 schema 条目\n * - SchemaEntry:有类型声明,包含 properties(用于 coerce)和 validator(用于校验)\n * - null:handler 无类型声明(如 GET() 无参数),跳过校验\n */\nexport type SchemaEntry = { properties: PropertyType[]; validator: ValidatorFn } | null;\n\n/**\n * 单个文件的所有 schema:schemaName -> SchemaEntry\n * schemaName 由 method + inputType 生成,如 'GETQuery'、'POSTBody'\n */\nexport type FileSchemas = Map<string, SchemaEntry>;\n\n/**\n * 完整 manifest:filePath -> FileSchemas\n */\nexport type SchemaManifest = Map<string, FileSchemas>;\n\n/**\n * Schema 注册表(单例)\n *\n * 统一管理 handler 的校验函数,作为 validateInput 的唯一数据来源。\n * - prd 模式:从 dist/faapi-schema.js import 加载\n * - dev 模式:启动时全量提取并生成函数,watch 时全量重建\n *\n * 三种状态区分:\n * - get() 返回 SchemaEntry:有类型声明,校验\n * - get() 返回 null:无类型声明,跳过校验\n * - get() 返回 undefined:manifest 不完整,抛错\n */\nclass SchemaRegistry {\n private manifest: SchemaManifest = new Map();\n\n /**\n * 批量加载 manifest\n * 覆盖已有数据\n */\n loadManifest(manifest: SchemaManifest): void {\n this.manifest.clear();\n for (const [filePath, fileSchemas] of manifest) {\n const copy: FileSchemas = new Map();\n fileSchemas.forEach((value, key) => copy.set(key, value));\n this.manifest.set(filePath, copy);\n }\n }\n\n /**\n * 查询单条 schema\n * @returns SchemaEntry | null | undefined\n * - SchemaEntry:有类型声明\n * - null:无类型声明(跳过校验)\n * - undefined:manifest 不完整(抛错)\n */\n get(filePath: string, schemaName: string): SchemaEntry | undefined {\n const fileSchemas = this.manifest.get(filePath);\n if (!fileSchemas) return undefined;\n return fileSchemas.get(schemaName);\n }\n\n /**\n * 设置单个文件的所有 schema\n * 覆盖该文件的已有数据\n */\n set(filePath: string, schemas: FileSchemas): void {\n const copy: FileSchemas = new Map();\n schemas.forEach((value, key) => copy.set(key, value));\n this.manifest.set(filePath, copy);\n }\n\n /**\n * 删除单个文件(文件被删除时)\n */\n delete(filePath: string): void {\n this.manifest.delete(filePath);\n }\n\n /**\n * 判断文件是否已注册\n */\n hasFile(filePath: string): boolean {\n return this.manifest.has(filePath);\n }\n\n /**\n * 清空(测试用 / watch 全量重建前)\n */\n clear(): void {\n this.manifest.clear();\n }\n\n /**\n * 已注册的文件数量\n */\n get size(): number {\n return this.manifest.size;\n }\n}\n\n/**\n * 全局单例\n */\nexport const schemaRegistry = new SchemaRegistry();\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 { schemaRegistry } from './schemaRegistry';\nimport { getSchemaName } from './schemaName';\nimport type { RuntimeType } from '../ast/resolveTypeNode';\n\n/**\n * 单个参数的 schema 描述(简化版,供扩展包消费)\n */\nexport interface SchemaPropertyDescriptor {\n name: string;\n type: string;\n required: boolean;\n}\n\n/**\n * 单个输入源的 schema 描述\n */\nexport interface InputSchemaDescriptor {\n /** 输入源类型名(如 'Query'、'CreateUserBody'),无类型声明时 null */\n schemaName: string | null;\n /** 参数列表 */\n properties: SchemaPropertyDescriptor[];\n}\n\n/**\n * 查询指定路由 handler 的输入参数 schema\n *\n * 复用 schemaRegistry 已有的类型提取结果,避免重复 AST 分析。\n * 在 schema 尚未注册时返回 undefined。\n *\n * @param filePath handler 文件绝对路径\n * @param method HTTP 方法(GET/POST 等)\n * @param inputType 输入源类型(query/body/params)\n */\nexport function getSchemaProperties(\n filePath: string,\n method: string,\n inputType: 'query' | 'body' | 'params',\n): InputSchemaDescriptor | undefined {\n const schemaName = getSchemaName(method, inputType);\n const entry = schemaRegistry.get(filePath, schemaName);\n\n // undefined = manifest 不完整;null = 无类型声明\n if (entry === undefined) return undefined;\n if (entry === null) {\n return { schemaName: null, properties: [] };\n }\n\n return {\n schemaName: schemaName,\n properties: entry.properties.map((prop) => ({\n name: prop.name,\n type: runtimeTypeToString(prop.type),\n required: !prop.optional,\n })),\n };\n}\n\n/**\n * 将 RuntimeType 转换为可读字符串\n */\nfunction runtimeTypeToString(type: RuntimeType): string {\n switch (type.kind) {\n case 'string':\n case 'number':\n case 'boolean':\n case 'bigint':\n case 'null':\n case 'undefined':\n case 'date':\n return type.kind;\n case 'literal':\n return JSON.stringify(type.value);\n case 'array':\n return `${runtimeTypeToString(type.element)}[]`;\n case 'tuple':\n return `[${type.elements.map((e) => (e.rest ? '...' : '') + runtimeTypeToString(e.type) + (e.optional ? '?' : '')).join(', ')}]`;\n case 'object':\n return 'object';\n case 'union':\n return type.members.map(runtimeTypeToString).join(' | ');\n case 'record':\n return `Record<${runtimeTypeToString(type.key)}, ${runtimeTypeToString(type.value)}>`;\n case 'ref':\n return type.name;\n case 'any':\n case 'unknown':\n return 'unknown';\n default:\n return 'unknown';\n }\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 if (!allowOrigin) {\n await next();\n return;\n }\n\n // 设置 CORS 响应头\n ctx.setHeader('Access-Control-Allow-Origin', allowOrigin);\n\n // 当 origin 为动态值(true 或数组)时,必须设置 Vary: Origin,\n // 防止 CDN/浏览器错误缓存针对某个 Origin 的响应给其他 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 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 LoggerOptions {\n /** 自定义日志输出函数,默认 console.log */\n log?: (message: string) => void;\n}\n\n/**\n * 创建请求日志中间件(洋葱模型)\n *\n * 日志格式:GET /api/users 200 12ms\n * 错误格式:POST /api/users 400 45ms - Error: ...\n *\n * before/after 一体,闭包变量共享开始时间,无需污染 ctx。\n * 错误用 try/catch 捕获,记录后重新抛出(让上层处理)。\n * 成功时从 next() 返回的 Response 读取状态码。\n */\nexport function logger(options: LoggerOptions = {}): FaapiMiddleware {\n const { log = console.log } = options;\n\n return async (ctx, next) => {\n const start = Date.now();\n try {\n const response = await next();\n const duration = Date.now() - start;\n log(`${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 // 从错误对象获取实际状态码(FaapiError 有 statusCode 字段),默认 500\n const status = (err as { statusCode?: number })?.statusCode ?? 500;\n log(`${ctx.method} ${ctx.path} ${status} ${duration}ms - ${message}`);\n throw err; // 重新抛出,让上层处理\n }\n };\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport type { FaapiConfig } from './configTypes';\n\n/**\n * 配置文件查找顺序\n *\n * 1. faapi.config.ts / faapi.config.js(基础配置)\n * 2. faapi.config.{env}.ts / faapi.config.{env}.js(环境覆盖)\n *\n * 环境覆盖深度合并到基础配置上(见 deepMerge)\n */\nconst BASE_CONFIG_FILES = ['faapi.config.ts', 'faapi.config.js'];\n\n/**\n * 获取当前运行环境\n *\n * 优先级:NODE_ENV → FAAPI_ENV → 'development'\n */\nfunction getEnv(): string {\n return process.env.NODE_ENV || process.env.FAAPI_ENV || 'development';\n}\n\n/**\n * 深度合并两个配置对象(后者覆盖前者)\n *\n * 特殊对象(Date / RegExp / Map / Set)直接替换,不递归合并。\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n override: Record<string, unknown>,\n): T {\n const result = { ...base };\n for (const key of Object.keys(override)) {\n const baseVal = (base as Record<string, unknown>)[key];\n const overVal = override[key];\n\n // 特殊对象类型直接替换(不递归合并)\n if (\n baseVal instanceof Date ||\n overVal instanceof Date ||\n baseVal instanceof RegExp ||\n overVal instanceof RegExp ||\n baseVal instanceof Map ||\n overVal instanceof Map ||\n baseVal instanceof Set ||\n overVal instanceof Set\n ) {\n (result as Record<string, unknown>)[key] = overVal;\n continue;\n }\n\n // 普通对象递归合并\n if (\n baseVal !== null &&\n overVal !== null &&\n typeof baseVal === 'object' &&\n typeof overVal === 'object' &&\n !Array.isArray(baseVal) &&\n !Array.isArray(overVal) &&\n !(baseVal instanceof Function) &&\n !(overVal instanceof Function)\n ) {\n (result as Record<string, unknown>)[key] = deepMerge(\n baseVal as Record<string, unknown>,\n overVal as Record<string, unknown>,\n );\n } else {\n (result as Record<string, unknown>)[key] = overVal;\n }\n }\n return result;\n}\n\n/**\n * 加载单个配置文件\n */\nasync function loadConfigFile(filePath: string): Promise<Partial<FaapiConfig> | null> {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n try {\n const url = pathToFileURL(filePath).href;\n const module = (await import(url)) as { default?: Partial<FaapiConfig> };\n return module.default ?? {};\n } catch (err) {\n throw new Error(\n `Failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\n/**\n * 加载 faapi 配置文件\n *\n * 查找顺序:\n * 1. 指定的 configPath\n * 2. faapi.config.ts / faapi.config.js(基础配置)\n * 3. faapi.config.{env}.ts / faapi.config.{env}.js(环境覆盖,深度合并)\n *\n * 环境由 NODE_ENV 或 FAAPI_ENV 决定,默认 'development'\n *\n * @param rootDir 项目根目录\n * @param configPath 指定的配置文件路径(可选)\n * @returns 合并后的配置,如果无配置文件则返回 null\n */\nexport async function loadConfig(\n rootDir: string,\n configPath?: string,\n): Promise<Partial<FaapiConfig> | null> {\n // 指定了配置文件路径时,直接加载该文件(文件不存在则抛错)\n if (configPath) {\n const resolvedPath = path.resolve(rootDir, configPath);\n if (!fs.existsSync(resolvedPath)) {\n throw new Error(`Config file not found: ${configPath}`);\n }\n return loadConfigFile(resolvedPath);\n }\n\n // 查找基础配置文件\n let baseConfig: Partial<FaapiConfig> | null = null;\n for (const fileName of BASE_CONFIG_FILES) {\n const filePath = path.join(rootDir, fileName);\n baseConfig = await loadConfigFile(filePath);\n if (baseConfig) break;\n }\n\n if (!baseConfig) {\n return null;\n }\n\n // 查找环境配置文件并合并\n const env = getEnv();\n const envFiles = [`faapi.config.${env}.ts`, `faapi.config.${env}.js`];\n\n for (const envFile of envFiles) {\n const envConfig = await loadConfigFile(path.join(rootDir, envFile));\n if (envConfig) {\n baseConfig = deepMerge(baseConfig, envConfig);\n break;\n }\n }\n\n return baseConfig;\n}\n"],"mappings":"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","names":["ts","ts","ts"]}
1
+ {"version":3,"sources":["../src/ast/createProgram.ts","../src/ast/extractHandlerTypes.ts","../src/ast/resolveTypeNode.ts","../src/runtime/inputType.ts","../src/validator/schemaRegistry.ts","../src/validator/schemaName.ts","../src/validator/getSchemaProperties.ts","../src/middleware/cors.ts","../src/middleware/logger.ts","../src/config/loadConfig.ts"],"sourcesContent":["import ts from 'typescript';\n\n/**\n * Program 缓存,key 为文件路径\n *\n * watch 模式下通过 invalidateProgramCache() 全量清理。\n * 同一文件多次创建 Program 时复用缓存,避免重复解析。\n */\nconst programCache = new Map<string, ts.Program>();\n\n/**\n * 清理所有 Program 缓存(watch 模式下文件变化时调用)\n *\n * 全量清理而非增量清理,理由:\n * - 简单可靠,无状态一致性问题\n * - 跨文件类型引用需要所有文件的 Program 同步更新\n * - dev 模式文件量有限,全量重建在百毫秒级\n */\nexport function invalidateProgramCache(): void {\n programCache.clear();\n}\n\n/**\n * 为指定文件创建 TypeScript 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 = ts.createProgram([filePath], {\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 programCache.set(filePath, program);\n return program;\n}\n","import ts from 'typescript';\nimport {\n resolveTypeNode,\n resolveInterfaceDeclaration,\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 *\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 // 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 return result;\n}\n\n/**\n * 从源文件中提取所有 interface 和 type 别名的类型信息\n *\n * 用于:\n * - 生成 schema 模块时遍历所有命名类型\n * - 作为 typeResolver 提供给 generateValidatorCode,解析循环引用中的 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 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}\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 );\n throw enriched;\n }\n throw err;\n }\n}\n","import ts from 'typescript';\n\n/**\n * Schema 提取错误\n *\n * 遇到无法解析或不支持运行时校验的类型时抛出,\n * 避免静默降级为 any 导致用户不知情。\n */\nexport class SchemaExtractionError extends Error {\n constructor(\n public readonly typeText: string,\n public readonly reason: string,\n options?: ErrorOptions,\n ) {\n super(`无法解析类型 \"${typeText}\": ${reason}`, options);\n this.name = 'SchemaExtractionError';\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[] }\n | { kind: 'union'; members: RuntimeType[] }\n | { kind: 'date' }\n | { kind: 'record'; key: RuntimeType; value: RuntimeType }\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\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>\n * - 元组类型:[string, number] / [string, number?] / [string, ...number[]](按位置校验)\n * - 对象类型:{ name: string; age?: number }\n * - 联合类型:string | null\n * - 交叉类型:A & B(按对象合并处理)\n * - 引用类型:Date / 其他 interface(递归解析)\n * - 工具类型:Record<K, V> / Partial<T>(best effort)\n * - Pick<T, K> / Omit<T, K>:K 支持字面量联合、类型别名、keyof T\n *\n * @param typeNode TypeScript 类型节点\n * @param checker 类型 checker(用于解析引用类型)\n * @param visited 防止递归循环\n */\nexport function resolveTypeNode(\n typeNode: ts.TypeNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\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),\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);\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),\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),\n optional: true,\n rest: false,\n };\n }\n // 普通元素\n return {\n type: resolveTypeNode(e, checker, visited),\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));\n return { kind: 'union', members };\n }\n\n // 交叉类型:A & B → 合并对象属性\n if (ts.isIntersectionTypeNode(typeNode)) {\n const properties: PropertyType[] = [];\n for (const t of typeNode.types) {\n const resolved = resolveTypeNode(t, checker, visited);\n if (resolved.kind === 'object') {\n properties.push(...resolved.properties);\n }\n }\n return { kind: 'object', properties };\n }\n\n // 内联对象类型:{ name: string; age?: number }\n if (ts.isTypeLiteralNode(typeNode)) {\n return resolveTypeLiteral(typeNode, checker, visited);\n }\n\n // keyof T — 用 checker 解析为字面量联合\n if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.KeyOfKeyword) {\n return resolveKeyOf(typeNode, checker);\n }\n\n // 引用类型:Date / 自定义 interface / Array<T> / Record<K,V> / Partial<T> 等\n if (ts.isTypeReferenceNode(typeNode)) {\n return resolveTypeReference(typeNode, checker, visited);\n }\n\n // 其他无法识别的语法节点\n throw new SchemaExtractionError(typeNode.getText(), '不支持的类型语法');\n}\n\n/**\n * 解析内联对象类型字面量\n */\nfunction resolveTypeLiteral(\n typeNode: ts.TypeLiteralNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n): RuntimeType {\n const properties: PropertyType[] = [];\n\n for (const member of typeNode.members) {\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)\n : { kind: 'any' as const };\n properties.push({ name, type, optional });\n }\n // 索引签名:[key: string]: T → 转为 record\n if (ts.isIndexSignatureDeclaration(member)) {\n const keyType = member.parameters[0]?.type\n ? resolveTypeNode(member.parameters[0].type, checker, visited)\n : { kind: 'any' as const };\n const valueType = member.type\n ? resolveTypeNode(member.type, checker, visited)\n : { kind: 'any' as const };\n return { kind: 'record', key: keyType, value: valueType };\n }\n }\n\n return { 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 * 解析类型引用(Date / 自定义 interface / Array<T> / Record<K,V> 等)\n */\nfunction resolveTypeReference(\n typeNode: ts.TypeReferenceNode,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n): RuntimeType {\n const typeName = typeNode.typeName.getText();\n\n // Date 类型\n if (typeName === 'Date') {\n return { kind: 'date' };\n }\n\n // Array<T>\n if (typeName === 'Array' && typeNode.typeArguments?.length === 1) {\n return {\n kind: 'array',\n element: resolveTypeNode(typeNode.typeArguments[0], checker, visited),\n };\n }\n\n // Record<K, V>\n if (typeName === 'Record' && typeNode.typeArguments?.length === 2) {\n return {\n kind: 'record',\n key: resolveTypeNode(typeNode.typeArguments[0], checker, visited),\n value: resolveTypeNode(typeNode.typeArguments[1], checker, visited),\n };\n }\n\n // Partial<T> / Required<T> / Readonly<T> — best effort,解析内部类型\n if (\n (typeName === 'Partial' || typeName === 'Required' || typeName === 'Readonly') &&\n typeNode.typeArguments?.length === 1\n ) {\n const inner = resolveTypeNode(typeNode.typeArguments[0], checker, visited);\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 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);\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));\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> / Set<T> — 运行时无法校验\n if (\n typeName === 'Map' ||\n typeName === 'Set' ||\n typeName === 'WeakMap' ||\n typeName === 'WeakSet'\n ) {\n throw new SchemaExtractionError(\n typeNode.getText(),\n `${typeName} 运行时无法校验,请改用对象或数组`,\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 typeNode.typeName.kind === ts.SyntaxKind.Identifier\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(declaration, checker, visited);\n }\n // type 别名声明\n if (ts.isTypeAliasDeclaration(declaration)) {\n return resolveTypeNode(declaration.type, checker, visited);\n }\n // enum 声明 → 字面量联合\n if (ts.isEnumDeclaration(declaration)) {\n return resolveEnumDeclaration(declaration);\n }\n }\n }\n }\n\n throw new SchemaExtractionError(typeNode.getText(), `无法解析的引用类型 \"${typeName}\"`);\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 */\nfunction 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 */\nexport function resolveInterfaceDeclaration(\n node: ts.InterfaceDeclaration,\n checker?: ts.TypeChecker,\n visited: Set<string> = new Set(),\n): RuntimeType {\n const properties: PropertyType[] = [];\n const propMap = new Map<string, PropertyType>();\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);\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 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)\n : { kind: 'any' as const };\n propMap.set(name, { name, type, optional });\n }\n // 索引签名 → record\n if (ts.isIndexSignatureDeclaration(member)) {\n const keyType = member.parameters[0]?.type\n ? resolveTypeNode(member.parameters[0].type, checker, visited)\n : { kind: 'any' as const };\n const valueType = member.type\n ? resolveTypeNode(member.type, checker, visited)\n : { kind: 'any' as const };\n return { kind: 'record', key: keyType, value: valueType };\n }\n }\n\n for (const prop of propMap.values()) {\n properties.push(prop);\n }\n\n return { kind: 'object', properties };\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","import type { ValidatorFn } from '../ast/generateValidatorCode';\nimport type { PropertyType } from '../ast/resolveTypeNode';\n\n/**\n * 单个 schema 条目\n * - SchemaEntry:有类型声明,包含 properties(用于 coerce)和 validator(用于校验)\n * - null:handler 无类型声明(如 GET() 无参数),跳过校验\n */\nexport type SchemaEntry = { properties: PropertyType[]; validator: ValidatorFn } | null;\n\n/**\n * 单个文件的所有 schema:schemaName -> SchemaEntry\n * schemaName 由 method + inputType 生成,如 'GETQuery'、'POSTBody'\n */\nexport type FileSchemas = Map<string, SchemaEntry>;\n\n/**\n * 完整 manifest:filePath -> FileSchemas\n */\nexport type SchemaManifest = Map<string, FileSchemas>;\n\n/**\n * Schema 注册表(单例)\n *\n * 统一管理 handler 的校验函数,作为 validateInput 的唯一数据来源。\n * - prd 模式:从 dist/faapi-schema.js import 加载\n * - dev 模式:启动时全量提取并生成函数,watch 时全量重建\n *\n * 三种状态区分:\n * - get() 返回 SchemaEntry:有类型声明,校验\n * - get() 返回 null:无类型声明,跳过校验\n * - get() 返回 undefined:manifest 不完整,抛错\n */\nclass SchemaRegistry {\n private manifest: SchemaManifest = new Map();\n\n /**\n * 批量加载 manifest\n * 覆盖已有数据\n */\n loadManifest(manifest: SchemaManifest): void {\n this.manifest.clear();\n for (const [filePath, fileSchemas] of manifest) {\n const copy: FileSchemas = new Map();\n fileSchemas.forEach((value, key) => copy.set(key, value));\n this.manifest.set(filePath, copy);\n }\n }\n\n /**\n * 查询单条 schema\n * @returns SchemaEntry | null | undefined\n * - SchemaEntry:有类型声明\n * - null:无类型声明(跳过校验)\n * - undefined:manifest 不完整(抛错)\n */\n get(filePath: string, schemaName: string): SchemaEntry | undefined {\n const fileSchemas = this.manifest.get(filePath);\n if (!fileSchemas) return undefined;\n return fileSchemas.get(schemaName);\n }\n\n /**\n * 设置单个文件的所有 schema\n * 覆盖该文件的已有数据\n */\n set(filePath: string, schemas: FileSchemas): void {\n const copy: FileSchemas = new Map();\n schemas.forEach((value, key) => copy.set(key, value));\n this.manifest.set(filePath, copy);\n }\n\n /**\n * 删除单个文件(文件被删除时)\n */\n delete(filePath: string): void {\n this.manifest.delete(filePath);\n }\n\n /**\n * 判断文件是否已注册\n */\n hasFile(filePath: string): boolean {\n return this.manifest.has(filePath);\n }\n\n /**\n * 清空(测试用 / watch 全量重建前)\n */\n clear(): void {\n this.manifest.clear();\n }\n\n /**\n * 已注册的文件数量\n */\n get size(): number {\n return this.manifest.size;\n }\n}\n\n/**\n * 全局单例\n */\nexport const schemaRegistry = new SchemaRegistry();\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 { schemaRegistry } from './schemaRegistry';\nimport { getSchemaName } from './schemaName';\nimport type { RuntimeType } from '../ast/resolveTypeNode';\n\n/**\n * 单个参数的 schema 描述(简化版,供扩展包消费)\n */\nexport interface SchemaPropertyDescriptor {\n name: string;\n type: string;\n required: boolean;\n}\n\n/**\n * 单个输入源的 schema 描述\n */\nexport interface InputSchemaDescriptor {\n /** 输入源类型名(如 'Query'、'CreateUserBody'),无类型声明时 null */\n schemaName: string | null;\n /** 参数列表 */\n properties: SchemaPropertyDescriptor[];\n}\n\n/**\n * 查询指定路由 handler 的输入参数 schema\n *\n * 复用 schemaRegistry 已有的类型提取结果,避免重复 AST 分析。\n * 在 schema 尚未注册时返回 undefined。\n *\n * @param filePath handler 文件绝对路径\n * @param method HTTP 方法(GET/POST 等)\n * @param inputType 输入源类型(query/body/params)\n */\nexport function getSchemaProperties(\n filePath: string,\n method: string,\n inputType: 'query' | 'body' | 'params',\n): InputSchemaDescriptor | undefined {\n const schemaName = getSchemaName(method, inputType);\n const entry = schemaRegistry.get(filePath, schemaName);\n\n // undefined = manifest 不完整;null = 无类型声明\n if (entry === undefined) return undefined;\n if (entry === null) {\n return { schemaName: null, properties: [] };\n }\n\n return {\n schemaName: schemaName,\n properties: entry.properties.map((prop) => ({\n name: prop.name,\n type: runtimeTypeToString(prop.type),\n required: !prop.optional,\n })),\n };\n}\n\n/**\n * 将 RuntimeType 转换为可读字符串\n */\nfunction runtimeTypeToString(type: RuntimeType): string {\n switch (type.kind) {\n case 'string':\n case 'number':\n case 'boolean':\n case 'bigint':\n case 'null':\n case 'undefined':\n case 'date':\n return type.kind;\n case 'literal':\n return JSON.stringify(type.value);\n case 'array':\n return `${runtimeTypeToString(type.element)}[]`;\n case 'tuple':\n return `[${type.elements.map((e) => (e.rest ? '...' : '') + runtimeTypeToString(e.type) + (e.optional ? '?' : '')).join(', ')}]`;\n case 'object':\n return 'object';\n case 'union':\n return type.members.map(runtimeTypeToString).join(' | ');\n case 'record':\n return `Record<${runtimeTypeToString(type.key)}, ${runtimeTypeToString(type.value)}>`;\n case 'ref':\n return type.name;\n case 'any':\n case 'unknown':\n return 'unknown';\n default:\n return 'unknown';\n }\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 if (!allowOrigin) {\n await next();\n return;\n }\n\n // 设置 CORS 响应头\n ctx.setHeader('Access-Control-Allow-Origin', allowOrigin);\n\n // 当 origin 为动态值(true 或数组)时,必须设置 Vary: Origin,\n // 防止 CDN/浏览器错误缓存针对某个 Origin 的响应给其他 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 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 LoggerOptions {\n /** 自定义日志输出函数,默认 console.log */\n log?: (message: string) => void;\n}\n\n/**\n * 创建请求日志中间件(洋葱模型)\n *\n * 日志格式:GET /api/users 200 12ms\n * 错误格式:POST /api/users 400 45ms - Error: ...\n *\n * before/after 一体,闭包变量共享开始时间,无需污染 ctx。\n * 错误用 try/catch 捕获,记录后重新抛出(让上层处理)。\n * 成功时从 next() 返回的 Response 读取状态码。\n */\nexport function logger(options: LoggerOptions = {}): FaapiMiddleware {\n const { log = console.log } = options;\n\n return async (ctx, next) => {\n const start = Date.now();\n try {\n const response = await next();\n const duration = Date.now() - start;\n log(`${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 // 从错误对象获取实际状态码(FaapiError 有 statusCode 字段),默认 500\n const status = (err as { statusCode?: number })?.statusCode ?? 500;\n log(`${ctx.method} ${ctx.path} ${status} ${duration}ms - ${message}`);\n throw err; // 重新抛出,让上层处理\n }\n };\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport type { FaapiConfig } from './configTypes';\n\n/**\n * 配置文件查找顺序\n *\n * 1. faapi.config.ts / faapi.config.js(基础配置)\n * 2. faapi.config.{env}.ts / faapi.config.{env}.js(环境覆盖)\n *\n * 环境覆盖深度合并到基础配置上(见 deepMerge)\n */\nconst BASE_CONFIG_FILES = ['faapi.config.ts', 'faapi.config.js'];\n\n/**\n * 获取当前运行环境(用于加载 faapi.config.{env}.ts)\n *\n * 优先级:FAAPI_ENV → NODE_ENV → 'development'\n *\n * FAAPI_ENV 优先:让 faapi 的环境切换不污染全局 NODE_ENV(其他库也读 NODE_ENV)。\n * FAAPI_ENV 未设时回退 NODE_ENV,符合 Node 生态默认直觉。\n */\nfunction getEnv(): string {\n return process.env.FAAPI_ENV || process.env.NODE_ENV || 'development';\n}\n\n/**\n * 深度合并两个配置对象(后者覆盖前者)\n *\n * 特殊对象(Date / RegExp / Map / Set)直接替换,不递归合并。\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n override: Record<string, unknown>,\n): T {\n const result = { ...base };\n for (const key of Object.keys(override)) {\n const baseVal = (base as Record<string, unknown>)[key];\n const overVal = override[key];\n\n // 特殊对象类型直接替换(不递归合并)\n if (\n baseVal instanceof Date ||\n overVal instanceof Date ||\n baseVal instanceof RegExp ||\n overVal instanceof RegExp ||\n baseVal instanceof Map ||\n overVal instanceof Map ||\n baseVal instanceof Set ||\n overVal instanceof Set\n ) {\n (result as Record<string, unknown>)[key] = overVal;\n continue;\n }\n\n // 普通对象递归合并\n if (\n baseVal !== null &&\n overVal !== null &&\n typeof baseVal === 'object' &&\n typeof overVal === 'object' &&\n !Array.isArray(baseVal) &&\n !Array.isArray(overVal) &&\n !(baseVal instanceof Function) &&\n !(overVal instanceof Function)\n ) {\n (result as Record<string, unknown>)[key] = deepMerge(\n baseVal as Record<string, unknown>,\n overVal as Record<string, unknown>,\n );\n } else {\n (result as Record<string, unknown>)[key] = overVal;\n }\n }\n return result;\n}\n\n/**\n * 加载单个配置文件\n */\nasync function loadConfigFile(filePath: string): Promise<Partial<FaapiConfig> | null> {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n try {\n const url = pathToFileURL(filePath).href;\n const module = (await import(url)) as { default?: Partial<FaapiConfig> };\n return module.default ?? {};\n } catch (err) {\n throw new Error(\n `Failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\n/**\n * 加载 faapi 配置文件\n *\n * 查找顺序:\n * 1. 指定的 configPath\n * 2. faapi.config.ts / faapi.config.js(基础配置)\n * 3. faapi.config.{env}.ts / faapi.config.{env}.js(环境覆盖,深度合并)\n *\n * 环境由 FAAPI_ENV 或 NODE_ENV 决定,默认 'development'\n *\n * @param rootDir 项目根目录\n * @param configPath 指定的配置文件路径(可选)\n * @returns 合并后的配置,如果无配置文件则返回 null\n */\nexport async function loadConfig(\n rootDir: string,\n configPath?: string,\n): Promise<Partial<FaapiConfig> | null> {\n // 指定了配置文件路径时,直接加载该文件(文件不存在则抛错)\n if (configPath) {\n const resolvedPath = path.resolve(rootDir, configPath);\n if (!fs.existsSync(resolvedPath)) {\n throw new Error(`Config file not found: ${configPath}`);\n }\n return loadConfigFile(resolvedPath);\n }\n\n // 查找基础配置文件\n let baseConfig: Partial<FaapiConfig> | null = null;\n for (const fileName of BASE_CONFIG_FILES) {\n const filePath = path.join(rootDir, fileName);\n baseConfig = await loadConfigFile(filePath);\n if (baseConfig) break;\n }\n\n if (!baseConfig) {\n return null;\n }\n\n // 查找环境配置文件并合并\n const env = getEnv();\n const envFiles = [`faapi.config.${env}.ts`, `faapi.config.${env}.js`];\n\n for (const envFile of envFiles) {\n const envConfig = await loadConfigFile(path.join(rootDir, envFile));\n if (envConfig) {\n baseConfig = deepMerge(baseConfig, envConfig);\n break;\n }\n }\n\n return baseConfig;\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AAQf,IAAM,eAAe,oBAAI,IAAwB;AAmB1C,SAAS,cAAc,UAA8B;AAC1D,QAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,GAAG,cAAc,CAAC,QAAQ,GAAG;AAAA,IAC3C,QAAQ;AAAA,IACR,QAAQ,GAAG,aAAa;AAAA,IACxB,QAAQ,GAAG,WAAW;AAAA,IACtB,kBAAkB,GAAG,qBAAqB;AAAA,IAC1C,cAAc;AAAA,IACd,QAAQ;AAAA,EACV,CAAC;AAED,eAAa,IAAI,UAAU,OAAO;AAClC,SAAO;AACT;;;AC5CA,OAAOA,SAAQ;;;ACAf,OAAOC,SAAQ;AAQR,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YACkB,UACA,QAChB,SACA;AACA,UAAM,yCAAW,QAAQ,MAAM,MAAM,IAAI,OAAO;AAJhC;AACA;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EANkB;AAAA,EACA;AAMpB;AAgEO,SAAS,gBACd,UACA,SACA,UAAuB,oBAAI,IAAI,GAClB;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,OAAO;AAAA,IACjE;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,OAAO;AAEtD,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,OAAO;AAAA,UAC9C,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,OAAO;AAAA,UAC9C,UAAU;AAAA,UACV,MAAM;AAAA,QACR;AAAA,MACF;AAEA,aAAO;AAAA,QACL,MAAM,gBAAgB,GAAG,SAAS,OAAO;AAAA,QACzC,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,OAAO,CAAC;AAC9E,WAAO,EAAE,MAAM,SAAS,QAAQ;AAAA,EAClC;AAGA,MAAIA,IAAG,uBAAuB,QAAQ,GAAG;AACvC,UAAM,aAA6B,CAAC;AACpC,eAAW,KAAK,SAAS,OAAO;AAC9B,YAAM,WAAW,gBAAgB,GAAG,SAAS,OAAO;AACpD,UAAI,SAAS,SAAS,UAAU;AAC9B,mBAAW,KAAK,GAAG,SAAS,UAAU;AAAA,MACxC;AAAA,IACF;AACA,WAAO,EAAE,MAAM,UAAU,WAAW;AAAA,EACtC;AAGA,MAAIA,IAAG,kBAAkB,QAAQ,GAAG;AAClC,WAAO,mBAAmB,UAAU,SAAS,OAAO;AAAA,EACtD;AAGA,MAAIA,IAAG,mBAAmB,QAAQ,KAAK,SAAS,aAAaA,IAAG,WAAW,cAAc;AACvF,WAAO,aAAa,UAAU,OAAO;AAAA,EACvC;AAGA,MAAIA,IAAG,oBAAoB,QAAQ,GAAG;AACpC,WAAO,qBAAqB,UAAU,SAAS,OAAO;AAAA,EACxD;AAGA,QAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,kDAAU;AAChE;AAKA,SAAS,mBACP,UACA,SACA,UAAuB,oBAAI,IAAI,GAClB;AACb,QAAM,aAA6B,CAAC;AAEpC,aAAW,UAAU,SAAS,SAAS;AAErC,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,OAAO,IAC7C,EAAE,MAAM,MAAe;AAC3B,iBAAW,KAAK,EAAE,MAAM,MAAM,SAAS,CAAC;AAAA,IAC1C;AAEA,QAAIA,IAAG,4BAA4B,MAAM,GAAG;AAC1C,YAAM,UAAU,OAAO,WAAW,CAAC,GAAG,OAClC,gBAAgB,OAAO,WAAW,CAAC,EAAE,MAAM,SAAS,OAAO,IAC3D,EAAE,MAAM,MAAe;AAC3B,YAAM,YAAY,OAAO,OACrB,gBAAgB,OAAO,MAAM,SAAS,OAAO,IAC7C,EAAE,MAAM,MAAe;AAC3B,aAAO,EAAE,MAAM,UAAU,KAAK,SAAS,OAAO,UAAU;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,UAAU,WAAW;AACtC;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;AAKA,SAAS,qBACP,UACA,SACA,UAAuB,oBAAI,IAAI,GAClB;AACb,QAAM,WAAW,SAAS,SAAS,QAAQ;AAG3C,MAAI,aAAa,QAAQ;AACvB,WAAO,EAAE,MAAM,OAAO;AAAA,EACxB;AAGA,MAAI,aAAa,WAAW,SAAS,eAAe,WAAW,GAAG;AAChE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,gBAAgB,SAAS,cAAc,CAAC,GAAG,SAAS,OAAO;AAAA,IACtE;AAAA,EACF;AAGA,MAAI,aAAa,YAAY,SAAS,eAAe,WAAW,GAAG;AACjE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,gBAAgB,SAAS,cAAc,CAAC,GAAG,SAAS,OAAO;AAAA,MAChE,OAAO,gBAAgB,SAAS,cAAc,CAAC,GAAG,SAAS,OAAO;AAAA,IACpE;AAAA,EACF;AAGA,OACG,aAAa,aAAa,aAAa,cAAc,aAAa,eACnE,SAAS,eAAe,WAAW,GACnC;AACA,UAAM,QAAQ,gBAAgB,SAAS,cAAc,CAAC,GAAG,SAAS,OAAO;AACzE,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,WAAO;AAAA,EACT;AAGA,OAAK,aAAa,UAAU,aAAa,WAAW,SAAS,eAAe,WAAW,GAAG;AACxF,UAAM,YAAY,gBAAgB,SAAS,cAAc,CAAC,GAAG,SAAS,OAAO;AAC7E,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,OAAO,CAAC;AAC5E,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,MACE,aAAa,SACb,aAAa,SACb,aAAa,aACb,aAAa,WACb;AACA,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,SACJ,SAAS,SAAS,SAASA,IAAG,WAAW,aACrC,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,4BAA4B,aAAa,SAAS,OAAO;AAAA,QAClE;AAEA,YAAIA,IAAG,uBAAuB,WAAW,GAAG;AAC1C,iBAAO,gBAAgB,YAAY,MAAM,SAAS,OAAO;AAAA,QAC3D;AAEA,YAAIA,IAAG,kBAAkB,WAAW,GAAG;AACrC,iBAAO,uBAAuB,WAAW;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,sBAAsB,SAAS,QAAQ,GAAG,2DAAc,QAAQ,GAAG;AAC/E;AAWA,SAAS,uBAAuB,MAAuC;AACrE,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;AAKO,SAAS,4BACd,MACA,SACA,UAAuB,oBAAI,IAAI,GAClB;AACb,QAAM,aAA6B,CAAC;AACpC,QAAM,UAAU,oBAAI,IAA0B;AAG9C,aAAW,kBAAkB,KAAK,mBAAmB,CAAC,GAAG;AACvD,QAAI,eAAe,UAAUA,IAAG,WAAW,gBAAgB;AACzD,iBAAW,QAAQ,eAAe,OAAO;AACvC,cAAM,aAAa,gBAAgB,MAAM,SAAS,OAAO;AACzD,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;AACjC,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,OAAO,IAC7C,EAAE,MAAM,MAAe;AAC3B,cAAQ,IAAI,MAAM,EAAE,MAAM,MAAM,SAAS,CAAC;AAAA,IAC5C;AAEA,QAAIA,IAAG,4BAA4B,MAAM,GAAG;AAC1C,YAAM,UAAU,OAAO,WAAW,CAAC,GAAG,OAClC,gBAAgB,OAAO,WAAW,CAAC,EAAE,MAAM,SAAS,OAAO,IAC3D,EAAE,MAAM,MAAe;AAC3B,YAAM,YAAY,OAAO,OACrB,gBAAgB,OAAO,MAAM,SAAS,OAAO,IAC7C,EAAE,MAAM,MAAe;AAC3B,aAAO,EAAE,MAAM,UAAU,KAAK,SAAS,OAAO,UAAU;AAAA,IAC1D;AAAA,EACF;AAEA,aAAW,QAAQ,QAAQ,OAAO,GAAG;AACnC,eAAW,KAAK,IAAI;AAAA,EACtB;AAEA,SAAO,EAAE,MAAM,UAAU,WAAW;AACtC;;;AD/iBO,SAAS,gBACd,SACA,UACA,UACwB;AACxB,QAAM,aAAa,QAAQ,cAAc,QAAQ;AACjD,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,UAAU,QAAQ,eAAe;AAGvC,MAAI,SAAiC;AAErC,EAAAC,IAAG,aAAa,YAAY,CAAC,SAAS;AACpC,QAAI,OAAQ;AAEZ,QAAIA,IAAG,uBAAuB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAClE,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,IAAI,QAAQ;AACpB,YAAM,cAAc;AAAA,QAAgB;AAAA,QAAU;AAAA,QAAU,MACtD,4BAA4B,MAAM,SAAS,OAAO;AAAA,MACpD;AACA,eAAS;AAAA,QACP,MAAM;AAAA,QACN,YAAY,YAAY,SAAS,WAAW,YAAY,aAAa,CAAC;AAAA,QACtE;AAAA,MACF;AACA;AAAA,IACF;AAGA,QAAIA,IAAG,uBAAuB,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAClE,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,IAAI,QAAQ;AACpB,YAAM,cAAc;AAAA,QAAgB;AAAA,QAAU;AAAA,QAAU,MACtD,gBAAgB,KAAK,MAAM,SAAS,OAAO;AAAA,MAC7C;AACA,eAAS;AAAA,QACP,MAAM;AAAA,QACN,YAAY,YAAY,SAAS,WAAW,YAAY,aAAa,CAAC;AAAA,QACtE;AAAA,MACF;AACA;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AA2DA,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,MACf;AACA,YAAM;AAAA,IACR;AACA,UAAM;AAAA,EACR;AACF;;;AE7IO,SAAS,sBAAsB,QAAkC;AACtE,QAAM,QAAQ,OAAO,YAAY;AACjC,MAAI,UAAU,SAAS,UAAU,YAAY,UAAU,QAAQ;AAC7D,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;ACiBA,IAAM,iBAAN,MAAqB;AAAA,EACX,WAA2B,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAM3C,aAAa,UAAgC;AAC3C,SAAK,SAAS,MAAM;AACpB,eAAW,CAAC,UAAU,WAAW,KAAK,UAAU;AAC9C,YAAM,OAAoB,oBAAI,IAAI;AAClC,kBAAY,QAAQ,CAAC,OAAO,QAAQ,KAAK,IAAI,KAAK,KAAK,CAAC;AACxD,WAAK,SAAS,IAAI,UAAU,IAAI;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,IAAI,UAAkB,YAA6C;AACjE,UAAM,cAAc,KAAK,SAAS,IAAI,QAAQ;AAC9C,QAAI,CAAC,YAAa,QAAO;AACzB,WAAO,YAAY,IAAI,UAAU;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,UAAkB,SAA4B;AAChD,UAAM,OAAoB,oBAAI,IAAI;AAClC,YAAQ,QAAQ,CAAC,OAAO,QAAQ,KAAK,IAAI,KAAK,KAAK,CAAC;AACpD,SAAK,SAAS,IAAI,UAAU,IAAI;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAwB;AAC7B,SAAK,SAAS,OAAO,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,UAA2B;AACjC,WAAO,KAAK,SAAS,IAAI,QAAQ;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAKO,IAAM,iBAAiB,IAAI,eAAe;;;AClG1C,SAAS,cAAc,QAAgB,WAAgD;AAC5F,SAAO,GAAG,OAAO,YAAY,CAAC,GAAG,UAAU,OAAO,CAAC,EAAE,YAAY,IAAI,UAAU,MAAM,CAAC,CAAC;AACzF;;;ACyBO,SAAS,oBACd,UACA,QACA,WACmC;AACnC,QAAM,aAAa,cAAc,QAAQ,SAAS;AAClD,QAAM,QAAQ,eAAe,IAAI,UAAU,UAAU;AAGrD,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,UAAU,MAAM;AAClB,WAAO,EAAE,YAAY,MAAM,YAAY,CAAC,EAAE;AAAA,EAC5C;AAEA,SAAO;AAAA,IACL;AAAA,IACA,YAAY,MAAM,WAAW,IAAI,CAAC,UAAU;AAAA,MAC1C,MAAM,KAAK;AAAA,MACX,MAAM,oBAAoB,KAAK,IAAI;AAAA,MACnC,UAAU,CAAC,KAAK;AAAA,IAClB,EAAE;AAAA,EACJ;AACF;AAKA,SAAS,oBAAoB,MAA2B;AACtD,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,KAAK;AAAA,IACd,KAAK;AACH,aAAO,KAAK,UAAU,KAAK,KAAK;AAAA,IAClC,KAAK;AACH,aAAO,GAAG,oBAAoB,KAAK,OAAO,CAAC;AAAA,IAC7C,KAAK;AACH,aAAO,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,EAAE,OAAO,QAAQ,MAAM,oBAAoB,EAAE,IAAI,KAAK,EAAE,WAAW,MAAM,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,IAC/H,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,KAAK,QAAQ,IAAI,mBAAmB,EAAE,KAAK,KAAK;AAAA,IACzD,KAAK;AACH,aAAO,UAAU,oBAAoB,KAAK,GAAG,CAAC,KAAK,oBAAoB,KAAK,KAAK,CAAC;AAAA,IACpF,KAAK;AACH,aAAO,KAAK;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;AC/EA,IAAM,kBAAkB,CAAC,OAAO,QAAQ,OAAO,SAAS,UAAU,QAAQ,SAAS;AAW5E,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;AAEA,QAAI,CAAC,aAAa;AAChB,YAAM,KAAK;AACX;AAAA,IACF;AAGA,QAAI,UAAU,+BAA+B,WAAW;AAIxD,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,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;;;ACpFO,SAAS,OAAO,UAAyB,CAAC,GAAoB;AACnE,QAAM,EAAE,MAAM,QAAQ,IAAI,IAAI;AAE9B,SAAO,OAAO,KAAK,SAAS;AAC1B,UAAM,QAAQ,KAAK,IAAI;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,KAAK;AAC5B,YAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,UAAI,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,SAAS,MAAM,IAAI,QAAQ,IAAI;AAChE,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAE/D,YAAM,SAAU,KAAiC,cAAc;AAC/D,UAAI,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,MAAM,IAAI,QAAQ,QAAQ,OAAO,EAAE;AACpE,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;ACpCA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAW9B,IAAM,oBAAoB,CAAC,mBAAmB,iBAAiB;AAU/D,SAAS,SAAiB;AACxB,SAAO,QAAQ,IAAI,aAAa,QAAQ,IAAI,YAAY;AAC1D;AAOO,SAAS,UACd,MACA,UACG;AACH,QAAM,SAAS,EAAE,GAAG,KAAK;AACzB,aAAW,OAAO,OAAO,KAAK,QAAQ,GAAG;AACvC,UAAM,UAAW,KAAiC,GAAG;AACrD,UAAM,UAAU,SAAS,GAAG;AAG5B,QACE,mBAAmB,QACnB,mBAAmB,QACnB,mBAAmB,UACnB,mBAAmB,UACnB,mBAAmB,OACnB,mBAAmB,OACnB,mBAAmB,OACnB,mBAAmB,KACnB;AACA,MAAC,OAAmC,GAAG,IAAI;AAC3C;AAAA,IACF;AAGA,QACE,YAAY,QACZ,YAAY,QACZ,OAAO,YAAY,YACnB,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,OAAO,KACtB,CAAC,MAAM,QAAQ,OAAO,KACtB,EAAE,mBAAmB,aACrB,EAAE,mBAAmB,WACrB;AACA,MAAC,OAAmC,GAAG,IAAI;AAAA,QACzC;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AACL,MAAC,OAAmC,GAAG,IAAI;AAAA,IAC7C;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAe,eAAe,UAAwD;AACpF,MAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,MAAM,cAAc,QAAQ,EAAE;AACpC,UAAM,SAAU,MAAM,OAAO;AAC7B,WAAO,OAAO,WAAW,CAAC;AAAA,EAC5B,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,8BAA8B,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AACF;AAgBA,eAAsB,WACpB,SACA,YACsC;AAEtC,MAAI,YAAY;AACd,UAAM,eAAe,KAAK,QAAQ,SAAS,UAAU;AACrD,QAAI,CAAC,GAAG,WAAW,YAAY,GAAG;AAChC,YAAM,IAAI,MAAM,0BAA0B,UAAU,EAAE;AAAA,IACxD;AACA,WAAO,eAAe,YAAY;AAAA,EACpC;AAGA,MAAI,aAA0C;AAC9C,aAAW,YAAY,mBAAmB;AACxC,UAAM,WAAW,KAAK,KAAK,SAAS,QAAQ;AAC5C,iBAAa,MAAM,eAAe,QAAQ;AAC1C,QAAI,WAAY;AAAA,EAClB;AAEA,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAGA,QAAM,MAAM,OAAO;AACnB,QAAM,WAAW,CAAC,gBAAgB,GAAG,OAAO,gBAAgB,GAAG,KAAK;AAEpE,aAAW,WAAW,UAAU;AAC9B,UAAM,YAAY,MAAM,eAAe,KAAK,KAAK,SAAS,OAAO,CAAC;AAClE,QAAI,WAAW;AACb,mBAAa,UAAU,YAAY,SAAS;AAC5C;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":["ts","ts","ts"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faapi/faapi",
3
- "version": "0.0.0-canary.0f443f9",
3
+ "version": "0.0.0-canary.c6dc0f7",
4
4
  "description": "函数即接口 - Function as API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,7 @@
35
35
  "tsx": "^4.19.0",
36
36
  "tsup": "^8.4.0",
37
37
  "vitest": "^3.1.0",
38
- "@faapi/schema": "0.0.0-canary.0f443f9"
38
+ "@faapi/schema": "0.0.0-canary.c6dc0f7"
39
39
  },
40
40
  "license": "MIT",
41
41
  "repository": {