@faapi/faapi 0.0.0-canary.22b65a2 → 0.0.0-canary.a3f7014

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
@@ -381,6 +381,18 @@ interface RouteInputSchema {
381
381
  schemaName: string | null;
382
382
  properties: RouteParamSchema[];
383
383
  }
384
+ /**
385
+ * 路由响应类型的 schema 描述
386
+ *
387
+ * 由 @faapi/schema 扩展包的 buildRouteSchemas 生成。
388
+ * output 为 null 表示无显式返回类型注解、void/Promise<void>、或解析失败降级。
389
+ */
390
+ interface RouteOutputSchema {
391
+ /** 命名类型名(如 'UserResponse'),内联类型为 null */
392
+ schemaName: string | null;
393
+ /** 顶层属性列表 */
394
+ properties: RouteParamSchema[];
395
+ }
384
396
  /**
385
397
  * 路由的完整 schema 描述
386
398
  *
@@ -393,6 +405,8 @@ interface RouteInfo {
393
405
  filePath: string;
394
406
  isDynamic: boolean;
395
407
  inputs: RouteInputSchema[];
408
+ /** 响应类型描述(null 表示无返回类型注解/void/解析失败) */
409
+ output: RouteOutputSchema | null;
396
410
  }
397
411
 
398
412
  /** HTTP 请求 handler 类型 */
@@ -410,8 +424,10 @@ type UpgradeHandler = (req: IncomingMessage, socket: Socket, head: Buffer) => vo
410
424
  interface PluginContext {
411
425
  /** 项目根目录 */
412
426
  rootDir: string;
413
- /** 当前路由清单 */
427
+ /** 当前路由清单(setup 时的快照,reloadRoutes 后不会更新;需最新路由用 getRoutes()) */
414
428
  routes: RouteManifest;
429
+ /** 获取最新路由清单(reloadRoutes 后返回更新后的数组) */
430
+ getRoutes: () => RouteManifest;
415
431
  /** HTTP 服务器实例(未 listen) */
416
432
  server: Server;
417
433
  /** 自定义业务配置(faapi.config.ts 中的自定义 key) */
@@ -487,20 +503,6 @@ interface Http2Options {
487
503
  cert?: string;
488
504
  }
489
505
 
490
- /**
491
- * 统一响应格式化函数
492
- *
493
- * 当配置了 responseFormat 时,handler 返回的非 Response 值会经过此函数包装
494
- * 例如:{ code: 0, data, message: 'success' }
495
- */
496
- type ResponseFormatFn = (data: unknown, ctx: FaapiContext) => unknown;
497
- /**
498
- * 错误响应格式化函数
499
- *
500
- * 优先于内置 formatErrorResponse 处理错误。返回 Response 表示已处理;
501
- * 返回 null/undefined 表示不处理,由内置 formatErrorResponse 兜底。
502
- */
503
- type ErrorFormatFn = (error: unknown, ctx?: FaapiContext) => Response | null | undefined;
504
506
  /**
505
507
  * 生命周期钩子
506
508
  */
@@ -510,17 +512,17 @@ interface LifecycleHooks {
510
512
  /** 服务器关闭时调用(适合清理资源、优雅关闭) */
511
513
  onClose?: (ctx: LifecycleContext) => Promise<void> | void;
512
514
  /**
513
- * 请求错误已被 errorFormat 处理为响应后调用(参考 Fastify onError 语义)
515
+ * 请求错误已被处理为响应后调用(参考 Fastify onError 语义)
514
516
  *
515
- * 时机:handler 抛错 → errorFormat 生成错误响应(失败则由框架内置 formatErrorResponse 兜底)
517
+ * 时机:handler 抛错 → 全局中间件 try/catch(若有) → 框架内置 formatErrorResponse 兜底
516
518
  * → 响应发出后 → onError 触发副作用
517
519
  *
518
- * 职责:日志上报、告警、链路追踪等副作用。**不修改、不替换已生成的响应**。
519
- * 自身抛错会被捕获并忽略,不影响响应已发送的事实。
520
+ * 职责:日志上报、告警、链路追踪等副作用。**不修改、不替换已生成的响应**。
521
+ * 自身抛错会被捕获并忽略,不影响响应已发送的事实。
520
522
  *
521
- * 与 errorFormat 的区别:
522
- * - errorFormat:把 error 翻译成 Response(主入口,决定响应内容)
523
- * - onError:响应发出后的副作用(不能改响应)
523
+ * 与全局错误中间件的区别:
524
+ * - 全局错误中间件:把 error 翻译成 Response(主入口,决定响应内容)
525
+ * - onError:响应发出后的副作用(不能改响应)
524
526
  */
525
527
  onError?: (error: unknown, ctx: FaapiContext) => Promise<void> | void;
526
528
  }
@@ -566,10 +568,6 @@ interface LifecycleContext {
566
568
  interface FaapiConfig {
567
569
  /** CORS 配置,false 禁用 */
568
570
  cors?: CorsOptions | boolean;
569
- /** 统一响应格式化函数 */
570
- responseFormat?: ResponseFormatFn;
571
- /** 错误响应格式化函数 */
572
- errorFormat?: ErrorFormatFn;
573
571
  /** 生命周期钩子 */
574
572
  lifecycle?: LifecycleHooks;
575
573
  /** 安全头配置,false 禁用 */
@@ -831,7 +829,79 @@ interface PropertyType {
831
829
  name: string;
832
830
  type: RuntimeType;
833
831
  optional: boolean;
832
+ /**
833
+ * 字段级 JSDoc 约束标签(@max/@min/@maxLength 等)
834
+ *
835
+ * 来自字段 JSDoc 注释,由 generateZodSchema 转为 zod 链式调用。
836
+ * 约束与字段类型不匹配时在提取阶段抛 SchemaExtractionError。
837
+ */
838
+ constraints?: TypeConstraint[];
834
839
  }
840
+ /**
841
+ * JSDoc 约束标签的运行时描述
842
+ *
843
+ * 由字段 JSDoc 注释提取,对应 zod schema 的链式约束方法。
844
+ * 仅在 PropertyType.constraints 中出现,不挂在嵌套类型(array 元素、tuple 元素等)上。
845
+ */
846
+ type TypeConstraint = {
847
+ kind: 'max';
848
+ value: number;
849
+ } | {
850
+ kind: 'min';
851
+ value: number;
852
+ } | {
853
+ kind: 'int';
854
+ } | {
855
+ kind: 'positive';
856
+ } | {
857
+ kind: 'negative';
858
+ } | {
859
+ kind: 'nonnegative';
860
+ } | {
861
+ kind: 'nonpositive';
862
+ } | {
863
+ kind: 'maxLength';
864
+ value: number;
865
+ } | {
866
+ kind: 'minLength';
867
+ value: number;
868
+ } | {
869
+ kind: 'length';
870
+ value: number;
871
+ } | {
872
+ kind: 'regex';
873
+ pattern: string;
874
+ flags?: string;
875
+ } | {
876
+ kind: 'email';
877
+ } | {
878
+ kind: 'url';
879
+ } | {
880
+ kind: 'uuid';
881
+ };
882
+ /**
883
+ * 将 TypeScript 类型节点解析为运行时类型描述
884
+ *
885
+ * 支持的类型:
886
+ * - 基础类型:string / number / boolean / null / undefined / any / unknown / void
887
+ * - bigint:不支持(HTTP/JSON 不能传输),AST 提取阶段抛 SchemaExtractionError
888
+ * - 字面量类型:'foo' / 42 / true
889
+ * - 数组类型:T[] / Array<T> / ReadonlyArray<T> / readonly T[]
890
+ * - 元组类型:[string, number] / [string, number?] / [string, ...number[]] / readonly [T, U](按位置校验)
891
+ * - 对象类型:{ name: string; age?: number }(含 readonly 字段修饰符,忽略 readonly)
892
+ * - 联合类型:string | null
893
+ * - 交叉类型:A & B(按对象合并处理)
894
+ * - 引用类型:Date / 其他 interface(递归解析)
895
+ * - 工具类型:Record<K, V> / Partial<T> / Readonly<T>(best effort)
896
+ * - Pick<T, K> / Omit<T, K>:K 支持字面量联合、类型别名、keyof T
897
+ *
898
+ * readonly 是编译期约束,运行时不产生校验语义,所有 readonly 修饰符统一忽略。
899
+ *
900
+ * @param typeNode TypeScript 类型节点
901
+ * @param checker 类型 checker(用于解析引用类型)
902
+ * @param visited 防止递归循环
903
+ */
904
+ declare function resolveTypeNode(typeNode: ts.TypeNode, checker?: ts.TypeChecker, visited?: Set<string>): RuntimeType;
835
905
 
836
906
  interface HandlerTypeInfo {
837
907
  name: string;
@@ -920,6 +990,60 @@ declare function collectRouteSchemaSources(routes: RouteManifest, rootDir?: stri
920
990
  */
921
991
  declare function loadConfig(rootDir: string, outDir: string): Promise<Partial<FaapiConfig> | null>;
922
992
 
993
+ declare const VALIDATION_ERROR = "VALIDATION_ERROR";
994
+ declare const ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND";
995
+ declare const METHOD_NOT_ALLOWED = "METHOD_NOT_ALLOWED";
996
+ declare const INTERNAL_ERROR = "INTERNAL_ERROR";
997
+ declare const MODULE_LOAD_ERROR = "MODULE_LOAD_ERROR";
998
+ type ErrorCode = typeof VALIDATION_ERROR | typeof ROUTE_NOT_FOUND | typeof METHOD_NOT_ALLOWED | typeof INTERNAL_ERROR | typeof MODULE_LOAD_ERROR;
999
+
1000
+ declare class FaapiError extends Error {
1001
+ readonly code: ErrorCode;
1002
+ readonly statusCode: number;
1003
+ constructor(code: ErrorCode, message: string, statusCode: number);
1004
+ }
1005
+
1006
+ declare class ValidationError extends FaapiError {
1007
+ readonly issues: ValidationIssue[];
1008
+ constructor(message: string, issues: ValidationIssue[]);
1009
+ }
1010
+ declare class RouteNotFoundError extends FaapiError {
1011
+ constructor(path: string);
1012
+ }
1013
+ declare class MethodNotAllowedError extends FaapiError {
1014
+ readonly allowedMethods: string[];
1015
+ constructor(method: string, path: string, allowedMethods: string[]);
1016
+ }
1017
+ declare class InternalError extends FaapiError {
1018
+ constructor(message: string);
1019
+ }
1020
+ declare class ModuleLoadError extends FaapiError {
1021
+ constructor(filePath: string, reason: string);
1022
+ }
1023
+ /**
1024
+ * 校验问题类型
1025
+ *
1026
+ * 结构化错误信息,便于上层(全局错误中间件/前端)按 code 做不同处理,
1027
+ * 不依赖字符串解析。message 仅为人类可读的兜底描述。
1028
+ *
1029
+ * code 与 HTTP 状态码的映射(由 ValidationError 推导):
1030
+ * - INVALID_FORMAT / MISSING_FIELD → 400 Bad Request
1031
+ * - TYPE_MISMATCH / INVALID_VALUE / COERCE_FAILED → 422 Unprocessable Entity
1032
+ */
1033
+ interface ValidationIssue {
1034
+ /** 字段路径,如 'user.address.city' */
1035
+ path: string;
1036
+ /** 错误码,机器可读的契约 */
1037
+ code: ValidationErrorCode;
1038
+ /** 期望类型/值,如 'number' / '"admin" | "user"' */
1039
+ expected: string;
1040
+ /** 实际类型/值,如 'string' / 'undefined' */
1041
+ received: string;
1042
+ /** 人类可读的本地化消息(兜底,不保证稳定) */
1043
+ message: string;
1044
+ }
1045
+ type ValidationErrorCode = 'TYPE_MISMATCH' | 'MISSING_FIELD' | 'INVALID_FORMAT' | 'INVALID_VALUE' | 'COERCE_FAILED';
1046
+
923
1047
  interface InjectOptions {
924
1048
  method?: string;
925
1049
  path?: string;
@@ -1013,4 +1137,4 @@ type ProdApp = AppBase;
1013
1137
  */
1014
1138
  declare function createProdApp(options?: CreateAppOptions): Promise<ProdApp>;
1015
1139
 
1016
- export { type ProdApp as App, type CorsOptions, type CreateAppOptions, type DevApp, type ErrorFormatFn, type FaapiConfig, type FaapiContext, type FaapiContextConfig, type FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, type HelmetOptions, type InjectOptions, type InjectResponse, type Injector, type InjectorMap, type LifecycleContext, type LifecycleHooks, type LoggerOptions, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type ResponseFormatFn, type RouteInfo, type RouteInputSchema, type RouteManifest, type RouteParamSchema, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, type SseEvent, type SseWriter, type UpgradeHandler, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, collectRouteSchemaSources, cors, createProdApp as createApp, createDevApp, createProdApp, createProgram, extractTypeInfo, getInputTypeForMethod, helmet, invalidateProgramCache, loadConfig, logger };
1140
+ export { type ProdApp as App, type CorsOptions, type CreateAppOptions, type DevApp, type FaapiConfig, type FaapiContext, type FaapiContextConfig, FaapiError, type FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, type HelmetOptions, type InjectOptions, type InjectResponse, type Injector, type InjectorMap, InternalError, type LifecycleContext, type LifecycleHooks, type LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type RouteInfo, type RouteInputSchema, type RouteManifest, RouteNotFoundError, type RouteOutputSchema, type RouteParamSchema, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, type SseEvent, type SseWriter, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, collectRouteSchemaSources, cors, createProdApp as createApp, createDevApp, createProdApp, createProgram, extractTypeInfo, getInputTypeForMethod, helmet, invalidateProgramCache, loadConfig, logger, resolveTypeNode };