@faapi/faapi 0.0.0-canary.0f443f9 → 0.0.0-canary.22b65a2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +4706 -4937
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +221 -40
- package/dist/index.js +2601 -157
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
package/dist/index.d.ts
CHANGED
|
@@ -119,6 +119,19 @@ interface FaapiContext {
|
|
|
119
119
|
* 设置响应头
|
|
120
120
|
*/
|
|
121
121
|
setHeader(key: string, value: string): void;
|
|
122
|
+
/**
|
|
123
|
+
* 设置 ETag 响应头
|
|
124
|
+
*
|
|
125
|
+
* handler 中基于业务数据(如 updatedAt / version / contentHash)设置 ETag:
|
|
126
|
+
* ```ts
|
|
127
|
+
* export function GET(ctx) {
|
|
128
|
+
* const data = await fetchData();
|
|
129
|
+
* ctx.setETag(`"${data.version}-${data.updatedAt}"`);
|
|
130
|
+
* return data;
|
|
131
|
+
* }
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
setETag(value: string): void;
|
|
122
135
|
/**
|
|
123
136
|
* 返回 JSON 响应(handler 直接 return)
|
|
124
137
|
*
|
|
@@ -272,15 +285,24 @@ interface CorsOptions {
|
|
|
272
285
|
*/
|
|
273
286
|
declare function cors(options?: CorsOptions): FaapiMiddleware;
|
|
274
287
|
|
|
288
|
+
type LoggerFn = (messageOrObj: string | Record<string, unknown>, message?: string) => void;
|
|
275
289
|
interface LoggerOptions {
|
|
276
|
-
/**
|
|
277
|
-
|
|
290
|
+
/**
|
|
291
|
+
* 自定义日志函数
|
|
292
|
+
*
|
|
293
|
+
* - 传入 `console.log`(默认):纯文本格式 `GET /api/users 200 12ms`
|
|
294
|
+
* - 传入 pino logger:结构化日志 `logger.info({ method, path, status, durationMs }, 'request completed')`
|
|
295
|
+
* - 传入 winston logger:`logger.info('GET /api/users 200 12ms', { method, path })`
|
|
296
|
+
*/
|
|
297
|
+
log?: LoggerFn;
|
|
278
298
|
}
|
|
279
299
|
/**
|
|
280
300
|
* 创建请求日志中间件(洋葱模型)
|
|
281
301
|
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
302
|
+
* 日志格式(文本模式):GET /api/users 200 12ms
|
|
303
|
+
* 错误格式(文本模式):POST /api/users 400 45ms - Error: ...
|
|
304
|
+
*
|
|
305
|
+
* 结构化模式:传入 pino/winston 等 logger 实例时,会自动传递结构化字段。
|
|
284
306
|
*
|
|
285
307
|
* before/after 一体,闭包变量共享开始时间,无需污染 ctx。
|
|
286
308
|
* 错误用 try/catch 捕获,记录后重新抛出(让上层处理)。
|
|
@@ -288,6 +310,23 @@ interface LoggerOptions {
|
|
|
288
310
|
*/
|
|
289
311
|
declare function logger(options?: LoggerOptions): FaapiMiddleware;
|
|
290
312
|
|
|
313
|
+
interface HelmetOptions {
|
|
314
|
+
contentSecurityPolicy?: string | false;
|
|
315
|
+
xFrameOptions?: 'DENY' | 'SAMEORIGIN' | false;
|
|
316
|
+
xContentTypeOptions?: boolean;
|
|
317
|
+
referrerPolicy?: string | false;
|
|
318
|
+
strictTransportSecurity?: string | false;
|
|
319
|
+
xDnsPrefetchControl?: boolean;
|
|
320
|
+
xDownloadOptions?: boolean;
|
|
321
|
+
xPermittedCrossDomainPolicies?: string | false;
|
|
322
|
+
crossOriginOpenerPolicy?: string | false;
|
|
323
|
+
crossOriginResourcePolicy?: string | false;
|
|
324
|
+
crossOriginEmbedderPolicy?: string | false;
|
|
325
|
+
originAgentCluster?: boolean;
|
|
326
|
+
xPoweredBy?: boolean;
|
|
327
|
+
}
|
|
328
|
+
declare function helmet(options?: HelmetOptions): FaapiMiddleware;
|
|
329
|
+
|
|
291
330
|
declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
292
331
|
type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
293
332
|
|
|
@@ -304,7 +343,26 @@ interface RouteRecord {
|
|
|
304
343
|
/** 路由对应的注入器映射表(从根到路由目录合并,构建时加载) */
|
|
305
344
|
injectors?: InjectorMap;
|
|
306
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* WebSocket 路由记录
|
|
348
|
+
*
|
|
349
|
+
* 与 HTTP RouteRecord 类似,但不绑定 HTTP 方法(WS 是协议升级,不区分 GET/POST)。
|
|
350
|
+
* 一个 handler.ts 中导出 WS 即生成一条 WS 路由记录。
|
|
351
|
+
*/
|
|
352
|
+
interface WsRouteRecord {
|
|
353
|
+
urlPath: string;
|
|
354
|
+
filePath: string;
|
|
355
|
+
paramNames: string[];
|
|
356
|
+
isDynamic: boolean;
|
|
357
|
+
/** 是否为 catch-all 路由([...slug]) */
|
|
358
|
+
isCatchAll?: boolean;
|
|
359
|
+
/** 路由对应的中间件集合(握手阶段执行,复用鉴权/CORS/日志) */
|
|
360
|
+
middlewares?: FaapiMiddleware[];
|
|
361
|
+
/** 路由对应的注入器映射表 */
|
|
362
|
+
injectors?: InjectorMap;
|
|
363
|
+
}
|
|
307
364
|
type RouteManifest = RouteRecord[];
|
|
365
|
+
type WsRouteManifest = WsRouteRecord[];
|
|
308
366
|
/**
|
|
309
367
|
* 路由单个参数的 schema 描述
|
|
310
368
|
*
|
|
@@ -424,6 +482,11 @@ type PluginDeclaration = string | [string, unknown] | {
|
|
|
424
482
|
options?: unknown;
|
|
425
483
|
};
|
|
426
484
|
|
|
485
|
+
interface Http2Options {
|
|
486
|
+
key?: string;
|
|
487
|
+
cert?: string;
|
|
488
|
+
}
|
|
489
|
+
|
|
427
490
|
/**
|
|
428
491
|
* 统一响应格式化函数
|
|
429
492
|
*
|
|
@@ -442,7 +505,7 @@ type ErrorFormatFn = (error: unknown, ctx?: FaapiContext) => Response | null | u
|
|
|
442
505
|
* 生命周期钩子
|
|
443
506
|
*/
|
|
444
507
|
interface LifecycleHooks {
|
|
445
|
-
/**
|
|
508
|
+
/** 服务器启动后调用(适合初始化数据库连接等) */
|
|
446
509
|
onReady?: (ctx: LifecycleContext) => Promise<void> | void;
|
|
447
510
|
/** 服务器关闭时调用(适合清理资源、优雅关闭) */
|
|
448
511
|
onClose?: (ctx: LifecycleContext) => Promise<void> | void;
|
|
@@ -479,7 +542,6 @@ interface LifecycleContext {
|
|
|
479
542
|
* ```ts
|
|
480
543
|
* import type { FaapiConfig } from '@faapi/faapi';
|
|
481
544
|
* export default {
|
|
482
|
-
* port: 3000,
|
|
483
545
|
* cors: { origin: '*' },
|
|
484
546
|
* } satisfies FaapiConfig;
|
|
485
547
|
* ```
|
|
@@ -488,7 +550,6 @@ interface LifecycleContext {
|
|
|
488
550
|
* ```ts
|
|
489
551
|
* import type { FaapiConfig } from '@faapi/faapi';
|
|
490
552
|
* export default {
|
|
491
|
-
* port: 3000,
|
|
492
553
|
* cors: { origin: '*' },
|
|
493
554
|
* // 自定义业务配置(任意 key)
|
|
494
555
|
* db: { host: 'localhost', port: 5432 },
|
|
@@ -496,12 +557,13 @@ interface LifecycleContext {
|
|
|
496
557
|
* ```
|
|
497
558
|
*
|
|
498
559
|
* 环境覆盖通过 faapi.config.{NODE_ENV}.ts 实现(如 faapi.config.production.ts)
|
|
560
|
+
*
|
|
561
|
+
* 框架元信息通过环境变量配置(不放在 config 内):
|
|
562
|
+
* - `FAAPI_APP_DIR`:源码目录前缀,默认 'src',设为 '.' 表示源码在项目根目录
|
|
563
|
+
* - `PORT`:服务端口,默认 3000
|
|
564
|
+
* - `FAAPI_OUT_DIR`:产物输出目录,dev 固定为 '.faapi/dev',prod 默认 'dist'
|
|
499
565
|
*/
|
|
500
566
|
interface FaapiConfig {
|
|
501
|
-
/** 服务端口,默认 3000(可被 --port / PORT 环境变量覆盖) */
|
|
502
|
-
port?: number;
|
|
503
|
-
/** 静态文件目录 */
|
|
504
|
-
staticDir?: string;
|
|
505
567
|
/** CORS 配置,false 禁用 */
|
|
506
568
|
cors?: CorsOptions | boolean;
|
|
507
569
|
/** 统一响应格式化函数 */
|
|
@@ -510,6 +572,14 @@ interface FaapiConfig {
|
|
|
510
572
|
errorFormat?: ErrorFormatFn;
|
|
511
573
|
/** 生命周期钩子 */
|
|
512
574
|
lifecycle?: LifecycleHooks;
|
|
575
|
+
/** 安全头配置,false 禁用 */
|
|
576
|
+
helmet?: HelmetOptions | boolean;
|
|
577
|
+
/** 请求体大小限制(字节),默认 10MB(10 * 1024 * 1024) */
|
|
578
|
+
bodyLimit?: number;
|
|
579
|
+
/** 日志中间件配置 */
|
|
580
|
+
logger?: LoggerOptions | boolean;
|
|
581
|
+
/** HTTP/2 配置,false 禁用(默认 http/1.1) */
|
|
582
|
+
http2?: Http2Options | boolean;
|
|
513
583
|
/**
|
|
514
584
|
* 全局中间件:对所有路由(HTTP + WebSocket 握手)生效
|
|
515
585
|
*
|
|
@@ -672,6 +742,15 @@ interface WsContext {
|
|
|
672
742
|
*/
|
|
673
743
|
type WsHandler = (ctx: WsContext) => WsEventHandlers | void;
|
|
674
744
|
|
|
745
|
+
/**
|
|
746
|
+
* 清理所有 Program 缓存(watch 模式下文件变化时调用)
|
|
747
|
+
*
|
|
748
|
+
* 全量清理而非增量清理,理由:
|
|
749
|
+
* - 简单可靠,无状态一致性问题
|
|
750
|
+
* - 跨文件类型引用需要所有文件的 Program 同步更新
|
|
751
|
+
* - dev 模式文件量有限,全量重建在百毫秒级
|
|
752
|
+
*/
|
|
753
|
+
declare function invalidateProgramCache(): void;
|
|
675
754
|
/**
|
|
676
755
|
* 为指定文件创建 TypeScript Program(带缓存)
|
|
677
756
|
*
|
|
@@ -788,48 +867,150 @@ declare function extractTypeInfo(program: ts.Program, filePath: string, typeName
|
|
|
788
867
|
declare function getInputTypeForMethod(method: string): 'query' | 'body';
|
|
789
868
|
|
|
790
869
|
/**
|
|
791
|
-
*
|
|
870
|
+
* 单个路由的 schema 提取结果
|
|
871
|
+
*
|
|
872
|
+
* key 使用 urlPath(如 '/api/hello')而非 filePath,因为 urlPath 在 dev/prod 完全一致,
|
|
873
|
+
* 无需 remapManifestKeys 桥接 .ts/.js 路径差异。
|
|
792
874
|
*/
|
|
793
|
-
interface
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
875
|
+
interface RouteSchemaSource {
|
|
876
|
+
/** 路由 URL 路径(如 '/api/hello'),作为 schema key */
|
|
877
|
+
urlPath: string;
|
|
878
|
+
/** 源文件绝对路径(用于 generateSchemaFiles 按文件分组生成 zod.js) */
|
|
879
|
+
filePath: string;
|
|
880
|
+
schemaName: string;
|
|
881
|
+
typeInfo: HandlerTypeInfo | null;
|
|
797
882
|
}
|
|
798
883
|
/**
|
|
799
|
-
*
|
|
884
|
+
* 从路由清单收集 schema 提取所需的原始数据
|
|
885
|
+
*
|
|
886
|
+
* dev 和 prd 共享的核心提取流程:
|
|
887
|
+
* 1. 按文件分组遍历路由
|
|
888
|
+
* 2. 对每个文件 createProgram + extractAllTypes 收集所有类型
|
|
889
|
+
* 3. 用 analyzeInjection + extractTypeInfo 提取每个路由的 schema 类型
|
|
890
|
+
* 4. 同时返回按文件分组的 allTypesMap 和合并后的全局 allTypes
|
|
891
|
+
*
|
|
892
|
+
* 调用方基于返回的 sources 和 allTypes 各自做最终转换:
|
|
893
|
+
* - dev:生成 JS 模块文件 → import 加载(用 allTypesByFile)
|
|
894
|
+
* - prd:生成 JS 模块代码 → SchemaModuleEntry[](用 allTypesByFile)
|
|
800
895
|
*/
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
896
|
+
declare function collectRouteSchemaSources(routes: RouteManifest, rootDir?: string): {
|
|
897
|
+
sources: RouteSchemaSource[];
|
|
898
|
+
/** 按文件分组的类型映射(prd writeSchemaModule 用) */
|
|
899
|
+
allTypesByFile: Map<string, Map<string, HandlerTypeInfo>>;
|
|
900
|
+
/** 合并后的全局类型映射(兼容旧调用方保留,新路径使用 allTypesByFile) */
|
|
901
|
+
mergedAllTypes: Map<string, HandlerTypeInfo>;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* 加载 faapi 配置文件
|
|
906
|
+
*
|
|
907
|
+
* 统一读取 `<outDir>/faapi-config.js` 产物:
|
|
908
|
+
* - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/dev/faapi-config.js`
|
|
909
|
+
* - prod 模式:`faapi build` 时由 `compileConfig` 生成 `dist/faapi-config.js`
|
|
910
|
+
*
|
|
911
|
+
* 产物由 `compileConfig` 在构建阶段合并 env 后固化,运行时不读源码、不现场编译、不按 env 合并。
|
|
912
|
+
*
|
|
913
|
+
* - 产物存在 → import 并返回 default
|
|
914
|
+
* - 产物不存在但源码有配置文件 → 抛错(强制 rebuild)
|
|
915
|
+
* - 源码也无配置文件 → 返回 `null`(配置可选)
|
|
916
|
+
*
|
|
917
|
+
* @param rootDir 项目根目录
|
|
918
|
+
* @param outDir 产物目录(如 'dist' 或 '.faapi/dev')
|
|
919
|
+
* @returns 配置对象,无配置文件时返回 null
|
|
920
|
+
*/
|
|
921
|
+
declare function loadConfig(rootDir: string, outDir: string): Promise<Partial<FaapiConfig> | null>;
|
|
922
|
+
|
|
923
|
+
interface InjectOptions {
|
|
924
|
+
method?: string;
|
|
925
|
+
path?: string;
|
|
926
|
+
headers?: Record<string, string>;
|
|
927
|
+
query?: Record<string, string>;
|
|
928
|
+
body?: unknown;
|
|
929
|
+
}
|
|
930
|
+
interface InjectResponse {
|
|
931
|
+
status: number;
|
|
932
|
+
headers: Headers;
|
|
933
|
+
body: unknown;
|
|
934
|
+
}
|
|
935
|
+
interface CreateAppOptions {
|
|
936
|
+
/** 项目根目录,默认 process.cwd() */
|
|
937
|
+
rootDir?: string;
|
|
938
|
+
/** 源码目录前缀,覆盖环境变量 FAAPI_APP_DIR,默认 'src' */
|
|
939
|
+
appDir?: string;
|
|
940
|
+
/** 端口号,也可在 listen() 时传入;默认环境变量 PORT 或 3000 */
|
|
941
|
+
port?: number;
|
|
942
|
+
}
|
|
943
|
+
/** 应用基础接口(dev/prod 共用,不含 reloadRoutes) */
|
|
944
|
+
interface AppBase {
|
|
945
|
+
/** Node.js Server 实例(listen 后可用,close 后置 null) */
|
|
946
|
+
server: Server | null;
|
|
947
|
+
/** 排序后的路由清单 */
|
|
948
|
+
routes: RouteManifest;
|
|
949
|
+
/** WebSocket 路由清单 */
|
|
950
|
+
wsRoutes: WsRouteManifest;
|
|
951
|
+
/** 项目根目录 */
|
|
952
|
+
rootDir: string;
|
|
953
|
+
/** 启动 HTTP server,打印路由表,执行 onReady 钩子 */
|
|
954
|
+
listen(port?: number): Promise<Server>;
|
|
955
|
+
/** 关闭 server,执行 onClose 钩子 */
|
|
956
|
+
close(): Promise<void>;
|
|
957
|
+
/**
|
|
958
|
+
* 无服务器测试注入
|
|
959
|
+
*
|
|
960
|
+
* 构建一个模拟请求直接走完整请求链路,不绑定端口。
|
|
961
|
+
* 需要在 listen() 之前调用(server 未启动时)。
|
|
962
|
+
*/
|
|
963
|
+
inject(options?: InjectOptions): Promise<InjectResponse>;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/** dev 应用接口(AppBase + reloadRoutes 热替换) */
|
|
967
|
+
interface DevApp extends AppBase {
|
|
968
|
+
/** 重新水合路由清单 + 清 schema 缓存 + 更新 server 路由引用(dev 热替换用) */
|
|
969
|
+
reloadRoutes(): Promise<void>;
|
|
806
970
|
}
|
|
807
971
|
/**
|
|
808
|
-
*
|
|
972
|
+
* dev 模式应用启动 API
|
|
973
|
+
*
|
|
974
|
+
* 在 createAppBase(共享逻辑)基础上增加 `reloadRoutes` 热替换能力,供 `faapi dev` watcher 调用。
|
|
809
975
|
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
976
|
+
* 与 createProdApp 的区别:
|
|
977
|
+
* - dev:含 reloadRoutes(重新扫描路由 + 重新生成 schema + 清缓存 + 更新 server 路由引用)
|
|
978
|
+
* - prod:精简,无 reloadRoutes(产物已固化,运行时不重建)
|
|
812
979
|
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
* @
|
|
980
|
+
* 由 `devCommand` 直接调用,devCommand 持有 app 引用并传给 watcher。
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* ```ts
|
|
984
|
+
* // devCommand 内部
|
|
985
|
+
* const app = await createDevApp();
|
|
986
|
+
* await app.listen();
|
|
987
|
+
* startWatcher({ rootDir, appDir, app });
|
|
988
|
+
* ```
|
|
816
989
|
*/
|
|
817
|
-
declare function
|
|
990
|
+
declare function createDevApp(options?: CreateAppOptions): Promise<DevApp>;
|
|
991
|
+
|
|
992
|
+
/** prod 应用接口(AppBase,无 reloadRoutes) */
|
|
993
|
+
type ProdApp = AppBase;
|
|
818
994
|
|
|
819
995
|
/**
|
|
820
|
-
*
|
|
996
|
+
* prod 模式应用启动 API
|
|
821
997
|
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
* 2. faapi.config.ts / faapi.config.js(基础配置)
|
|
825
|
-
* 3. faapi.config.{env}.ts / faapi.config.{env}.js(环境覆盖,深度合并)
|
|
998
|
+
* 直接返回 createAppBase 结果(共享逻辑),不含 dev 专用能力(reloadRoutes、缓存失效)。
|
|
999
|
+
* 产物在 `faapi build` 阶段已固化,运行时不重建。
|
|
826
1000
|
*
|
|
827
|
-
*
|
|
1001
|
+
* 框架采用零入口设计——用户无需编写 main.ts:
|
|
1002
|
+
* - `faapi build` 自动生成 `dist/main.js` 启动入口,内部调用 `createProdApp()` + `listen()` 启动生产服务器
|
|
1003
|
+
* - 用户自定义启动逻辑通过 `faapi.config.ts` 的 `lifecycle.onReady` / `onClose` 钩子实现
|
|
828
1004
|
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
* @
|
|
1005
|
+
* 编程式调用场景(如自定义 CLI 启动器)也可直接调用:
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* import { createProdApp } from '@faapi/faapi';
|
|
1010
|
+
* const app = await createProdApp();
|
|
1011
|
+
* await app.listen();
|
|
1012
|
+
* ```
|
|
832
1013
|
*/
|
|
833
|
-
declare function
|
|
1014
|
+
declare function createProdApp(options?: CreateAppOptions): Promise<ProdApp>;
|
|
834
1015
|
|
|
835
|
-
export { type CorsOptions, type ErrorFormatFn, type FaapiConfig, type FaapiContext, type FaapiContextConfig, type FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, type
|
|
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 };
|