@faapi/faapi 6.24.0 → 6.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,204 @@
1
+ import { F as FaapiContext } from './contextTypes-gYf84EXF.js';
2
+
3
+ /**
4
+ * faapi 中间件(洋葱模型)
5
+ *
6
+ * 单一 async 函数,通过 `await next()` 衔接前置/后置逻辑:
7
+ * - `await next()` 之前的代码:前置处理(鉴权、日志开始计时等)
8
+ * - `await next()` 之后的代码:后置处理(日志输出、响应修改等)
9
+ * - 不调用 `next()` 即拦截请求(如鉴权失败直接返回 Response)
10
+ * - `next()` 返回内层 Response,中间件可选择使用或替换
11
+ * - 返回 `Response`:作为响应返回(可用于拦截或错误处理)
12
+ * - 返回 `void`:使用 `await next()` 返回的内层响应
13
+ *
14
+ * 错误处理用 try/catch 包裹 `await next()`,而非独立的 error 钩子。
15
+ *
16
+ * 执行顺序(洋葱模型):
17
+ * ```
18
+ * mw1.before → mw2.before → handler → mw2.after → mw1.after
19
+ * ```
20
+ *
21
+ * 示例 middlewares.ts:
22
+ * ```ts
23
+ * import type { FaapiMiddleware } from '@faapi/faapi';
24
+ *
25
+ * export default [
26
+ * // 鉴权:不调 next() 即拦截
27
+ * async (ctx, next) => {
28
+ * const token = ctx.headers.get('authorization');
29
+ * if (!token) return new Response('Unauthorized', { status: 401 });
30
+ * ctx.user = await verifyToken(token);
31
+ * await next();
32
+ * },
33
+ * // 日志:before/after 一体,闭包共享状态
34
+ * async (ctx, next) => {
35
+ * const start = Date.now();
36
+ * await next();
37
+ * console.log(`${ctx.method} ${ctx.path} ${Date.now() - start}ms`);
38
+ * },
39
+ * // 错误处理:try/catch 语义
40
+ * async (ctx, next) => {
41
+ * try {
42
+ * await next();
43
+ * } catch (err) {
44
+ * return new Response(JSON.stringify({ error: String(err) }), { status: 500 });
45
+ * }
46
+ * },
47
+ * ] satisfies FaapiMiddleware[];
48
+ * ```
49
+ */
50
+ type FaapiMiddleware = (ctx: FaapiContext, next: () => Promise<Response>) => Promise<void | Response>;
51
+
52
+ /**
53
+ * 注入器:按参数名匹配,提供 handler 所需的依赖
54
+ *
55
+ * 注入器是 faapi 的依赖注入扩展点,与中间件解耦:
56
+ * - 中间件只管请求流程(鉴权、日志、错误处理)
57
+ * - 注入器只管提供依赖(数据库连接、用户对象等)
58
+ *
59
+ * 注入器可以读取中间件塞进 ctx 的值(如鉴权中间件塞的 ctx.user),
60
+ * 也可以独立提供依赖(如数据库连接池)。
61
+ *
62
+ * 注入器按需执行:只对 handler 声明的参数执行对应的注入器,避免无谓计算。
63
+ *
64
+ * 在 middlewares.ts 中通过命名导出 `injectors` 注册:
65
+ * ```ts
66
+ * import type { InjectorMap } from '@faapi/faapi';
67
+ *
68
+ * export const injectors: InjectorMap = {
69
+ * db: () => getDbConnection(),
70
+ * user: (ctx) => ctx.user, // 取中间件塞的值
71
+ * };
72
+ * ```
73
+ */
74
+ type Injector = (ctx: FaapiContext) => unknown | Promise<unknown>;
75
+ /**
76
+ * 注入器映射表:参数名 → 注入器函数
77
+ *
78
+ * key 必须与 handler 参数名一致,运行时按参数名匹配执行。
79
+ */
80
+ type InjectorMap = Record<string, Injector>;
81
+
82
+ interface CorsOptions {
83
+ origin?: string | string[] | true;
84
+ methods?: string[];
85
+ allowedHeaders?: string[];
86
+ exposeHeaders?: string[];
87
+ credentials?: boolean;
88
+ maxAge?: number;
89
+ }
90
+ /**
91
+ * 创建 CORS 中间件(洋葱模型)
92
+ *
93
+ * - origin=true: 允许所有来源(反射请求的 Origin)
94
+ * - origin=string: 允许指定来源
95
+ * - origin=string[]: 允许多个来源
96
+ *
97
+ * OPTIONS 预检请求直接返回 204,不调用 next()。
98
+ */
99
+ declare function cors(options?: CorsOptions): FaapiMiddleware;
100
+
101
+ interface HelmetOptions {
102
+ contentSecurityPolicy?: string | false;
103
+ xFrameOptions?: 'DENY' | 'SAMEORIGIN' | false;
104
+ xContentTypeOptions?: boolean;
105
+ referrerPolicy?: string | false;
106
+ strictTransportSecurity?: string | false;
107
+ xDnsPrefetchControl?: boolean;
108
+ xDownloadOptions?: boolean;
109
+ xPermittedCrossDomainPolicies?: string | false;
110
+ crossOriginOpenerPolicy?: string | false;
111
+ crossOriginResourcePolicy?: string | false;
112
+ crossOriginEmbedderPolicy?: string | false;
113
+ originAgentCluster?: boolean;
114
+ xPoweredBy?: boolean;
115
+ }
116
+ declare function helmet(options?: HelmetOptions): FaapiMiddleware;
117
+
118
+ declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
119
+ type HttpMethod = (typeof HTTP_METHODS)[number];
120
+
121
+ interface RouteRecord {
122
+ method: HttpMethod;
123
+ urlPath: string;
124
+ filePath: string;
125
+ paramNames: string[];
126
+ isDynamic: boolean;
127
+ /** 是否为 catch-all 路由([...slug]) */
128
+ isCatchAll?: boolean;
129
+ /** 中间件文件绝对路径列表(根在前,路由目录在后;按需加载用) */
130
+ middlewarePaths?: string[];
131
+ /** 路由对应的中间件集合(从根到路由目录合并,按需加载后缓存) */
132
+ middlewares?: FaapiMiddleware[];
133
+ /** 路由对应的注入器映射表(从根到路由目录合并,按需加载后缓存) */
134
+ injectors?: InjectorMap;
135
+ }
136
+ /**
137
+ * WebSocket 路由记录
138
+ *
139
+ * 与 HTTP RouteRecord 类似,但不绑定 HTTP 方法(WS 是协议升级,不区分 GET/POST)。
140
+ * 一个 handler.ts 中导出 WS 即生成一条 WS 路由记录。
141
+ */
142
+ interface WsRouteRecord {
143
+ urlPath: string;
144
+ filePath: string;
145
+ paramNames: string[];
146
+ isDynamic: boolean;
147
+ /** 是否为 catch-all 路由([...slug]) */
148
+ isCatchAll?: boolean;
149
+ /** 中间件文件绝对路径列表(根在前,路由目录在后;按需加载用) */
150
+ middlewarePaths?: string[];
151
+ /** 路由对应的中间件集合(握手阶段执行,复用鉴权/CORS/日志;按需加载后缓存) */
152
+ middlewares?: FaapiMiddleware[];
153
+ /** 路由对应的注入器映射表 */
154
+ injectors?: InjectorMap;
155
+ }
156
+ type RouteManifest = RouteRecord[];
157
+ type WsRouteManifest = WsRouteRecord[];
158
+ /**
159
+ * 路由单个参数的 schema 描述
160
+ *
161
+ * 供 @faapi/schema 扩展包消费,通过 MCP 暴露给 LLM。
162
+ */
163
+ interface RouteParamSchema {
164
+ name: string;
165
+ type: string;
166
+ required: boolean;
167
+ }
168
+ /**
169
+ * 路由单个输入源的 schema 描述
170
+ */
171
+ interface RouteInputSchema {
172
+ source: 'query' | 'body' | 'params';
173
+ schemaName: string | null;
174
+ properties: RouteParamSchema[];
175
+ }
176
+ /**
177
+ * 路由响应类型的 schema 描述
178
+ *
179
+ * 由 @faapi/schema 扩展包的 buildRouteSchemas 生成。
180
+ * output 为 null 表示无显式返回类型注解、void/Promise<void>、或解析失败降级。
181
+ */
182
+ interface RouteOutputSchema {
183
+ /** 命名类型名(如 'UserResponse'),内联类型为 null */
184
+ schemaName: string | null;
185
+ /** 顶层属性列表 */
186
+ properties: RouteParamSchema[];
187
+ }
188
+ /**
189
+ * 路由的完整 schema 描述
190
+ *
191
+ * 由 @faapi/schema 扩展包的 buildRouteSchemas 生成。
192
+ * 主包只定义类型契约,逻辑实现在扩展包。
193
+ */
194
+ interface RouteInfo {
195
+ method: string;
196
+ path: string;
197
+ filePath: string;
198
+ isDynamic: boolean;
199
+ inputs: RouteInputSchema[];
200
+ /** 响应类型描述(null 表示无返回类型注解/void/解析失败) */
201
+ output: RouteOutputSchema | null;
202
+ }
203
+
204
+ export { type CorsOptions as C, type FaapiMiddleware as F, type HelmetOptions as H, type InjectorMap as I, type RouteManifest as R, type WsRouteManifest as W, type Injector as a, type RouteInfo as b, type RouteInputSchema as c, type RouteOutputSchema as d, type RouteParamSchema as e, cors as f, helmet as h };
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { a as FaapiContext, F as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions } from './routeTypes-D1swYDVC.js';
1
+ import { F as FaapiContext } from './contextTypes-gYf84EXF.js';
2
+ import { F as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions } from './routeTypes-DlD6nnbT.js';
2
3
  import { Server } from 'node:http';
3
4
  import { WebSocket } from 'ws';
4
5
 
@@ -25,7 +26,12 @@ declare function createTestContext(options: CreateTestContextOptions): FaapiCont
25
26
  interface CreateTestContextOptions {
26
27
  /** app 级注册表(测试 handler 声明 agent/agents 参数时注入用,可选) */
27
28
  registries?: FaapiContext['registries'];
28
- /** 运行时资源根目录(测试 handler 读 ctx.resourcesDir 时传入,可选) */
29
+ /**
30
+ * 运行时资源根目录(可选)
31
+ *
32
+ * 传入时同时做两件事:绑定全局 readResource 读取根(handler 直调
33
+ * `readResource('x.md')` 依赖)+ 挂到 ctx.resourcesDir 数据字段
34
+ */
29
35
  resourcesDir?: string;
30
36
  /** 请求方法,默认 'GET' */
31
37
  method?: string;