@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 +20 -9
- package/dist/index.d.ts +259 -220
- package/dist/index.js +87 -2
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @h-ai/serv
|
|
2
2
|
|
|
3
|
-
> hai-framework 的
|
|
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) 创建
|
|
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 })` 注入
|
|
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.
|
|
245
|
-
3.
|
|
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
|
-
-
|
|
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)`:创建
|
|
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 {
|
|
5
|
-
import {
|
|
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
|
-
/**
|
|
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 —
|
|
210
|
+
* @h-ai/serv — HTTP app 装配
|
|
414
211
|
*
|
|
415
|
-
* 将 oRPC contract + procedures + http 配置组合为可运行的
|
|
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.
|
|
423
|
-
* 5.
|
|
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`
|
|
232
|
+
* - 注册位置固定在内置 `securityHeaders` 之后、`transport` / 业务路由之前
|
|
436
233
|
*/
|
|
437
234
|
interface ServMiddlewareMount {
|
|
438
235
|
readonly path?: string;
|
|
439
236
|
readonly middleware: ServMiddleware;
|
|
440
237
|
}
|
|
441
238
|
/**
|
|
442
|
-
*
|
|
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
|
|
271
|
+
* 自定义 HTTP middleware(当前内部由 Hono 承载,但应用 API 不暴露 Hono app)。
|
|
464
272
|
*
|
|
465
273
|
* 典型用途:请求日志、trace、指标、CORS、限流、租户头校验等 HTTP 层横切逻辑。
|
|
466
|
-
* 这些 middleware
|
|
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
|
-
* 创建并装配
|
|
314
|
+
* 创建并装配 HTTP API app。
|
|
505
315
|
*
|
|
506
316
|
* @param options - contract/procedures/http 三段式配置
|
|
507
|
-
* @returns
|
|
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>):
|
|
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 };
|