@h-ai/serv 0.1.0-alpha.15 → 0.1.0-alpha.18

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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @h-ai/serv
2
2
 
3
- > hai-framework 的 Hono + oRPC API 服务运行时,把 `@h-ai/api-contract` 的契约挂载为跨端可访问的 HTTP API。
3
+ > hai-framework 的 API 服务运行时:对外暴露稳定的 HTTP App 抽象,把 `@h-ai/api-contract` 契约挂载为跨端可访问的 HTTP API;Hono 仅作为内部运行时实现。
4
4
 
5
5
  ## 能力概览
6
6
 
7
- - 一行启动:`serv.createApp(...)` → `serv.listen(app, { port })`
7
+ - 一行启动:`serv.createApp(...)` → `serv.listen(app, { port })`,应用代码不需要 import Hono
8
8
  - 扁平 API(最小知识):`serv.listen / serv.toFetch / serv.requireAuth / serv.requirePermission / serv.generateSpec / ...`
9
9
  - 自定义 pipeline:`createApp({ middlewares })` 挂 HTTP middleware;procedure wrapper 复用根入口导出的共享类型
10
10
  - 默认 feature procedures:`createIamProcedures()`、`createStorageProcedures()`、`createAiProcedures()`
@@ -30,7 +30,7 @@ const procedures = {
30
30
  ai: createAiProcedures({ ai }),
31
31
  }
32
32
 
33
- // 3) 创建 app
33
+ // 3) 创建 HTTP App
34
34
  const app = serv.createApp({
35
35
  contract,
36
36
  procedures,
@@ -203,16 +203,17 @@ const updateProfile = p.users.update.handler(
203
203
 
204
204
  `@h-ai/serv` 的“pipeline”分三层,分别对应三种扩展方式:
205
205
 
206
- 1. **HTTP middleware 层**:通过 `serv.createApp({ middlewares })` 注入 Hono middleware。
206
+ 1. **HTTP middleware 层**:通过 `serv.createApp({ middlewares })` 注入 HTTP middleware。
207
207
  2. **context 层**:通过 `verifyToken` / `createContext` / `serv.buildAuthContextFactory()` 定制请求上下文。
208
208
  3. **procedure wrapper 层**:通过 `ServProcedureWrapper` / `ServGuardedProcedureWrapper` 组合认证、审计、租户校验等业务包装。
209
209
 
210
210
  #### 1) 自定义 HTTP middleware
211
211
 
212
- 当你需要加请求日志、trace、限流、CORS、租户头校验等 **HTTP 层横切逻辑** 时,使用 `middlewares`:
212
+ 当你需要加请求日志、trace、限流、CORS、租户头校验等 **HTTP 层横切逻辑** 时,使用 `middlewares`。常见 CORS 场景可直接复用 `serv.cors(...)`:
213
213
 
214
214
  ```ts
215
215
  import type { ServMiddleware } from '@h-ai/serv'
216
+ import { serv } from '@h-ai/serv'
216
217
 
217
218
  const requestMetrics: ServMiddleware = async (c, next) => {
218
219
  const startedAt = Date.now()
@@ -225,6 +226,13 @@ const app = serv.createApp({
225
226
  procedures,
226
227
  http: { apiPrefix: '/api/v1' },
227
228
  middlewares: [
229
+ {
230
+ middleware: serv.cors({
231
+ origin: origin => origin === 'https://app.example.com',
232
+ credentials: true,
233
+ exposedHeaders: ['X-Encrypted', 'X-Request-Id'],
234
+ }),
235
+ },
228
236
  { middleware: requestMetrics }, // 默认挂到 '*'
229
237
  {
230
238
  path: '/api/v1/*',
@@ -241,15 +249,17 @@ const app = serv.createApp({
241
249
  执行顺序固定为:
242
250
 
243
251
  1. 内置 `securityHeaders`
244
- 2. 内置 `transport`(若启用)
245
- 3. 你的 `middlewares`
252
+ 2. 你的 `middlewares`
253
+ 3. 内置 `transport`(若启用)
246
254
  4. health / refresh-cookie / OpenAPI / RPC / docs / oRPC routes
247
255
 
248
256
  说明:
249
257
 
250
258
  - `middlewares` 按数组顺序注册
251
259
  - `path` 省略时默认 `'*'`
252
- - 这一层直接操作 Hono `Context`,返回的是 **HTTP Response**,不是 `HaiResult`
260
+ - 由于 `middlewares` 先于 `transport` 执行,CORS 这类 preflight middleware 可以直接短路;若需要读取解密后的业务 body,请改用 context / procedure 层扩展
261
+ - 浏览器若需读取自定义响应头(例如 transport 的 `X-Encrypted`),请通过 `serv.cors({ exposedHeaders: [...] })` 显式暴露
262
+ - 这一层直接操作 HTTP middleware context,返回的是 **HTTP Response**,不是 `HaiResult`
253
263
 
254
264
  #### 2) 自定义 context pipeline
255
265
 
@@ -375,7 +385,7 @@ await apiClient.init({
375
385
 
376
386
  ## API 概览
377
387
 
378
- - `serv.createApp(options)`:创建 Hono app,挂载自定义 `middlewares`、健康检查、OpenAPI handler、可选文档与 RPC
388
+ - `serv.createApp(options)`:创建 HTTP App 抽象,挂载自定义 `middlewares`、健康检查、OpenAPI handler、可选文档与 RPC
379
389
  - `serv.parseRequestContext({ request })`:默认上下文解析(提取 Bearer token + requestId,不填充 session)
380
390
  - `serv.listen(app, options)`:在 Node.js 启动 HTTP 服务,返回 `{ server, address, close }`
381
391
  - `serv.toFetch(app)`:包装为标准 `fetch(Request)` handler
@@ -391,6 +401,7 @@ await apiClient.init({
391
401
  另有根级类型 / 命名导出:
392
402
 
393
403
  - `ServConfigSchema`:用于 `core.config.validate('serv', ServConfigSchema)` 校验 `config/_serv.yml`
404
+ - HTTP App 抽象:`serv.createApp()` 返回具备 `fetch/request` 能力的公开应用对象;内部是否使用 Hono 不影响应用代码
394
405
  - `ServMiddlewareMount`:`createApp({ middlewares })` 的挂载项类型(`{ path?, middleware }`)
395
406
  - `ServMiddleware` / `ServProcedureWrapper` / `ServGuardedProcedureWrapper`:自定义 pipeline 时复用的共享类型
396
407
 
package/dist/index.d.ts CHANGED
@@ -1,124 +1,14 @@
1
1
  import * as _h_ai_core from '@h-ai/core';
2
2
  import { HaiResult, ValidationFormError } from '@h-ai/core';
3
3
  import { Router, implement } from '@orpc/server';
4
- import { Hono, MiddlewareHandler } from 'hono';
5
- import { ServerType } from '@hono/node-server';
6
- import { AddressInfo } from 'node:net';
4
+ import { AnyContractRouter, OpenAPI } from '@orpc/contract';
5
+ import { MiddlewareHandler } from 'hono';
7
6
  import { S as ServContext, a as ServIam, R as RefreshCookieConfig, b as ServSession, C as CreateServContext, p as parseRequestContext, c as buildAuthContextFactory } from './serv-context-CwewduFi.js';
8
7
  export { d as CreateServContextInput, e as RefreshTokenPair } from './serv-context-CwewduFi.js';
9
- import { AnyContractRouter, OpenAPI } from '@orpc/contract';
10
8
  import { z, ZodType } from 'zod';
11
9
  import { CryptoFunctions } from '@h-ai/crypto';
12
-
13
- /**
14
- * @h-ai/serv — Fetch 适配器
15
- *
16
- * 将 Hono app 包装为标准 Web `fetch(Request)` 处理函数,
17
- * 适用于 Cloudflare Workers、Deno、Bun 等 Fetch-first 运行时。
18
- * @module adapters/serv-adapter-fetch
19
- */
20
-
21
- /**
22
- * Fetch runtime handler。
23
- *
24
- * 兼容标准 Web `fetch` 签名 `(input, init)` —— 既可作为 Cloudflare Workers / Bun
25
- * 的 `fetch` 导出,也可作为 oRPC client `fetch` 选项的替代实现,让客户端在测试
26
- * 中直连 in-process 服务。
27
- */
28
- type ServFetchHandler = typeof fetch;
29
- /**
30
- * 将 Hono app 转成 Fetch handler。
31
- *
32
- * @param app - Hono app
33
- * @returns fetch-compatible handler
34
- *
35
- * @example
36
- * ```ts
37
- * // Cloudflare Workers
38
- * export default { fetch: serv.toFetch(app) }
39
- *
40
- * // Bun
41
- * Bun.serve({ fetch: serv.toFetch(app), port: 3000 })
42
- *
43
- * // 测试:把服务端 app 当作 fetch 注入到 client,免去 HTTP 监听
44
- * const client = apiClient.create(contract)
45
- * await client.init({ baseUrl: 'http://test', fetch: serv.toFetch(app) })
46
- * ```
47
- */
48
- declare function toFetch(app: Hono): ServFetchHandler;
49
-
50
- /**
51
- * @h-ai/serv — Node.js 适配器
52
- *
53
- * 封装 `@hono/node-server`,将 Hono app 以 Node.js HTTP 服务器形式启动。
54
- * 默认监听 `127.0.0.1`(仅本机可达);如需对外暴露,需显式设置 `host: '0.0.0.0'` 或指定 IP。
55
- * @module adapters/serv-adapter-node
56
- */
57
-
58
- /** Node 服务器启动配置。 */
59
- interface ServListenOptions {
60
- /**
61
- * 监听端口。
62
- * 优先级:显式值 > `PORT` 环境变量 > 默认 `3000`。
63
- */
64
- readonly port?: number;
65
- /**
66
- * 监听地址。
67
- * 优先级:显式值 > `HOST` 环境变量 > 默认 `'127.0.0.1'`(仅本机可达)。
68
- *
69
- * - `'127.0.0.1'`:仅本机(推荐用于本地开发、容器内 sidecar、反向代理后端)
70
- * - `'0.0.0.0'`:监听所有 IPv4 网卡(容器对外暴露、需公网/内网访问)
71
- * - 指定 IP:仅监听该网卡(多网卡精确绑定)
72
- */
73
- readonly host?: string;
74
- /** 服务启动就绪后的回调。 */
75
- readonly onListening?: (info: AddressInfo) => void;
76
- /**
77
- * 优雅关闭回调(释放业务模块资源)。
78
- *
79
- * 传入后 `serv.listen` 自动监听 `SIGINT` / `SIGTERM`,按以下顺序关闭:
80
- * 1. 停止接受新 HTTP 连接
81
- * 2. 调用此回调释放业务资源
82
- *
83
- * 应用层无需手动注册 `process.once('SIGINT', ...)` 或维护 `shuttingDown` 标志。
84
- */
85
- readonly onClose?: () => Promise<void>;
86
- }
87
- /** Node 服务运行句柄。 */
88
- interface ServNodeServer {
89
- /** 底层 Node.js HTTP Server 实例。 */
90
- readonly server: ServerType;
91
- /** 实际监听地址(host 与解析后的 port)。 */
92
- readonly address: {
93
- readonly host: string;
94
- readonly port: number;
95
- };
96
- /** 优雅关闭服务器并等待已有连接断开。 */
97
- readonly close: () => Promise<void>;
98
- }
99
- /**
100
- * 在 Node.js 中启动 Hono app。
101
- *
102
- * 默认监听 `127.0.0.1:3000`(可通过 `PORT` / `HOST` 环境变量覆盖)。
103
- * 传入 `onClose` 后自动注册 SIGINT/SIGTERM 优雅关闭,应用层无需手动处理信号。
104
- *
105
- * @param app - Hono app
106
- * @param options - 监听配置(port / host / onListening / onClose)
107
- * @returns 运行句柄
108
- *
109
- * @example
110
- * ```ts
111
- * // 最简:读取 PORT / HOST 环境变量,自动优雅关闭
112
- * serv.listen(app, {
113
- * onListening: info => logger.info('listening', { port: info.port }),
114
- * onClose: closeApp,
115
- * })
116
- *
117
- * // 容器对外暴露
118
- * serv.listen(app, { host: '0.0.0.0', onClose: closeApp })
119
- * ```
120
- */
121
- declare function listen(app: Hono, options?: ServListenOptions): ServNodeServer;
10
+ import { ServerType } from '@hono/node-server';
11
+ import { AddressInfo } from 'node:net';
122
12
 
123
13
  /**
124
14
  * @h-ai/serv — Pipeline 共享类型
@@ -149,99 +39,6 @@ type ServProcedureWrapper = <TInput, TOutput>(handler: ServProcedureHandler<TInp
149
39
  /** 带额外参数的 procedure 包装器(例如 `requirePermission('x', handler)`)。 */
150
40
  type ServGuardedProcedureWrapper<TRequirement> = <TInput, TOutput>(requirement: TRequirement, handler: ServProcedureHandler<TInput, TOutput>) => ServProcedureHandler<TInput, TOutput>;
151
41
 
152
- /**
153
- * @h-ai/serv — Pipeline 公共 helper
154
- *
155
- * 放置不属于 Hono middleware / oRPC wrapper 默认实现本身、
156
- * 但会被多个 pipeline 复用的公共工具。
157
- * @module pipelines/serv-pipeline-helper
158
- */
159
-
160
- /**
161
- * 捕获未处理异常并转换为 HaiResult。
162
- *
163
- * 虽然它不是 middleware / 鉴权 wrapper,但属于所有 procedure pipeline
164
- * 都可能复用的“公共包装能力”,因此与 `buildHaiErrorBody()` 一起放在 helper 中。
165
- *
166
- * @param handler - 被包装的 procedure handler
167
- * @returns 带异常保护的新 handler
168
- *
169
- * @example
170
- * ```ts
171
- * const safeHandler = serv.mapHaiError(myHandler)
172
- * ```
173
- */
174
- declare function mapHaiError<TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
175
-
176
- /**
177
- * @h-ai/serv — requireAuth 默认实现
178
- * @module pipelines/serv-pipeline-require-auth
179
- */
180
-
181
- /**
182
- * 需要已验证会话的 procedure 包装器。
183
- *
184
- * `context.session` 由 `buildAuthContextFactory(verifyToken)`(或 `serv.createApp({ iam })` 自动启用)填充。
185
- * token 不存在或无效(verifyToken 失败)时 `session` 均为 `undefined`,统一返回 401 UNAUTHORIZED。
186
- * 内置调用 `mapHaiError`,无需外层再次封装。
187
- *
188
- * @param handler - 被包装的 procedure handler
189
- * @returns 带认证检查的新 handler
190
- *
191
- * @example
192
- * ```ts
193
- * const protectedHandler = serv.requireAuth(myHandler)
194
- * ```
195
- */
196
- declare function requireAuth<TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
197
-
198
- /**
199
- * @h-ai/serv — requirePermission 默认实现
200
- * @module pipelines/serv-pipeline-require-permission
201
- */
202
-
203
- /**
204
- * 需要指定权限的 procedure 包装器。
205
- *
206
- * 内置调用 `requireAuth`,在验证 Token 后进一步匹配 `session.permissions`。
207
- * 拥有 {@link WILDCARD_PERMISSION}(`'*'`)的用户自动通过所有权限检查。
208
- *
209
- * @param permission - 所需权限字符串(如 `'iam.users.read'`)
210
- * @param handler - 被包装的 procedure handler
211
- * @returns 带权限检查的新 handler
212
- *
213
- * @example
214
- * ```ts
215
- * const adminHandler = serv.requirePermission('iam.users.write', myHandler)
216
- * ```
217
- */
218
- declare function requirePermission<TInput, TOutput>(permission: string, handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
219
-
220
- /**
221
- * @h-ai/serv — requireRole 默认实现
222
- * @module pipelines/serv-pipeline-require-role
223
- */
224
-
225
- /**
226
- * 需要指定角色的 procedure 包装器。
227
- *
228
- * 内置调用 `requireAuth`,在验证 Token 后进一步匹配 `session.roles`。
229
- * 拥有 {@link WILDCARD_ROLE}(`'*'`)的用户自动通过所有角色检查。
230
- *
231
- * **使用建议**:优先使用 {@link requirePermission}(基于行为的细粒度授权);
232
- * `requireRole` 适用于按角色分流的粗粒度路由(如 `'admin'` 专属入口)。
233
- *
234
- * @param role - 所需角色字符串(如 `'admin'`)
235
- * @param handler - 被包装的 procedure handler
236
- * @returns 带角色检查的新 handler
237
- *
238
- * @example
239
- * ```ts
240
- * const adminOnly = serv.requireRole('admin', myHandler)
241
- * ```
242
- */
243
- declare function requireRole<TInput, TOutput>(role: string, handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
244
-
245
42
  /**
246
43
  * @h-ai/serv — HTTP 配置
247
44
  *
@@ -277,7 +74,7 @@ interface ServTransportRuntimeConfig {
277
74
  readonly excludePaths: readonly `/${string}`[];
278
75
  readonly maxClients: number;
279
76
  }
280
- /** Hono app 的 HTTP 挂载配置。 */
77
+ /** ServHttpApp 的 HTTP 挂载配置。 */
281
78
  interface ServHttpConfig {
282
79
  readonly apiPrefix: `/api/${string}`;
283
80
  readonly openapi: false | ServOpenAPIHttpConfig;
@@ -410,17 +207,17 @@ interface ServTransportConfig {
410
207
  }
411
208
 
412
209
  /**
413
- * @h-ai/serv — Hono app 装配
210
+ * @h-ai/serv — HTTP app 装配
414
211
  *
415
- * 将 oRPC contract + procedures + http 配置组合为可运行的 Hono app,
212
+ * 将 oRPC contract + procedures + http 配置组合为可运行的 ServHttpApp,
416
213
  * 自动挂载安全响应头、健康检查、OpenAPI JSON、Scalar 文档页与 oRPC handler。
417
214
  *
418
215
  * 关键装配顺序:
419
216
  * 1. 解析 HTTP 配置
420
217
  * 2. 选择请求上下文工厂
421
218
  * 3. 挂基础安全 middleware
422
- * 4. (可选)挂传输加密 middleware
423
- * 5. 挂自定义 middleware(若提供)
219
+ * 4. 挂自定义 middleware(若提供)
220
+ * 5. (可选)挂传输加密 middleware
424
221
  * 6. 挂基础路由(health / refresh-cookie)
425
222
  * 7. 挂业务路由(oRPC / RPC)
426
223
  * 8. 挂文档路由(openapi / docs)
@@ -432,14 +229,25 @@ interface ServTransportConfig {
432
229
  *
433
230
  * - `path` 省略时默认挂到 `'*'`
434
231
  * - 按数组顺序注册
435
- * - 注册位置固定在内置 `securityHeaders` / `transport` 之后、业务路由之前
232
+ * - 注册位置固定在内置 `securityHeaders` 之后、`transport` / 业务路由之前
436
233
  */
437
234
  interface ServMiddlewareMount {
438
235
  readonly path?: string;
439
236
  readonly middleware: ServMiddleware;
440
237
  }
441
238
  /**
442
- * 创建 Hono app 的配置。
239
+ * `@h-ai/serv` 对外暴露的 HTTP app 抽象。
240
+ *
241
+ * Hono 是当前内部实现细节,应用代码只应依赖此最小接口:
242
+ * - `fetch`:部署到 Fetch-first 运行时或交给 `serv.listen()`。
243
+ * - `request`:测试 / 本地直连时发起 in-process 请求,避免消费方 import Hono。
244
+ */
245
+ interface ServHttpApp {
246
+ readonly fetch: (request: Request) => Response | Promise<Response>;
247
+ readonly request: (input: RequestInfo | URL, init?: RequestInit) => Response | Promise<Response>;
248
+ }
249
+ /**
250
+ * 创建 ServHttpApp 的配置。
443
251
  *
444
252
  * **认证 / 会话填充设计**(遵循最小知识原则):
445
253
  *
@@ -460,10 +268,12 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
460
268
  readonly procedures: TProcedures;
461
269
  readonly http?: ServHttpConfigInput;
462
270
  /**
463
- * 自定义 Hono middleware。
271
+ * 自定义 HTTP middleware(当前内部由 Hono 承载,但应用 API 不暴露 Hono app)。
464
272
  *
465
273
  * 典型用途:请求日志、trace、指标、CORS、限流、租户头校验等 HTTP 层横切逻辑。
466
- * 这些 middleware 会在内置安全头 / 传输加密之后、health/oRPC/OpenAPI/docs 路由之前注册。
274
+ * 这些 middleware 会在内置安全头之后、传输加密与业务路由之前注册;
275
+ * 因此 CORS 这类 preflight middleware 可以直接短路返回,若需要读取解密后的业务 body,
276
+ * 应改用 context / procedure 层扩展,而不是 HTTP middleware。
467
277
  */
468
278
  readonly middlewares?: readonly ServMiddlewareMount[];
469
279
  /**
@@ -501,10 +311,10 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
501
311
  readonly transport?: ServTransportConfig;
502
312
  }
503
313
  /**
504
- * 创建并装配 Hono API app。
314
+ * 创建并装配 HTTP API app。
505
315
  *
506
316
  * @param options - contract/procedures/http 三段式配置
507
- * @returns Hono app
317
+ * @returns 可被 `serv.listen()` / `serv.toFetch()` 使用的 ServHttpApp
508
318
  *
509
319
  * @example
510
320
  * ```ts
@@ -520,7 +330,235 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
520
330
  * serv.listen(app, { port: 3000, host: '0.0.0.0' })
521
331
  * ```
522
332
  */
523
- declare function createApp<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>>(options: CreateServAppOptions<TContract, TProcedures>): Hono;
333
+ declare function createApp<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>>(options: CreateServAppOptions<TContract, TProcedures>): ServHttpApp;
334
+
335
+ /**
336
+ * @h-ai/serv — Fetch 适配器
337
+ *
338
+ * 将 ServHttpApp 包装为标准 Web `fetch(Request)` 处理函数,
339
+ * 适用于 Cloudflare Workers、Deno、Bun 等 Fetch-first 运行时。
340
+ * @module adapters/serv-adapter-fetch
341
+ */
342
+
343
+ /**
344
+ * Fetch runtime handler。
345
+ *
346
+ * 兼容标准 Web `fetch` 签名 `(input, init)` —— 既可作为 Cloudflare Workers / Bun
347
+ * 的 `fetch` 导出,也可作为 oRPC client `fetch` 选项的替代实现,让客户端在测试
348
+ * 中直连 in-process 服务。
349
+ */
350
+ type ServFetchHandler = typeof fetch;
351
+ /**
352
+ * 将 ServHttpApp 转成 Fetch handler。
353
+ *
354
+ * @param app - `serv.createApp()` 返回的 HTTP app
355
+ * @returns fetch-compatible handler
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * // Cloudflare Workers
360
+ * export default { fetch: serv.toFetch(app) }
361
+ *
362
+ * // Bun
363
+ * Bun.serve({ fetch: serv.toFetch(app), port: 3000 })
364
+ *
365
+ * // 测试:把服务端 app 当作 fetch 注入到 client,免去 HTTP 监听
366
+ * const client = apiClient.create(contract)
367
+ * await client.init({ baseUrl: 'http://test', fetch: serv.toFetch(app) })
368
+ * ```
369
+ */
370
+ declare function toFetch(app: ServHttpApp): ServFetchHandler;
371
+
372
+ /**
373
+ * @h-ai/serv — Node.js 适配器
374
+ *
375
+ * 封装 `@hono/node-server`,将 ServHttpApp 以 Node.js HTTP 服务器形式启动。
376
+ * 默认监听 `127.0.0.1`(仅本机可达);如需对外暴露,需显式设置 `host: '0.0.0.0'` 或指定 IP。
377
+ * @module adapters/serv-adapter-node
378
+ */
379
+
380
+ /** Node 服务器启动配置。 */
381
+ interface ServListenOptions {
382
+ /**
383
+ * 监听端口。
384
+ * 优先级:显式值 > `PORT` 环境变量 > 默认 `3000`。
385
+ */
386
+ readonly port?: number;
387
+ /**
388
+ * 监听地址。
389
+ * 优先级:显式值 > `HOST` 环境变量 > 默认 `'127.0.0.1'`(仅本机可达)。
390
+ *
391
+ * - `'127.0.0.1'`:仅本机(推荐用于本地开发、容器内 sidecar、反向代理后端)
392
+ * - `'0.0.0.0'`:监听所有 IPv4 网卡(容器对外暴露、需公网/内网访问)
393
+ * - 指定 IP:仅监听该网卡(多网卡精确绑定)
394
+ */
395
+ readonly host?: string;
396
+ /** 服务启动就绪后的回调。 */
397
+ readonly onListening?: (info: AddressInfo) => void;
398
+ /**
399
+ * 优雅关闭回调(释放业务模块资源)。
400
+ *
401
+ * 传入后 `serv.listen` 自动监听 `SIGINT` / `SIGTERM`,按以下顺序关闭:
402
+ * 1. 停止接受新 HTTP 连接
403
+ * 2. 调用此回调释放业务资源
404
+ *
405
+ * 应用层无需手动注册 `process.once('SIGINT', ...)` 或维护 `shuttingDown` 标志。
406
+ */
407
+ readonly onClose?: () => Promise<void>;
408
+ }
409
+ /** Node 服务运行句柄。 */
410
+ interface ServNodeServer {
411
+ /** 底层 Node.js HTTP Server 实例。 */
412
+ readonly server: ServerType;
413
+ /** 实际监听地址(host 与解析后的 port)。 */
414
+ readonly address: {
415
+ readonly host: string;
416
+ readonly port: number;
417
+ };
418
+ /** 优雅关闭服务器并等待已有连接断开。 */
419
+ readonly close: () => Promise<void>;
420
+ }
421
+ /**
422
+ * 在 Node.js 中启动 ServHttpApp。
423
+ *
424
+ * 默认监听 `127.0.0.1:3000`(可通过 `PORT` / `HOST` 环境变量覆盖)。
425
+ * 传入 `onClose` 后自动注册 SIGINT/SIGTERM 优雅关闭,应用层无需手动处理信号。
426
+ *
427
+ * @param app - `serv.createApp()` 返回的 HTTP app
428
+ * @param options - 监听配置(port / host / onListening / onClose)
429
+ * @returns 运行句柄
430
+ *
431
+ * @example
432
+ * ```ts
433
+ * // 最简:读取 PORT / HOST 环境变量,自动优雅关闭
434
+ * serv.listen(app, {
435
+ * onListening: info => logger.info('listening', { port: info.port }),
436
+ * onClose: closeApp,
437
+ * })
438
+ *
439
+ * // 容器对外暴露
440
+ * serv.listen(app, { host: '0.0.0.0', onClose: closeApp })
441
+ * ```
442
+ */
443
+ declare function listen(app: ServHttpApp, options?: ServListenOptions): ServNodeServer;
444
+
445
+ /**
446
+ * @h-ai/serv — Pipeline 公共 helper
447
+ *
448
+ * 放置不属于 Hono middleware / oRPC wrapper 默认实现本身、
449
+ * 但会被多个 pipeline 复用的公共工具。
450
+ * @module pipelines/serv-pipeline-helper
451
+ */
452
+
453
+ /**
454
+ * 捕获未处理异常并转换为 HaiResult。
455
+ *
456
+ * 虽然它不是 middleware / 鉴权 wrapper,但属于所有 procedure pipeline
457
+ * 都可能复用的“公共包装能力”,因此与 `buildHaiErrorBody()` 一起放在 helper 中。
458
+ *
459
+ * @param handler - 被包装的 procedure handler
460
+ * @returns 带异常保护的新 handler
461
+ *
462
+ * @example
463
+ * ```ts
464
+ * const safeHandler = serv.mapHaiError(myHandler)
465
+ * ```
466
+ */
467
+ declare function mapHaiError<TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
468
+
469
+ /**
470
+ * @h-ai/serv — requireAuth 默认实现
471
+ * @module pipelines/serv-pipeline-require-auth
472
+ */
473
+
474
+ /**
475
+ * 需要已验证会话的 procedure 包装器。
476
+ *
477
+ * `context.session` 由 `buildAuthContextFactory(verifyToken)`(或 `serv.createApp({ iam })` 自动启用)填充。
478
+ * token 不存在或无效(verifyToken 失败)时 `session` 均为 `undefined`,统一返回 401 UNAUTHORIZED。
479
+ * 内置调用 `mapHaiError`,无需外层再次封装。
480
+ *
481
+ * @param handler - 被包装的 procedure handler
482
+ * @returns 带认证检查的新 handler
483
+ *
484
+ * @example
485
+ * ```ts
486
+ * const protectedHandler = serv.requireAuth(myHandler)
487
+ * ```
488
+ */
489
+ declare function requireAuth<TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
490
+
491
+ /**
492
+ * @h-ai/serv — requirePermission 默认实现
493
+ * @module pipelines/serv-pipeline-require-permission
494
+ */
495
+
496
+ /**
497
+ * 需要指定权限的 procedure 包装器。
498
+ *
499
+ * 内置调用 `requireAuth`,在验证 Token 后进一步匹配 `session.permissions`。
500
+ * 拥有 {@link WILDCARD_PERMISSION}(`'*'`)的用户自动通过所有权限检查。
501
+ *
502
+ * @param permission - 所需权限字符串(如 `'iam.users.read'`)
503
+ * @param handler - 被包装的 procedure handler
504
+ * @returns 带权限检查的新 handler
505
+ *
506
+ * @example
507
+ * ```ts
508
+ * const adminHandler = serv.requirePermission('iam.users.write', myHandler)
509
+ * ```
510
+ */
511
+ declare function requirePermission<TInput, TOutput>(permission: string, handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
512
+
513
+ /**
514
+ * @h-ai/serv — requireRole 默认实现
515
+ * @module pipelines/serv-pipeline-require-role
516
+ */
517
+
518
+ /**
519
+ * 需要指定角色的 procedure 包装器。
520
+ *
521
+ * 内置调用 `requireAuth`,在验证 Token 后进一步匹配 `session.roles`。
522
+ * 拥有 {@link WILDCARD_ROLE}(`'*'`)的用户自动通过所有角色检查。
523
+ *
524
+ * **使用建议**:优先使用 {@link requirePermission}(基于行为的细粒度授权);
525
+ * `requireRole` 适用于按角色分流的粗粒度路由(如 `'admin'` 专属入口)。
526
+ *
527
+ * @param role - 所需角色字符串(如 `'admin'`)
528
+ * @param handler - 被包装的 procedure handler
529
+ * @returns 带角色检查的新 handler
530
+ *
531
+ * @example
532
+ * ```ts
533
+ * const adminOnly = serv.requireRole('admin', myHandler)
534
+ * ```
535
+ */
536
+ declare function requireRole<TInput, TOutput>(role: string, handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
537
+
538
+ /**
539
+ * @h-ai/serv — CORS middleware
540
+ *
541
+ * 统一处理 OPTIONS 预检与常规响应的 CORS 头注入,让应用层只保留 origin 白名单策略。
542
+ * @module serv-cors
543
+ */
544
+
545
+ /** serv CORS 配置。 */
546
+ interface ServCorsConfig {
547
+ /** 允许的 origin:`'*'`、单个字符串、数组或自定义匹配函数。默认 `'*'`。 */
548
+ readonly origin?: '*' | string | readonly string[] | ((origin: string) => boolean);
549
+ /** 允许的方法。默认 `GET/HEAD/PUT/PATCH/POST/DELETE/OPTIONS`。 */
550
+ readonly methods?: readonly string[];
551
+ /** 允许的请求头;若预检请求带 `Access-Control-Request-Headers`,优先回显该值。 */
552
+ readonly allowedHeaders?: readonly string[];
553
+ /** 暴露给浏览器 JS 的响应头。 */
554
+ readonly exposedHeaders?: readonly string[];
555
+ /** 是否允许携带 credentials(cookie / Authorization)。默认 `false`。 */
556
+ readonly credentials?: boolean;
557
+ /** 预检缓存秒数。默认 `86400`。 */
558
+ readonly maxAge?: number;
559
+ }
560
+ /** 创建可复用的 CORS middleware。 */
561
+ declare function cors(config?: ServCorsConfig): ServMiddleware;
524
562
 
525
563
  /**
526
564
  * @h-ai/serv — OpenAPI 文档
@@ -630,6 +668,7 @@ declare const serv: {
630
668
  resolveRequestLocale: typeof resolveRequestLocale;
631
669
  listen: typeof listen;
632
670
  toFetch: typeof toFetch;
671
+ cors: typeof cors;
633
672
  generateSpec: typeof generateSpec;
634
673
  /** 基于 contract 创建可注入 context 的 procedure builder(封装 `@orpc/server` 的 `implement`)。 */
635
674
  implement: typeof implement;
@@ -643,4 +682,4 @@ declare const serv: {
643
682
  m: (key: "$schema" | "serv_validationFailed" | "serv_validationRequired" | "serv_validationInvalid" | "serv_validationInvalidType" | "serv_validationStringMin" | "serv_validationStringMax" | "serv_validationNumberMin" | "serv_validationNumberMax" | "serv_validationArrayMin" | "serv_validationArrayMax" | "serv_validationTooSmall" | "serv_validationTooBig" | "serv_validationEmail" | "serv_validationUrl" | "serv_validationUuid" | "serv_validationEnum" | "serv_errorUnauthorized" | "serv_errorForbidden" | "serv_errorInternal" | "serv_storageKeyRequired" | "serv_storageKeyTooLong" | "serv_storageKeyIllegalChars" | "serv_storageKeyDotSegments" | "serv_iamCannotDeleteCurrentUser" | "serv_transportInvalidPayload" | "serv_transportUnknownClient" | "serv_transportClientIdRequired" | "serv_transportClientKeyNotFound" | "serv_transportDecryptFailed" | "serv_transportEncryptFailed" | "serv_transportKeyExchangeFailed", options?: _h_ai_core.MessageOptions) => string;
644
683
  };
645
684
 
646
- export { type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServDocsHttpConfig, type ServFetchHandler, type ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, ServSession, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
685
+ export { type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServDocsHttpConfig, type ServFetchHandler, type ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, ServSession, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };